Browse Source

Only use HTTP/2 over TLS, and HTTP/1.1 for plain http

Requesting CURL_HTTP_VERSION_2 makes curl send an h2c Upgrade request on
plain http URLs, which some servers mishandle. PHP 7.3's built-in server
drops the connection, which failed the curl tests on PHP 7.3. Use
CURL_HTTP_VERSION_2TLS instead, curl's own default since 7.62: https
still negotiates HTTP/2.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
main
Aaron Parecki 1 day ago
parent
commit
5eaf3df200
3 changed files with 19 additions and 5 deletions
  1. +6
    -5
      src/p3k/HTTP/Curl.php
  2. +9
    -0
      tests/CurlPinningTest.php
  3. +4
    -0
      tests/server/index.php

+ 6
- 5
src/p3k/HTTP/Curl.php View File

@ -124,12 +124,13 @@ class Curl implements Transport, Pinnable {
private function _http_version() {
if (static::$_http_version !== null)
return static::$_http_version;
if (defined('CURL_HTTP_VERSION_2')) { // PHP 7.0.7
static::$_http_version = CURL_HTTP_VERSION_2;
} else if (defined('CURL_HTTP_VERSION_2_0')) { // Recommended in online articles
static::$_http_version = CURL_HTTP_VERSION_2_0;
// HTTP/2 over TLS only, and HTTP/1.1 for plain http, which is curl's own
// default since 7.62. Asking for HTTP/2 on plain http makes curl send an
// h2c Upgrade request, which some servers mishandle.
if (defined('CURL_HTTP_VERSION_2TLS')) { // PHP 7.0.7
static::$_http_version = CURL_HTTP_VERSION_2TLS;
} else { // Linked curl might be newer than PHP, send (current) INT value anyway.
static::$_http_version = 3;
static::$_http_version = 4;
}
return static::$_http_version;
}

+ 9
- 0
tests/CurlPinningTest.php View File

@ -78,4 +78,13 @@ class CurlPinningTest extends TestCase {
$this->assertSame(0, $response['code']);
$this->assertNotSame('', $response['error_description']);
}
// HTTP/2 is only negotiated over TLS. Plain http stays on HTTP/1.1 rather
// than sending an h2c Upgrade request, which some servers mishandle (PHP
// 7.3's built-in server drops the connection).
public function testPlainHttpDoesNotAttemptHttp2Upgrade() {
$response = (new HTTP('test'))->get('http://127.0.0.1:' . self::$port . '/?upgrade=1');
$this->assertSame(200, $response['code']);
$this->assertSame('upgrade=', $response['body']);
}
}

+ 4
- 0
tests/server/index.php View File

@ -4,4 +4,8 @@ if(($_GET['to'] ?? '') !== '') {
header('Location: ' . $_GET['to'], true, 302);
exit;
}
if(isset($_GET['upgrade'])) {
echo 'upgrade=' . ($_SERVER['HTTP_UPGRADE'] ?? '');
exit;
}
echo 'host=' . ($_SERVER['HTTP_HOST'] ?? '');

Loading…
Cancel
Save