Skip to content

Conversation

pjfanning
Copy link
Member

@pjfanning pjfanning marked this pull request as draft September 26, 2025 00:35
@pjfanning
Copy link
Member Author

Breaks 1 Http2ClientSpec test

- should send data frames to entity stream and ignore trailing headers *** FAILED *** (74 milliseconds)
[info]     java.lang.AssertionError: assertion failed: expected OnComplete, found OnError(org.apache.pekko.http.scaladsl.model.ParsingException: Illegal HTTP header name: :grpc-status
[info] 	at org.apache.pekko.http.impl.engine.http2.RequestParsing$.parseHeaderPair(RequestParsing.scala:197)
[info] 	at org.apache.pekko.http.impl.engine.http2.Http2ClientDemux.$anonfun$wrapTrailingHeaders$1(Http2Demux.scala:66)
[info] 	at scala.collection.immutable.Vector1.map(Vector.scala:2141)
[info] 	at scala.collection.immutable.Vector1.map(Vector.scala:386)
[info] 	at org.apache.pekko.http.impl.engine.http2.Http2ClientDemux.wrapTrailingHeaders(Http2Demux.scala:64)
[info] 	at org.apache.pekko.http.impl.engine.http2.Http2Demux$Logic$1$.wrapTrailingHeaders(Http2Demux.scala:243)
[info] 	at org.apache.pekko.http.impl.engine.http2.Http2StreamHandling$IncomingStreamBuffer.onTrailingHeaders(Http2StreamHandling.scala:634)
[info] 	at org.apache.pekko.http.impl.engine.http2.Http2StreamHandling$ReceivingDataWithBuffer.onTrailer(Http2StreamHandling.scala:499)
[info] 	at org.apache.pekko.http.impl.engine.http2.Http2StreamHandling$ReceivingData.handle(Http2StreamHandling.scala:478)
[info] 	at org.apache.pekko.http.impl.engine.http2.Http2StreamHandling.handleStreamEvent(Http2StreamHandling.scala:118)
[info] 	at org.apache.pekko.http.impl.engine.http2.Http2StreamHandling.handleStreamEvent$(Http2StreamHandling.scala:117)
[info] 	at org.apache.pekko.http.impl.engine.http2.Http2Demux$Logic$1$$anon$1.onPush(Http2Demux.scala:236)
[info] 	at org.apache.pekko.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:556)
[info] 	at org.apache.pekko.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:434)
[info] 	at org.apache.pekko.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:662)
[info] 	at org.apache.pekko.stream.impl.fusing.ActorGraphInterpreter$SimpleBoundaryEvent.execute(ActorGraphInterpreter.scala:71)
[info] 	at org.apache.pekko.stream.impl.fusing.ActorGraphInterpreter$SimpleBoundaryEvent.execute$(ActorGraphInterpreter.scala:67)
[info] 	at org.apache.pekko.stream.impl.fusing.ActorGraphInterpreter$BatchingActorInputBoundary$OnNext.execute(ActorGraphInterpreter.scala:114)
[info] 	at org.apache.pekko.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:637)
[info] 	at org.apache.pekko.stream.impl.fusing.ActorGraphInterpreter.org$apache$pekko$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:813)
[info] 	at org.apache.pekko.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:831)
[info] 	at org.apache.pekko.actor.Actor.aroundReceive(Actor.scala:547)
[info] 	at org.apache.pekko.actor.Actor.aroundReceive$(Actor.scala:545)
[info] 	at org.apache.pekko.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:729)
[info] 	at org.apache.pekko.actor.ActorCell.receiveMessage(ActorCell.scala:590)
[info] 	at org.apache.pekko.actor.ActorCell.invoke(ActorCell.scala:557)
[info] 	at org.apache.pekko.dispatch.Mailbox.processMailbox(Mailbox.scala:272)
[info] 	at org.apache.pekko.dispatch.Mailbox.run(Mailbox.scala:233)
[info] 	at org.apache.pekko.dispatch.Mailbox.exec(Mailbox.scala:245)
[info] 	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
[info] 	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
[info] 	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
[info] 	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
[info] 	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
[info] )
[info]     at scala.Predef$.assert(Predef.scala:279)
[info]     at org.apache.pekko.testkit.TestKitBase.expectMsg_internal(TestKit.scala:473)
[info]     at org.apache.pekko.testkit.TestKitBase.expectMsg(TestKit.scala:449)
[info]     at org.apache.pekko.testkit.TestKitBase.expectMsg$(TestKit.scala:449)
[info]     at org.apache.pekko.testkit.TestKit.expectMsg(TestKit.scala:982)
[info]     at org.apache.pekko.stream.testkit.TestSubscriber$ManualProbe.expectComplete(StreamTestKit.scala:632)
[info]     at org.apache.pekko.http.impl.engine.ws.ByteStringSinkProbe$$anon$1.expectComplete(ByteStringSinkProbe.scala:112)
[info]     at org.apache.pekko.http.impl.engine.http2.Http2ClientSpec$$anon$19.<init>(Http2ClientSpec.scala:422)
[info]     at org.apache.pekko.http.impl.engine.http2.Http2ClientSpec.$anonfun$new$26(Http2ClientSpec.scala:413)
[info]     at org.apache.pekko.http.impl.engine.http2.Http2ClientSpec$InWithStoppedStages.$anonfun$inAssertAllStagesStopped$2(Http2ClientSpec.scala:963)
[info]     at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
[info]     at org.apache.pekko.stream.testkit.scaladsl.StreamTestKit$.assertAllStagesStopped(StreamTestKit.scala:42)
[info]     at org.apache.pekko.http.impl.engine.http2.Http2ClientSpec$InWithStoppedStages.$anonfun$inAssertAllStagesStopped$1(Http2ClientSpec.scala:962)
[info]     at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
[info]     at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
[info]     at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
[info]     at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
[info]     at org.scalatest.Transformer.apply(Transformer.scala:22)
[info]     at org.scalatest.Transformer.apply(Transformer.scala:20)
[info]     at org.scalatest.wordspec.AnyWordSpecLike$$anon$3.apply(AnyWordSpecLike.scala:1240)
[info]     at org.apache.pekko.http.impl.util.WithLogCapturing.$anonfun$withFixture$1(WithLogCapturing.scala:65)
[info]     at org.apache.pekko.testkit.EventFilter.intercept(TestEventListener.scala:129)
[info]     at org.apache.pekko.http.impl.util.WithLogCapturing.withFixture(WithLogCapturing.scala:63)
[info]     at org.apache.pekko.http.impl.util.WithLogCapturing.withFixture$(WithLogCapturing.scala:50)
[info]     at org.apache.pekko.http.impl.util.PekkoSpecWithMaterializer.withFixture(PekkoSpecWithMaterializer.scala:24)

