Skip to content

Expose the raw query string to the application #2159

Description

@atymic

Problem

HttpRequestEvent::rebuildQueryString() re-parses and re-encodes rawQueryString on every API Gateway v2 request, and there's no way to get the original back:

if ($this->isFormatV2()) {
    $queryString = $this->event['rawQueryString'] ?? '';
    // We re-parse the query string to make sure it is URL-encoded
    return http_build_query(self::queryStringToArray($queryString), '', '&', \PHP_QUERY_RFC3986);
}

An unencoded URL in a query value gets split on its own &:

in:  location=https://zoom.us/w/1?tk=AAA&pwd=SECRET
out: location => 'https://zoom.us/w/1?tk=AAA'
     pwd      => 'SECRET'

Re-encoding then bakes the split in and turns the ? into %3F, so an app can't tell "this was split" apart from "this was always properly encoded". This isn't hypothetical, it silently truncated live customer links for us (ActiveCampaign has no urlencode modifier, so a merge tag holding a URL lands in the query string raw).

I get why the normalisation is there and I'm not proposing changing it. Just asking to keep the original reachable.

No userland workaround

getQueryString(), getUri() and getQueryParameters() all derive from the normalised property. rawQueryString sits at the event top level, so it's not in getRequestContext() either. The only way in is subclassing the handler to read $event->toArray()['rawQueryString'], which forces a serverless.yml handler swap that's easy to lose on a deploy.

(v1/ALB is worse, not a fallback: AWS has already split those into queryStringParameters.)

Proposed change

Add an accessor:

public function getRawQueryString(): string
{
    return $this->event['rawQueryString'] ?? '';
}

and surface it as a server var in Psr7Bridge::convertRequest():

'BREF_RAW_QUERY_STRING' => $event->getRawQueryString(),

That covers Octane without touching laravel-bridge: OctaneHandler calls SymfonyRequestBridge::convertRequest(), which wraps Psr7Bridge::convertRequest() and then server->add()s its own LAMBDA_* vars on top, so anything already in the PSR-7 server array reaches $request->server in Laravel.

Additive, no behaviour change, no config.

One thing for review: on v1/ALB there's no rawQueryString in the event at all. Empty string, or leave the var unset so an app can tell "not available" apart from "genuinely empty"? Note Psr7Bridge filters $server on !== null, so '' survives but null drops out.

Happy to send a PR if you're open to it, just let me know which way you'd want that to go.

Environment

  • Bref 3.0.9 (checked against master), PHP 8.5
  • API Gateway HTTP API (payload format 2.0)
  • Octane, via brefphp/laravel-bridge

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions