Skip to content

Allow empty header values. #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/protocol/http1/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ module HTTP1
HEADER = /\A(#{FIELD_NAME}):#{OWS}(?:(#{FIELD_VALUE})#{OWS})?\z/.freeze

VALID_FIELD_NAME = /\A#{FIELD_NAME}\z/.freeze
VALID_FIELD_VALUE = /\A#{FIELD_VALUE}\z/.freeze
VALID_FIELD_VALUE = /\A#{FIELD_VALUE}?\z/.freeze

DEFAULT_MAXIMUM_LINE_LENGTH = 8192

Expand Down
247 changes: 169 additions & 78 deletions test/protocol/http1/connection/headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,112 +9,203 @@
describe Protocol::HTTP1::Connection do
include_context ConnectionContext

let(:headers) {Array.new}
with "#write_response" do
before do
server.open!
end

before do
client.stream.write "GET / HTTP/1.1\r\nHost: localhost\r\n#{headers.join("\r\n")}\r\n\r\n"
client.stream.close
end
def validate_headers!(expected_headers = self.headers)
server.write_empty_body
client.open!

version, status, reason, headers, body = client.read_response("GET")

expect(headers).to be == headers
end

with "header that contains tab characters" do
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"
]}
with "a content-type header" do
let(:headers) {{"content-type" => "text/plain"}}

it "can parse the header" do
authority, method, target, version, headers, body = server.read_request
it "can parse the header" do
server.write_response("HTTP/1.1", 200, headers)

expect(headers).to have_keys(
"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"
)
validate_headers!
end
end
end

with "header that contains obsolete folding whitespace" do
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"
]}
with "an empty header" do
let(:headers) {{"nothing" => ""}}

it "rejects the request" do
expect do
server.read_request
end.to raise_exception(Protocol::HTTP1::BadHeader)
it "can parse the header" do
server.write_response("HTTP/1.1", 200, headers)

validate_headers!
end
end
end

with "header that contains invalid characters" do
let(:headers) {[
"user-agent: Mozilla\x00Hacker Browser"
]}
with "a header that contains tab characters" do
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"}}

it "can parse the header" do
server.write_response("HTTP/1.1", 200, headers)

it "rejects the request" do
expect do
server.read_request
end.to raise_exception(Protocol::HTTP1::BadHeader)
validate_headers!
end
end
end

with "header that contains invalid high characters" do
let(:headers) {[
"user-agent: Mozilla\x7FHacker Browser"
]}
with "a header that contains obsolete folding whitespace" do
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"}}

it "rejects the response" do
expect do
server.write_response("HTTP/1.1", 200, headers)
end.to raise_exception(Protocol::HTTP1::BadHeader)
end
end

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

expect(headers).to have_keys(
"user-agent" => be == "Mozilla\x7FHacker Browser"
)
it "rejects the response" do
expect do
server.write_response("HTTP/1.1", 200, headers)
end.to raise_exception(Protocol::HTTP1::BadHeader)
end
end
end

with "header that contains null character" do
let(:headers) {[
"user-agent: Mozilla\x00Hacker Browser"
]}
with "a header that contains invalid high characters" do
let(:headers) {{"user-agent" => "Mozilla\x7FHacker Browser"}}

it "allows the response" do
server.write_response("HTTP/1.1", 200, headers)

it "rejects the request" do
expect do
server.read_request
end.to raise_exception(Protocol::HTTP1::BadHeader)
validate_headers!
end
end
end

with "header that has empty value" do
let(:headers) {[
"user-agent: "
]}
with "#read_request" do
let(:headers) {Array.new}

before do
client.stream.write "GET / HTTP/1.1\r\nHost: localhost\r\n#{headers.join("\r\n")}\r\n\r\n"
client.stream.close
end

with "a header that contains tab characters" do
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"
]}

it "can parse the header" do
authority, method, target, version, headers, body = server.read_request
it "can parse the header" do
authority, method, target, version, headers, body = server.read_request

expect(headers).to have_keys(
"user-agent" => be == ""
)
expect(headers).to have_keys(
"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"
)
end
end
end

with "header that has invalid name" do
let(:headers) {[
"invalid name: value"
]}
with "a header that contains obsolete folding whitespace" do
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"
]}

it "rejects the request" do
expect do
server.read_request
end.to raise_exception(Protocol::HTTP1::BadHeader)
it "rejects the request" do
expect do
server.read_request
end.to raise_exception(Protocol::HTTP1::BadHeader)
end
end

with "a header that contains invalid characters" do
let(:headers) {[
"user-agent: Mozilla\x00Hacker Browser"
]}

it "rejects the request" do
expect do
server.read_request
end.to raise_exception(Protocol::HTTP1::BadHeader)
end
end

with "a header that contains invalid high characters" do
let(:headers) {[
"user-agent: Mozilla\x7FHacker Browser"
]}

it "allows the request" do
authority, method, target, version, headers, body = server.read_request

expect(headers).to have_keys(
"user-agent" => be == "Mozilla\x7FHacker Browser"
)
end
end

with "a header that contains null character" do
let(:headers) {[
"user-agent: Mozilla\x00Hacker Browser"
]}

it "rejects the request" do
expect do
server.read_request
end.to raise_exception(Protocol::HTTP1::BadHeader)
end
end

with "a header that has empty value but includes optional whitespace" do
let(:headers) {[
"user-agent: "
]}

it "can parse the header" do
authority, method, target, version, headers, body = server.read_request

expect(headers).to have_keys(
"user-agent" => be == ""
)
end
end

with "a header that has empty value" do
let(:headers) {[
"user-agent:"
]}

it "can parse the header" do
authority, method, target, version, headers, body = server.read_request

expect(headers).to have_keys(
"user-agent" => be == ""
)
end
end

with "a header that has invalid name" do
let(:headers) {[
"invalid name: value"
]}

it "rejects the request" do
expect do
server.read_request
end.to raise_exception(Protocol::HTTP1::BadHeader)
end
end
end

with "header that has empty name" do
let(:headers) {[
": value"
]}
with "a header that has empty name" do
let(:headers) {[
": value"
]}

it "rejects the request" do
expect do
server.read_request
end.to raise_exception(Protocol::HTTP1::BadHeader)
it "rejects the request" do
expect do
server.read_request
end.to raise_exception(Protocol::HTTP1::BadHeader)
end
end
end
end
Loading