Skip to content

Commit ce515c5

Browse files
tomchristiepgjones
authored andcommitted
Preserve header casing
1 parent 3e14821 commit ce515c5

File tree

5 files changed

+22
-20
lines changed

5 files changed

+22
-20
lines changed

h11/_connection.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -560,27 +560,27 @@ def _clean_up_response_headers_for_sending(self, response):
560560
# but the HTTP spec says that if our peer does this then we have
561561
# to fix it instead of erroring out, so we'll accord the user the
562562
# same respect).
563-
headers = set_comma_header(headers, b"content-length", [])
563+
headers = set_comma_header(headers, b"Content-Length", [])
564564
if self.their_http_version is None or self.their_http_version < b"1.1":
565565
# Either we never got a valid request and are sending back an
566566
# error (their_http_version is None), so we assume the worst;
567567
# or else we did get a valid HTTP/1.0 request, so we know that
568568
# they don't understand chunked encoding.
569-
headers = set_comma_header(headers, b"transfer-encoding", [])
569+
headers = set_comma_header(headers, b"Transfer-Encoding", [])
570570
# This is actually redundant ATM, since currently we
571571
# unconditionally disable keep-alive when talking to HTTP/1.0
572572
# peers. But let's be defensive just in case we add
573573
# Connection: keep-alive support later:
574574
if self._request_method != b"HEAD":
575575
need_close = True
576576
else:
577-
headers = set_comma_header(headers, b"transfer-encoding", ["chunked"])
577+
headers = set_comma_header(headers, b"Transfer-Encoding", ["chunked"])
578578

579579
if not self._cstate.keep_alive or need_close:
580580
# Make sure Connection: close is set
581581
connection = set(get_comma_header(headers, b"connection"))
582582
connection.discard(b"keep-alive")
583583
connection.add(b"close")
584-
headers = set_comma_header(headers, b"connection", sorted(connection))
584+
headers = set_comma_header(headers, b"Connection", sorted(connection))
585585

586586
response.headers = headers

h11/_headers.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,14 @@ def get_comma_header(headers, name):
170170

171171
def set_comma_header(headers, name, new_values):
172172
# The header name `name` is expected to be lower-case bytes.
173+
raw_name = name
174+
name = name.lower()
173175
new_headers = []
174176
for found_raw_name, found_name, found_raw_value in headers.raw_items:
175177
if found_name != name:
176178
new_headers.append((found_raw_name, found_raw_value))
177179
for new_value in new_values:
178-
new_headers.append((name, new_value))
180+
new_headers.append((raw_name, new_value))
179181
return normalize_and_validate(new_headers)
180182

181183

h11/_writers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ def write_headers(headers, write):
4141
raw_items = headers.raw_items
4242
for raw_name, name, value in raw_items:
4343
if name == b"host":
44-
write(bytesmod(b"%s: %s\r\n", (name, value)))
44+
write(bytesmod(b"%s: %s\r\n", (raw_name, value)))
4545
for raw_name, name, value in raw_items:
4646
if name != b"host":
47-
write(bytesmod(b"%s: %s\r\n", (name, value)))
47+
write(bytesmod(b"%s: %s\r\n", (raw_name, value)))
4848
write(b"\r\n")
4949

5050

h11/tests/test_connection.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def test_Connection_basics_and_content_length():
9696
),
9797
)
9898
assert data == (
99-
b"GET / HTTP/1.1\r\n" b"host: example.com\r\n" b"content-length: 10\r\n\r\n"
99+
b"GET / HTTP/1.1\r\n" b"Host: example.com\r\n" b"Content-Length: 10\r\n\r\n"
100100
)
101101

102102
for conn in p.conns:
@@ -113,7 +113,7 @@ def test_Connection_basics_and_content_length():
113113
assert data == b"HTTP/1.1 100 \r\n\r\n"
114114

115115
data = p.send(SERVER, Response(status_code=200, headers=[("Content-Length", "11")]))
116-
assert data == b"HTTP/1.1 200 \r\ncontent-length: 11\r\n\r\n"
116+
assert data == b"HTTP/1.1 200 \r\nContent-Length: 11\r\n\r\n"
117117

