Skip to content

Commit bfbbe12

Browse files
Allow empty headers. (#46)
1 parent 410ad4b commit bfbbe12

File tree

2 files changed

+170
-79
lines changed

2 files changed

+170
-79
lines changed

lib/protocol/http1/connection.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ module HTTP1
4343
HEADER = /\A(#{FIELD_NAME}):#{OWS}(?:(#{FIELD_VALUE})#{OWS})?\z/.freeze
4444

4545
VALID_FIELD_NAME = /\A#{FIELD_NAME}\z/.freeze
46-
VALID_FIELD_VALUE = /\A#{FIELD_VALUE}\z/.freeze
46+
VALID_FIELD_VALUE = /\A#{FIELD_VALUE}?\z/.freeze
4747

4848
DEFAULT_MAXIMUM_LINE_LENGTH = 8192
4949

test/protocol/http1/connection/headers.rb

+169-78
Original file line numberDiff line numberDiff line change
@@ -9,112 +9,203 @@
99
describe Protocol::HTTP1::Connection do
1010
include_context ConnectionContext
1111

12-
let(:headers) {Array.new}
12+
with "#write_response" do
13+
before do
14+
server.open!
15+
end
1316

14-
before do
15-
client.stream.write "GET / HTTP/1.1\r\nHost: localhost\r\n#{headers.join("\r\n")}\r\n\r\n"
16-
client.stream.close
17-
end
17+
def validate_headers!(expected_headers = self.headers)
18+
server.write_empty_body
19+
client.open!
20+
21+
version, status, reason, headers, body = client.read_response("GET")
22+
23+
expect(headers).to be == headers
24+
end
1825

19-
with "header that contains tab characters" do
20-
let(:headers) {[
21-
"user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) \t\t\tChrome/55.0.2883.95 Safari/537.36"
22-
]}
26+
with "a content-type header" do
27+
let(:headers) {{"content-type" => "text/plain"}}
2328

24-
it "can parse the header" do
25-
authority, method, target, version, headers, body = server.read_request
29+
it "can parse the header" do
30+
server.write_response("HTTP/1.1", 200, headers)
2631

27-
expect(headers).to have_keys(
28-
"user-agent" => be == "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) \t\t\tChrome/55.0.2883.95 Safari/537.36"
29-
)
32+
validate_headers!
33+
end
3034
end
31-
end
3235

33-
with "header that contains obsolete folding whitespace" do
34-
let(:headers) {[
35-
"user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko)\n\tChrome/55.0.2883.95 Safari/537.36"
36-
]}
36+
with "an empty header" do
37+
let(:headers) {{"nothing" => ""}}
3738

38-
it "rejects the request" do
39-
expect do
40-
server.read_request
41-
end.to raise_exception(Protocol::HTTP1::BadHeader)
39+
it "can parse the header" do
40+
server.write_response("HTTP/1.1", 200, headers)
41+
42+
validate_headers!
43+
end
4244
end
43-
end
4445

45-
with "header that contains invalid characters" do
46-
let(:headers) {[
47-
"user-agent: Mozilla\x00Hacker Browser"
48-
]}
46+
with "a header that contains tab characters" do
47+
let(:headers) {{"user-agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) \t\t\tChrome/55.0.2883.95 Safari/537.36"}}
48+
49+
it "can parse the header" do
50+
server.write_response("HTTP/1.1", 200, headers)
4951

50-
it "rejects the request" do
51-
expect do
52-
server.read_request
53-
end.to raise_exception(Protocol::HTTP1::BadHeader)
52+
validate_headers!
53+
end
5454
end
55-
end
5655

57-
with "header that contains invalid high characters" do
58-
let(:headers) {[
59-
"user-agent: Mozilla\x7FHacker Browser"
60-
]}
56+
with "a header that contains obsolete folding whitespace" do
57+
let(:headers) {{"user-agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko)\n\tChrome/55.0.2883.95 Safari/537.36"}}
58+
59+
it "rejects the response" do
60+
expect do
61+
server.write_response("HTTP/1.1", 200, headers)
62+
end.to raise_exception(Protocol::HTTP1::BadHeader)
63+
end
64+
end
6165

62-
it "allows the request" do
63-
authority, method, target, version, headers, body = server.read_request
66+
with "a header that contains invalid characters" do
67+
let(:headers) {{"user-agent" => "Mozilla\x00Hacker Browser"}}
6468

65-
expect(headers).to have_keys(
66-
"user-agent" => be == "Mozilla\x7FHacker Browser"
67-
)
69+
it "rejects the response" do
70+
expect do
71+
server.write_response("HTTP/1.1", 200, headers)
72+
end.to raise_exception(Protocol::HTTP1::BadHeader)
73+
end
6874
end
69-
end
7075

71-
with "header that contains null character" do
72-
let(:headers) {[
73-
"user-agent: Mozilla\x00Hacker Browser"
74-
]}
76+
with "a header that contains invalid high characters" do
77+
let(:headers) {{"user-agent" => "Mozilla\x7FHacker Browser"}}
78+
79+
it "allows the response" do
80+
server.write_response("HTTP/1.1", 200, headers)
7581

76-
it "rejects the request" do
77-
expect do
78-
server.read_request
79-
end.to raise_exception(Protocol::HTTP1::BadHeader)
82+
validate_headers!
83+
end
8084
end
8185
end
8286

83-
with "header that has empty value" do
84-
let(:headers) {[
85-
"user-agent: "
86-
]}
87+
with "#read_request" do
88+
let(:headers) {Array.new}
89+
90+
before do
91+
client.stream.write "GET / HTTP/1.1\r\nHost: localhost\r\n#{headers.join("\r\n")}\r\n\r\n"
92+
client.stream.close
93+
end
94+
95+
with "a header that contains tab characters" do
96+
let(:headers) {[
97+
"user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) \t\t\tChrome/55.0.2883.95 Safari/537.36"
98+
]}
8799

88-
it "can parse the header" do
89-
authority, method, target, version, headers, body = server.read_request
100+
it "can parse the header" do
101+
authority, method, target, version, headers, body = server.read_request
90102

91-
expect(headers).to have_keys(
92-
"user-agent" => be == ""
93-
)
103+
expect(headers).to have_keys(
104+
"user-agent" => be == "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) \t\t\tChrome/55.0.2883.95 Safari/537.36"
105+
)
106+
end
94107
end
95-
end
96108

97-
with "header that has invalid name" do
98-
let(:headers) {[
99-
"invalid name: value"
100-
]}
109+
with "a header that contains obsolete folding whitespace" do
110+
let(:headers) {[
111+
"user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko)\n\tChrome/55.0.2883.95 Safari/537.36"
112+
]}
101113

102-
it "rejects the request" do
103-
expect do
104-
server.read_request
105-
end.to raise_exception(Protocol::HTTP1::BadHeader)
114+
it "rejects the request" do
115+
expect do
116+
server.read_request
117+
end.to raise_exception(Protocol::HTTP1::BadHeader)
118+
end
119+
end
120+
121+
with "a header that contains invalid characters" do
122+
let(:headers) {[
123+
"user-agent: Mozilla\x00Hacker Browser"
124+
]}
125+
126+
it "rejects the request" do
127+
expect do
128+
server.read_request
129+
end.to raise_exception(Protocol::HTTP1::BadHeader)
130+
end
131+
end
132+
133+
with "a header that contains invalid high characters" do
134+
let(:headers) {[
135+
"user-agent: Mozilla\x7FHacker Browser"
136+
]}
137+
138+
it "allows the request" do
139+
authority, method, target, version, headers, body = server.read_request
140+
141+
expect(headers).to have_keys(
142+
"user-agent" => be == "Mozilla\x7FHacker Browser"
143+
)
144+
end
145+
end
146+
147+
with "a header that contains null character" do
148+
let(:headers) {[
149+
"user-agent: Mozilla\x00Hacker Browser"
150+
]}
151+
152+
it "rejects the request" do
153+
expect do
154+
server.read_request
155+
end.to raise_exception(Protocol::HTTP1::BadHeader)
156+
end
157+
end
158+
159+
with "a header that has empty value but includes optional whitespace" do
160+
let(:headers) {[
161+
"user-agent: "
162+
]}
163+
164+
it "can parse the header" do
165+
authority, method, target, version, headers, body = server.read_request
166+
167+
expect(headers).to have_keys(
168+
"user-agent" => be == ""
169+
)
170+
end
171+
end
172+
173+
with "a header that has empty value" do
174+
let(:headers) {[
175+
"user-agent:"
176+
]}
177+
178+
it "can parse the header" do
179+
authority, method, target, version, headers, body = server.read_request
180+
181+
expect(headers).to have_keys(
182+
"user-agent" => be == ""
183+
)
184+
end
185+
end
186+
187+
with "a header that has invalid name" do
188+
let(:headers) {[
189+
"invalid name: value"
190+
]}
191+
192+
it "rejects the request" do
193+
expect do
194+
server.read_request
195+
end.to raise_exception(Protocol::HTTP1::BadHeader)
196+
end
106197
end
107-
end
108198

109-
with "header that has empty name" do
110-
let(:headers) {[
111-
": value"
112-
]}
199+
with "a header that has empty name" do
200+
let(:headers) {[
201+
": value"
202+
]}
113203

114-
it "rejects the request" do
115-
expect do
116-
server.read_request
117-
end.to raise_exception(Protocol::HTTP1::BadHeader)
204+
it "rejects the request" do
205+
expect do
206+
server.read_request
207+
end.to raise_exception(Protocol::HTTP1::BadHeader)
208+
end
118209
end
119210
end
120211
end

0 commit comments

Comments
 (0)