Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proxy support #72

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ here are prose; you might also want to check out the [API documentation][].
* [Authenticating your request][authentication]
* Advanced Usage
* [Custom authentication][authentication-custom]
* [Requests through proxy][proxy]
* [Hooking system][hooks]

[goals]: goals.md
Expand All @@ -23,4 +24,5 @@ here are prose; you might also want to check out the [API documentation][].
[usage-advanced]: usage-advanced.md
[authentication]: authentication.md
[authentication-custom]: authentication-custom.md
[hooks]: hooks.md
[hooks]: hooks.md
[proxy]: proxy.md
23 changes: 23 additions & 0 deletions docs/proxy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Proxy Support
=============

You can easily make requests through HTTP proxies.

To make requests through an open proxy, specify the following options:

```php
$options = array(
'proxy' => '127.0.0.1:3128'
);
Requests::get('http://httpbin.org/ip', array(), $options);
```

If your proxy needs you to authenticate, the option will become an array like
the following:

```php
$options = array(
'proxy' => array( '127.0.0.1:3128', 'my_username', 'my_password' )
);
Requests::get('http://httpbin.org/ip', array(), $options);
```
18 changes: 18 additions & 0 deletions examples/proxy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

// First, include Requests
include('../library/Requests.php');

// Next, make sure Requests can load internal classes
Requests::register_autoloader();

// Now let's make a request via a proxy.
$options = array(
'proxy' => '127.0.0.1:8080', // syntax: host:port, eg 12.13.14.14:8080 or someproxy.com:3128
// If you need to authenticate, use the following syntax:
// 'proxy' => array( '127.0.0.1:8080', 'username', 'password' ),
);
$request = Requests::get('http://httpbin.org/ip', array(), $options );

