Commit f2abc575 authored by Chris Graham's avatar Chris Graham
Browse files

Fixed MANTIS-0 (Chunked transfers that have no HTTP payload will stall)

parent 11859209
Loading
Loading
Loading
Loading
+43 −13
Original line number Diff line number Diff line
@@ -1692,9 +1692,11 @@ function _http_download_file($url, $byte_limit = null, $trigger_error = true, $n
        $buffer_unprocessed = '';
        $_frh = array($mysock);
        $_fwh = null;
        while (($chunked) || (!@feof($mysock))) { // @'d because socket might have died. If so fread will will return false and hence we'll break
            if ((function_exists('stream_select')) && (count($_frh) > 0) && (!@stream_select($_frh, $_fwh, $_fwh, intval($timeout), fmod($timeout, 1.0) / 1000000.0))) {
                if ((!$chunked) || ($buffer_unprocessed == '')) {
        while ((!@feof($mysock)) || (($chunked) && ($buffer_unprocessed != ''))) { // @'d because socket might have died. If so fread will will return false and hence we'll break
            if ((count($_frh) == 0) || ((function_exists('stream_select')) && (!@stream_select($_frh, $_fwh, $_fwh, intval($timeout), fmod($timeout, 1.0) / 1000000.0)))) {
                // Detected timeout
                if (($chunked) && ($buffer_unprocessed != '')) {
                    // Either a broken chunked stream or no input at all yet...
                    if ($trigger_error) {
                        warn_exit(do_lang_tempcode('HTTP_DOWNLOAD_CONNECTION_STALLED', escape_html($url)));
                    } else {
@@ -1709,23 +1711,29 @@ function _http_download_file($url, $byte_limit = null, $trigger_error = true, $n
                        }
                    }
                    return _detect_character_encoding($input);
                } else {
                    // We don't know our stream is broken, so let's consider us done, just that the connection was not detected on our as properly closed with EOF...
                    break;
                }
            }

            $line = @fread($mysock, 32000);
            if ($line === false) {
                if ((!$chunked) || ($buffer_unprocessed == '')) {
                // Implicit EOF, so terminate
                if (($chunked) && ($buffer_unprocessed != '')) {
                    $_frh = array(); // Will trigger a detected timeout on next loop cycle. Note stream_select also can empty $_frh, and we are simulating the same thing here but without any overhead on waiting for stream_select
                    $line = '';
                } else {
                    break;
                }
                $line = '';
            }
            if ($data_started) {
                $line = $buffer_unprocessed . $line;
                $buffer_unprocessed = '';

                if ($chunked) {
                    if (isset($line[1]) && $line[0] == "\r" && $line[1] == "\n") {
                        $line = substr($line, 2);
                    if ((isset($line[1])) && ($line[0] == "\r") && ($line[1] == "\n")) {
                        $line = @substr($line, 2);
                    }

                    $hexdec_chunk_details = '';
@@ -1733,7 +1741,7 @@ function _http_download_file($url, $byte_limit = null, $trigger_error = true, $n
                    for ($hexdec_read = 0; $hexdec_read < $chunk_line_length; $hexdec_read++) {
                        $chunk_char = $line[$hexdec_read];
                        if ($chunk_char == "\r") {
                            $chunk_char_is_hex = false;
                            break;
                        } else {
                            if ($has_ctype_xdigit) {
                                $chunk_char_is_hex = ctype_xdigit($chunk_char);
@@ -1748,11 +1756,28 @@ function _http_download_file($url, $byte_limit = null, $trigger_error = true, $n
                            break;
                        }
                    }
                    if ($hexdec_chunk_details == '') { // No data

                    if ($hexdec_chunk_details == '') {
                        // No usable data here...

                        if ($line == '') {
                            // We just have no data yet
                            continue;
                        }

                        // Corrupt, so terminate as empty
                        if ($write_to_file === null) {
                            $input = '';
                        } else {
                            ftruncate($write_to_file, 0);
                        }
                        $input_len = 0;
                        $buffer_unprocessed = '';
                        break;
                    }
                    $chunk_end_pos = strpos($line, "\r\n");
                    if ($chunk_end_pos === false) {
                        // Not a full chunk yet
                        $buffer_unprocessed = $line;
                        continue;
                    }
@@ -1764,8 +1789,10 @@ function _http_download_file($url, $byte_limit = null, $trigger_error = true, $n
                    }
                    $buffer_unprocessed = substr($line, $chunk_end_pos + 2 + $amount_wanted); // May be some more extra read
                    $line = substr($line, $chunk_end_pos + 2, $amount_wanted);

                    if ($line == '') {
                        break; // Terminating chunk
                        // Terminates if an empty chunk
                        break;
                    }

                    $input_len += $amount_wanted;
@@ -1945,11 +1972,14 @@ function _http_download_file($url, $byte_limit = null, $trigger_error = true, $n
                    }
                    if ($line == "\r\n") {
                        $data_started = true;
                        $buffer_unprocessed = substr($old_line, $tally);
                        $buffer_unprocessed = @substr($old_line, $tally);
                        if ($buffer_unprocessed === false) {
                            $buffer_unprocessed = '';
                        }
                        break;

                        if (($chunked) && (isset($buffer_unprocessed[1])) && ($buffer_unprocessed[0] == "\r") && ($buffer_unprocessed[1] == "\n")) {
                            break; // Explicit termination after headers, we should not try and wait for more
                        }
                    }
                }
            }