Very simple reproducer with explanation: https://github.com/mkurz/akka-http-percent-encoding-bug (that reproducer is still based on akka-http but should work 1:1 when applied to pekko-http)
Copy and pasted README (in case I ever delete that repo):
pekko-http's (experimental?) http2 support does not correctly handle URL parsing errors.
Specially when passing an invalid percent-encoded character in the path and/or query string (here %_D):
As reproducer the example from the akka docs can be used, but with HTTP/2 enabled:
# Start the server
sbt run
# HTTP/1 works:
$ curl --http1.1 -v http://localhost:8080/?param=%_D
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /?param=%_D HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.87.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 400 Bad Request
< Server: akka-http/10.2.10
< Date: Wed, 15 Feb 2023 16:29:18 GMT
< Connection: close
< Content-Type: text/plain; charset=UTF-8
< Content-Length: 78
<
* Closing connection 0
Illegal request-target: Invalid input '_', expected HEXDIG (line 1, column 10)
# HTTP/2 gives error:
$ curl --http2-prior-knowledge -v http://localhost:8080/?param=%_D
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
* Using HTTP2, server supports multiplexing
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* h2h3 [:method: GET]
* h2h3 [:path: /?param=%_D]
* h2h3 [:scheme: http]
* h2h3 [:authority: localhost:8080]
* h2h3 [user-agent: curl/7.87.0]
* h2h3 [accept: */*]
* Using Stream ID: 1 (easy handle 0xaaabd90f0dc0)
> GET /?param=%_D HTTP/2
> Host: localhost:8080
> user-agent: curl/7.87.0
> accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 256)!
* Closing connection 0
curl: (16) Error in the HTTP2 framing layer
At some point, no matter if using HTTP/1 or HTTP/2, the parser ends up here
As you can see it will (also) be looked for pct-encoded characters, which expect a % sign, followed by two HEXDIG signs:
Now if that fails...
...then the method parseHttpRequestTarget fails with (=throws) an IllegalUriException, which will later be handled so a 400 Bad Request with following body will be send:
Illegal request-target: Invalid input '_', expected HEXDIG (line 1, column 10)
(There are various places in the http-core module where IllegalUriExceptions and ParsingExceptions are caught and handled)
PathAndQuery.parse in following line throws an ParsingException which is never handled (this is in the org.apache.pekko.http.impl.engine.http2 package, so not used by HTTP/1):
A couple of lines later only an IOException gets caught:
If you see where the containing method parseAndEmit gets called, the ParsingException never gets handled.
Expected Result
When using HTTP/2 I would expect to also receive a 400 Bad Request with the body:
Illegal request-target: Invalid input '_', expected HEXDIG (line 1, column 10)
Very simple reproducer with explanation: https://github.com/mkurz/akka-http-percent-encoding-bug (that reproducer is still based on akka-http but should work 1:1 when applied to pekko-http)
Copy and pasted README (in case I ever delete that repo):
pekko-http's (experimental?) http2 support does not correctly handle URL parsing errors.
Specially when passing an invalid percent-encoded character in the path and/or query string (here
%_D):As reproducer the example from the akka docs can be used, but with HTTP/2 enabled:
# Start the server sbt runAt some point, no matter if using HTTP/1 or HTTP/2, the parser ends up here
As you can see it will (also) be looked for
pct-encodedcharacters, which expect a%sign, followed by twoHEXDIGsigns:Now if that fails...
...then the method
parseHttpRequestTargetfails with (=throws) anIllegalUriException, which will later be handled so a 400 Bad Request with following body will be send:(There are various places in the http-core module where
IllegalUriExceptions andParsingExceptions are caught and handled)PathAndQuery.parsein following line throws anParsingExceptionwhich is never handled (this is in theorg.apache.pekko.http.impl.engine.http2package, so not used by HTTP/1):A couple of lines later only an
IOExceptiongets caught:If you see where the containing method
parseAndEmitgets called, theParsingExceptionnever gets handled.Expected Result
When using HTTP/2 I would expect to also receive a 400 Bad Request with the body: