Passed
Push — master ( 65fed3...c68220 )
by frey
05:44 queued 02:42
created

LibcurlWrapper::performSendingRequest()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 20
ccs 0
cts 16
cp 0
rs 8.8571
cc 6
eloc 10
nc 7
nop 0
crap 42
1
<?php
2
3
namespace Freyo\Flysystem\QcloudCOSv4\Client;
4
5
// A simple wrapper for libcurl using multi interface to do transfers in parallel.
6
class LibcurlWrapper
7
{
8
    private $sequence; // integer: sequence id for each request.
9
    private $curlMultiHandle; // curl handle: curl multi handle.
10
    private $curlHandleInfo; // array: array of active curl handle.
11
    private $idleCurlHandle; // array: idle curl handle which can be reused.
12
13
    public function __construct()
14
    {
15
        $this->sequence = 0;
16
        $this->curlMultiHandle = curl_multi_init();
17
        $this->idleCurlHandle = [];
18
    }
19
20
    public function __destruct()
21
    {
22
        curl_multi_close($this->curlMultiHandle);
23
        foreach ($this->idleCurlHandle as $handle) {
24
            curl_close($handle);
25
        }
26
        $this->idleCurlHandle = [];
27
    }
28
29
    public function startSendingRequest($httpRequest, $done)
30
    {
31
        $this->sequence += 1;
32
33
        if (count($this->idleCurlHandle) !== 0) {
34
            $curlHandle = array_pop($this->idleCurlHandle);
35
        } else {
36
            $curlHandle = curl_init();
37
            if ($curlHandle === false) {
38
                return false;
39
            }
40
        }
41
42
        curl_setopt($curlHandle, CURLOPT_TIMEOUT_MS, $httpRequest->timeoutMs);
43
        curl_setopt($curlHandle, CURLOPT_URL, $httpRequest->url);
44
        curl_setopt($curlHandle, CURLOPT_HEADER, 1);
45
        curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, 1);
46
        $headers = $httpRequest->customHeaders;
47
        array_push($headers, 'User-Agent:'.Conf::getUserAgent());
48
        if ($httpRequest->method === 'POST') {
49
            if (defined('CURLOPT_SAFE_UPLOAD')) {
50
                curl_setopt($curlHandle, CURLOPT_SAFE_UPLOAD, true);
51
            }
52
53
            curl_setopt($curlHandle, CURLOPT_POST, true);
54
            $arr = LibcurlHelper::buildCustomPostFields($httpRequest->dataToPost);
55
            array_push($headers, 'Expect: 100-continue');
56
            array_push($headers, 'Content-Type: multipart/form-data; boundary='.$arr[0]);
57
            curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $arr[1]);
58
        }
59
        curl_setopt($curlHandle, CURLOPT_HTTPHEADER, $headers);
60
61
        curl_multi_add_handle($this->curlMultiHandle, $curlHandle);
62
63
        $this->curlHandleInfo[$this->sequence]['handle'] = $curlHandle;
64
        $this->curlHandleInfo[$this->sequence]['done'] = $done;
65
        $this->curlHandleInfo[$this->sequence]['request'] = $httpRequest;
66
    }
67
68
    public function performSendingRequest()
69
    {
70
        for (; ;) {
71
            $active = null;
72
73
            $mrc = $this->doCurlMulti();
74
75
            while ($active && $mrc == CURLM_OK) {
76
                if (curl_multi_select($this->curlMultiHandle) == -1) {
77
                    usleep(1);
78
                }
79
80
                $this->doCurlMulti();
81
            }
82
83
            if (count($this->curlHandleInfo) == 0) {
84
                break;
85
            }
86
        }
87
    }
88
89
    private function processResult($info)
90
    {
91
        $result = $info['result'];
92
        $handle = $info['handle'];
93
        $sequence = 0;
94
95
        foreach ($this->curlHandleInfo as $key => $info) {
96
            if ($info['handle'] === $handle) {
97
                $sequence = $key;
98
                break;
99
            }
100
        }
101
102
        $request = $this->curlHandleInfo[$sequence]['request'];
103
        $done = $this->curlHandleInfo[$sequence]['done'];
104
        $response = new HttpResponse();
105
106
        if ($result !== CURLE_OK) {
107
            $response->curlErrorCode = $result;
108
            $response->curlErrorMessage = curl_error($handle);
109
110
            call_user_func($done, $request, $response);
111
        } else {
112
            $responseStr = curl_multi_getcontent($handle);
113
            $headerSize = curl_getinfo($handle, CURLINFO_HEADER_SIZE);
114
            $headerStr = substr($responseStr, 0, $headerSize);
115
            $body = substr($responseStr, $headerSize);
116
117
            $response->curlErrorCode = curl_errno($handle);
118
            $response->curlErrorMessage = curl_error($handle);
119
            $response->statusCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
120
            $headLines = explode("\r\n", $headerStr);
121
            foreach ($headLines as $head) {
122
                $arr = explode(':', $head);
123
                if (count($arr) >= 2) {
124
                    $response->headers[trim($arr[0])] = trim($arr[1]);
125
                }
126
            }
127
            $response->body = $body;
128
129
            call_user_func($done, $request, $response);
130
        }
131
132
        unset($this->curlHandleInfo[$sequence]);
133
        curl_multi_remove_handle($this->curlMultiHandle, $handle);
134
135
        array_push($this->idleCurlHandle, $handle);
136
    }
137
138
    private function resetCurl($handle)
139
    {
140
        if (function_exists('curl_reset')) {
141
            curl_reset($handle);
142
        } else {
143
            LibcurlHelper::my_curl_reset($handle);
144
        }
145
    }
146
147
    private function doCurlMulti()
148
    {
149
        do {
150
            $mrc = curl_multi_exec($this->curlMultiHandle, $active);
151
            $info = curl_multi_info_read($this->curlMultiHandle);
152
            if ($info !== false) {
153
                $this->processResult($info);
154
            }
155
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);
156
157
        return $mrc;
158
    }
159
}
160