Skip to content

Commit

Permalink
Merge 9e7791a into ea37f1e
Browse files Browse the repository at this point in the history
  • Loading branch information
GeLoLabs committed Apr 18, 2015
2 parents ea37f1e + 9e7791a commit df564c9
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 66 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -0,0 +1,3 @@
/composer.lock
/vendor
/tests/coverage
35 changes: 34 additions & 1 deletion library/Requests.php
Expand Up @@ -54,6 +54,20 @@ class Requests {
*/
const DELETE = 'DELETE';

/**
* OPTIONS method
*
* @var string
*/
const OPTIONS = 'OPTIONS';

/**
* TRACE method
*
* @var string
*/
const TRACE = 'TRACE';

/**
* PATCH method
*
Expand Down Expand Up @@ -181,7 +195,7 @@ protected static function get_transport($capabilities = array()) {
if (self::$transport[$cap_string] === null) {
throw new Requests_Exception('No working transports found', 'notransport', self::$transports);
}

return new self::$transport[$cap_string]();
}

Expand Down Expand Up @@ -235,6 +249,20 @@ public static function put($url, $headers = array(), $data = array(), $options =
return self::request($url, $headers, $data, self::PUT, $options);
}

/**
* Send an OPTIONS request
*/
public static function options($url, $headers = array(), $data = array(), $options = array()) {
return self::request($url, $headers, $data, self::OPTIONS, $options);
}

/**
* Send a TRACE request
*/
public static function trace($url, $headers = array(), $data = array(), $options = array()) {
return self::request($url, $headers, $data, self::TRACE, $options);
}

/**
* Send a PATCH request
*
Expand Down Expand Up @@ -450,6 +478,7 @@ protected static function get_default_options($multirequest = false) {
'timeout' => 10,
'connect_timeout' => 10,
'useragent' => 'php-requests/' . self::VERSION,
'protocol_version' => 1.1,
'redirected' => 0,
'redirects' => 10,
'follow_redirects' => true,
Expand Down Expand Up @@ -519,6 +548,10 @@ protected static function set_defaults(&$url, &$headers, &$data, &$type, &$optio
$iri->host = Requests_IDNAEncoder::encode($iri->ihost);
$url = $iri->uri;
}

if (!isset($options['data_as_query'])) {
$options['data_as_query'] = in_array($options['type'], array(Requests::HEAD, Requests::GET, Requests::DELETE));
}
}

/**
Expand Down
50 changes: 29 additions & 21 deletions library/Requests/Transport/cURL.php
Expand Up @@ -64,19 +64,6 @@ class Requests_Transport_cURL implements Requests_Transport {
public function __construct() {
$curl = curl_version();
$this->version = $curl['version_number'];
$this->fp = curl_init();

curl_setopt($this->fp, CURLOPT_HEADER, false);
curl_setopt($this->fp, CURLOPT_RETURNTRANSFER, 1);
if ($this->version >= self::CURL_7_10_5) {
curl_setopt($this->fp, CURLOPT_ENCODING, '');
}
if (defined('CURLOPT_PROTOCOLS')) {
curl_setopt($this->fp, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
}
if (defined('CURLOPT_REDIR_PROTOCOLS')) {
curl_setopt($this->fp, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
}
}

/**
Expand Down Expand Up @@ -225,14 +212,31 @@ public function &get_subrequest_handle($url, $headers, $data, $options) {
* @param array $options Request options, see {@see Requests::response()} for documentation
*/
protected function setup_handle($url, $headers, $data, $options) {
$this->fp = curl_init();

curl_setopt($this->fp, CURLOPT_HEADER, false);
curl_setopt($this->fp, CURLOPT_RETURNTRANSFER, 1);
if ($this->version >= self::CURL_7_10_5) {
curl_setopt($this->fp, CURLOPT_ENCODING, '');
}
if (defined('CURLOPT_PROTOCOLS')) {
curl_setopt($this->fp, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
}
if (defined('CURLOPT_REDIR_PROTOCOLS')) {
curl_setopt($this->fp, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
}

$options['hooks']->dispatch('curl.before_request', array(&$this->fp));

$headers = Requests::flatten($headers);
if (in_array($options['type'], array(Requests::HEAD, Requests::GET, Requests::DELETE)) & !empty($data)) {
$url = self::format_get($url, $data);
}
elseif (!empty($data) && !is_string($data)) {
$data = http_build_query($data, null, '&');

if (!empty($data)) {
if ($options['data_as_query']) {
$url = self::format_get($url, $data);
$data = '';
} elseif (!is_string($data)) {
$data = http_build_query($data, null, '&');
}
}

switch ($options['type']) {
Expand All @@ -242,15 +246,18 @@ protected function setup_handle($url, $headers, $data, $options) {
break;
case Requests::PATCH:
case Requests::PUT:
case Requests::DELETE:
case Requests::OPTIONS:
curl_setopt($this->fp, CURLOPT_CUSTOMREQUEST, $options['type']);
curl_setopt($this->fp, CURLOPT_POSTFIELDS, $data);
break;
case Requests::DELETE:
curl_setopt($this->fp, CURLOPT_CUSTOMREQUEST, 'DELETE');
break;
case Requests::HEAD:
curl_setopt($this->fp, CURLOPT_CUSTOMREQUEST, $options['type']);
curl_setopt($this->fp, CURLOPT_NOBODY, true);
break;
case Requests::TRACE:
curl_setopt($this->fp, CURLOPT_CUSTOMREQUEST, $options['type']);
break;
}

if( is_int($options['timeout']) or $this->version < self::CURL_7_16_2 ) {
Expand All @@ -267,6 +274,7 @@ protected function setup_handle($url, $headers, $data, $options) {
curl_setopt($this->fp, CURLOPT_REFERER, $url);
curl_setopt($this->fp, CURLOPT_USERAGENT, $options['useragent']);
curl_setopt($this->fp, CURLOPT_HTTPHEADER, $headers);
curl_setopt($this->fp, CURLOPT_HTTP_VERSION, $options['protocol_version'] == 1.1 ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0);

if (true === $options['blocking']) {
curl_setopt($this->fp, CURLOPT_HEADERFUNCTION, array(&$this, 'stream_headers'));
Expand Down
83 changes: 39 additions & 44 deletions library/Requests/Transport/fsockopen.php
Expand Up @@ -48,7 +48,8 @@ class Requests_Transport_fsockopen implements Requests_Transport {
* @param array $options Request options, see {@see Requests::response()} for documentation
* @return string Raw HTTP result
*/
public function request($url, $headers = array(), $data = array(), $options = array()) {
public function request($url, $headers = array(), $data = array(), $options = array())
{
$options['hooks']->dispatch('fsockopen.before_request');

$url_parts = parse_url($url);
Expand Down Expand Up @@ -89,13 +90,12 @@ public function request($url, $headers = array(), $data = array(), $options = ar
}

stream_context_set_option($context, array('ssl' => $context_options));
}
else {
} else {
$remote_socket = 'tcp://' . $host;
}

$proxy = isset( $options['proxy'] );
$proxy_auth = $proxy && isset( $options['proxy_username'] ) && isset( $options['proxy_password'] );
$proxy = isset($options['proxy']);
$proxy_auth = $proxy && isset($options['proxy_username']) && isset($options['proxy_password']);

if (!isset($url_parts['port'])) {
$url_parts['port'] = 80;
Expand All @@ -110,62 +110,48 @@ public function request($url, $headers = array(), $data = array(), $options = ar

restore_error_handler();

if ($verifyname) {
if (!$this->verify_certificate_from_context($host, $context)) {
throw new Requests_Exception('SSL certificate did not match the requested domain name', 'ssl.no_match');
}
if ($verifyname && !$this->verify_certificate_from_context($host, $context)) {
throw new Requests_Exception('SSL certificate did not match the requested domain name', 'ssl.no_match');
}

if (!$fp) {
if ($errno === 0) {
// Connection issue
throw new Requests_Exception(rtrim($this->connect_error), 'fsockopen.connect_error');
}
else {
throw new Requests_Exception($errstr, 'fsockopenerror');
return;
}

throw new Requests_Exception($errstr, 'fsockopenerror');
}

$request_body = '';
$out = '';
switch ($options['type']) {
case Requests::POST:
case Requests::PUT:
case Requests::PATCH:
if (isset($url_parts['path'])) {
$path = $url_parts['path'];
if (isset($url_parts['query'])) {
$path .= '?' . $url_parts['query'];
}
}
else {
$path = '/';
}

$options['hooks']->dispatch( 'fsockopen.remote_host_path', array( &$path, $url ) );
$out = $options['type'] . " $path HTTP/1.0\r\n";
if ($options['data_as_query']) {
$path = self::format_get($url_parts, $data);
$data = '';
} else {
$path = self::format_get($url_parts, array());
}

if (is_array($data)) {
$request_body = http_build_query($data, null, '&');
}
else {
$request_body = $data;
}
$options['hooks']->dispatch('fsockopen.remote_host_path', array(&$path, $url));
$out = $options['type'] . " $path HTTP/" . $options['protocol_version'] . "\r\n";

if ($options['type'] !== Requests::TRACE) {
if (is_array($data)) {
$request_body = http_build_query($data, null, '&');
} else {
$request_body = $data;
}

if (!empty($data)) {
if (empty($headers['Content-Length'])) {
$headers['Content-Length'] = strlen($request_body);
}

if (empty($headers['Content-Type'])) {
$headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
}
break;
case Requests::HEAD:
case Requests::GET:
case Requests::DELETE:
$path = self::format_get($url_parts, $data);
$options['hooks']->dispatch('fsockopen.remote_host_path', array(&$path, $url));
$out = $options['type'] . " $path HTTP/1.0\r\n";
break;
}
}
$out .= "Host: {$url_parts['host']}";

Expand All @@ -174,7 +160,12 @@ public function request($url, $headers = array(), $data = array(), $options = ar
}
$out .= "\r\n";

$out .= "User-Agent: {$options['useragent']}\r\n";
$lowerHeaders = array_change_key_case($headers);

if (!isset($lowerHeaders['user-agent'])) {
$out .= "User-Agent: {$options['useragent']}\r\n";
}

$accept_encoding = $this->accept_encoding();
if (!empty($accept_encoding)) {
$out .= "Accept-Encoding: $accept_encoding\r\n";
Expand All @@ -192,7 +183,11 @@ public function request($url, $headers = array(), $data = array(), $options = ar
$out .= "\r\n";
}

$out .= "Connection: Close\r\n\r\n" . $request_body;
if (!isset($lowerHeaders['connection'])) {
$out .= "Connection: Close\r\n";
}

$out .= "\r\n" . $request_body;

$options['hooks']->dispatch('fsockopen.before_send', array(&$out));

Expand Down
39 changes: 39 additions & 0 deletions tests/Transport/Base.php
Expand Up @@ -110,6 +110,11 @@ public function testHEAD() {
$this->assertEquals('', $request->body);
}

public function testTRACE() {
$request = Requests::trace(httpbin('/get'), array(), $this->getOptions());
$this->assertEquals(200, $request->status_code);
}

public function testRawPOST() {
$data = 'test';
$request = Requests::post(httpbin('/post'), array(), $data, $this->getOptions());
Expand Down Expand Up @@ -215,6 +220,11 @@ public function testPATCHWithArray() {
$this->assertEquals(array('test' => 'true', 'test2' => 'test'), $result['form']);
}

public function testOPTIONS() {
$request = Requests::options(httpbin('/post'), array(), array(), $this->getOptions());
$this->assertEquals(200, $request->status_code);
}

public function testDELETE() {
$request = Requests::delete(httpbin('/delete'), array(), $this->getOptions());
$this->assertEquals(200, $request->status_code);
Expand Down Expand Up @@ -680,4 +690,33 @@ public function testHostHeader() {
$portXpathMatches = $portXpath->query('//p/b');
$this->assertEquals(8080, $portXpathMatches->item(0)->nodeValue);
}

public function testReusableTransport() {
$options = $this->getOptions(array('transport' => new $this->transport()));

$request1 = Requests::get(httpbin('/get'), array(), $options);
$request2 = Requests::get(httpbin('/get'), array(), $options);

$this->assertEquals(200, $request1->status_code);
$this->assertEquals(200, $request2->status_code);

$result1 = json_decode($request1->body, true);
$result2 = json_decode($request2->body, true);

$this->assertEquals(httpbin('/get'), $result1['url']);
$this->assertEquals(httpbin('/get'), $result2['url']);

$this->assertEmpty($result1['args']);
$this->assertEmpty($result2['args']);
}

public function testDataAsQuery() {
$data = array('test' => 'true', 'test2' => 'test');
$request = Requests::post(httpbin('/post'), array(), $data, $this->getOptions(array('data_as_query' => true)));
$this->assertEquals(200, $request->status_code);

$result = json_decode($request->body, true);
$this->assertEquals(httpbin('/post').'?test=true&test2=test', $result['url']);
$this->assertEquals('', $result['data']);
}
}

0 comments on commit df564c9

Please sign in to comment.