Skip to content
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
24 changes: 24 additions & 0 deletions proxy/hdrs/HTTP.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1781,6 +1781,30 @@ HTTPHdr::url_printed_length(unsigned normalization_flags)
return zret;
}

// Look for headers that the proxy will need to be able to process
// Return false if the proxy does not know how to process the header
// Currently just looking at TRANSFER_ENCODING. The proxy only knows how to
// process the chunked action
bool
HTTPHdr::check_hdr_implements()
{
bool retval = true;
MIMEField *transfer_encode =
mime_hdr_field_find(this->m_http->m_fields_impl, MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING);
if (transfer_encode) {
int len;
const char *val;
do {
val = transfer_encode->value_get(&len);
if (len != 7 || 0 != strncasecmp(val, "chunked", len)) {
retval = false;
}
transfer_encode = transfer_encode->m_next_dup;
} while (retval && transfer_encode);
}
return retval;
}

/***********************************************************************
* *
* M A R S H A L I N G *
Expand Down
2 changes: 2 additions & 0 deletions proxy/hdrs/HTTP.h
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,8 @@ class HTTPHdr : public MIMEHdr
size_t max_request_line_size = UINT16_MAX, size_t max_hdr_field_size = UINT16_MAX);
ParseResult parse_resp(HTTPParser *parser, IOBufferReader *r, int *bytes_used, bool eof);

bool check_hdr_implements();

public:
// Utility routines
bool is_cache_control_set(const char *cc_directive_wks);
Expand Down
13 changes: 13 additions & 0 deletions proxy/http/HttpSM.cc
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,12 @@ HttpSM::state_read_client_request_header(int event, void *data)
case PARSE_RESULT_DONE:
SMDebug("http", "[%" PRId64 "] done parsing client request header", sm_id);

if (!t_state.hdr_info.client_request.check_hdr_implements()) {
t_state.http_return_code = HTTP_STATUS_NOT_IMPLEMENTED;
call_transact_and_set_next_state(HttpTransact::BadRequest);
break;
}

if (_from_early_data) {
// Only allow early data for safe methods defined in RFC7231 Section 4.2.1.
// https://tools.ietf.org/html/rfc7231#section-4.2.1
Expand Down Expand Up @@ -2051,6 +2057,13 @@ HttpSM::state_read_server_response_header(int event, void *data)
// fallthrough

case PARSE_RESULT_DONE:

if (!t_state.hdr_info.server_response.check_hdr_implements()) {
t_state.http_return_code = HTTP_STATUS_BAD_GATEWAY;
call_transact_and_set_next_state(HttpTransact::BadRequest);
break;
}

SMDebug("http_seq", "Done parsing server response header");

// Now that we know that we have all of the origin server
Expand Down
4 changes: 4 additions & 0 deletions proxy/http/HttpTransact.cc
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,10 @@ HttpTransact::BadRequest(State *s)
status = s->http_return_code;
reason = "URI Too Long";
break;
case HTTP_STATUS_NOT_IMPLEMENTED:
status = s->http_return_code;
reason = "Field not implemented";
body_factory_template = "transcoding#unsupported";
default:
break;
}
Expand Down
71 changes: 71 additions & 0 deletions tests/gold_tests/chunked_encoding/bad_chunked_encoding.test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'''
'''
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os

Test.Summary = '''
Test unsupported values for chunked_encoding
'''

Test.ContinueOnFail = True

# Define default ATS
ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=False)
server = Test.MakeOriginServer("server")

testName = ""
request_header = {"headers": "POST /case1 HTTP/1.1\r\nHost: www.example.com\r\nuuid:1\r\n\r\n",
"timestamp": "1469733493.993",
"body": "stuff"
}
response_header = {"headers": "HTTP/1.1 200 OK\r\nServer: uServer\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\n",
"timestamp": "1469733493.993",
"body": "more stuff"}

server.addResponse("sessionlog.json", request_header, response_header)

ts.Disk.records_config.update({'proxy.config.diags.debug.enabled': 0,
'proxy.config.diags.debug.tags': 'http'})

ts.Disk.remap_config.AddLine(
'map / http://127.0.0.1:{0}'.format(server.Variables.Port)
)

# HTTP1.1 POST: www.example.com/case1 with gzip transfer-encoding
tr = Test.AddTestRun()
tr.TimeOut = 5
tr.Processes.Default.Command = 'curl -H "host: example.com" -H "transfer-encoding: gzip" -d "stuff" http://127.0.0.1:{0}/case1 --verbose'.format(
ts.Variables.port)
tr.Processes.Default.ReturnCode = 0
tr.Processes.Default.StartBefore(server)
tr.Processes.Default.StartBefore(Test.Processes.ts)
tr.Processes.Default.Streams.All = Testers.ContainsExpression("501 Field not implemented", "Should fail")
tr.Processes.Default.Streams.All = Testers.ExcludesExpression("200 OK", "Should not succeed")
tr.StillRunningAfter = server
tr.StillRunningAfter = ts

# HTTP1.1 POST: www.example.com/case1 with gzip and chunked transfer-encoding
tr = Test.AddTestRun()
tr.TimeOut = 5
tr.Processes.Default.Command = 'curl -H "host: example.com" -H "transfer-encoding: gzip" -H "transfer-encoding: chunked" -d "stuff" http://127.0.0.1:{0}/case1 --verbose'.format(
ts.Variables.port)
tr.Processes.Default.ReturnCode = 0
tr.Processes.Default.Streams.All = Testers.ContainsExpression("501 Field not implemented", "Should fail")
tr.Processes.Default.Streams.All = Testers.ExcludesExpression("200 OK", "Should not succeed")
tr.StillRunningAfter = server
tr.StillRunningAfter = ts