Make WordPress Core


Ignore:
Timestamp:
03/25/2011 02:42:20 AM (16 years ago)
Author:
dd32
Message:

First run of introducing Stream-To-File for the WP_HTTP API. Reduces memory consumption during file downloads. Implemented in download_url() for upgraders. Props sivel. See #16236

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-includes/class-http.php

    r17551 r17555  
    9898                        'compress' => false,
    9999                        'decompress' => true,
    100                         'sslverify' => true
     100                        'sslverify' => true,
     101                        'stream' => false,
     102                        'filename' => null
    101103                );
    102104
    … …  
    136138                $r['local'] = $homeURL['host'] == $arrURL['host'] || 'localhost' == $arrURL['host'];
    137139                unset( $homeURL );
     140
     141                // If we are streaming to a file but no filename was given drop it in the WP temp dir
     142                // and pick it's name using the basename of the $url
     143                if ( $r['stream']  && empty( $r['filename'] ) )
     144                        $r['filename'] = get_temp_dir() . basename( $url );
     145
     146                // Force some settings if we are streaming to a file and check for existence and perms of destination directory
     147                if ( $r['stream'] ) {
     148                        $r['blocking'] = true;
     149                        if ( ! is_writable( dirname( $r['filename'] ) ) )
     150                                return new WP_Error( 'http_request_failed', __( 'Destination directory for file streaming does not exist or is not writable.' ) );
     151                }
    138152
    139153                if ( is_null( $r['headers'] ) )
    … …  
    660674
    661675                $strResponse = '';
    662                 while ( ! feof($handle) )
    663                         $strResponse .= fread($handle, 4096);
    664 
    665                 fclose($handle);
     676                $bodyStarted = false;
     677
     678                // If streaming to a file setup the file handle
     679                if ( $r['stream'] ) {
     680                        if ( ! WP_DEBUG )
     681                                $stream_handle = @fopen( $r['filename'], 'w+' );
     682                        else
     683                                $stream_handle = fopen( $r['filename'], 'w+' );
     684                        if ( ! $stream_handle )
     685                                return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) );
     686
     687                        while ( ! feof($handle) ) {
     688                                $block = fread( $handle, 4096 );
     689                                if ( $bodyStarted ) {
     690                                        fwrite( $stream_handle, $block );
     691                                } else {
     692                                        $strResponse .= $block;
     693                                        if ( strpos( $strResponse, "\r\n\r\n" ) ) {
     694                                                $process = WP_Http::processResponse( $strResponse );
     695                                                $bodyStarted = true;
     696                                                fwrite( $stream_handle, $process['body'] );
     697                                                unset( $strResponse );
     698                                                $process['body'] = '';
     699                                        }
     700                                }
     701                        }
     702
     703                        fclose( $stream_handle );
     704
     705                } else {
     706                        while ( ! feof($handle) )
     707                                $strResponse .= fread( $handle, 4096 );
     708
     709                        $process = WP_Http::processResponse( $strResponse );
     710                        unset( $strResponse );
     711                }
     712
     713                fclose( $handle );
    666714
    667715                if ( true === $secure_transport )
    668716                        error_reporting($error_reporting);
    669717
    670                 $process = WP_Http::processResponse($strResponse);
    671                 $arrHeaders = WP_Http::processHeaders($process['headers']);
     718                $arrHeaders = WP_Http::processHeaders( $process['headers'] );
    672719
    673720                // Is the response code within the 400 range?
    … …  
    691738                        $process['body'] = WP_Http_Encoding::decompress( $process['body'] );
    692739
    693                 return array('headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response'], 'cookies' => $arrHeaders['cookies']);
     740                return array( 'headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response'], 'cookies' => $arrHeaders['cookies'], 'filename' => $r['filename'] );
    694741        }
    695742
    … …  
    835882                }
    836883
    837                 $strResponse = stream_get_contents($handle);
    838                 $meta = stream_get_meta_data($handle);
    839 
    840                 fclose($handle);
     884                if ( $r['stream'] ) {
     885                        if ( ! WP_DEBUG )
     886                                $stream_handle = @fopen( $r['filename'], 'w+' );
     887                        else
     888                                $stream_handle = fopen( $r['filename'], 'w+' );
     889
     890                        if ( ! $stream_handle )
     891                                return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) );
     892
     893                        stream_copy_to_stream( $handle, $stream_handle );
     894
     895                        fclose( $stream_handle );
     896                        $strResponse = '';
     897                } else {
     898                        $strResponse = stream_get_contents( $handle );
     899                }
     900
     901                $meta = stream_get_meta_data( $handle );
     902
     903                fclose( $handle );
    841904
    842905                $processedHeaders = array();
    … …  
    857920                        $strResponse = WP_Http_Encoding::decompress( $strResponse );
    858921
    859                 return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']);
     922                return array( 'headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies'], 'filename' => $r['filename'] );
    860923        }
    861924
    … …  
    10071070                        $theBody = http_inflate( $theBody );
    10081071
     1072                if ( $r['stream'] ) {
     1073                        if ( !WP_DEBUG )
     1074                                $stream_handle = @fopen( $r['filename'], 'w+' );
     1075                        else
     1076                                $stream_handle = fopen( $r['filename'], 'w+' );
     1077
     1078                        if ( ! $stream_handle )
     1079                                return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) );
     1080
     1081                        fwrite( $stream_handle, $theBody );
     1082                        fclose( $stream_handle );
     1083                        $theBody = '';
     1084                }
     1085
    10091086                $theResponse = array();
    10101087                $theResponse['code'] = $info['response_code'];
    10111088                $theResponse['message'] = get_status_header_desc($info['response_code']);
    10121089
    1013                 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $theResponse, 'cookies' => $theHeaders['cookies']);
     1090                return array( 'headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $theResponse, 'cookies' => $theHeaders['cookies'], 'filename' => $r['filename'] );
    10141091        }
    10151092
    … …  
    10371114 */
    10381115class WP_Http_Curl {
     1116
     1117        /**
     1118         * Temporary header storage for use with streaming to a file.
     1119         *
     1120         * @since 3.2.0
     1121         * @access private
     1122         * @var string
     1123         */
     1124        private $headers;
    10391125
    10401126        /**
    … …  
    11221208
    11231209                if ( true === $r['blocking'] )
    1124                         curl_setopt( $handle, CURLOPT_HEADER, true );
    1125                 else
    1126                         curl_setopt( $handle, CURLOPT_HEADER, false );
     1210                        curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( &$this, 'stream_headers' ) );
     1211
     1212                curl_setopt( $handle, CURLOPT_HEADER, false );
     1213
     1214                // If streaming to a file open a file handle, and setup our curl streaming handler
     1215                if ( $r['stream'] ) {
     1216                        if ( ! WP_DEBUG )
     1217                                $stream_handle = @fopen( $r['filename'], 'w+' );
     1218                        else
     1219                                $stream_handle = fopen( $r['filename'], 'w+' );
     1220                        if ( ! $stream_handle )
     1221                                return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) );
     1222                        curl_setopt( $handle, CURLOPT_FILE, $stream_handle );
     1223                }
    11271224
    11281225                // The option doesn't work with safe mode or when open_basedir is set.
    … …  
    11561253
    11571254                $theResponse = curl_exec( $handle );
    1158 
    1159                 if ( !empty($theResponse) ) {
    1160                         $headerLength = curl_getinfo($handle, CURLINFO_HEADER_SIZE);
    1161                         $theHeaders = trim( substr($theResponse, 0, $headerLength) );
    1162                         if ( strlen($theResponse) > $headerLength )
    1163                                 $theBody = substr( $theResponse, $headerLength );
    1164                         else
    1165                                 $theBody = '';
    1166                         if ( false !== strpos($theHeaders, "\r\n\r\n") ) {
    1167                                 $headerParts = explode("\r\n\r\n", $theHeaders);
    1168                                 $theHeaders = $headerParts[ count($headerParts) -1 ];
    1169                         }
    1170                         $theHeaders = WP_Http::processHeaders($theHeaders);
    1171                 } else {
     1255                $theBody = '';
     1256                $theHeaders = WP_Http::processHeaders( $this->headers );
     1257
     1258                if ( ! empty($theResponse) && ! is_bool( $theResponse ) ) // is_bool: when using $args['stream'], curl_exec will return (bool)true
     1259                        $theBody = $theResponse;
     1260
     1261                // If no response, and It's not a HEAD request with valid headers returned
     1262                if ( empty($theResponse) && 'HEAD' != $args['method'] && ! empty($this->headers) ) {
    11721263                        if ( $curl_error = curl_error($handle) )
    11731264                                return new WP_Error('http_request_failed', $curl_error);
    11741265                        if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array(301, 302) ) )
    11751266                                return new WP_Error('http_request_failed', __('Too many redirects.'));
    1176 
    1177                         $theHeaders = array( 'headers' => array(), 'cookies' => array() );
    1178                         $theBody = '';
    1179                 }
     1267                }
     1268
     1269                unset( $this->headers );
    11801270
    11811271                $response = array();
    … …  
    11851275                curl_close( $handle );
    11861276
     1277                if ( $r['stream'] )
     1278                        fclose( $stream_handle );
     1279
    11871280                // See #11305 - When running under safe mode, redirection is disabled above. Handle it manually.
    1188                 if ( !empty($theHeaders['headers']['location']) && (ini_get('safe_mode') || ini_get('open_basedir')) && 0 !== $r['_redirection'] ) {
     1281                if ( ! empty( $theHeaders['headers']['location'] ) && ( ini_get( 'safe_mode' ) || ini_get( 'open_basedir' ) ) && 0 !== $r['_redirection'] ) {
    11891282                        if ( $r['redirection']-- > 0 ) {
    1190                                 return $this->request($theHeaders['headers']['location'], $r);
     1283                                return $this->request( $theHeaders['headers']['location'], $r );
    11911284                        } else {
    1192                                 return new WP_Error('http_request_failed', __('Too many redirects.'));
     1285                                return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
    11931286                        }
    11941287                }
    … …  
    11971290                        $theBody = WP_Http_Encoding::decompress( $theBody );
    11981291
    1199                 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response, 'cookies' => $theHeaders['cookies']);
     1292                return array( 'headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response, 'cookies' => $theHeaders['cookies'], 'filename' => $r['filename'] );
     1293        }
     1294
     1295        /**
     1296         * Grab the headers of the cURL request
     1297         *
     1298         * Each header is sent individually to this callback, so we append to the $header property for temporary storage
     1299         *
     1300         * @since 3.2.0
     1301         * @access private
     1302         * @return int
     1303         */
     1304        private function stream_headers( $handle, $headers ) {
     1305                $this->headers .= $headers;
     1306                return strlen( $headers );
    12001307        }
    12011308
Note: See TracChangeset for help on using the changeset viewer.