httpHeaderParser.parseHeaderLine(pekko.util.ByteString(concHeaderLine))()
httpHeaderParser.resultHeader
} else {
HttpHeader.parse(name, value, httpHeaderParser.settings) match {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general that's really cleaner. I don't expect a performance hit, there's a bit of a difference of how the parsers are looked up between HttpHeader.parse and httpHeaderParser.parseHeaderLine. HttpHeader.parse uses binary search to find the right parser and httpHeaderParser.parseHeaderLine uses kind of a lut / radix lookup once the parser is primed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @jrudolph.
I had to use the startsWith(":") check because HttpHeader.parse doesn't support pseudo headers and 1 test includes a :grpc-status pseudo header.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's annoying

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But wait, does it even parse it into anything useful?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll debug it later. There is an existing test that relies on the header parser not failing. Whether it needs the parser to actually process the pseudo header at all is something that I haven't yet tested.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's a typo in the test? Is :grpc-status a thing, i.e. is it allowed for gRPC (outside the HTTP/2 spec) to extend the set of : pseudo headers?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pekko-grpc also doesn't use the colon form so maybe it's just a typo and we could just fix the test.

Added comments to clarify handling of pseudo-headers in the parseHeaderPair method.
httpHeaderParser.parseHeaderLine(ByteString(concHeaderLine))()
httpHeaderParser.resultHeader
import HttpHeader.ParsingResult
if (name.startsWith(":")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just a look ahead char is better

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.

3 participants