Skip to content

Commit cd7ec35

Browse files
committed
Add support for HTTP2 and HTTP3
1 parent dd85454 commit cd7ec35

File tree

4 files changed

+92
-23
lines changed

4 files changed

+92
-23
lines changed

src/Requests.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ protected static function parse_response($headers, $url, $req_headers, $req_data
772772
// Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2)
773773
$headers = preg_replace('/\n[ \t]/', ' ', $headers);
774774
$headers = explode("\n", $headers);
775-
preg_match('#^HTTP/(1\.\d)[ \t]+(\d+)#i', array_shift($headers), $matches);
775+
preg_match('#^HTTP/(1\.\d|2|3)[ \t]+(\d+)#i', array_shift($headers), $matches);
776776
if (empty($matches)) {
777777
throw new Exception('Response could not be parsed', 'noversion', $headers);
778778
}

src/Transport/Curl.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -374,11 +374,11 @@ private function setup_handle($url, $headers, $data, $options) {
374374
* add as much as a second to the time it takes for cURL to perform a request. To
375375
* prevent this, we need to set an empty "Expect" header. To match the behaviour of
376376
* Guzzle, we'll add the empty header to requests that are smaller than 1 MB and use
377-
* HTTP/1.1.
377+
* HTTP/1.0.
378378
*
379379
* https://curl.se/mail/lib-2017-07/0013.html
380380
*/
381-
if (!isset($headers['Expect']) && $options['protocol_version'] === 1.1) {
381+
if (!isset($headers['Expect']) && $options['protocol_version'] < 1.1) {
382382
$headers['Expect'] = $this->get_expect_header($data);
383383
}
384384

@@ -446,10 +446,19 @@ private function setup_handle($url, $headers, $data, $options) {
446446
curl_setopt($this->handle, CURLOPT_HTTPHEADER, $headers);
447447
}
448448

449-
if ($options['protocol_version'] === 1.1) {
450-
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
451-
} else {
452-
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
449+
switch ($options['protocol_version']) {
450+
case 3.0:
451+
// The CURL_HTTP_VERSION_3 constant is only available from PHP 8.4
452+
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, 30);
453+
break;
454+
case 2.0:
455+
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
456+
break;
457+
case 1.1:
458+
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
459+
break;
460+
default:
461+
curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
453462
}
454463

455464
if ($options['blocking'] === true) {

tests/RequestsTest.php

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@ public function dataRequestInvalidUrl() {
5555
];
5656
}
5757

58+
/**
59+
* Data Provider.
60+
*
61+
* @return array
62+
*/
63+
public function dataProtocolVerison() {
64+
return [
65+
'HTTP/1.0' => ['1.0', 1.0],
66+
'HTTP/1.1' => ['1.1', 1.1],
67+
'HTTP/2' => ['2', 2.0],
68+
'HTTP/3' => ['3', 3.0],
69+
];
70+
}
71+
5872
/**
5973
* Tests receiving an exception when the request() method received an invalid input type as `$type`.
6074
*
@@ -287,18 +301,21 @@ public function testHeaderParsing() {
287301
}
288302
}
289303

290-
public function testProtocolVersionParsing() {
304+
/**
305+
* @dataProvider dataProtocolVerison
306+
*/
307+
public function testProtocolVersionParsing($version, $expected) {
291308
$transport = new RawTransportMock();
292309
$transport->data =
293-
"HTTP/1.0 200 OK\r\n" .
310+
"HTTP/$version 200 OK\r\n" .
294311
"Host: localhost\r\n\r\n";
295312

296313
$options = [
297314
'transport' => $transport,
298315
];
299316

300317
$response = Requests::get('http://example.com/', [], $options);
301-
$this->assertSame(1.0, $response->protocol_version);
318+
$this->assertSame($expected, $response->protocol_version);
302319
}
303320

304321
public function testRawAccess() {

tests/Transport/CurlTest.php

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ public function testDoesntOverwriteExpectHeaderIfManuallySet() {
5555
/**
5656
* @small
5757
*/
58-
public function testDoesntSetExpectHeaderIfBodyExactly1MbButProtocolIsnt11() {
58+
public function testDoesNotSetEmptyExpectHeaderIfBodyExactly1MbAndProtocolIs11() {
5959
$options = [
60-
'protocol_version' => 1.0,
60+
'protocol_version' => 1.1,
6161
];
6262
$request = Requests::post($this->httpbin('/post'), [], str_repeat('x', 1048576), $this->getOptions($options));
6363

@@ -69,7 +69,35 @@ public function testDoesntSetExpectHeaderIfBodyExactly1MbButProtocolIsnt11() {
6969
/**
7070
* @small
7171
*/
72-
public function testSetsEmptyExpectHeaderWithDefaultSettings() {
72+
public function testDoesNotSetEmptyExpectHeaderIfBodyExactly1MbAndProtocolIs20() {
73+
$options = [
74+
'protocol_version' => 2.0,
75+
];
76+
$request = Requests::post($this->httpbin('/post'), [], str_repeat('x', 1048576), $this->getOptions($options));
77+
78+
$result = json_decode($request->body, true);
79+
80+
$this->assertFalse(isset($result['headers']['Expect']));
81+
}
82+
83+
/**
84+
* @small
85+
*/
86+
public function testDoesNotSetEmptyExpectHeaderIfBodyExactly1MbAndProtocolIs30() {
87+
$options = [
88+
'protocol_version' => 3.0,
89+
];
90+
$request = Requests::post($this->httpbin('/post', true), [], str_repeat('x', 1048576), $this->getOptions($options));
91+
92+
$result = json_decode($request->body, true);
93+
94+
$this->assertFalse(isset($result['headers']['Expect']));
95+
}
96+
97+
/**
98+
* @small
99+
*/
100+
public function testDoesNotSetsEmptyExpectHeaderWithDefaultSettings() {
73101
$request = Requests::post($this->httpbin('/post'), [], [], $this->getOptions());
74102

75103
$result = json_decode($request->body, true);
@@ -80,29 +108,38 @@ public function testSetsEmptyExpectHeaderWithDefaultSettings() {
80108
/**
81109
* @small
82110
*/
83-
public function testSetsEmptyExpectHeaderIfBodyIsANestedArrayLessThan1Mb() {
111+
public function testSetsEmptyExpectHeaderIfBodyIsANestedArrayLessThan1MbAndProtocolIs10() {
112+
$options = [
113+
'protocol_version' => 1.0,
114+
];
84115
$data = [
85116
str_repeat('x', 148576),
86117
[
87118
str_repeat('x', 548576),
88119
],
89120
];
90-
$request = Requests::post($this->httpbin('/post'), [], $data, $this->getOptions());
121+
$request = Requests::post($this->httpbin('/post'), [], $data, $this->getOptions($options));
91122

92123
$result = json_decode($request->body, true);
93124

94125
$this->assertFalse(isset($result['headers']['Expect']));
95126
}
96127

97-
public function testSetsExpectHeaderIfBodyIsExactlyA1MbString() {
98-
$request = Requests::post($this->httpbin('/post'), [], str_repeat('x', 1048576), $this->getOptions());
128+
public function testSetsExpectHeaderIfBodyIsExactlyA1MbStringAndProtocolIs10() {
129+
$options = [
130+
'protocol_version' => 1.0,
131+
];
132+
$request = Requests::post($this->httpbin('/post'), [], str_repeat('x', 1048576), $this->getOptions($options));
99133

100134
$result = json_decode($request->body, true);
101135

102136
$this->assertSame('100-Continue', $result['headers']['Expect']);
103137
}
104138

105-
public function testSetsExpectHeaderIfBodyIsANestedArrayGreaterThan1Mb() {
139+
public function testSetsExpectHeaderIfBodyIsANestedArrayGreaterThan1MbAndProtocolIs10() {
140+
$options = [
141+
'protocol_version' => 1.0,
142+
];
106143
$data = [
107144
str_repeat('x', 148576),
108145
[
@@ -112,15 +149,18 @@ public function testSetsExpectHeaderIfBodyIsANestedArrayGreaterThan1Mb() {
112149
],
113150
],
114151
];
115-
$request = Requests::post($this->httpbin('/post'), [], $data, $this->getOptions());
152+
$request = Requests::post($this->httpbin('/post'), [], $data, $this->getOptions($options));
116153

117154
$result = json_decode($request->body, true);
118155

119156
$this->assertSame('100-Continue', $result['headers']['Expect']);
120157
}
121158

122-
public function testSetsExpectHeaderIfBodyExactly1Mb() {
123-
$request = Requests::post($this->httpbin('/post'), [], str_repeat('x', 1048576), $this->getOptions());
159+
public function testSetsExpectHeaderIfBodyExactly1MbAndProtocolIs10() {
160+
$options = [
161+
'protocol_version' => 1.0,
162+
];
163+
$request = Requests::post($this->httpbin('/post'), [], str_repeat('x', 1048576), $this->getOptions($options));
124164

125165
$result = json_decode($request->body, true);
126166

@@ -130,8 +170,11 @@ public function testSetsExpectHeaderIfBodyExactly1Mb() {
130170
/**
131171
* @small
132172
*/
133-
public function testSetsEmptyExpectHeaderIfBodySmallerThan1Mb() {
134-
$request = Requests::post($this->httpbin('/post'), [], str_repeat('x', 1048575), $this->getOptions());
173+
public function testSetsEmptyExpectHeaderIfBodySmallerThan1MbAndProtocolIs10() {
174+
$options = [
175+
'protocol_version' => 1.0,
176+
];
177+
$request = Requests::post($this->httpbin('/post'), [], str_repeat('x', 1048575), $this->getOptions($options));
135178

136179
$result = json_decode($request->body, true);
137180

0 commit comments

Comments
 (0)