Skip to content

Send the request method uppercase so PSR-7 v3 does not break the API - #286

Merged
taylorotwell merged 2 commits into
laravel:masterfrom
datashaman:fix-request-method-casing
Sep 3, 2026
Merged

taylorotwell merged 2 commits into
laravel:masterfrom
datashaman:fix-request-method-casing

Conversation

@datashaman

Copy link
Copy Markdown
Contributor

Fixes deploys breaking on v1.70.4. Credit for the diagnosis goes to @maliknikolaj, who identified the cause in #285 (comment) — I'm opening this because nobody had raised the fix yet and three of us are pinned to 1.70.3 in the meantime. Happy to close it if @maliknikolaj would rather submit their own.

The problem

ConsoleVaporClient passes its verbs in lowercase everywhere — request('get', ...), requestWithErrorHandling('post', ...) and so on, 88 call sites in total.

PSR-7 v1 and v2 uppercased the method in the Request constructor. v3 preserves whatever casing it is given:

// guzzlehttp/psr7 2.13.1
self::warnOnMethodCasingChange($method);
$this->method = Utils::asciiToUpper($method);

// guzzlehttp/psr7 3.1.0
$this->method = $method;

v1.70.4 widened the guzzle constraint to allow ^8.0 (#284), and guzzle 8 pulls in psr7 3. So the CLI now puts a lowercase verb on the request line, and the load balancer in front of vapor.laravel.com rejects it before the application sees it:

$ curl -s -o /dev/null -w '%{http_code} %{content_type}\n' -X POST -H 'Accept: application/json' \
    https://vapor.laravel.com/api/projects/1/environments/production/linted-manifest
401 application/json

$ curl -s -o /dev/null -w '%{http_code} %{content_type}\n' -X post -H 'Accept: application/json' \
    https://vapor.laravel.com/api/projects/1/environments/production/linted-manifest
400 text/html

Captured against a local socket, calling it the way requestWithoutErrorHandling() does:

guzzle 7.15.5 / psr7 2.13.1   POST /api/projects/1/environments/production/linted-manifest HTTP/1.1
guzzle 8.1.0  / psr7 3.1.0    post /api/projects/1/environments/production/linted-manifest HTTP/1.1

Because validateManifest() is the first call vapor deploy makes, every deploy dies about a second in, before any build step, with the 400's HTML body and no validation bag. That is also why it surfaces as the bare "Whoops!" reported in #285.

The fix

One strtoupper() at the single choke point. All 88 lowercase call sites route through requestWithoutErrorHandling(), so nothing else needs touching, and AwsStorageProvider already passes 'PUT' uppercase in all four places, so the asset upload path was never affected.

Normalizing here rather than editing 88 call sites also means a lowercase verb can't reintroduce this later.

Tests

RequestMethodCasingTest asserts the method reaches the client uppercase for lowercase, uppercase and mixed-case input. Verified it actually catches the regression: with the fix reverted, 2 of the 3 cases fail on psr7 3.1.0.

One honest limitation — the assertion reads the method off the constructed PSR-7 request, so on psr7 v1/v2 it passes whether or not the fix is present, since psr7 uppercases it anyway. Asserting earlier would mean overriding Client::request(), whose signature differs across guzzle 6/7/8 and which the package still supports. The test is meaningful on any matrix leg resolving psr7 3.

Full suite passes (59 tests). phpstan reports the same 106 pre-existing errors before and after, none in this file.

Related

@taylorotwell
taylorotwell merged commit 26546fa into laravel:master Sep 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants