Skip to content

Commit bbedc67

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

File tree

4 files changed

+94
-23
lines changed

4 files changed

+94
-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: 58 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,37 @@ 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+
'timeout' => 30,
90+
'connect_timeout' => 30
91+
];
92+
$request = Requests::post($this->httpbin('/post', true), [], str_repeat('x', 1048576), $this->getOptions($options));
93+
94+
$result = json_decode($request->body, true);
95+
96+
$this->assertFalse(isset($result['headers']['Expect']));
97+
}
98+
99+
/**
100+
* @small
101+
*/
102+
public function testDoesNotSetsEmptyExpectHeaderWithDefaultSettings() {
73103
$request = Requests::post($this->httpbin('/post'), [], [], $this->getOptions());
74104

75105
$result = json_decode($request->body, true);
@@ -80,29 +110,38 @@ public function testSetsEmptyExpectHeaderWithDefaultSettings() {
80110
/**
81111
* @small
82112
*/
83-
public function testSetsEmptyExpectHeaderIfBodyIsANestedArrayLessThan1Mb() {
113+
public function testSetsEmptyExpectHeaderIfBodyIsANestedArrayLessThan1MbAndProtocolIs10() {
114+
$options = [
115+
'protocol_version' => 1.0,
116+
];
84117
$data = [
85118
str_repeat('x', 148576),
86119
[
87120
str_repeat('x', 548576),
88121
],
89122
];
90-
$request = Requests::post($this->httpbin('/post'), [], $data, $this->getOptions());
123+
$request = Requests::post($this->httpbin('/post'), [], $data, $this->getOptions($options));
91124

92125
$result = json_decode($request->body, true);
93126

94127
$this->assertFalse(isset($result['headers']['Expect']));
95128
}
96129

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

100136
$result = json_decode($request->body, true);
101137

102138
$this->assertSame('100-Continue', $result['headers']['Expect']);
103139
}
104140

105-
public function testSetsExpectHeaderIfBodyIsANestedArrayGreaterThan1Mb() {
141+
public function testSetsExpectHeaderIfBodyIsANestedArrayGreaterThan1MbAndProtocolIs10() {
142+
$options = [
143+
'protocol_version' => 1.0,
144+
];
106145
$data = [
107146
str_repeat('x', 148576),
108147
[
@@ -112,15 +151,18 @@ public function testSetsExpectHeaderIfBodyIsANestedArrayGreaterThan1Mb() {
112151
],
113152
],
114153
];
115-
$request = Requests::post($this->httpbin('/post'), [], $data, $this->getOptions());
154+
$request = Requests::post($this->httpbin('/post'), [], $data, $this->getOptions($options));
116155

117156
$result = json_decode($request->body, true);
118157

119158
$this->assertSame('100-Continue', $result['headers']['Expect']);
120159
}
121160

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

125167
$result = json_decode($request->body, true);
126168

@@ -130,8 +172,11 @@ public function testSetsExpectHeaderIfBodyExactly1Mb() {
130172
/**
131173
* @small
132174
*/
133-
public function testSetsEmptyExpectHeaderIfBodySmallerThan1Mb() {
134-
$request = Requests::post($this->httpbin('/post'), [], str_repeat('x', 1048575), $this->getOptions());
175+
public function testSetsEmptyExpectHeaderIfBodySmallerThan1MbAndProtocolIs10() {
176+
$options = [
177+
'protocol_version' => 1.0,
178+
];
179+
$request = Requests::post($this->httpbin('/post'), [], str_repeat('x', 1048575), $this->getOptions($options));
135180

136181
$result = json_decode($request->body, true);
137182

0 commit comments

Comments
 (0)