Opened 9 years ago
Last modified 2 years ago
#32932 new defect (bug)
WP_Http::request hangs on badly behaving servers
Reported by: | Lutz Donnerhacke | Owned by: | |
---|---|---|---|
Milestone: | Priority: | normal | |
Severity: | normal | Version: | |
Component: | HTTP API | Keywords: | |
Focuses: | Cc: |
Description
Some plugin includes a call to "fetch_feed( 'https://wpml.org/feed/' )" which takes 300 seconds to complete. The whole backend hangs during this rending of the dashboard widget.
Tracing the problem down reveals, that the function WP_Http::request has a problem with the response from the server.
The server does answer the HTTP/1.0 request with an HTTP/1.1 response and 300 seconds timeout:
$ openssl s_client -connect wpml.org:443 SSL-Session: Protocol : TLSv1 Cipher : DHE-RSA-AES128-SHA Session-ID: 45...30 Session-ID-ctx: Master-Key: 68...4F Key-Arg : None Start Time: 1436367230 Timeout : 300 (sec) --- GET /feed/ HTTP/1.0 Host: wpml.org HTTP/1.1 302 Found ... [300 seconds to wait until the server closes the connection]
The WP_Http::request function does not handle this case correctly.
It located in this part of the code:
$header_length = 0; while ( ! feof( $handle ) && $keep_reading ) { $block = fread( $handle, $block_size ); $strResponse .= $block; if ( ! $bodyStarted && strpos( $strResponse, ... $header_length = strpos( $strResponse, ... $bodyStarted = true; } $keep_reading = ( ! $bodyStarted || !... }
fread() waits the default 10s timeout and returns nothing (after the initial two reads). The repeats 30 times accumulating to 300 seconds.
Count Avg.Time Tot.Time Name (linenumbers differ from the original) 1 301.201028 301.201028 wp-includes/class-http.php:889 1 0.595707 0.595707 -> 1065 1 0.000048 0.000048 -> 1067 1 0.000008 0.000008 -> 1075 1 0.000007 0.000007 -> 1084 1 0.000006 0.000006 -> 1131 32 0.000033 0.001070 -> 1134 32 9.386880 300.380167 -> 1136 1 0.000025 0.000025 -> 1145 1 0.000030 0.000030 -> 1148 1 0.000109 0.000109 -> 1153
The obvious solution is to stop reading if nothing is returned.
$header_length = 0; while ( ! feof( $handle ) && $keep_reading ) { $block = fread( $handle, $block_size ); $strResponse .= $block; if ( ! $bodyStarted && strpos( $strResponse, ... $header_length = strpos( $strResponse, ... $bodyStarted = true; } $keep_reading = ( ! $bodyStarted || !... + if(strlen($block) === 0) break; }
This solves the problem for the badly behaving servers (in this case the one from the plugin).
(From #meta1104
In the present ( I do not know if at the time of your writing ) you can give timeouts to http remote get. Would that solve this issue?