1<?php
2/**
3 * HTTP Proxy connection interface
4 *
5 * @package Requests\Proxy
6 * @since 1.6
7 */
8
9namespace WpOrg\Requests\Proxy;
10
11use WpOrg\Requests\Exception\ArgumentCount;
12use WpOrg\Requests\Exception\InvalidArgument;
13use WpOrg\Requests\Hooks;
14use WpOrg\Requests\Proxy;
15
16/**
17 * HTTP Proxy connection interface
18 *
19 * Provides a handler for connection via an HTTP proxy
20 *
21 * @package Requests\Proxy
22 * @since 1.6
23 */
24final class Http implements Proxy {
25 /**
26 * Proxy host and port
27 *
28 * Notation: "host:port" (eg 127.0.0.1:8080 or someproxy.com:3128)
29 *
30 * @var string
31 */
32 public $proxy;
33
34 /**
35 * Username
36 *
37 * @var string
38 */
39 public $user;
40
41 /**
42 * Password
43 *
44 * @var string
45 */
46 public $pass;
47
48 /**
49 * Do we need to authenticate? (ie username & password have been provided)
50 *
51 * @var boolean
52 */
53 public $use_authentication;
54
55 /**
56 * Constructor
57 *
58 * @since 1.6
59 *
60 * @param array|string|null $args Proxy as a string or an array of proxy, user and password.
61 * When passed as an array, must have exactly one (proxy)
62 * or three elements (proxy, user, password).
63 *
64 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not an array, a string or null.
65 * @throws \WpOrg\Requests\Exception\ArgumentCount On incorrect number of arguments (`proxyhttpbadargs`)
66 */
67 public function __construct($args = null) {
68 if (is_string($args)) {
69 $this->proxy = $args;
70 } elseif (is_array($args)) {
71 if (count($args) === 1) {
72 list($this->proxy) = $args;
73 } elseif (count($args) === 3) {
74 list($this->proxy, $this->user, $this->pass) = $args;
75 $this->use_authentication = true;
76 } else {
77 throw ArgumentCount::create(
78 'an array with exactly one element or exactly three elements',
79 count($args),
80 'proxyhttpbadargs'
81 );
82 }
83 } elseif ($args !== null) {
84 throw InvalidArgument::create(1, '$args', 'array|string|null', gettype($args));
85 }
86 }
87
88 /**
89 * Register the necessary callbacks
90 *
91 * @since 1.6
92 * @see \WpOrg\Requests\Proxy\Http::curl_before_send()
93 * @see \WpOrg\Requests\Proxy\Http::fsockopen_remote_socket()
94 * @see \WpOrg\Requests\Proxy\Http::fsockopen_remote_host_path()
95 * @see \WpOrg\Requests\Proxy\Http::fsockopen_header()
96 * @param \WpOrg\Requests\Hooks $hooks Hook system
97 */
98 public function register(Hooks $hooks) {
99 $hooks->register('curl.before_send', [$this, 'curl_before_send']);
100
101 $hooks->register('fsockopen.remote_socket', [$this, 'fsockopen_remote_socket']);
102 $hooks->register('fsockopen.remote_host_path', [$this, 'fsockopen_remote_host_path']);
103 if ($this->use_authentication) {
104 $hooks->register('fsockopen.after_headers', [$this, 'fsockopen_header']);
105 }
106 }
107
108 /**
109 * Set cURL parameters before the data is sent
110 *
111 * @since 1.6
112 * @param resource|\CurlHandle $handle cURL handle
113 */
114 public function curl_before_send(&$handle) {
115 curl_setopt($handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
116 curl_setopt($handle, CURLOPT_PROXY, $this->proxy);
117
118 if ($this->use_authentication) {
119 curl_setopt($handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
120 curl_setopt($handle, CURLOPT_PROXYUSERPWD, $this->get_auth_string());
121 }
122 }
123
124 /**
125 * Alter remote socket information before opening socket connection
126 *
127 * @since 1.6
128 * @param string $remote_socket Socket connection string
129 */
130 public function fsockopen_remote_socket(&$remote_socket) {
131 $remote_socket = $this->proxy;
132 }
133
134 /**
135 * Alter remote path before getting stream data
136 *
137 * @since 1.6
138 * @param string $path Path to send in HTTP request string ("GET ...")
139 * @param string $url Full URL we're requesting
140 */
141 public function fsockopen_remote_host_path(&$path, $url) {
142 $path = $url;
143 }
144
145 /**
146 * Add extra headers to the request before sending
147 *
148 * @since 1.6
149 * @param string $out HTTP header string
150 */
151 public function fsockopen_header(&$out) {
152 $out .= sprintf("Proxy-Authorization: Basic %s\r\n", base64_encode($this->get_auth_string()));
153 }
154
155 /**
156 * Get the authentication string (user:pass)
157 *
158 * @since 1.6
159 * @return string
160 */
161 public function get_auth_string() {
162 return $this->user . ':' . $this->pass;
163 }
164}
165