// See result
var_dump( $request->body );
12 changes: 11 additions & 1 deletion library/Requests.php
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ public static function patch($url, $headers, $data = array(), $options = array()
* - `auth`: Authentication handler or array of user/password details to use
* for Basic authentication
* (Requests_Auth|array|boolean, default: false)
* - `proxy`: Proxy details to use for proxy by-passing and authentication
* (Requests_Proxy|array|boolean, default: false)
* - `idn`: Enable IDN parsing
* (boolean, default: true)
* - `transport`: Custom transport. Either a class name, or a
Expand Down Expand Up @@ -447,6 +449,7 @@ protected static function get_default_options($multirequest = false) {
'type' => self::GET,
'filename' => false,
'auth' => false,
'proxy' => false,
'idn' => true,
'hooks' => null,
'transport' => null,
Expand Down Expand Up @@ -477,14 +480,21 @@ protected static function set_defaults(&$url, &$headers, &$data, &$type, &$optio
if (empty($options['hooks'])) {
$options['hooks'] = new Requests_Hooks();
}

if (is_array($options['auth'])) {
$options['auth'] = new Requests_Auth_Basic($options['auth']);
}
if ($options['auth'] !== false) {
$options['auth']->register($options['hooks']);
}

if (!empty($options['proxy'])) {
$options['proxy'] = new Requests_Proxy_HTTP($options['proxy']);
}
if ($options['proxy'] !== false) {
$options['proxy']->register($options['hooks']);
}

if ($options['idn'] !== false) {
$iri = new Requests_IRI($url);
$iri->host = Requests_IDNAEncoder::encode($iri->ihost);
Expand Down
35 changes: 35 additions & 0 deletions library/Requests/Proxy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
/**
* Proxy connection interface
*
* @package Requests
* @subpackage Proxy
* @since 1.6
*/

/**
* Proxy connection interface
*
* Implement this interface to handle proxy settings and authentication
*
* Parameters should be passed via the constructor where possible, as this
* makes it much easier for users to use your provider.
*
* @see Requests_Hooks
* @package Requests
* @subpackage Proxy
* @since 1.6
*/
interface Requests_Proxy {
/**
* Register hooks as needed
*
* This method is called in {@see Requests::request} when the user has set
* an instance as the 'auth' option. Use this callback to register all the
* hooks you'll need.
*
* @see Requests_Hooks::register
* @param Requests_Hooks $hooks Hook system
*/
public function register(Requests_Hooks &$hooks);
}
148 changes: 148 additions & 0 deletions library/Requests/Proxy/HTTP.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<?php
/**
* HTTP Proxy connection interface
*
* @package Requests
* @subpackage Proxy
* @since 1.6
*/

/**
* HTTP Proxy connection interface
*
* Provides a handler for connection via an HTTP proxy
*
* @package Requests
* @subpackage Proxy
* @since 1.6
*/
class Requests_Proxy_HTTP implements Requests_Proxy {
/**
* Proxy host and port
*
* Notation: "host:port" (eg 127.0.0.1:8080 or someproxy.com:3128)
*
* @var string
*/
public $proxy;

/**
* Username
*
* @var string
*/
public $user;

/**
* Password
*
* @var string
*/
public $pass;

/**
* Do we need to authenticate? (ie username & password have been provided)
*
* @var boolean
*/
public $use_authentication;

/**
* Constructor
*
* @since 1.6
* @throws Requests_Exception On incorrect number of arguments (`authbasicbadargs`)
* @param array|null $args Array of user and password. Must have exactly two elements
*/
public function __construct($args = null) {
if( is_string( $args ) ) {
$this->proxy = $args;
} elseif( is_array( $args ) ) {
if( count( $args ) == 1 ) {
list( $this->proxy ) = $args;
} elseif( count( $args ) == 3 ) {
list( $this->proxy, $this->user, $this->pass ) = $args;
$this->use_authentication = true;
} else {
throw new Requests_Exception( 'Invalid number of arguments', 'proxyhttpbadargs');
}
}

}

/**
* Register the necessary callbacks
*
* @since 1.6
* @see curl_before_send
* @see fsockopen_remote_socket
* @see fsockopen_remote_host_path
* @see fsockopen_header
* @param Requests_Hooks $hooks Hook system
*/
public function register(Requests_Hooks &$hooks) {
$hooks->register('curl.before_send', array(&$this, 'curl_before_send'));

$hooks->register('fsockopen.remote_socket', array(&$this, 'fsockopen_remote_socket'));
$hooks->register('fsockopen.remote_host_path', array(&$this, 'fsockopen_remote_host_path'));
if( $this->use_authentication ) {
$hooks->register('fsockopen.after_headers', array(&$this, 'fsockopen_header'));
}
}

/**
* Set cURL parameters before the data is sent
*
* @since 1.6
* @param resource $handle cURL resource
*/
public function curl_before_send(&$handle) {
curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
curl_setopt( $handle, CURLOPT_PROXY, $this->proxy );

if( $this->use_authentication ) {
curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $this->getAuthString() );
}
}

/**
* Alter remote socket information before opening socket connection
*
* @since 1.6
* @param string $out HTTP header string
*/
public function fsockopen_remote_socket( &$remote_socket ) {
$remote_socket = $this->proxy;
}

/**
* Alter remote path before getting stream data
*
* @since 1.6
* @param string $out HTTP header string
*/
public function fsockopen_remote_host_path( &$path, $url ) {
$path = $url;
}

/**
* Add extra headers to the request before sending
*
* @since 1.6
* @param string $out HTTP header string
*/
public function fsockopen_header( &$out ) {
$out .= "Proxy-Authorization: Basic " . base64_encode( $this->getAuthString() ) . "\r\n";
}

/**
* Get the authentication string (user:pass)
*
* @since 1.6
* @return string
*/
public function getAuthString() {
return $this->user . ':' . $this->pass;
}
}
2 changes: 1 addition & 1 deletion library/Requests/Transport/cURL.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public function __construct() {
*/
public function request($url, $headers = array(), $data = array(), $options = array()) {
$this->setup_handle($url, $headers, $data, $options);

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

if ($options['filename'] !== false) {
Expand Down
21 changes: 16 additions & 5 deletions library/Requests/Transport/fsockopen.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,21 @@ public function request($url, $headers = array(), $data = array(), $options = ar
else {
$remote_socket = 'tcp://' . $host;
}


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

if (!isset($url_parts['port'])) {
$url_parts['port'] = 80;
}
$remote_socket .= ':' . $url_parts['port'];

set_error_handler(array($this, 'connect_error_handler'), E_WARNING | E_NOTICE);
$fp = stream_socket_client($remote_socket, $errno, $errstr, $options['timeout'], STREAM_CLIENT_CONNECT, $context);

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

$fp = stream_socket_client( $remote_socket, $errno, $errstr, $options['timeout'], STREAM_CLIENT_CONNECT, $context);

restore_error_handler();

if (!$fp) {
Expand Down Expand Up @@ -114,7 +121,10 @@ public function request($url, $headers = array(), $data = array(), $options = ar
else {
$path = '/';
}

$options['hooks']->dispatch( 'fsockopen.remote_host_path', array( &$path, $url ) );
$out = $options['type'] . " $path HTTP/1.0\r\n";

if (is_array($data)) {
$request_body = http_build_query($data, null, '&');
}
Expand All @@ -131,16 +141,17 @@ public function request($url, $headers = array(), $data = array(), $options = ar
case Requests::HEAD:
case Requests::GET:
case Requests::DELETE:
$get = self::format_get($url_parts, $data);
$out = $options['type'] . " $get HTTP/1.0\r\n";
$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']}";

if ($url_parts['port'] !== 80) {
$out .= ":{$url_parts['port']}";
}

$out .= "\r\n";

$out .= "User-Agent: {$options['useragent']}\r\n";
Expand Down
Loading