Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Http
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 6
650
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 get
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 post
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 put
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 delete
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 request
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 1
380
1<?php
2namespace Ably;
3
4use Ably\AblyRest;
5use Ably\Log;
6use Ably\Exceptions\AblyException;
7use Ably\Exceptions\AblyRequestException;
8use Ably\Utils\CurlWrapper;
9
10/**
11 * Makes HTTP requests using cURL
12 */
13class Http {
14
15    /**
16     * @var string $postDataFormat How $params is interpreted when sent as a string.
17     * Default: 'json'. 'msgpack' support may be added in future
18     */
19    protected $postDataFormat;
20
21    /**
22     * @var integer $timeout Timeout for a cURL connection in ms.
23     */
24    protected $connectTimeout;
25    
26    /**
27     * @var integer $timeout Timeout for a cURL request in ms.
28     */
29    protected $requestTimeout;
30
31    /**
32     * @var \Ably\Utils\CurlWrapper $curl Holds a CurlWrapper instance used for building requests.
33     */
34    protected $curl;
35
36    /**
37     * Constructor
38     */
39    public function __construct( $clientOptions ) {
40        $this->postDataFormat = $clientOptions->useBinaryProtocol ? 'msgpack' : 'json';
41        $this->connectTimeout = $clientOptions->httpOpenTimeout;
42        $this->requestTimeout = $clientOptions->httpRequestTimeout;
43        $this->curl = new CurlWrapper();
44    }
45
46    /**
47     * Wrapper to do a GET request
48     * @see Http::request()
49     */
50    public function get( $url, $headers = [], $params = [] ) {
51        return $this->request( 'GET', $url, $headers, $params );
52    }
53
54    /**
55     * Wrapper to do a POST request
56     * @see Http::request()
57     */
58    public function post( $url, $headers = [], $params = [] ) {
59        return $this->request( 'POST', $url, $headers, $params );
60    }
61
62    /**
63     * Wrapper to do a PUT request
64     * @see Http::request()
65     */
66    public function put( $url, $headers = [], $params = [] ) {
67        return $this->request( 'PUT', $url, $headers, $params );
68    }
69
70    /**
71     * Wrapper to do a DELETE request
72     * @see Http::request()
73     */
74    public function delete( $url, $headers = [], $params = [] ) {
75        return $this->request( 'DELETE', $url, $headers, $params );
76    }
77
78    /**
79     * Executes a cURL request
80     * @param string $method HTTP method (GET, POST, PUT, DELETE, ...)
81     * @param string $url Absolute URL to make a request on
82     * @param array $headers HTTP headers to send
83     * @param array|string $params Array of parameters to submit or a JSON string
84     * @throws AblyRequestException if the request fails
85     * @throws AblyRequestTimeoutException if the request times out
86     * @return array with 'headers' and 'body' fields, body is automatically decoded
87     */
88    public function request( $method, $url, $headers = [], $params = [] ) {
89
90        $ch = $this->curl->init($url);
91
92        $this->curl->setOpt($ch, CURLOPT_CONNECTTIMEOUT_MS, $this->connectTimeout); 
93        $this->curl->setOpt($ch, CURLOPT_TIMEOUT_MS, $this->requestTimeout);
94
95        if (!empty($params)) {
96            if (is_array( $params )) {
97                $paramsQuery = http_build_query( $params );
98
99                if ($method == 'GET') {
100                    if ($paramsQuery) {
101                        $url .= '?' . $paramsQuery;
102                    }
103                    $this->curl->setOpt( $ch, CURLOPT_URL, $url );
104                } else if ($method == 'POST') {
105                    $this->curl->setOpt( $ch, CURLOPT_POST, true );
106                    $this->curl->setOpt( $ch, CURLOPT_POSTFIELDS, $paramsQuery );
107                } else {
108                    $this->curl->setOpt( $ch, CURLOPT_CUSTOMREQUEST, $method );
109                    $this->curl->setOpt( $ch, CURLOPT_POSTFIELDS, $paramsQuery );
110                }
111            } else if (is_string( $params )) { // json or msgpack
112                if ($method == 'GET') {
113                } else if ($method == 'POST') {
114                    $this->curl->setOpt( $ch, CURLOPT_POST, true );
115                } else {
116                    $this->curl->setOpt( $ch, CURLOPT_CUSTOMREQUEST, $method );
117                }
118
119                $this->curl->setOpt( $ch, CURLOPT_POSTFIELDS, $params );
120
121                if ($this->postDataFormat == 'json') {
122                    array_push( $headers, 'Content-Type: application/json' );
123                }
124            } else {
125                throw new AblyRequestException( 'Unknown $params format' );
126            }
127        }
128
129        if (!empty($headers)) {
130            $this->curl->setOpt( $ch, CURLOPT_HTTPHEADER, $headers );
131        }
132
133        $this->curl->setOpt( $ch, CURLOPT_RETURNTRANSFER, true );
134        if ( Log::getLogLevel() >= Log::VERBOSE ) {
135            $this->curl->setOpt( $ch, CURLOPT_VERBOSE, true );
136        }
137        $this->curl->setOpt( $ch, CURLOPT_HEADER, true ); // return response headers
138
139        Log::d( 'cURL command:', $this->curl->getCommand( $ch ) );
140
141        $raw = $this->curl->exec( $ch );
142        $info = $this->curl->getInfo( $ch );
143        $err = $this->curl->getErrNo( $ch );
144        $errmsg = $err ? $this->curl->getError( $ch ) : '';
145        
146        $this->curl->close( $ch );
147
148        if ( $err ) { // a connection error has occured (no data received)
149            Log::e( 'cURL error:', $err, $errmsg );
150            throw new AblyRequestException( 'cURL error: ' . $errmsg, 50003, 500 );
151        }
152
153        $resHeaders = substr( $raw, 0, $info['header_size'] );
154        $body = substr( $raw, $info['header_size'] );
155        $decodedBody = json_decode( $body );
156
157        $response = [ 'headers' => $resHeaders, 'body' => $decodedBody ? $decodedBody : $body ];
158
159        Log::v( 'cURL request response:', $info['http_code'], $response );
160
161        if ( $info['http_code'] < 200 || $info['http_code'] >= 300 ) {
162            $ablyCode = empty( $decodedBody->error->code ) ? $info['http_code'] * 100 : $decodedBody->error->code * 1;
163            $errorMessage = empty( $decodedBody->error->message ) ? 'cURL request failed' : $decodedBody->error->message;
164
165            throw new AblyRequestException( $errorMessage, $ablyCode, $info['http_code'], $response );
166        }
167
168        return $response;
169    }
170}