Make WordPress Core

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's profile 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

Change History (1)

#1 @ramon fincken
2 years ago

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?

Note: See TracTickets for help on using tickets.