WordPress.org

Make WordPress Core

Changeset 17555


Ignore:
Timestamp:
03/25/11 02:42:20 (3 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

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-admin/includes/file.php

    r17525 r17555  
    154154 
    155155/** 
    156  * Determines a writable directory for temporary files. 
    157  * Function's preference is to WP_CONTENT_DIR followed by the return value of <code>sys_get_temp_dir()</code>, before finally defaulting to /tmp/ 
    158  * 
    159  * In the event that this function does not find a writable location, It may be overridden by the <code>WP_TEMP_DIR</code> constant in your <code>wp-config.php</code> file. 
    160  * 
    161  * @since 2.5.0 
    162  * 
    163  * @return string Writable temporary directory 
    164  */ 
    165 function get_temp_dir() { 
    166     static $temp; 
    167     if ( defined('WP_TEMP_DIR') ) 
    168         return trailingslashit(WP_TEMP_DIR); 
    169  
    170     if ( $temp ) 
    171         return trailingslashit($temp); 
    172  
    173     $temp = WP_CONTENT_DIR . '/'; 
    174     if ( is_dir($temp) && @is_writable($temp) ) 
    175         return $temp; 
    176  
    177     if  ( function_exists('sys_get_temp_dir') ) { 
    178         $temp = sys_get_temp_dir(); 
    179         if ( @is_writable($temp) ) 
    180             return trailingslashit($temp); 
    181     } 
    182  
    183     $temp = ini_get('upload_tmp_dir'); 
    184     if ( is_dir($temp) && @is_writable($temp) ) 
    185         return trailingslashit($temp); 
    186  
    187     $temp = '/tmp/'; 
    188     return $temp; 
    189 } 
    190  
    191 /** 
    192156 * Returns a filename of a Temporary unique file. 
    193157 * Please note that the calling function must unlink() this itself. 
     
    520484        return new WP_Error('http_no_file', __('Could not create Temporary file.')); 
    521485 
    522     $handle = @fopen($tmpfname, 'wb'); 
    523     if ( ! $handle ) 
    524         return new WP_Error('http_no_file', __('Could not create Temporary file.')); 
    525  
    526     $response = wp_remote_get($url, array('timeout' => $timeout)); 
    527  
    528     if ( is_wp_error($response) ) { 
    529         fclose($handle); 
    530         unlink($tmpfname); 
     486    $response = wp_remote_get( $url, array( 'timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname ) ); 
     487 
     488    if ( is_wp_error( $response ) ) { 
     489        unlink( $tmpfname ); 
    531490        return $response; 
    532491    } 
    533492 
    534493    if ( $response['response']['code'] != '200' ){ 
    535         fclose($handle); 
    536         unlink($tmpfname); 
    537         return new WP_Error('http_404', trim($response['response']['message'])); 
    538     } 
    539  
    540     fwrite($handle, $response['body']); 
    541     fclose($handle); 
     494        unlink( $tmpfname ); 
     495        return new WP_Error( 'http_404', trim( $response['response']['message'] ) ); 
     496    } 
    542497 
    543498    return $tmpfname; 
  • 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 
  • trunk/wp-includes/functions.php

    r17516 r17555  
    21092109 
    21102110    return rtrim($base, '/') . '/' . ltrim($path, '/'); 
     2111} 
     2112 
     2113/** 
     2114 * Determines a writable directory for temporary files. 
     2115 * Function's preference is to WP_CONTENT_DIR followed by the return value of <code>sys_get_temp_dir()</code>, before finally defaulting to /tmp/ 
     2116 * 
     2117 * In the event that this function does not find a writable location, It may be overridden by the <code>WP_TEMP_DIR</code> constant in your <code>wp-config.php</code> file. 
     2118 * 
     2119 * @since 2.5.0 
     2120 * 
     2121 * @return string Writable temporary directory 
     2122 */ 
     2123function get_temp_dir() { 
     2124    static $temp; 
     2125    if ( defined('WP_TEMP_DIR') ) 
     2126        return trailingslashit(WP_TEMP_DIR); 
     2127 
     2128    if ( $temp ) 
     2129        return trailingslashit($temp); 
     2130 
     2131    $temp = WP_CONTENT_DIR . '/'; 
     2132    if ( is_dir($temp) && @is_writable($temp) ) 
     2133        return $temp; 
     2134 
     2135    if  ( function_exists('sys_get_temp_dir') ) { 
     2136        $temp = sys_get_temp_dir(); 
     2137        if ( @is_writable($temp) ) 
     2138            return trailingslashit($temp); 
     2139    } 
     2140 
     2141    $temp = ini_get('upload_tmp_dir'); 
     2142    if ( is_dir($temp) && @is_writable($temp) ) 
     2143        return trailingslashit($temp); 
     2144 
     2145    $temp = '/tmp/'; 
     2146    return $temp; 
    21112147} 
    21122148 
Note: See TracChangeset for help on using the changeset viewer.