118118
for conn in p.conns:
119119
assert conn.states == {CLIENT: SEND_BODY, SERVER: SEND_BODY}
@@ -243,7 +243,7 @@ def test_server_talking_to_http10_client():
243243
# We automatically Connection: close back at them
244244
assert (
245245
c.send(Response(status_code=200, headers=[]))
246-
== b"HTTP/1.1 200 \r\nconnection: close\r\n\r\n"
246+
== b"HTTP/1.1 200 \r\nConnection: close\r\n\r\n"
247247
)
248248

249249
assert c.send(Data(data=b"12345")) == b"12345"
@@ -303,7 +303,7 @@ def test_automatic_transfer_encoding_in_response():
303303
receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n")
304304
assert (
305305
c.send(Response(status_code=200, headers=user_headers))
306-
== b"HTTP/1.1 200 \r\nconnection: close\r\n\r\n"
306+
== b"HTTP/1.1 200 \r\nConnection: close\r\n\r\n"
307307
)
308308
assert c.send(Data(data=b"12345")) == b"12345"
309309

@@ -876,7 +876,7 @@ def test_errors():
876876
if role is SERVER:
877877
assert (
878878
c.send(Response(status_code=400, headers=[]))
879-
== b"HTTP/1.1 400 \r\nconnection: close\r\n\r\n"
879+
== b"HTTP/1.1 400 \r\nConnection: close\r\n\r\n"
880880
)
881881

882882
# After an error sending, you can no longer send
@@ -988,14 +988,14 @@ def setup(method, http_version):
988988
c = setup(method, b"1.1")
989989
assert (
990990
c.send(Response(status_code=200, headers=[])) == b"HTTP/1.1 200 \r\n"
991-
b"transfer-encoding: chunked\r\n\r\n"
991+
b"Transfer-Encoding: chunked\r\n\r\n"
992992
)
993993

994994
# No Content-Length, HTTP/1.0 peer, frame with connection: close
995995
c = setup(method, b"1.0")
996996
assert (
997997
c.send(Response(status_code=200, headers=[])) == b"HTTP/1.1 200 \r\n"
998-
b"connection: close\r\n\r\n"
998+
b"Connection: close\r\n\r\n"
999999
)
10001000

10011001
# Content-Length + Transfer-Encoding, TE wins
@@ -1011,7 +1011,7 @@ def setup(method, http_version):
10111011
)
10121012
)
10131013
== b"HTTP/1.1 200 \r\n"
1014-
b"transfer-encoding: chunked\r\n\r\n"
1014+
b"Transfer-Encoding: chunked\r\n\r\n"
10151015
)
10161016

10171017

h11/tests/test_io.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@
3131
target="/a",
3232
headers=[("Host", "foo"), ("Connection", "close")],
3333
),
34-
b"GET /a HTTP/1.1\r\nhost: foo\r\nconnection: close\r\n\r\n",
34+
b"GET /a HTTP/1.1\r\nHost: foo\r\nConnection: close\r\n\r\n",
3535
),
3636
(
3737
(SERVER, SEND_RESPONSE),
3838
Response(status_code=200, headers=[("Connection", "close")], reason=b"OK"),
39-
b"HTTP/1.1 200 OK\r\nconnection: close\r\n\r\n",
39+
b"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n",
4040
),
4141
(
4242
(SERVER, SEND_RESPONSE),
@@ -48,7 +48,7 @@
4848
InformationalResponse(
4949
status_code=101, headers=[("Upgrade", "websocket")], reason=b"Upgrade"
5050
),
51-
b"HTTP/1.1 101 Upgrade\r\nupgrade: websocket\r\n\r\n",
51+
b"HTTP/1.1 101 Upgrade\r\nUpgrade: websocket\r\n\r\n",
5252
),
5353
(
5454
(SERVER, SEND_RESPONSE),
@@ -435,7 +435,7 @@ def test_ChunkedWriter():
435435

436436
assert (
437437
dowrite(w, EndOfMessage(headers=[("Etag", "asdf"), ("a", "b")]))
438-
== b"0\r\netag: asdf\r\na: b\r\n\r\n"
438+
== b"0\r\nEtag: asdf\r\na: b\r\n\r\n"
439439
)
440440

441441

@@ -503,5 +503,5 @@ def test_host_comes_first():
503503
tw(
504504
write_headers,
505505
normalize_and_validate([("foo", "bar"), ("Host", "example.com")]),
506-
b"host: example.com\r\nfoo: bar\r\n\r\n",
506+
b"Host: example.com\r\nfoo: bar\r\n\r\n",
507507
)

0 commit comments

Comments
 (0)