[ https://issues.apache.org/jira/browse/TS-2616?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Dimitry Andric updated TS-2616: ------------------------------- Attachment: fix-ts-2616-1.diff > Multiple Transfer-Encoding: chunked headers are not sanitized > ------------------------------------------------------------- > > Key: TS-2616 > URL: https://issues.apache.org/jira/browse/TS-2616 > Project: Traffic Server > Issue Type: Bug > Components: HTTP > Reporter: Dimitry Andric > Attachments: fix-ts-2616-1.diff > > > I encountered an origin server which sometimes sends multiple copies of the > same header. Obviously, this origin server is buggy, but I cannot touch it, > or modify it in any way. An example of such a request: > {noformat} > CLIENT> GET /example/request?token=12345678 HTTP/1.1 > CLIENT> Host: 192.168.42.42:8080 > CLIENT> Accept-Encoding: deflate, gzip > CLIENT> User-Agent: Mozilla/5.0 (Mac OS X; en-us) AppleWebKit/535.7.0 (KHTML, > like Gecko) Version/4.0.4 Mobile/7B334b Safari/535.7.0 > CLIENT> Accept: application/json > CLIENT> Referer: http://example.com/ > CLIENT> Client-ip: 127.0.0.1 > CLIENT> X-Forwarded-For: 127.0.0.1 > CLIENT> Via: http/1.1 > trafficserver.localdomain[FF000000000000000000000000000011] > (ApacheTrafficServer/4.0.2) > CLIENT> > SERVER> HTTP/1.1 200 OK > SERVER> Access-Control-Allow-Origin: * > SERVER> Access-Control-Allow-Headers : Content-Type > SERVER> Access-Control-Allow-Methods : GET, PUT, POST, DELETE > SERVER> Server: Apache-Coyote/1.1 > SERVER> Transfer-Encoding: chunked > SERVER> Content-Encoding: gzip > SERVER> Vary: Accept-Encoding > SERVER> Date: Tue, 04 Feb 2014 13:55:56 GMT > SERVER> Transfer-Encoding: chunked > SERVER> > SERVER> 14 > SERVER> .................... > SERVER> 0 > {noformat} > When this request is initially passed through by TrafficServer, the client > sees at least one "Transfer-Encoding: chunked" header, and assumes a chunked > transfer. Everything works fine. > After TrafficServer has cached the request, subsequent requests from the > client will receive a non-chunked transfer. However, while TrafficServer has > coalesced multiple headers into one, it removes only *one* instance of the > "chunked" value: > {noformat} > CLIENT> GET http://192.168.42.42:8080/example/request?token=12345678 HTTP/1.1 > CLIENT> Host: 192.168.42.42 > CLIENT> Accept-Encoding: deflate, gzip > CLIENT> Proxy-Connection: Keep-Alive > CLIENT> User-Agent: User-Agent: Mozilla/5.0 (Mac OS X; en-us) > AppleWebKit/535.7.0 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b > Safari/535.7.0 > CLIENT> Accept: application/json > CLIENT> Referer: http://example.com/ > CLIENT> > SERVER> HTTP/1.1 200 OK > SERVER> Access-Control-Allow-Origin: * > SERVER> Access-Control-Allow-Headers : Content-Type > SERVER> Access-Control-Allow-Methods : GET, PUT, POST, DELETE > SERVER> Server: ATS/4.0.2 > SERVER> Content-Encoding: gzip > SERVER> Vary: Accept-Encoding > SERVER> Date: Tue, 04 Feb 2014 13:57:36 GMT > SERVER> Transfer-Encoding: chunked > SERVER> Age: 1546 > SERVER> Content-Length: 20 > SERVER> Proxy-Connection: keep-alive > SERVER> > SERVER> .................... > {noformat} > Now the client sees both a "Content-Length: 20" and "Transfer-Encoding: > chunked" header, which should not happen. When e.g. curl is used as a > client, it will assume the transfer is chunked, and it attempts to parse the > initial bytes as a hexadecimal size line. Of course this fails, since there > is no real chunked transfer, and curl will abort the transfer with an error, > after reading approximately 256 kiB. > The problem is due to a piece of code in > HttpTransact::initialize_state_variables_from_response(), which scans the > Transfer-Encoding: headers, coalesces them, and removes any "chunked" value > from them. However, it does not handle multiple "chunked" values, as > explicitly noticed in a comment on line 5795: > {noformat} > 5720 void > 5721 HttpTransact::initialize_state_variables_from_response(State* s, > HTTPHdr* incoming_response) > 5722 { > ... > 5785 if (incoming_response->presence(MIME_PRESENCE_TRANSFER_ENCODING)) { > 5786 MIMEField *field = > incoming_response->field_find(MIME_FIELD_TRANSFER_ENCODING, > MIME_LEN_TRANSFER_ENCODING); > ... > 5792 while (enc_value) { > 5793 const char *wks_value = hdrtoken_string_to_wks(enc_value, > enc_val_len); > 5794 > 5795 // FIX ME: What is chunked appears more than once? Old > 5796 // code didn't deal with this so I don't either > 5797 if (wks_value == HTTP_VALUE_CHUNKED) { > ... > 5813 // Loop over the all the values in existing Trans-enc header > and > 5814 // copy the ones that aren't our chunked value to a new > field > 5815 while (new_enc_val) { > 5816 if (new_enc_val != enc_value) { > 5817 if (new_enc_field) { > 5818 new_enc_field->value_append(incoming_response->m_heap, > incoming_response->m_mime, new_enc_val, new_enc_len, true); > 5819 } else { > 5820 new_enc_field = incoming_response->field_create(); > 5821 incoming_response->field_value_set(new_enc_field, > new_enc_val, new_enc_len); > 5822 } > 5823 } > 5824 > 5825 new_enc_val = new_enc_iter.get_next(&new_enc_len); > 5826 } > {noformat} > The comparison on line 5816 compares the char pointers new_enc_val and > enc_value, to pluck out any non-"chunked" values, while it should really > compare the *contents* of the strings. Although it is probably better to > just re-use hdrtoken_string_to_wks(), and compare the result with the special > HTTP_VALUE_CHUNKED value, e.g.: > {noformat} > 5813 // Loop over the all the values in existing Trans-enc header > and > 5814 // copy the ones that aren't our chunked value to a new > field > 5815 while (new_enc_val) { > 5816 const char *new_wks_val = > hdrtoken_string_to_wks(new_enc_val, new_enc_len); > 5817 if (new_wks_val != HTTP_VALUE_CHUNKED) { > 5818 if (new_enc_field) { > 5819 new_enc_field->value_append(incoming_response->m_heap, > incoming_response->m_mime, new_enc_val, new_enc_len, true); > 5820 } else { > 5821 new_enc_field = incoming_response->field_create(); > 5822 incoming_response->field_value_set(new_enc_field, > new_enc_val, new_enc_len); > 5823 } > 5824 } > 5825 > 5826 new_enc_val = new_enc_iter.get_next(&new_enc_len); > 5827 } > {noformat} > After locally modifying my copy of TrafficServer 4.0.2 in the above fashion, > I have successfully used it to sanitize the multiple Transfer-Encoding: > headers from the origin server. > Please review the patch I will attach to this issue, which modifies > proxy/http/HttpTransact.cc as shown above. -- This message was sent by Atlassian JIRA (v6.2#6252)