WordPress.org

Make WordPress Core

Ticket #9072: 9072.patch

File 9072.patch, 11.6 KB (added by jacobsantos, 8 years ago)

Tested and working patch for Sockets. Will need a bit more testing, but it does handle requests and appears to work fine.

  • class-http.php

     
    9292                        if ( true === WP_Http_ExtHttp::test($args) ) {
    9393                                $working_transport['exthttp'] = new WP_Http_ExtHttp();
    9494                                $blocking_transport[] = &$working_transport['exthttp'];
    95                         } else if ( true === WP_Http_Curl::test($args) ) {
     95                        }
     96                        if ( true === WP_Http_Curl::test($args) ) {
    9697                                $working_transport['curl'] = new WP_Http_Curl();
    9798                                $blocking_transport[] = &$working_transport['curl'];
    98                         } else if ( true === WP_Http_Streams::test($args) ) {
     99                        }
     100                        if ( true === WP_Http_Sockets::test($args) ) {
     101                                $working_transport['sockets'] = new WP_Http_Sockets();
     102                                $blocking_transport[] = &$working_transport['sockets'];
     103                        }
     104                        if ( true === WP_Http_Streams::test($args) ) {
    99105                                $working_transport['streams'] = new WP_Http_Streams();
    100106                                $blocking_transport[] = &$working_transport['streams'];
    101                         } else if ( true === WP_Http_Fopen::test($args) ) {
     107                        }
     108                        if ( true === WP_Http_Fopen::test($args) ) {
    102109                                $working_transport['fopen'] = new WP_Http_Fopen();
    103110                                $blocking_transport[] = &$working_transport['fopen'];
    104                         } else if ( true === WP_Http_Fsockopen::test($args) ) {
     111                        }
     112                        if ( true === WP_Http_Fsockopen::test($args) ) {
    105113                                $working_transport['fsockopen'] = new WP_Http_Fsockopen();
    106114                                $blocking_transport[] = &$working_transport['fsockopen'];
    107115                        }
     
    142150                        if ( true === WP_Http_ExtHttp::test($args) ) {
    143151                                $working_transport['exthttp'] = new WP_Http_ExtHttp();
    144152                                $blocking_transport[] = &$working_transport['exthttp'];
    145                         } else if ( true === WP_Http_Curl::test($args) ) {
     153                        }
     154                        if ( true === WP_Http_Curl::test($args) ) {
    146155                                $working_transport['curl'] = new WP_Http_Curl();
    147156                                $blocking_transport[] = &$working_transport['curl'];
    148                         } else if ( true === WP_Http_Streams::test($args) ) {
     157                        }
     158                        if ( true === WP_Http_Sockets::test($args) ) {
     159                                $working_transport['sockets'] = new WP_Http_Sockets();
     160                                $blocking_transport[] = &$working_transport['sockets'];
     161                        }
     162                        if ( true === WP_Http_Streams::test($args) ) {
    149163                                $working_transport['streams'] = new WP_Http_Streams();
    150164                                $blocking_transport[] = &$working_transport['streams'];
    151                         } else if ( true === WP_Http_Fsockopen::test($args) ) {
     165                        }
     166                        if ( true === WP_Http_Fsockopen::test($args) ) {
    152167                                $working_transport['fsockopen'] = new WP_Http_Fsockopen();
    153168                                $blocking_transport[] = &$working_transport['fsockopen'];
    154169                        }
     
    11101125}
    11111126
    11121127/**
     1128 * HTTP request method uses PHP5 sockets to retrieve the url.
     1129 *
     1130 * Actually, this works pretty well. It supports SSL, nonblocking, quick, handles redirection and,
     1131 * if cURL is not available and on PHP5, this will be preferred.
     1132 *
     1133 * @package WordPress
     1134 * @subpackage HTTP
     1135 * @since {unknown}
     1136 */
     1137class WP_Http_Sockets {
     1138        /**
     1139         * Send a HTTP request to a URI using streams with stream_socket_client().
     1140         *
     1141         * @access public
     1142         * @since {unknown}
     1143         *
     1144         * @param string $url
     1145         * @param str|array $args Optional. Override the defaults.
     1146         * @return array 'headers', 'body', 'cookies' and 'response' keys.
     1147         */
     1148        function request($url, $args = array()) {
     1149                $defaults = array(
     1150                        'method' => 'GET', 'timeout' => 5,
     1151                        'redirection' => 5, 'httpversion' => '1.0',
     1152                        'blocking' => true,
     1153                        'headers' => array(), 'body' => null, 'cookies' => array()
     1154                );
     1155
     1156                $r = wp_parse_args( $args, $defaults );
     1157
     1158                // Construct Cookie: header if any cookies are set
     1159                WP_Http::buildCookieHeader( $r );
     1160
     1161                if ( isset($r['headers']['User-Agent']) ) {
     1162                        $r['user-agent'] = $r['headers']['User-Agent'];
     1163                        unset($r['headers']['User-Agent']);
     1164                } else if( isset($r['headers']['user-agent']) ) {
     1165                        $r['user-agent'] = $r['headers']['user-agent'];
     1166                        unset($r['headers']['user-agent']);
     1167                }
     1168
     1169                $arrURL = parse_url($url);
     1170
     1171                // Not the best check for URLs, but works for the purposes of what we are using it.
     1172                if ( false === $arrURL )
     1173                        return new WP_Error('http_request_failed', sprintf(__('Malformed URL: %s'), $url));
     1174
     1175                // Only do this check once.
     1176                if ( ! isset($r['ssl']) )
     1177                        $r['ssl'] = $arrURL['scheme'] == 'https' || $arrURL['scheme'] == 'ssl';
     1178
     1179                // Nonblocking only works for SSL PHP 5.2.11+, might not work on Windows.
     1180                // http://bugs.php.net/48182
     1181                if( $r['ssl'] && ! $r['blocking'] && version_compare(PHP_VERSION, '5.2.11', '<') )
     1182                        return new WP_Error('http_request_failed', __('Not possible to do nonblocking request with SSL.'));
     1183
     1184                // Create socket context. You only have ssl and socket transports. Socket has few options,
     1185                // none that matter for HTTP requests. SSL has quite a few and we set those later.
     1186                $context = stream_context_create();
     1187
     1188                $host = "tcp://".$arrURL['host'].':'.( isset($arrURL['port']) ? $arrURL['port'] : '80' );
     1189
     1190                if ( $r['ssl'] && extension_loaded('openssl') && in_array( 'ssl', stream_get_transports() ) ) {
     1191                        $host = "ssl://".$arrURL['host'].':'.( isset($arrURL['port']) ? $arrURL['port'] : '443' );
     1192
     1193                        $is_local = isset($r['local']) && $r['local'];
     1194                        $ssl_verify = isset($r['sslverify']) && $r['sslverify'];
     1195                        if ( $is_local )
     1196                                $ssl_verify = apply_filters('https_local_ssl_verify', $ssl_verify);
     1197                        elseif ( ! $is_local )
     1198                                $ssl_verify = apply_filters('https_ssl_verify', $ssl_verify);
     1199
     1200                        stream_context_set_option($context, 'ssl', 'verify_peer', $ssl_verify);
     1201                        stream_context_set_option($context, 'ssl', 'verify_host', $ssl_verify);
     1202                }
     1203
     1204                // This is the message body for the request, it includes the header and the body if available.
     1205                // It works similar to Fsockopen, in that it doesn't handle HTTP protocol directly.
     1206                $request_message = $this->_build_request_message($r, $url, $arrURL);
     1207
     1208                // Holds the error number.
     1209                $error_num = 0;
     1210
     1211                // Holds the error string.
     1212                $error_string = '';
     1213
     1214                $proxy = new WP_HTTP_Proxy();
     1215
     1216                $flags = STREAM_CLIENT_CONNECT;
     1217                if ( ! $r['blocking'] )
     1218                        $flags = STREAM_CLIENT_ASYNC_CONNECT | STREAM_CLIENT_CONNECT;
     1219
     1220                // stream_socket_client only supports tcp and udp, change to tcp for HTTP protocol.
     1221                if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) )
     1222                        $handle = stream_socket_client( $proxy->host().':'.$proxy->port(), $error_num, $error_string, $r['timeout'], $flags, $context );
     1223                else
     1224                        $handle = stream_socket_client( $host, $error_num, $error_string, $r['timeout'], $flags, $context );
     1225
     1226                if ( ! $handle )
     1227                        return new WP_Error('http_request_failed', sprintf(__('Could not open handle for stream_socket_client() to %s'), $url));
     1228
     1229                $timeout = (int) floor( $r['timeout'] );
     1230                $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000;
     1231
     1232                if ( ! $r['blocking'] ) {
     1233                        stream_set_blocking($handle, 0);
     1234                        $read = $except = null;
     1235                        $write = array($handle);
     1236                        while ( ! empty( $request_message ) ) {
     1237                                $nonblocking = stream_select($read, $write, $except, $timeout, $utimeout);
     1238
     1239                                if ( $nonblocking === false )
     1240                                        return new WP_Error('http_request_failed', sprintf(__('Could not handle nonblocking request to %s'), $url));
     1241
     1242                                // Short requests should complete after the first try, but longer ones will only
     1243                                // send so much before kicking it back out.
     1244                                $bytes_written = fwrite( $handle, $request_message, strlen($request_message) );
     1245
     1246                                // Dare god I hope this works. May mess up with UTF8 strings, but the execution after will catch it.
     1247                                if ( $bytes_written == strlen($request_message) )
     1248                                        break;
     1249
     1250                                $request_message = substr($request_message, $bytes_written);
     1251                        }
     1252
     1253                        fclose($handle);
     1254                        return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
     1255                } else {
     1256                        // We don't use stream_set_timeout with stream_select();
     1257                        stream_set_timeout( $handle, $timeout, $utimeout );
     1258                }
     1259               
     1260                while ( ! empty( $request_message ) ) {
     1261                        // Short requests should complete after the first try, but longer ones will only send so
     1262                        // much before kicking it back out.
     1263                        $bytes_written = fwrite( $handle, $request_message, strlen($request_message) );
     1264
     1265                        if ( $bytes_written == strlen($request_message) )
     1266                                break;
     1267
     1268                        $request_message = substr($request_message, $bytes_written);
     1269                }
     1270
     1271                $response = stream_get_contents($handle);
     1272
     1273                fclose($handle);
     1274
     1275                $process = WP_Http::processResponse($response);
     1276                $arrHeaders = WP_Http::processHeaders($process['headers']);
     1277
     1278                // Is the response code within the 400 range?
     1279                if ( (int) $arrHeaders['response']['code'] >= 400 && (int) $arrHeaders['response']['code'] < 500 )
     1280                        return new WP_Error('http_request_failed', $arrHeaders['response']['code'] . ': ' . $arrHeaders['response']['message']);
     1281
     1282                // If location is found, then assume redirect and redirect to location.
     1283                if ( 'HEAD' != $r['method'] && isset($arrHeaders['headers']['location']) ) {
     1284                        if ( $r['redirection']-- > 0 ) {
     1285                                return $this->request($arrHeaders['headers']['location'], $r);
     1286                        } else {
     1287                                return new WP_Error('http_request_failed', __('Too many redirects.'));
     1288                        }
     1289                }
     1290
     1291                // If the body was chunk encoded, then decode it.
     1292                if ( ! empty( $process['body'] ) && isset( $arrHeaders['headers']['transfer-encoding'] ) && 'chunked' == $arrHeaders['headers']['transfer-encoding'] )
     1293                        $process['body'] = WP_Http::chunkTransferDecode($process['body']);
     1294
     1295                if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($arrHeaders['headers']) )
     1296                        $process['body'] = WP_Http_Encoding::decompress( $process['body'] );
     1297
     1298                return array('headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response'], 'cookies' => $arrHeaders['cookies']);
     1299        }
     1300
     1301        /**
     1302         * Builds the request message for sending to the URL.
     1303         *
     1304         * @access private
     1305         * @since {unknown}
     1306         *
     1307         * @param array $r Request arguments.
     1308         * @param string $url URL string.
     1309         * @param array $arrURL Processed URL string.
     1310         * @return string Full request message.
     1311         */
     1312        function _build_request_message($r, $url, &$arrURL) {
     1313                $proxy = new WP_HTTP_Proxy();
     1314
     1315                // Create request headers and combine body.
     1316                if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) //Some proxies require full URL in this field.
     1317                        $requestPath = $url;
     1318                else
     1319                        $requestPath = $arrURL['path'] . ( isset($arrURL['query']) ? '?' . $arrURL['query'] : '' );
     1320
     1321                if ( empty($requestPath) )
     1322                        $requestPath .= '/';
     1323
     1324                $strHeaders = strtoupper($r['method']) . ' ' . $requestPath . ' HTTP/' . $r['httpversion'] . "\r\n";
     1325
     1326                if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) )
     1327                        $strHeaders .= 'Host: ' . $arrURL['host'] . ':' . $arrURL['port'] . "\r\n";
     1328                else
     1329                        $strHeaders .= 'Host: ' . $arrURL['host'] . "\r\n";
     1330
     1331                if ( isset($r['user-agent']) )
     1332                        $strHeaders .= 'User-agent: ' . $r['user-agent'] . "\r\n";
     1333
     1334                if ( is_array($r['headers']) ) {
     1335                        foreach ( (array) $r['headers'] as $header => $headerValue )
     1336                                $strHeaders .= $header . ': ' . $headerValue . "\r\n";
     1337                } else {
     1338                        $strHeaders .= $r['headers'];
     1339                }
     1340
     1341                if ( $proxy->use_authentication() )
     1342                        $strHeaders .= $proxy->authentication_header() . "\r\n";
     1343
     1344                $strHeaders .= "\r\n";
     1345
     1346                if ( ! is_null($r['body']) )
     1347                        $strHeaders .= $r['body'];
     1348
     1349                return $strHeaders;
     1350        }
     1351
     1352        /**
     1353         * Whether this class can be used for retrieving an URL.
     1354         *
     1355         * @static
     1356         * @access public
     1357         * @since {unknown}
     1358         *
     1359         * @return boolean False means this class can not be used, true means it can.
     1360         */
     1361        function test($args = array()) {
     1362                if ( ! function_exists('stream_socket_client') )
     1363                        return false;
     1364
     1365                return apply_filters('use_socket_transport', true, $args);
     1366        }
     1367}
     1368
     1369/**
    11131370 * HTTP request method uses HTTP extension to retrieve the url.
    11141371 *
    11151372 * Requires the HTTP extension to be installed. This would be the preferred transport since it can