~tcuthbert/wordpress/openstack-objectstorage

« back to all changes in this revision

Viewing changes to vendor/guzzlehttp/guzzle/src/Adapter/Curl/CurlAdapter.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
 
 
3
namespace GuzzleHttp\Adapter\Curl;
 
4
 
 
5
use GuzzleHttp\Adapter\AdapterInterface;
 
6
use GuzzleHttp\Adapter\TransactionInterface;
 
7
use GuzzleHttp\Event\RequestEvents;
 
8
use GuzzleHttp\Exception\AdapterException;
 
9
use GuzzleHttp\Message\MessageFactoryInterface;
 
10
 
 
11
/**
 
12
 * HTTP adapter that uses cURL easy handles as a transport layer.
 
13
 *
 
14
 * Requires PHP 5.5+
 
15
 *
 
16
 * When using the CurlAdapter, custom curl options can be specified as an
 
17
 * associative array of curl option constants mapping to values in the
 
18
 * **curl** key of a request's configuration options.
 
19
 */
 
20
class CurlAdapter implements AdapterInterface
 
21
{
 
22
    /** @var CurlFactory */
 
23
    private $curlFactory;
 
24
 
 
25
    /** @var MessageFactoryInterface */
 
26
    private $messageFactory;
 
27
 
 
28
    /** @var array Array of curl easy handles */
 
29
    private $handles = [];
 
30
 
 
31
    /** @var array Array of owned curl easy handles */
 
32
    private $ownedHandles = [];
 
33
 
 
34
    /** @var int Total number of idle handles to keep in cache */
 
35
    private $maxHandles;
 
36
 
 
37
    /**
 
38
     * Accepts an associative array of options:
 
39
     *
 
40
     * - handle_factory: Optional callable factory used to create cURL handles.
 
41
     *   The callable is invoked with the following arguments:
 
42
     *   TransactionInterface, MessageFactoryInterface, and an optional cURL
 
43
     *   handle to modify. The factory method must then return a cURL resource.
 
44
     * - max_handles: Maximum number of idle handles (defaults to 5).
 
45
     *
 
46
     * @param MessageFactoryInterface $messageFactory
 
47
     * @param array $options Array of options to use with the adapter
 
48
     */
 
49
    public function __construct(
 
50
        MessageFactoryInterface $messageFactory,
 
51
        array $options = []
 
52
    ) {
 
53
        $this->handles = $this->ownedHandles = [];
 
54
        $this->messageFactory = $messageFactory;
 
55
        $this->curlFactory = isset($options['handle_factory'])
 
56
            ? $options['handle_factory']
 
57
            : new CurlFactory();
 
58
        $this->maxHandles = isset($options['max_handles'])
 
59
            ? $options['max_handles']
 
60
            : 5;
 
61
    }
 
62
 
 
63
    public function __destruct()
 
64
    {
 
65
        foreach ($this->handles as $handle) {
 
66
            if (is_resource($handle)) {
 
67
                curl_close($handle);
 
68
            }
 
69
        }
 
70
    }
 
71
 
 
72
    public function send(TransactionInterface $transaction)
 
73
    {
 
74
        RequestEvents::emitBefore($transaction);
 
75
        if ($response = $transaction->getResponse()) {
 
76
            return $response;
 
77
        }
 
78
 
 
79
        $factory = $this->curlFactory;
 
80
        $handle = $factory(
 
81
            $transaction,
 
82
            $this->messageFactory,
 
83
            $this->checkoutEasyHandle()
 
84
        );
 
85
 
 
86
        curl_exec($handle);
 
87
        $info = curl_getinfo($handle);
 
88
        $info['curl_result'] = curl_errno($handle);
 
89
 
 
90
        if ($info['curl_result']) {
 
91
            $this->handleError($transaction, $info, $handle);
 
92
        } else {
 
93
            $this->releaseEasyHandle($handle);
 
94
            if ($body = $transaction->getResponse()->getBody()) {
 
95
                $body->seek(0);
 
96
            }
 
97
            RequestEvents::emitComplete($transaction, $info);
 
98
        }
 
99
 
 
100
        return $transaction->getResponse();
 
101
    }
 
102
 
 
103
    private function handleError(
 
104
        TransactionInterface $transaction,
 
105
        $info,
 
106
        $handle
 
107
    ) {
 
108
        $error = curl_error($handle);
 
109
        $this->releaseEasyHandle($handle);
 
110
        RequestEvents::emitError(
 
111
            $transaction,
 
112
            new AdapterException("cURL error {$info['curl_result']}: {$error}"),
 
113
            $info
 
114
        );
 
115
    }
 
116
 
 
117
    private function checkoutEasyHandle()
 
118
    {
 
119
        // Find an unused handle in the cache
 
120
        if (false !== ($key = array_search(false, $this->ownedHandles, true))) {
 
121
            $this->ownedHandles[$key] = true;
 
122
            return $this->handles[$key];
 
123
        }
 
124
 
 
125
        // Add a new handle
 
126
        $handle = curl_init();
 
127
        $id = (int) $handle;
 
128
        $this->handles[$id] = $handle;
 
129
        $this->ownedHandles[$id] = true;
 
130
 
 
131
        return $handle;
 
132
    }
 
133
 
 
134
    private function releaseEasyHandle($handle)
 
135
    {
 
136
        $id = (int) $handle;
 
137
        if (count($this->ownedHandles) > $this->maxHandles) {
 
138
            curl_close($this->handles[$id]);
 
139
            unset($this->handles[$id], $this->ownedHandles[$id]);
 
140
        } else {
 
141
            // curl_reset doesn't clear these out for some reason
 
142
            curl_setopt_array($handle, [
 
143
                CURLOPT_HEADERFUNCTION   => null,
 
144
                CURLOPT_WRITEFUNCTION    => null,
 
145
                CURLOPT_READFUNCTION     => null,
 
146
                CURLOPT_PROGRESSFUNCTION => null
 
147
            ]);
 
148
            curl_reset($handle);
 
149
            $this->ownedHandles[$id] = false;
 
150
        }
 
151
    }
 
152
}