1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
14: namespace Cake\Http\Client\Adapter;
15:
16: use Cake\Http\Client\AdapterInterface;
17: use Cake\Http\Client\Request;
18: use Cake\Http\Client\Response;
19: use Cake\Http\Exception\HttpException;
20:
21: 22: 23: 24: 25: 26: 27: 28:
29: class Curl implements AdapterInterface
30: {
31: 32: 33:
34: public function send(Request $request, array $options)
35: {
36: $ch = curl_init();
37: $options = $this->buildOptions($request, $options);
38: curl_setopt_array($ch, $options);
39:
40: $body = $this->exec($ch);
41: if ($body === false) {
42: $errorCode = curl_errno($ch);
43: $error = curl_error($ch);
44: curl_close($ch);
45:
46: $status = 500;
47: if ($errorCode === CURLE_OPERATION_TIMEOUTED) {
48: $status = 504;
49: }
50: throw new HttpException("cURL Error ({$errorCode}) {$error}", $status);
51: }
52:
53: $responses = $this->createResponse($ch, $body);
54: curl_close($ch);
55:
56: return $responses;
57: }
58:
59: 60: 61: 62: 63: 64: 65:
66: public function buildOptions(Request $request, array $options)
67: {
68: $headers = [];
69: foreach ($request->getHeaders() as $key => $values) {
70: $headers[] = $key . ': ' . implode(', ', $values);
71: }
72:
73: $out = [
74: CURLOPT_URL => (string)$request->getUri(),
75: CURLOPT_HTTP_VERSION => $this->getProtocolVersion($request),
76: CURLOPT_RETURNTRANSFER => true,
77: CURLOPT_HEADER => true,
78: CURLOPT_HTTPHEADER => $headers
79: ];
80: switch ($request->getMethod()) {
81: case Request::METHOD_GET:
82: $out[CURLOPT_HTTPGET] = true;
83: break;
84:
85: case Request::METHOD_POST:
86: $out[CURLOPT_POST] = true;
87: break;
88:
89: default:
90: $out[CURLOPT_POST] = true;
91: $out[CURLOPT_CUSTOMREQUEST] = $request->getMethod();
92: break;
93: }
94:
95: $body = $request->getBody();
96: if ($body) {
97: $body->rewind();
98: $out[CURLOPT_POSTFIELDS] = $body->getContents();
99: }
100:
101: if (empty($options['ssl_cafile'])) {
102: $options['ssl_cafile'] = CORE_PATH . 'config' . DIRECTORY_SEPARATOR . 'cacert.pem';
103: }
104: if (!empty($options['ssl_verify_host'])) {
105:
106: $options['ssl_verify_host'] = 2;
107: }
108: $optionMap = [
109: 'timeout' => CURLOPT_TIMEOUT,
110: 'ssl_verify_peer' => CURLOPT_SSL_VERIFYPEER,
111: 'ssl_verify_host' => CURLOPT_SSL_VERIFYHOST,
112: 'ssl_cafile' => CURLOPT_CAINFO,
113: 'ssl_local_cert' => CURLOPT_SSLCERT,
114: 'ssl_passphrase' => CURLOPT_SSLCERTPASSWD,
115: ];
116: foreach ($optionMap as $option => $curlOpt) {
117: if (isset($options[$option])) {
118: $out[$curlOpt] = $options[$option];
119: }
120: }
121: if (isset($options['proxy']['proxy'])) {
122: $out[CURLOPT_PROXY] = $options['proxy']['proxy'];
123: }
124: if (isset($options['proxy']['username'])) {
125: $password = !empty($options['proxy']['password']) ? $options['proxy']['password'] : '';
126: $out[CURLOPT_PROXYUSERPWD] = $options['proxy']['username'] . ':' . $password;
127: }
128: if (isset($options['curl']) && is_array($options['curl'])) {
129:
130: foreach ($options['curl'] as $key => $value) {
131: $out[$key] = $value;
132: }
133: }
134:
135: return $out;
136: }
137:
138: 139: 140: 141: 142: 143:
144: protected function getProtocolVersion(Request $request)
145: {
146: switch ($request->getProtocolVersion()) {
147: case '1.0':
148: return CURL_HTTP_VERSION_1_0;
149: case '1.1':
150: return CURL_HTTP_VERSION_1_1;
151: case '2.0':
152: if (defined('CURL_HTTP_VERSION_2_0')) {
153: return CURL_HTTP_VERSION_2_0;
154: }
155: throw new HttpException('libcurl 7.33 needed for HTTP 2.0 support');
156: }
157:
158: return CURL_HTTP_VERSION_NONE;
159: }
160:
161: 162: 163: 164: 165: 166: 167:
168: protected function createResponse($handle, $responseData)
169: {
170: $headerSize = curl_getinfo($handle, CURLINFO_HEADER_SIZE);
171: $headers = trim(substr($responseData, 0, $headerSize));
172: $body = substr($responseData, $headerSize);
173: $response = new Response(explode("\r\n", $headers), $body);
174:
175: return [$response];
176: }
177:
178: 179: 180: 181: 182: 183:
184: protected function exec($ch)
185: {
186: return curl_exec($ch);
187: }
188: }
189: