~tcuthbert/wordpress/openstack-objectstorage

« back to all changes in this revision

Viewing changes to vendor/guzzlehttp/guzzle/src/Adapter/Curl/CurlFactory.php

  • Committer: Jacek Nykis
  • Date: 2015-02-11 15:35:31 UTC
  • Revision ID: jacek.nykis@canonical.com-20150211153531-hmy6zi0ov2qfkl0b
Initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
namespace GuzzleHttp\Adapter\Curl;
 
3
 
 
4
use GuzzleHttp\Adapter\TransactionInterface;
 
5
use GuzzleHttp\Exception\AdapterException;
 
6
use GuzzleHttp\Message\MessageFactoryInterface;
 
7
use GuzzleHttp\Message\RequestInterface;
 
8
use GuzzleHttp\Stream\LazyOpenStream;
 
9
use GuzzleHttp\Stream\Stream;
 
10
 
 
11
/**
 
12
 * Creates curl resources from a request and response object
 
13
 */
 
14
class CurlFactory
 
15
{
 
16
    /**
 
17
     * Creates a cURL handle based on a transaction.
 
18
     *
 
19
     * @param TransactionInterface $transaction Holds a request and response
 
20
     * @param MessageFactoryInterface $messageFactory Used to create responses
 
21
     * @param null|resource $handle Optionally provide a curl handle to modify
 
22
     *
 
23
     * @return resource Returns a prepared cURL handle
 
24
     * @throws AdapterException when an option cannot be applied
 
25
     */
 
26
    public function __invoke(
 
27
        TransactionInterface $transaction,
 
28
        MessageFactoryInterface $messageFactory,
 
29
        $handle = null
 
30
    ) {
 
31
        $request = $transaction->getRequest();
 
32
        $mediator = new RequestMediator($transaction, $messageFactory);
 
33
        $options = $this->getDefaultOptions($request, $mediator);
 
34
        $this->applyMethod($request, $options);
 
35
        $this->applyTransferOptions($request, $mediator, $options);
 
36
        $this->applyHeaders($request, $options);
 
37
        unset($options['_headers']);
 
38
 
 
39
        // Add adapter options from the request's configuration options
 
40
        if ($config = $request->getConfig()['curl']) {
 
41
            $options = $this->applyCustomCurlOptions($config, $options);
 
42
        }
 
43
 
 
44
        if (!$handle) {
 
45
            $handle = curl_init();
 
46
        }
 
47
 
 
48
        curl_setopt_array($handle, $options);
 
49
 
 
50
        return $handle;
 
51
    }
 
52
 
 
53
    protected function getDefaultOptions(
 
54
        RequestInterface $request,
 
55
        RequestMediator $mediator
 
56
    ) {
 
57
        $url = $request->getUrl();
 
58
 
 
59
        // Strip fragment from URL. See:
 
60
        // https://github.com/guzzle/guzzle/issues/453
 
61
        if (($pos = strpos($url, '#')) !== false) {
 
62
            $url = substr($url, 0, $pos);
 
63
        }
 
64
 
 
65
        $config = $request->getConfig();
 
66
        $options = [
 
67
            CURLOPT_URL            => $url,
 
68
            CURLOPT_CONNECTTIMEOUT => $config['connect_timeout'] ?: 150,
 
69
            CURLOPT_RETURNTRANSFER => false,
 
70
            CURLOPT_HEADER         => false,
 
71
            CURLOPT_WRITEFUNCTION  => [$mediator, 'writeResponseBody'],
 
72
            CURLOPT_HEADERFUNCTION => [$mediator, 'receiveResponseHeader'],
 
73
            CURLOPT_READFUNCTION   => [$mediator, 'readRequestBody'],
 
74
            CURLOPT_HTTP_VERSION   => $request->getProtocolVersion() === '1.0'
 
75
                ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1,
 
76
            CURLOPT_SSL_VERIFYPEER => 1,
 
77
            CURLOPT_SSL_VERIFYHOST => 2,
 
78
            '_headers'             => $request->getHeaders()
 
79
        ];
 
80
 
 
81
        if (defined('CURLOPT_PROTOCOLS')) {
 
82
            // Allow only HTTP and HTTPS protocols
 
83
            $options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
 
84
        }
 
85
 
 
86
        // cURL sometimes adds a content-type by default. Prevent this.
 
87
        if (!$request->hasHeader('Content-Type')) {
 
88
            $options[CURLOPT_HTTPHEADER][] = 'Content-Type:';
 
89
        }
 
90
 
 
91
        return $options;
 
92
    }
 
93
 
 
94
    private function applyMethod(RequestInterface $request, array &$options)
 
95
    {
 
96
        $method = $request->getMethod();
 
97
        if ($method == 'HEAD') {
 
98
            $options[CURLOPT_NOBODY] = true;
 
99
            unset($options[CURLOPT_WRITEFUNCTION], $options[CURLOPT_READFUNCTION]);
 
100
        } else {
 
101
            $options[CURLOPT_CUSTOMREQUEST] = $method;
 
102
            if (!$request->getBody()) {
 
103
                unset($options[CURLOPT_READFUNCTION]);
 
104
            } else {
 
105
                $this->applyBody($request, $options);
 
106
            }
 
107
        }
 
108
    }
 
109
 
 
110
    private function applyBody(RequestInterface $request, array &$options)
 
111
    {
 
112
        if ($request->hasHeader('Content-Length')) {
 
113
            $size = (int) $request->getHeader('Content-Length');
 
114
        } else {
 
115
            $size = null;
 
116
        }
 
117
 
 
118
        $request->getBody()->seek(0);
 
119
 
 
120
        // You can send the body as a string using curl's CURLOPT_POSTFIELDS
 
121
        if (($size !== null && $size < 32768) ||
 
122
            isset($request->getConfig()['curl']['body_as_string'])
 
123
        ) {
 
124
            $options[CURLOPT_POSTFIELDS] = $request->getBody()->getContents();
 
125
            // Don't duplicate the Content-Length header
 
126
            $this->removeHeader('Content-Length', $options);
 
127
            $this->removeHeader('Transfer-Encoding', $options);
 
128
        } else {
 
129
            $options[CURLOPT_UPLOAD] = true;
 
130
            // Let cURL handle setting the Content-Length header
 
131
            if ($size !== null) {
 
132
                $options[CURLOPT_INFILESIZE] = $size;
 
133
                $this->removeHeader('Content-Length', $options);
 
134
            }
 
135
        }
 
136
 
 
137
        // If the Expect header is not present, prevent curl from adding it
 
138
        if (!$request->hasHeader('Expect')) {
 
139
            $options[CURLOPT_HTTPHEADER][] = 'Expect:';
 
140
        }
 
141
    }
 
142
 
 
143
    private function applyHeaders(RequestInterface $request, array &$options)
 
144
    {
 
145
        foreach ($options['_headers'] as $name => $values) {
 
146
            $options[CURLOPT_HTTPHEADER][] = $name . ': ' . implode(', ', $values);
 
147
        }
 
148
 
 
149
        // Remove the Expect header if one was not set
 
150
        if (!$request->hasHeader('Accept')) {
 
151
            $options[CURLOPT_HTTPHEADER][] = 'Accept:';
 
152
        }
 
153
    }
 
154
 
 
155
    private function applyTransferOptions(
 
156
        RequestInterface $request,
 
157
        RequestMediator $mediator,
 
158
        array &$options
 
159
    ) {
 
160
        static $methods;
 
161
        if (!$methods) {
 
162
            $methods = array_flip(get_class_methods(__CLASS__));
 
163
        }
 
164
 
 
165
        foreach ($request->getConfig()->toArray() as $key => $value) {
 
166
            $method = "add_{$key}";
 
167
            if (isset($methods[$method])) {
 
168
                $this->{$method}($request, $mediator, $options, $value);
 
169
            }
 
170
        }
 
171
    }
 
172
 
 
173
    private function add_debug(
 
174
        RequestInterface $request,
 
175
        RequestMediator $mediator,
 
176
        &$options,
 
177
        $value
 
178
    ) {
 
179
        if (!$value) {
 
180
            return;
 
181
        }
 
182
 
 
183
        if (!is_resource($value)) {
 
184
            $value = defined('STDOUT') ? STDOUT : fopen('php://output', 'w');
 
185
        }
 
186
 
 
187
        $options[CURLOPT_STDERR] = $value;
 
188
        $options[CURLOPT_VERBOSE] = true;
 
189
    }
 
190
 
 
191
    private function add_proxy(
 
192
        RequestInterface $request,
 
193
        RequestMediator $mediator,
 
194
        &$options,
 
195
        $value
 
196
    ) {
 
197
        if (!is_array($value)) {
 
198
            $options[CURLOPT_PROXY] = $value;
 
199
        } else {
 
200
            $scheme = $request->getScheme();
 
201
            if (isset($value[$scheme])) {
 
202
                $options[CURLOPT_PROXY] = $value[$scheme];
 
203
            }
 
204
        }
 
205
    }
 
206
 
 
207
    private function add_timeout(
 
208
        RequestInterface $request,
 
209
        RequestMediator $mediator,
 
210
        &$options,
 
211
        $value
 
212
    ) {
 
213
        $options[CURLOPT_TIMEOUT_MS] = $value * 1000;
 
214
    }
 
215
 
 
216
    private function add_connect_timeout(
 
217
        RequestInterface $request,
 
218
        RequestMediator $mediator,
 
219
        &$options,
 
220
        $value
 
221
    ) {
 
222
        $options[CURLOPT_CONNECTTIMEOUT_MS] = $value * 1000;
 
223
    }
 
224
 
 
225
    private function add_verify(
 
226
        RequestInterface $request,
 
227
        RequestMediator $mediator,
 
228
        &$options,
 
229
        $value
 
230
    ) {
 
231
        if ($value === false) {
 
232
            unset($options[CURLOPT_CAINFO]);
 
233
            $options[CURLOPT_SSL_VERIFYHOST] = 0;
 
234
            $options[CURLOPT_SSL_VERIFYPEER] = false;
 
235
        } elseif ($value === true || is_string($value)) {
 
236
            $options[CURLOPT_SSL_VERIFYHOST] = 2;
 
237
            $options[CURLOPT_SSL_VERIFYPEER] = true;
 
238
            if ($value !== true) {
 
239
                if (!file_exists($value)) {
 
240
                    throw new AdapterException('SSL certificate authority file'
 
241
                        . " not found: {$value}");
 
242
                }
 
243
                $options[CURLOPT_CAINFO] = $value;
 
244
            }
 
245
        }
 
246
    }
 
247
 
 
248
    private function add_cert(
 
249
        RequestInterface $request,
 
250
        RequestMediator $mediator,
 
251
        &$options,
 
252
        $value
 
253
    ) {
 
254
        if (!file_exists($value)) {
 
255
            throw new AdapterException("SSL certificate not found: {$value}");
 
256
        }
 
257
 
 
258
        $options[CURLOPT_SSLCERT] = $value;
 
259
    }
 
260
 
 
261
    private function add_ssl_key(
 
262
        RequestInterface $request,
 
263
        RequestMediator $mediator,
 
264
        &$options,
 
265
        $value
 
266
    ) {
 
267
        if (is_array($value)) {
 
268
            $options[CURLOPT_SSLKEYPASSWD] = $value[1];
 
269
            $value = $value[0];
 
270
        }
 
271
 
 
272
        if (!file_exists($value)) {
 
273
            throw new AdapterException("SSL private key not found: {$value}");
 
274
        }
 
275
 
 
276
        $options[CURLOPT_SSLKEY] = $value;
 
277
    }
 
278
 
 
279
    private function add_stream(
 
280
        RequestInterface $request,
 
281
        RequestMediator $mediator,
 
282
        &$options,
 
283
        $value
 
284
    ) {
 
285
        if ($value === false) {
 
286
            return;
 
287
        }
 
288
 
 
289
        throw new AdapterException('cURL adapters do not support the "stream"'
 
290
            . ' request option. This error is typically encountered when trying'
 
291
            . ' to send requests with the "stream" option set to true in '
 
292
            . ' parallel. You will either need to send these one at a time or'
 
293
            . ' implement a custom ParallelAdapterInterface that supports'
 
294
            . ' sending these types of requests in parallel. This error can'
 
295
            . ' also occur if the StreamAdapter is not available on your'
 
296
            . ' system (e.g., allow_url_fopen is disabled in your php.ini).');
 
297
    }
 
298
 
 
299
    private function add_save_to(
 
300
        RequestInterface $request,
 
301
        RequestMediator $mediator,
 
302
        &$options,
 
303
        $value
 
304
    ) {
 
305
        $mediator->setResponseBody(is_string($value)
 
306
            ? new LazyOpenStream($value, 'w')
 
307
            : Stream::factory($value));
 
308
    }
 
309
 
 
310
    private function add_decode_content(
 
311
        RequestInterface $request,
 
312
        RequestMediator $mediator,
 
313
        &$options,
 
314
        $value
 
315
    ) {
 
316
        if (!$request->hasHeader('Accept-Encoding')) {
 
317
            $options[CURLOPT_ENCODING] = '';
 
318
            // Don't let curl send the header over the wire
 
319
            $options[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
 
320
        } else {
 
321
            $options[CURLOPT_ENCODING] = $request->getHeader('Accept-Encoding');
 
322
        }
 
323
    }
 
324
 
 
325
    /**
 
326
     * Takes an array of curl options specified in the 'curl' option of a
 
327
     * request's configuration array and maps them to CURLOPT_* options.
 
328
     *
 
329
     * This method is only called when a  request has a 'curl' config setting.
 
330
     * Array key strings that start with CURL that have a matching constant
 
331
     * value will be automatically converted to the matching constant.
 
332
     *
 
333
     * @param array $config  Configuration array of custom curl option
 
334
     * @param array $options Array of existing curl options
 
335
     *
 
336
     * @return array Returns a new array of curl options
 
337
     */
 
338
    private function applyCustomCurlOptions(array $config, array $options)
 
339
    {
 
340
        unset($config['body_as_string']);
 
341
        $curlOptions = [];
 
342
 
 
343
        // Map curl constant strings to defined values
 
344
        foreach ($config as $key => $value) {
 
345
            if (defined($key) && substr($key, 0, 4) === 'CURL') {
 
346
                $key = constant($key);
 
347
            }
 
348
            $curlOptions[$key] = $value;
 
349
        }
 
350
 
 
351
        return $curlOptions + $options;
 
352
    }
 
353
 
 
354
    /**
 
355
     * Remove a header from the options array
 
356
     *
 
357
     * @param string $name    Case-insensitive header to remove
 
358
     * @param array  $options Array of options to modify
 
359
     */
 
360
    private function removeHeader($name, array &$options)
 
361
    {
 
362
        foreach (array_keys($options['_headers']) as $key) {
 
363
            if (!strcasecmp($key, $name)) {
 
364
                unset($options['_headers'][$key]);
 
365
                return;
 
366
            }
 
367
        }
 
368
    }
 
369
}