diff --git a/httprl.module b/httprl.module index 6dc1ff4..ff22f49 100644 --- a/httprl.module +++ b/httprl.module @@ -901,11 +901,6 @@ function httprl_request($urls, $options = array()) { // Build the request string. $request = httprl_build_request_string($uri, $these_options); - // If a range header is set, calculate how many bytes need to be downloaded. - if (!empty($these_options['headers']['Range'])) { - $these_options['max_data_size'] = ''; - } - // Put all variables into an array for easy alterations. $connections[] = array($socket, $flags, $uri, $url, $these_options, $result, $request); $return[$url] = TRUE; @@ -1206,7 +1201,35 @@ function httprl_send_request($results = NULL) { // Now that we have the headers, increase the chunk size. $responses[$id]->chunk_size = $responses[$id]->options['chunk_size_read']; + + // If a range header is set and a 200 was returned, calculate how + // many bytes need to be downloaded. + if (!empty($responses[$id]->options['headers']['Range']) && $responses[$id]->code == 200) { + $responses[$id]->ranges = httprl_get_ranges($responses[$id]->options['headers']['Range']); + $responses[$id]->options['max_data_size'] = httprl_get_last_byte_from_range($responses[$id]->ranges); + } + } + } + + // Close the connection if a Range request was made and the currently + // downloaded data size is larger than the Range request. + if ( !empty($responses[$id]->options['max_data_size']) + && !is_null($responses[$id]->options['max_data_size']) + && $responses[$id]->options['max_data_size'] < httprl_strlen($responses[$id]->data) + ) { + $responses[$id]->status = 'Done.'; + $responses[$id]->code = 206; + + // Make the data conform to the range request. + $new_data = array(); + foreach ($responses[$id]->ranges as $range) { + $new_data[] = substr($responses[$id]->data, $range['start'], ($range['end']+1) - $range['start']); } + $responses[$id]->data = implode('', $new_data); + + // Do post processing on the stream. + httprl_post_processing($id, $responses, $output); + continue; } // Get stream data. @@ -1221,6 +1244,7 @@ function httprl_send_request($results = NULL) { // Do post processing on the stream. httprl_post_processing($id, $responses, $output); + continue; } else { $responses[$id]->status = 'Reading data'; @@ -1263,6 +1287,7 @@ function httprl_send_request($results = NULL) { // Do post processing on the stream. httprl_post_processing($id, $responses, $output); + continue; } elseif ($bytes >= $len) { $stream_write_count--; @@ -1277,6 +1302,7 @@ function httprl_send_request($results = NULL) { $responses[$id]->status = 'Non-Blocking request sent out. Not waiting for the response.'; // Do post processing on the stream. httprl_post_processing($id, $responses, $output); + continue; } else { // All data has been written to the socket. We are read only from here on out. @@ -1307,6 +1333,7 @@ function httprl_send_request($results = NULL) { // Do post processing on the stream. httprl_post_processing($id, $responses, $output); + continue; } } if (!$rw_done) { @@ -1459,6 +1486,7 @@ function httprl_parse_data(&$result) { switch ($code) { case 200: // OK + case 206: // Partial Content case 304: // Not modified break; @@ -1520,6 +1548,55 @@ function httprl_parse_data(&$result) { } /** + * Parse a range header into start and end byte ranges. + * + * @param $input + * String in the form of bytes=0-1024 or bytes=0-1024,2048-4096 + * @return array + * Keyed arrays containing start and end values for the byte ranges. + * Empty array if the string can not be parsed. + */ +function httprl_get_ranges($input) { + $ranges = array(); + // Make sure the input string matches the correct format. + $string = preg_match('/^bytes=((\d*-\d*,? ?)+)$/', $input, $matches) ? $matches[1] : FALSE; + if (!empty($string)) { + // Handle mutiple ranges + foreach (explode(',', $string) as $range) { + // Get the start and end byte values for this range. + $values = explode('-', $range); + if (count($values) != 2) { + return FALSE; + } + $ranges[] = array('start' => $values[0], 'end' => $values[1]); + } + } + return $ranges; +} + +/** + * Given an array of ranges, get the last byte we need to download. + * + * @param $ranges + * Multi dimentional array + * @return int or NULL + * NULL: Get all values; int: last byte to download. + */ +function httprl_get_last_byte_from_range($ranges) { + $max = 0; + if (empty($ranges)) { + return NULL; + } + foreach ($ranges as $range) { + if (!is_numeric($range['start']) || !is_numeric($range['end'])) { + return NULL; + } + $max = max($range['end']+1, $max); + } + return $max; +} + +/** * Run post processing on the request if we are done reading. * * Decode transfer-encoding and content-encoding.