This repository has been archived by the owner on Dec 19, 2023. It is now read-only.
/
SagCURLHTTPAdapter.php
133 lines (110 loc) · 3.95 KB
/
SagCURLHTTPAdapter.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<?php
/**
* Uses the PHP cURL bindings for HTTP communication with CouchDB. This gives
* you more advanced features, like SSL supports, with the cost of an
* additional dependency that your shared hosting environment might now have.
*
* @version 0.8.0
* @package HTTP
*/
require_once('SagHTTPAdapter.php');
class SagCURLHTTPAdapter extends SagHTTPAdapter {
private $ch;
public function __construct($host, $port) {
if(!extension_loaded('curl')) {
throw new SagException('Sag cannot use cURL on this system: the PHP cURL extension is not installed.');
}
parent::__construct($host, $port);
$this->ch = curl_init();
}
public function procPacket($method, $url, $data = null, $headers = array(), $specialHost = null, $specialPort = null) {
// the base cURL options
$opts = array(
CURLOPT_URL => "{$this->proto}://{$this->host}:{$this->port}{$url}",
CURLOPT_PORT => $this->port,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HEADER => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_NOBODY => false,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => $method
);
// cURL wants the headers as an array of strings, not an assoc array
if(is_array($headers) && sizeof($headers) > 0) {
$opts[CURLOPT_HTTPHEADER] = array();
foreach($headers as $k => $v) {
$opts[CURLOPT_HTTPHEADER][] = "$k: $v";
}
}
// send data through cURL's poorly named opt
if($data) {
$opts[CURLOPT_POSTFIELDS] = $data;
}
// special considerations for HEAD requests
if($method == 'HEAD') {
$opts[CURLOPT_NOBODY] = true;
}
// connect timeout
if(is_int($this->socketOpenTimeout)) {
$opts[CURLOPT_CONNECTTIMEOUT] = $this->socketOpenTimeout;
}
// exec timeout (seconds)
if(is_int($this->socketRWTimeoutSeconds)) {
$opts[CURLOPT_TIMEOUT] = $this->socketRWTimeoutSeconds;
}
// exec timeout (ms)
if(is_int($this->socketRWTimeoutMicroseconds)) {
$opts[CURLOPT_TIMEOUT_MS] = $this->socketRWTimeoutMicroseconds;
}
// SSL support: don't verify unless we have a cert set
if($this->proto === 'https') {
if(!$this->sslCertPath) {
$opts[CURLOPT_SSL_VERIFYPEER] = false;
}
else {
$opts[CURLOPT_SSL_VERIFYPEER] = true;
$opts[CURLOPT_SSL_VERIFYHOST] = true;
$opts[CURLOPT_CAINFO] = $this->sslCertPath;
}
}
curl_setopt_array($this->ch, $opts);
$chResponse = curl_exec($this->ch);
if($chResponse !== false) {
// prepare the response object
$response = new stdClass();
$response->headers = new stdClass();
$response->headers->_HTTP = new stdClass();
$response->body = '';
// split headers and body
list($headers, $response->body) = explode("\r\n\r\n", $chResponse);
// split up the headers
$headers = explode("\r\n", $headers);
for($i = 0; $i < sizeof($headers); $i++) {
// first element will always be the HTTP status line
if($i === 0) {
$response->headers->_HTTP->raw = $headers[$i];
preg_match('(^HTTP/(?P<version>\d+\.\d+)\s+(?P<status>\d+))S', $headers[$i], $match);
$response->headers->_HTTP->version = $match['version'];
$response->headers->_HTTP->status = $match['status'];
$response->status = $match['status'];
}
else {
$line = explode(':', $headers[$i], 2);
$line[0] = strtolower($line[0]);
$response->headers->$line[0] = ltrim($line[1]);
if($line[0] == 'Set-Cookie') {
$response->cookies = $this->parseCookieString($line[1]);
}
}
}
}
else if(curl_errno($this->ch)) {
throw new SagException('cURL error #' . curl_errno($this->ch) . ': ' . curl_error($this->ch));
}
else {
throw new SagException('cURL returned false without providing an error.');
}
return self::makeResult($response, $method);
}
}
?>