You are here

function soap_transport_http::getResponse in Salesforce Suite 5

Same name in this branch
  1. 5 includes/nusoap.php \soap_transport_http::getResponse()
  2. 5 includes/nusoap.orig.php \soap_transport_http::getResponse()
Same name and namespace in other branches
  1. 5.2 includes/nusoap.php \soap_transport_http::getResponse()
  2. 5.2 includes/nusoap.orig.php \soap_transport_http::getResponse()
2 calls to soap_transport_http::getResponse()
soap_transport_http::send in includes/nusoap.php
* send the SOAP message via HTTP * *
soap_transport_http::send in includes/nusoap.orig.php
* send the SOAP message via HTTP * *

File

includes/nusoap.orig.php, line 2537

Class

soap_transport_http
transport class for sending/receiving data via HTTP and HTTPS NOTE: PHP must be compiled with the CURL extension for HTTPS support

Code

function getResponse() {
  $this->incoming_payload = '';
  if ($this->scheme == 'http' || $this->scheme == 'ssl') {

    // loop until headers have been retrieved
    $data = '';
    while (!isset($lb)) {

      // We might EOF during header read.
      if (feof($this->fp)) {
        $this->incoming_payload = $data;
        $this
          ->debug('found no headers before EOF after length ' . strlen($data));
        $this
          ->debug("received before EOF:\n" . $data);
        $this
          ->setError('server failed to send headers');
        return false;
      }
      $tmp = fgets($this->fp, 256);
      $tmplen = strlen($tmp);
      $this
        ->debug("read line of {$tmplen} bytes: " . trim($tmp));
      if ($tmplen == 0) {
        $this->incoming_payload = $data;
        $this
          ->debug('socket read of headers timed out after length ' . strlen($data));
        $this
          ->debug("read before timeout: " . $data);
        $this
          ->setError('socket read of headers timed out');
        return false;
      }
      $data .= $tmp;
      $pos = strpos($data, "\r\n\r\n");
      if ($pos > 1) {
        $lb = "\r\n";
      }
      else {
        $pos = strpos($data, "\n\n");
        if ($pos > 1) {
          $lb = "\n";
        }
      }

      // remove 100 header
      if (isset($lb) && ereg('^HTTP/1.1 100', $data)) {
        unset($lb);
        $data = '';
      }

      //
    }

    // store header data
    $this->incoming_payload .= $data;
    $this
      ->debug('found end of headers after length ' . strlen($data));

    // process headers
    $header_data = trim(substr($data, 0, $pos));
    $header_array = explode($lb, $header_data);
    $this->incoming_headers = array();
    $this->incoming_cookies = array();
    foreach ($header_array as $header_line) {
      $arr = explode(':', $header_line, 2);
      if (count($arr) > 1) {
        $header_name = strtolower(trim($arr[0]));
        $this->incoming_headers[$header_name] = trim($arr[1]);
        if ($header_name == 'set-cookie') {

          // TODO: allow multiple cookies from parseCookie
          $cookie = $this
            ->parseCookie(trim($arr[1]));
          if ($cookie) {
            $this->incoming_cookies[] = $cookie;
            $this
              ->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
          }
          else {
            $this
              ->debug('did not find cookie in ' . trim($arr[1]));
          }
        }
      }
      else {
        if (isset($header_name)) {

          // append continuation line to previous header
          $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
        }
      }
    }

    // loop until msg has been received
    if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
      $content_length = 2147483647;

      // ignore any content-length header
      $chunked = true;
      $this
        ->debug("want to read chunked content");
    }
    elseif (isset($this->incoming_headers['content-length'])) {
      $content_length = $this->incoming_headers['content-length'];
      $chunked = false;
      $this
        ->debug("want to read content of length {$content_length}");
    }
    else {
      $content_length = 2147483647;
      $chunked = false;
      $this
        ->debug("want to read content to EOF");
    }
    $data = '';
    do {
      if ($chunked) {
        $tmp = fgets($this->fp, 256);
        $tmplen = strlen($tmp);
        $this
          ->debug("read chunk line of {$tmplen} bytes");
        if ($tmplen == 0) {
          $this->incoming_payload = $data;
          $this
            ->debug('socket read of chunk length timed out after length ' . strlen($data));
          $this
            ->debug("read before timeout:\n" . $data);
          $this
            ->setError('socket read of chunk length timed out');
          return false;
        }
        $content_length = hexdec(trim($tmp));
        $this
          ->debug("chunk length {$content_length}");
      }
      $strlen = 0;
      while ($strlen < $content_length && !feof($this->fp)) {
        $readlen = min(8192, $content_length - $strlen);
        $tmp = fread($this->fp, $readlen);
        $tmplen = strlen($tmp);
        $this
          ->debug("read buffer of {$tmplen} bytes");
        if ($tmplen == 0 && !feof($this->fp)) {
          $this->incoming_payload = $data;
          $this
            ->debug('socket read of body timed out after length ' . strlen($data));
          $this
            ->debug("read before timeout:\n" . $data);
          $this
            ->setError('socket read of body timed out');
          return false;
        }
        $strlen += $tmplen;
        $data .= $tmp;
      }
      if ($chunked && $content_length > 0) {
        $tmp = fgets($this->fp, 256);
        $tmplen = strlen($tmp);
        $this
          ->debug("read chunk terminator of {$tmplen} bytes");
        if ($tmplen == 0) {
          $this->incoming_payload = $data;
          $this
            ->debug('socket read of chunk terminator timed out after length ' . strlen($data));
          $this
            ->debug("read before timeout:\n" . $data);
          $this
            ->setError('socket read of chunk terminator timed out');
          return false;
        }
      }
    } while ($chunked && $content_length > 0 && !feof($this->fp));
    if (feof($this->fp)) {
      $this
        ->debug('read to EOF');
    }
    $this
      ->debug('read body of length ' . strlen($data));
    $this->incoming_payload .= $data;
    $this
      ->debug('received a total of ' . strlen($this->incoming_payload) . ' bytes of data from server');

    // close filepointer
    if (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close' || !$this->persistentConnection || feof($this->fp)) {
      fclose($this->fp);
      $this->fp = false;
      $this
        ->debug('closed socket');
    }

    // connection was closed unexpectedly
    if ($this->incoming_payload == '') {
      $this
        ->setError('no response from server');
      return false;
    }

    // decode transfer-encoding
    //		if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
    //			if(!$data = $this->decodeChunked($data, $lb)){
    //				$this->setError('Decoding of chunked data failed');
    //				return false;
    //			}

    //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";

    // set decoded payload
    //			$this->incoming_payload = $header_data.$lb.$lb.$data;
    //		}
  }
  else {
    if ($this->scheme == 'https') {

      // send and receive
      $this
        ->debug('send and receive with cURL');
      $this->incoming_payload = curl_exec($this->ch);
      $data = $this->incoming_payload;
      $cErr = curl_error($this->ch);
      if ($cErr != '') {
        $err = 'cURL ERROR: ' . curl_errno($this->ch) . ': ' . $cErr . '<br>';

        // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
        foreach (curl_getinfo($this->ch) as $k => $v) {
          $err .= "{$k}: {$v}<br>";
        }
        $this
          ->debug($err);
        $this
          ->setError($err);
        curl_close($this->ch);
        return false;
      }
      else {

        //echo '<pre>';

        //var_dump(curl_getinfo($this->ch));

        //echo '</pre>';
      }

      // close curl
      $this
        ->debug('No cURL error, closing cURL');
      curl_close($this->ch);

      // remove 100 header(s)
      while (ereg('^HTTP/1.1 100', $data)) {
        if ($pos = strpos($data, "\r\n\r\n")) {
          $data = ltrim(substr($data, $pos));
        }
        elseif ($pos = strpos($data, "\n\n")) {
          $data = ltrim(substr($data, $pos));
        }
      }

      // separate content from HTTP headers
      if ($pos = strpos($data, "\r\n\r\n")) {
        $lb = "\r\n";
      }
      elseif ($pos = strpos($data, "\n\n")) {
        $lb = "\n";
      }
      else {
        $this
          ->debug('no proper separation of headers and document');
        $this
          ->setError('no proper separation of headers and document');
        return false;
      }
      $header_data = trim(substr($data, 0, $pos));
      $header_array = explode($lb, $header_data);
      $data = ltrim(substr($data, $pos));
      $this
        ->debug('found proper separation of headers and document');
      $this
        ->debug('cleaned data, stringlen: ' . strlen($data));

      // clean headers
      foreach ($header_array as $header_line) {
        $arr = explode(':', $header_line, 2);
        if (count($arr) > 1) {
          $header_name = strtolower(trim($arr[0]));
          $this->incoming_headers[$header_name] = trim($arr[1]);
          if ($header_name == 'set-cookie') {

            // TODO: allow multiple cookies from parseCookie
            $cookie = $this
              ->parseCookie(trim($arr[1]));
            if ($cookie) {
              $this->incoming_cookies[] = $cookie;
              $this
                ->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
            }
            else {
              $this
                ->debug('did not find cookie in ' . trim($arr[1]));
            }
          }
        }
        else {
          if (isset($header_name)) {

            // append continuation line to previous header
            $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
          }
        }
      }
    }
  }
  $arr = explode(' ', $header_array[0], 3);
  $http_version = $arr[0];
  $http_status = intval($arr[1]);
  $http_reason = count($arr) > 2 ? $arr[2] : '';

  // see if we need to resend the request with http digest authentication
  if (isset($this->incoming_headers['location']) && $http_status == 301) {
    $this
      ->debug("Got 301 {$http_reason} with Location: " . $this->incoming_headers['location']);
    $this
      ->setURL($this->incoming_headers['location']);
    $this->tryagain = true;
    return false;
  }

  // see if we need to resend the request with http digest authentication
  if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
    $this
      ->debug("Got 401 {$http_reason} with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
    if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
      $this
        ->debug('Server wants digest authentication');

      // remove "Digest " from our elements
      $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);

      // parse elements into array
      $digestElements = explode(',', $digestString);
      foreach ($digestElements as $val) {
        $tempElement = explode('=', trim($val), 2);
        $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
      }

      // should have (at least) qop, realm, nonce
      if (isset($digestRequest['nonce'])) {
        $this
          ->setCredentials($this->username, $this->password, 'digest', $digestRequest);
        $this->tryagain = true;
        return false;
      }
    }
    $this
      ->debug('HTTP authentication failed');
    $this
      ->setError('HTTP authentication failed');
    return false;
  }
  if ($http_status >= 300 && $http_status <= 307 || $http_status >= 400 && $http_status <= 417 || $http_status >= 501 && $http_status <= 505) {
    $this
      ->setError("Unsupported HTTP response status {$http_status} {$http_reason} (soapclient->response has contents of the response)");
    return false;
  }

  // decode content-encoding
  if (isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != '') {
    if (strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip') {

      // if decoding works, use it. else assume data wasn't gzencoded
      if (function_exists('gzinflate')) {

        //$timer->setMarker('starting decoding of gzip/deflated content');

        // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
        // this means there are no Zlib headers, although there should be
        $this
          ->debug('The gzinflate function exists');
        $datalen = strlen($data);
        if ($this->incoming_headers['content-encoding'] == 'deflate') {
          if ($degzdata = @gzinflate($data)) {
            $data = $degzdata;
            $this
              ->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
            if (strlen($data) < $datalen) {

              // test for the case that the payload has been compressed twice
              $this
                ->debug('The inflated payload is smaller than the gzipped one; try again');
              if ($degzdata = @gzinflate($data)) {
                $data = $degzdata;
                $this
                  ->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
              }
            }
          }
          else {
            $this
              ->debug('Error using gzinflate to inflate the payload');
            $this
              ->setError('Error using gzinflate to inflate the payload');
          }
        }
        elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
          if ($degzdata = @gzinflate(substr($data, 10))) {

            // do our best
            $data = $degzdata;
            $this
              ->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
            if (strlen($data) < $datalen) {

              // test for the case that the payload has been compressed twice
              $this
                ->debug('The un-gzipped payload is smaller than the gzipped one; try again');
              if ($degzdata = @gzinflate(substr($data, 10))) {
                $data = $degzdata;
                $this
                  ->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
              }
            }
          }
          else {
            $this
              ->debug('Error using gzinflate to un-gzip the payload');
            $this
              ->setError('Error using gzinflate to un-gzip the payload');
          }
        }

        //$timer->setMarker('finished decoding of gzip/deflated content');

        //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";

        // set decoded payload
        $this->incoming_payload = $header_data . $lb . $lb . $data;
      }
      else {
        $this
          ->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
        $this
          ->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
      }
    }
    else {
      $this
        ->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
      $this
        ->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
    }
  }
  else {
    $this
      ->debug('No Content-Encoding header');
  }
  if (strlen($data) == 0) {
    $this
      ->debug('no data after headers!');
    $this
      ->setError('no data present after HTTP headers');
    return false;
  }
  return $data;
}