You are here

function httprl_parse_data in HTTP Parallel Request & Threading Library 7

Same name and namespace in other branches
  1. 6 httprl.module \httprl_parse_data()

Extract the header and meta data from the http data stream.

@todo Send cookies in the redirect request if domain/path match.

Parameters

object $result: An object from httprl_send_request.

See also

drupal_http_request()

1 call to httprl_parse_data()
httprl_send_request in ./httprl.module
Perform many HTTP requests.

File

./httprl.module, line 1802
HTTP Parallel Request Library module.

Code

function httprl_parse_data(&$result) {

  // If in non blocking mode, skip.
  if (empty($result->options['blocking'])) {
    return;
  }

  // If the headers are already parsed, skip.
  if (!empty($result->headers)) {
    return;
  }

  // If the t function is not available use httprl_pr.
  $t = function_exists('t') ? 't' : 'httprl_pr';

  // Parse response headers from the response body.
  // Be tolerant of malformed HTTP responses that separate header and body with
  // \n\n or \r\r instead of \r\n\r\n.
  $response = $result->data;
  list($response, $result->data) = preg_split("/\r\n\r\n|\n\n|\r\r/", $response, 2);
  $response = preg_split("/\r\n|\n|\r/", $response);

  // Parse the response status line.
  $protocol_code_array = explode(' ', trim(array_shift($response)), 3);
  $result->protocol = $protocol_code_array[0];
  $code = (int) $protocol_code_array[1];

  // If the response does not include a description, don't try to process it.
  $result->status_message = isset($protocol_code_array[2]) ? $protocol_code_array[2] : '';
  unset($protocol_code_array);
  $result->headers = array();

  // Parse the response headers.
  $cookie_primary_counter = 0;
  while ($line = trim(array_shift($response))) {
    list($name, $value) = explode(':', $line, 2);
    $name = strtolower($name);

    // Parse cookies before they get added to the header.
    if ($name == 'set-cookie') {

      // Extract the key value pairs for this cookie.
      foreach (explode(';', $value) as $cookie_name_value) {
        $temp = explode('=', trim($cookie_name_value));
        $cookie_key = trim($temp[0]);
        $cookie_value = isset($temp[1]) ? trim($temp[1]) : '';
        unset($temp);

        // The cookie name-value pair always comes first (RFC 2109 4.2.2).
        if (!isset($result->cookies[$cookie_primary_counter])) {
          $result->cookies[$cookie_primary_counter] = array(
            'name' => $cookie_key,
            'value' => $cookie_value,
          );
        }
        else {
          $result->cookies[$cookie_primary_counter] += array(
            $cookie_key => $cookie_value,
          );
        }
      }
      $cookie_primary_counter++;
    }

    // Add key value pairs to the header; including cookies.
    if (isset($result->headers[$name]) && $name == 'set-cookie') {

      // RFC 2109: the Set-Cookie response header comprises the token Set-
      // Cookie:, followed by a comma-separated list of one or more cookies.
      $result->headers[$name] .= ',' . trim($value);
    }
    else {
      $result->headers[$name] = trim($value);
    }
  }
  $responses = array(
    100 => 'Continue',
    101 => 'Switching Protocols',
    200 => 'OK',
    201 => 'Created',
    202 => 'Accepted',
    203 => 'Non-Authoritative Information',
    204 => 'No Content',
    205 => 'Reset Content',
    206 => 'Partial Content',
    300 => 'Multiple Choices',
    301 => 'Moved Permanently',
    302 => 'Found',
    303 => 'See Other',
    304 => 'Not Modified',
    305 => 'Use Proxy',
    307 => 'Temporary Redirect',
    400 => 'Bad Request',
    401 => 'Unauthorized',
    402 => 'Payment Required',
    403 => 'Forbidden',
    404 => 'Not Found',
    405 => 'Method Not Allowed',
    406 => 'Not Acceptable',
    407 => 'Proxy Authentication Required',
    408 => 'Request Time-out',
    409 => 'Conflict',
    410 => 'Gone',
    411 => 'Length Required',
    412 => 'Precondition Failed',
    413 => 'Request Entity Too Large',
    414 => 'Request-URI Too Large',
    415 => 'Unsupported Media Type',
    416 => 'Requested range not satisfiable',
    417 => 'Expectation Failed',
    500 => 'Internal Server Error',
    501 => 'Not Implemented',
    502 => 'Bad Gateway',
    503 => 'Service Unavailable',
    504 => 'Gateway Time-out',
    505 => 'HTTP Version not supported',
  );

  // RFC 2616 states that all unknown HTTP codes must be treated the same as the
  // base code in their class.
  if (!isset($responses[$code])) {
    $code = floor($code / 100) * 100;
  }
  $result->code = $code;
  switch ($code) {
    case 200:

    // OK
    case 201:

    // Created
    case 202:

    // Accepted
    case 206:

    // Partial Content
    case 304:

      // Not modified
      break;
    case 301:

    // Moved permanently
    case 302:

    // Moved temporarily
    case 307:

      // Moved temporarily
      $location = @parse_url($result->headers['location']);

      // If location isn't fully qualified URL (as per W3 RFC2616), build one.
      if (empty($location['scheme']) || empty($location['host'])) {
        $hostname = httprl_get_hostname();

        // Get the important parts from the original request.
        $original_location = @parse_url($result->url);

        // Assume request is to self if none of this was setup correctly.
        $location['scheme'] = !empty($location['scheme']) ? $location['scheme'] : $original_location['scheme'];
        $location['host'] = !empty($location['host']) ? $location['host'] : (!empty($original_location['host']) ? $original_location['host'] : $hostname);
        $location['port'] = !empty($location['port']) ? $location['port'] : (!empty($original_location['port']) ? $original_location['port'] : '');
        $location = httprl_glue_url($location);
      }
      else {
        $location = $result->headers['location'];
      }

      // Set internal redirect states.
      $result->options['internal_states']['redirect_code_array'][] = $code;
      $result->options['internal_states']['redirect_url_array'][] = $location;
      if (!isset($result->options['internal_states']['original_url'])) {
        $result->options['internal_states']['original_url'] = $result->url;
      }

      // Error out if we hit the max redirect.
      if ($result->options['max_redirects'] <= 0) {
        $result->code = HTTPRL_REQUEST_ALLOWED_REDIRECTS_EXHAUSTED;
        $result->error = $t('Maximum allowed redirects exhausted.');
      }
      else {

        // Prepare the received cookie values from the previous request and pass
        // them on to the next.
        $cookie_string = '';
        $parsed_location = parse_url($location);
        if (isset($result->cookies)) {
          foreach ($result->cookies as $cookie) {

            // Only add the cookie values if the cookie domain matches.
            $cookie_domain = ltrim($cookie['domain'], '.');
            if (strpos($parsed_location['host'], $cookie_domain) !== FALSE) {
              if (!empty($cookie_string)) {
                $cookie_string .= ';';
              }
              $cookie_string .= $cookie['name'] . '=' . $cookie['value'];
            }
          }

          // If the cookie string is changed and thus not empty we want to set
          // it in the options array.
          if (!empty($cookie_string)) {
            $result->options['headers']['Cookie'] = $cookie_string;
          }
        }

        // Redirect to the new location.
        $result->options['max_redirects']--;
        if (isset($result->options['headers']['Referer'])) {
          $result->options['headers']['Referer'] = $result->url;
        }

        // Remove the host from the header.
        unset($result->options['headers']['Host']);

        // Pass along running time.
        $result->options['internal_states']['running_time'] = $result->running_time;

        // Send new request.
        httprl_request($location, $result->options);

        // Kill this request.
        $result->options['internal_states']['kill'] = TRUE;
      }
      break;
    default:
      $result->error = $result->status_message;
  }
}