Opened 9 years ago
Last modified 6 years ago
#34053 new defect (bug)
HTTP API (Curl backend) inappropriately sends Content-Length header on POST requests made through a proxy server CONNECT
Reported by: |
|
Owned by: | |
---|---|---|---|
Milestone: | Priority: | normal | |
Severity: | normal | Version: | 4.3.1 |
Component: | HTTP API | Keywords: | |
Focuses: | administration | Cc: |
Description
When WordPress is configured to communicate with the outside world through a HTTP proxy server, using
define('WP_PROXY_HOST', 'some-proxy-server'); define('WP_PROXY_PORT', '3128');
in wp-config.php, HTTP POST requests that are over HTTPS, and pass through the proxy server, get a Content-Length header inserted in the CONNECT request, instead of in the headers of the POST request made inside the HTTPS tunnel.
On certain proxy servers configured to parse requests strictly, they will reject this outer CONNECT request with a HTTP 400 Bad Request, as the Content-Length header should not be there(?)
Here is an HTTP POST request and response, going via a HTTPS CONNECT request (in this case a request for Google's Recaptcha through the Contact Form 7 plugin) behind a BlueCoat proxy server configured with tolerant-request-parsing off:
CONNECT www.google.com:443 HTTP/1.1 Host: www.google.com:443 User-Agent: WordPress/4.3.1; https://staging.testvalley.hants.sch.uk Proxy-Connection: Keep-Alive Accept-Encoding: deflate;q=1.0, compress;q=0.5, gzip;q=0.5 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Content-Length: 1052 HTTP/1.1 400 Bad Request Cache-Control: no-cache Pragma: no-cache Content-Type: text/html; charset=utf-8 Proxy-Connection: close Connection: close Content-Length: 513 invalid_request: Your request could not be processed. Request could not be handled
To demonstrate this is not a plugin issue, a similar request (a check for core updates by clicking 'Check again' on the Updates page in WP-Admin) that is also affected:
CONNECT api.wordpress.org:443 HTTP/1.1 Host: api.wordpress.org:443 User-Agent: WordPress/4.3.1; https://staging.testvalley.hants.sch.uk Proxy-Connection: Keep-Alive Accept-Encoding: deflate;q=1.0, compress;q=0.5, gzip;q=0.5 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Content-Length: 193 HTTP/1.1 400 Bad Request Cache-Control: no-cache Pragma: no-cache Content-Type: text/html; charset=utf-8 Proxy-Connection: close Connection: close Content-Length: 513 invalid_request: Your request could not be processed. Request could not be handled
HTTP (not HTTPS) requests, including POST, proceed fine through the proxy. HTTPS GET requests also work correctly.
This seems to be an issue that derives from where includes/class-http.php injects the Content-Length header before dispatching the request to the chosen HTTP backend. This header injection does not cause issues with a HTTP POST request, but in the case of an HTTPS POST request going through the proxy, the Content-Length header ends up in the HTTPS CONNECT request to the proxy, rather than the actual request to the server that is wrapped inside the established tunnel.
Commenting out lines 273 and 274 of includes/class-http.php causes the above two requests to succeed.
Change History (4)
#2
@
9 years ago
We're seeing the same issue here. In our current case, HTTPS POST connections to https://www.google.com/recaptcha/api/siteverify (with content in the body) time out 100% of the time. When the POSTs are submitted without body content, the requests complete no problem.
Commenting out those lines in class-http.php has been the only fix that's worked for us.
#3
@
9 years ago
Actually, just discovered that tapping into the http_api_curl
action provides a functioning workaround.
<?php function bu_http_api_curl( $handle, $r, $url ) { if ( ! empty( $r['body'] ) || 'POST' == $r['method'] || 'PUT' == $r['method'] ) { unset($r['headers']['Content-Length']); curl_setopt( $handle, CURLOPT_HTTPHEADER, $r['headers'] ); } } add_action( 'http_api_curl', 'bu_http_api_curl', 10, 3 );
#4
@
9 years ago
Just started running into this as well. The proxy servers outright reject the request because the Content-Length header triggers a malformed warning. Currently, we're using a tiny mu-plugin fix similar to @awbauer's comment and are exploring the logistics of contributing a patch.
This really should be fixed since Content-Length is definitively non-standard.
This is a kind of wacky cURL behaviour.. but kind of makes sense.
cURL has fixed this within the last year in 7.37.0, and now has an extra set of options to control the behaviour:
http://curl.haxx.se/libcurl/c/CURLOPT_HTTPHEADER.html
http://curl.haxx.se/libcurl/c/CURLOPT_HEADEROPT.html
Unfortunately, PHP's cURL handler doesn't appear to support these yet. https://bugs.php.net/bug.php?id=68176
I'm not sure we should/want to remove the field, but it effectively breaks some proxy servers.