You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2018/03/06 18:16:21 UTC
[trafficserver] 03/04: Respond with 400 code when Content-Length
headers mismatch,
remove duplicate copies of the Content-Length header with exactly same
values,
and remove Content-Length headers if Transfer-Encoding header exists.
This is an automated email from the ASF dual-hosted git repository.
zwoop pushed a commit to branch 7.1.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit 2616e580de7d66b9098c464d503a049c7814e35a
Author: Bryan Call <bc...@apache.org>
AuthorDate: Mon Mar 5 16:21:18 2018 -0800
Respond with 400 code when Content-Length headers mismatch, remove
duplicate copies of the Content-Length header with exactly same values,
and remove Content-Length headers if Transfer-Encoding header exists.
(cherry picked from commit 44af6e5ba6d323354da0dd00f35aa7920d477805)
---
proxy/hdrs/HTTP.cc | 60 +++++++++++++++++++++++++++++++--
proxy/hdrs/HTTP.h | 1 +
tests/gold_tests/headers/syntax.test.py | 31 ++++++++++++++---
3 files changed, 85 insertions(+), 7 deletions(-)
diff --git a/proxy/hdrs/HTTP.cc b/proxy/hdrs/HTTP.cc
index c265e12..cb32ed7 100644
--- a/proxy/hdrs/HTTP.cc
+++ b/proxy/hdrs/HTTP.cc
@@ -967,8 +967,12 @@ http_parser_parse_req(HTTPParser *parser, HdrHeap *heap, HTTPHdrImpl *hh, const
}
ParseResult ret = mime_parser_parse(&parser->m_mime_parser, heap, hh->m_fields_impl, start, end, must_copy_strings, eof);
+ // If we're done with the main parse do some validation
if (ret == PARSE_RESULT_DONE) {
- ret = validate_hdr_host(hh); // if we're done with the main parse, check HOST.
+ ret = validate_hdr_host(hh); // check HOST header
+ }
+ if (ret == PARSE_RESULT_DONE) {
+ ret = validate_hdr_content_length(heap, hh);
}
return ret;
}
@@ -1118,8 +1122,12 @@ http_parser_parse_req(HTTPParser *parser, HdrHeap *heap, HTTPHdrImpl *hh, const
parser->m_parsing_http = false;
ParseResult ret = mime_parser_parse(&parser->m_mime_parser, heap, hh->m_fields_impl, start, end, must_copy_strings, eof);
+ // If we're done with the main parse do some validation
if (ret == PARSE_RESULT_DONE) {
- ret = validate_hdr_host(hh); // if we're done with the main parse, check HOST.
+ ret = validate_hdr_host(hh); // check HOST header
+ }
+ if (ret == PARSE_RESULT_DONE) {
+ ret = validate_hdr_content_length(heap, hh);
}
return ret;
}
@@ -1166,6 +1174,54 @@ validate_hdr_host(HTTPHdrImpl *hh)
return ret;
}
+ParseResult
+validate_hdr_content_length(HdrHeap *heap, HTTPHdrImpl *hh)
+{
+ MIMEField *content_length_field = mime_hdr_field_find(hh->m_fields_impl, MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
+
+ if (content_length_field) {
+ // RFC 7230 section 3.3.3:
+ // If a message is received with both a Transfer-Encoding and a
+ // Content-Length header field, the Transfer-Encoding overrides
+ // the Content-Length
+ if (mime_hdr_field_find(hh->m_fields_impl, MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING) != nullptr) {
+ // Delete all Content-Length headers
+ Debug("http", "Transfer-Encoding header and Content-Length headers the request, removing all Content-Length headers");
+ mime_hdr_field_delete(heap, hh->m_fields_impl, content_length_field);
+ return PARSE_RESULT_DONE;
+ }
+
+ // RFC 7230 section 3.3.3:
+ // If a message is received without Transfer-Encoding and with
+ // either multiple Content-Length header fields having differing
+ // field-values or a single Content-Length header field having an
+ // invalid value, then the message framing is invalid and the
+ // recipient MUST treat it as an unrecoverable error. If this is a
+ // request message, the server MUST respond with a 400 (Bad Request)
+ // status code and then close the connection
+ int content_length_len = 0;
+ const char *content_length_val = content_length_field->value_get(&content_length_len);
+
+ while (content_length_field->has_dups()) {
+ int content_length_len_2 = 0;
+ const char *content_length_val_2 = content_length_field->m_next_dup->value_get(&content_length_len_2);
+
+ if ((content_length_len != content_length_len_2) ||
+ (memcmp(content_length_val, content_length_val_2, content_length_len) != 0)) {
+ // Values are different, parse error
+ Debug("http", "Content-Length headers don't match, returning parse error");
+ return PARSE_RESULT_ERROR;
+ } else {
+ // Delete the duplicate since it has the same value
+ Debug("http", "Deleting duplicate Content-Length header");
+ mime_hdr_field_delete(heap, hh->m_fields_impl, content_length_field->m_next_dup, false);
+ }
+ }
+ }
+
+ return PARSE_RESULT_DONE;
+}
+
/*-------------------------------------------------------------------------
-------------------------------------------------------------------------*/
diff --git a/proxy/hdrs/HTTP.h b/proxy/hdrs/HTTP.h
index 3cd61a3..ff50e1b 100644
--- a/proxy/hdrs/HTTP.h
+++ b/proxy/hdrs/HTTP.h
@@ -445,6 +445,7 @@ void http_parser_clear(HTTPParser *parser);
ParseResult http_parser_parse_req(HTTPParser *parser, HdrHeap *heap, HTTPHdrImpl *hh, const char **start, const char *end,
bool must_copy_strings, bool eof, bool strict_uri_parsing);
ParseResult validate_hdr_host(HTTPHdrImpl *hh);
+ParseResult validate_hdr_content_length(HdrHeap *heap, HTTPHdrImpl *hh);
ParseResult http_parser_parse_resp(HTTPParser *parser, HdrHeap *heap, HTTPHdrImpl *hh, const char **start, const char *end,
bool must_copy_strings, bool eof);
diff --git a/tests/gold_tests/headers/syntax.test.py b/tests/gold_tests/headers/syntax.test.py
index e38b97e..2938304 100644
--- a/tests/gold_tests/headers/syntax.test.py
+++ b/tests/gold_tests/headers/syntax.test.py
@@ -64,21 +64,42 @@ tr = Test.AddTestRun()
tr.Processes.Default.StartBefore(server)
tr.Processes.Default.StartBefore(Test.Processes.ts)
tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port))
-tr.Processes.Default.Command = 'curl -s -D - -v --ipv4 --http1.1 -H " foo: bar" -H "Host: www.example.com" http://localhost:8080/'
+tr.Processes.Default.Command = 'curl -s -D - -v --ipv4 --http1.1 -H " foo: bar" -H "Host: www.example.com" http://localhost:8080/'
tr.Processes.Default.ReturnCode = 0
tr.Processes.Default.Streams.stdout = "syntax.200.gold"
tr.StillRunningAfter = ts
-# Test 2 - 400 Response
+# Test 2 - 400 Response - Single space after field name
tr = Test.AddTestRun()
-tr.Processes.Default.Command = 'curl -s -D - -v --ipv4 --http1.1 -H "foo : bar" -H "Host: www.example.com" http://localhost:8080/'
+tr.Processes.Default.Command = 'curl -s -D - -v --ipv4 --http1.1 -H "foo : bar" -H "Host: www.example.com" http://localhost:8080/'
tr.Processes.Default.ReturnCode = 0
tr.Processes.Default.Streams.stdout = "syntax.400.gold"
tr.StillRunningAfter = ts
-# Test 3 - 400 Response
+# Test 3 - 400 Response - Double space after field name
tr = Test.AddTestRun()
-tr.Processes.Default.Command = 'curl -s -D - -v --ipv4 --http1.1 -H "foo : bar" -H "Host: www.example.com" http://localhost:8080/'
+tr.Processes.Default.Command = 'curl -s -D - -v --ipv4 --http1.1 -H "foo : bar" -H "Host: www.example.com" http://localhost:8080/'
tr.Processes.Default.ReturnCode = 0
tr.Processes.Default.Streams.stdout = "syntax.400.gold"
tr.StillRunningAfter = ts
+
+# Test 4 - 400 Response - Three different Content-Length headers
+tr = Test.AddTestRun()
+tr.Processes.Default.Command = 'curl -s -D - -v --ipv4 --http1.1 -d "hello world" -H "Content-Length: 11" -H "Content-Length: 10" -H "Content-Length: 9" -H "Host: www.example.com" http://localhost:8080/'
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Streams.stdout = "syntax.400.gold"
+tr.StillRunningAfter = ts
+
+# Test 4 - 200 Response - Three same Content-Length headers
+tr = Test.AddTestRun()
+tr.Processes.Default.Command = 'curl -s -D - -v --ipv4 --http1.1 -d "hello world" -H "Content-Length: 11" -H "Content-Length: 11" -H "Content-Length: 11" -H "Host: www.example.com" http://localhost:8080/'
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Streams.stdout = "syntax.200.gold"
+tr.StillRunningAfter = ts
+
+# Test 4 - 200 Response - Three different Content-Length headers with a Transfer ecoding header
+tr = Test.AddTestRun()
+tr.Processes.Default.Command = 'curl -s -D - -v --ipv4 --http1.1 -d "hello world" -H "Transfer-Encoding: chunked" -H "Content-Length: 11" -H "Content-Length: 10" -H "Content-Length: 9" -H "Host: www.example.com" http://localhost:8080/'
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Streams.stdout = "syntax.200.gold"
+tr.StillRunningAfter = ts
--
To stop receiving notification emails like this one, please contact
zwoop@apache.org.