forked from GNUsocial/gnu-social
PEAR::HTTP_Request2 updated to 2.2.1
Release date: 2014-01-16 19:53 UTC
This commit is contained in:
@@ -4,41 +4,18 @@
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENSE:
|
||||
* LICENSE
|
||||
*
|
||||
* Copyright (c) 2008-2012, Alexey Borzov <avb@php.net>
|
||||
* All rights reserved.
|
||||
* This source file is subject to BSD 3-Clause License that is bundled
|
||||
* with this package in the file LICENSE and available at the URL
|
||||
* https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * The names of the authors may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @category HTTP
|
||||
* @package HTTP_Request2
|
||||
* @author Alexey Borzov <avb@php.net>
|
||||
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
||||
* @version SVN: $Id: Socket.php 324953 2012-04-08 07:24:12Z avb $
|
||||
* @link http://pear.php.net/package/HTTP_Request2
|
||||
* @category HTTP
|
||||
* @package HTTP_Request2
|
||||
* @author Alexey Borzov <avb@php.net>
|
||||
* @copyright 2008-2014 Alexey Borzov <avb@php.net>
|
||||
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
||||
* @link http://pear.php.net/package/HTTP_Request2
|
||||
*/
|
||||
|
||||
/** Base class for HTTP_Request2 adapters */
|
||||
@@ -56,8 +33,8 @@ require_once 'HTTP/Request2/SocketWrapper.php';
|
||||
* @category HTTP
|
||||
* @package HTTP_Request2
|
||||
* @author Alexey Borzov <avb@php.net>
|
||||
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
||||
* @version Release: 2.1.1
|
||||
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
||||
* @version Release: 2.2.1
|
||||
* @link http://pear.php.net/package/HTTP_Request2
|
||||
*/
|
||||
class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
||||
@@ -70,7 +47,7 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
||||
/**
|
||||
* Regular expression for 'quoted-string' rule from RFC 2616
|
||||
*/
|
||||
const REGEXP_QUOTED_STRING = '"(?:\\\\.|[^\\\\"])*"';
|
||||
const REGEXP_QUOTED_STRING = '"(?>[^"\\\\]+|\\\\.)*"';
|
||||
|
||||
/**
|
||||
* Connected sockets, needed for Keep-Alive support
|
||||
@@ -129,6 +106,12 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
||||
*/
|
||||
protected $redirectCountdown = null;
|
||||
|
||||
/**
|
||||
* Whether to wait for "100 Continue" response before sending request body
|
||||
* @var bool
|
||||
*/
|
||||
protected $expect100Continue = false;
|
||||
|
||||
/**
|
||||
* Sends request to the remote server and returns its response
|
||||
*
|
||||
@@ -147,9 +130,21 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
||||
$this->socket->write($headers);
|
||||
// provide request headers to the observer, see request #7633
|
||||
$this->request->setLastEvent('sentHeaders', $headers);
|
||||
$this->writeBody();
|
||||
|
||||
$response = $this->readResponse();
|
||||
if (!$this->expect100Continue) {
|
||||
$this->writeBody();
|
||||
$response = $this->readResponse();
|
||||
|
||||
} else {
|
||||
$response = $this->readResponse();
|
||||
if (!$response || 100 == $response->getStatus()) {
|
||||
$this->expect100Continue = false;
|
||||
// either got "100 Continue" or timed out -> send body
|
||||
$this->writeBody();
|
||||
$response = $this->readResponse();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($jar = $request->getCookieJar()) {
|
||||
$jar->addCookiesFromResponse($response, $request->getUrl());
|
||||
@@ -256,19 +251,25 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
||||
'Keep-Alive' == $headers['connection']);
|
||||
|
||||
$options = array();
|
||||
if ($ip = $this->request->getConfig('local_ip')) {
|
||||
$options['socket'] = array(
|
||||
'bindto' => (false === strpos($ip, ':') ? $ip : '[' . $ip . ']') . ':0'
|
||||
);
|
||||
}
|
||||
if ($secure || $tunnel) {
|
||||
$options['ssl'] = array();
|
||||
foreach ($this->request->getConfig() as $name => $value) {
|
||||
if ('ssl_' == substr($name, 0, 4) && null !== $value) {
|
||||
if ('ssl_verify_host' == $name) {
|
||||
if ($value) {
|
||||
$options['CN_match'] = $reqHost;
|
||||
$options['ssl']['CN_match'] = $reqHost;
|
||||
}
|
||||
} else {
|
||||
$options[substr($name, 4)] = $value;
|
||||
$options['ssl'][substr($name, 4)] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
ksort($options);
|
||||
ksort($options['ssl']);
|
||||
}
|
||||
|
||||
// Use global request timeout if given, see feature requests #5735, #8964
|
||||
@@ -863,6 +864,11 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
||||
$this->addAuthorizationHeader($headers, $host, $requestUrl);
|
||||
$this->addProxyAuthorizationHeader($headers, $requestUrl);
|
||||
$this->calculateRequestLength($headers);
|
||||
if ('1.1' == $this->request->getConfig('protocol_version')) {
|
||||
$this->updateExpectHeader($headers);
|
||||
} else {
|
||||
$this->expect100Continue = false;
|
||||
}
|
||||
|
||||
$headersStr = $this->request->getMethod() . ' ' . $requestUrl . ' HTTP/' .
|
||||
$this->request->getConfig('protocol_version') . "\r\n";
|
||||
@@ -873,6 +879,78 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
||||
return $headersStr . "\r\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or removes 'Expect: 100-continue' header from request headers
|
||||
*
|
||||
* Also sets the $expect100Continue property. Parsing of existing header
|
||||
* is somewhat needed due to its complex structure and due to the
|
||||
* requirement in section 8.2.3 of RFC 2616:
|
||||
* > A client MUST NOT send an Expect request-header field (section
|
||||
* > 14.20) with the "100-continue" expectation if it does not intend
|
||||
* > to send a request body.
|
||||
*
|
||||
* @param array &$headers Array of headers prepared for the request
|
||||
*
|
||||
* @throws HTTP_Request2_LogicException
|
||||
* @link http://pear.php.net/bugs/bug.php?id=19233
|
||||
* @link http://tools.ietf.org/html/rfc2616#section-8.2.3
|
||||
*/
|
||||
protected function updateExpectHeader(&$headers)
|
||||
{
|
||||
$this->expect100Continue = false;
|
||||
$expectations = array();
|
||||
if (isset($headers['expect'])) {
|
||||
if ('' === $headers['expect']) {
|
||||
// empty 'Expect' header is technically invalid, so just get rid of it
|
||||
unset($headers['expect']);
|
||||
return;
|
||||
}
|
||||
// build regexp to parse the value of existing Expect header
|
||||
$expectParam = ';\s*' . self::REGEXP_TOKEN . '(?:\s*=\s*(?:'
|
||||
. self::REGEXP_TOKEN . '|'
|
||||
. self::REGEXP_QUOTED_STRING . '))?\s*';
|
||||
$expectExtension = self::REGEXP_TOKEN . '(?:\s*=\s*(?:'
|
||||
. self::REGEXP_TOKEN . '|'
|
||||
. self::REGEXP_QUOTED_STRING . ')\s*(?:'
|
||||
. $expectParam . ')*)?';
|
||||
$expectItem = '!(100-continue|' . $expectExtension . ')!A';
|
||||
|
||||
$pos = 0;
|
||||
$length = strlen($headers['expect']);
|
||||
|
||||
while ($pos < $length) {
|
||||
$pos += strspn($headers['expect'], " \t", $pos);
|
||||
if (',' === substr($headers['expect'], $pos, 1)) {
|
||||
$pos++;
|
||||
continue;
|
||||
|
||||
} elseif (!preg_match($expectItem, $headers['expect'], $m, 0, $pos)) {
|
||||
throw new HTTP_Request2_LogicException(
|
||||
"Cannot parse value '{$headers['expect']}' of Expect header",
|
||||
HTTP_Request2_Exception::INVALID_ARGUMENT
|
||||
);
|
||||
|
||||
} else {
|
||||
$pos += strlen($m[0]);
|
||||
if (strcasecmp('100-continue', $m[0])) {
|
||||
$expectations[] = $m[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (1024 < $this->contentLength) {
|
||||
$expectations[] = '100-continue';
|
||||
$this->expect100Continue = true;
|
||||
}
|
||||
|
||||
if (empty($expectations)) {
|
||||
unset($headers['expect']);
|
||||
} else {
|
||||
$headers['expect'] = implode(',', $expectations);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the request body
|
||||
*
|
||||
@@ -888,6 +966,8 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
||||
|
||||
$position = 0;
|
||||
$bufferSize = $this->request->getConfig('buffer_size');
|
||||
$headers = $this->request->getHeaders();
|
||||
$chunked = isset($headers['transfer-encoding']);
|
||||
while ($position < $this->contentLength) {
|
||||
if (is_string($this->requestBody)) {
|
||||
$str = substr($this->requestBody, $position, $bufferSize);
|
||||
@@ -896,11 +976,20 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
||||
} else {
|
||||
$str = $this->requestBody->read($bufferSize);
|
||||
}
|
||||
$this->socket->write($str);
|
||||
if (!$chunked) {
|
||||
$this->socket->write($str);
|
||||
} else {
|
||||
$this->socket->write(dechex(strlen($str)) . "\r\n{$str}\r\n");
|
||||
}
|
||||
// Provide the length of written string to the observer, request #7630
|
||||
$this->request->setLastEvent('sentBodyPart', strlen($str));
|
||||
$position += strlen($str);
|
||||
}
|
||||
|
||||
// write zero-length chunk
|
||||
if ($chunked) {
|
||||
$this->socket->write("0\r\n\r\n");
|
||||
}
|
||||
$this->request->setLastEvent('sentBody', $this->contentLength);
|
||||
}
|
||||
|
||||
@@ -913,15 +1002,31 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
||||
protected function readResponse()
|
||||
{
|
||||
$bufferSize = $this->request->getConfig('buffer_size');
|
||||
// http://tools.ietf.org/html/rfc2616#section-8.2.3
|
||||
// ...the client SHOULD NOT wait for an indefinite period before sending the request body
|
||||
$timeout = $this->expect100Continue ? 1 : null;
|
||||
|
||||
do {
|
||||
$response = new HTTP_Request2_Response(
|
||||
$this->socket->readLine($bufferSize), true, $this->request->getUrl()
|
||||
);
|
||||
do {
|
||||
$headerLine = $this->socket->readLine($bufferSize);
|
||||
$response->parseHeaderLine($headerLine);
|
||||
} while ('' != $headerLine);
|
||||
try {
|
||||
$response = new HTTP_Request2_Response(
|
||||
$this->socket->readLine($bufferSize, $timeout), true, $this->request->getUrl()
|
||||
);
|
||||
do {
|
||||
$headerLine = $this->socket->readLine($bufferSize);
|
||||
$response->parseHeaderLine($headerLine);
|
||||
} while ('' != $headerLine);
|
||||
|
||||
} catch (HTTP_Request2_MessageException $e) {
|
||||
if (HTTP_Request2_Exception::TIMEOUT === $e->getCode()
|
||||
&& $this->expect100Continue
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
if ($this->expect100Continue && 100 == $response->getStatus()) {
|
||||
return $response;
|
||||
}
|
||||
} while (in_array($response->getStatus(), array(100, 101)));
|
||||
|
||||
$this->request->setLastEvent('receivedHeaders', $response);
|
||||
|
Reference in New Issue
Block a user