Anon2Tokyo commented on issue #16414:
URL: https://github.com/apache/dubbo/issues/16414#issuecomment-5221616509

   @1536191986zh-cloud 
   I investigated this issue and found that it is caused by decoder state loss 
in the no-stub gRPC provider path.  
   
   ### Root cause  This only affects the no-stub provider path, where 
`GrpcHttp2ServerTransportListener` first uses `LazyFindMethodListener` to parse 
the initial client-streaming message and then switches to the real method 
listener.  The problematic case is when the first HTTP/2 DATA frame contains 
more than one gRPC message fragment, for example:  ```text [complete message 
#1][beginning of message #2]
   
   In the old implementation, `LazyFindMethodListener` created a temporary 
`GrpcStreamingDecoder` to decode the first message. If the same DATA frame also 
contained the beginning of the second message, that partial second message was 
buffered inside the temporary decoder.
   
   After the listener switched to the real method listener, the real streaming 
decoder did not have that buffered state. As a result, decoding continued from 
an incorrect message boundary, which could lead to protobuf parse errors, 
invalid compression/reserved-bit errors, or Netty buffer reference-count errors.
   
   This is why the issue is hard to reproduce locally: it depends on HTTP/2 
DATA frame splitting/coalescing timing. It is more likely with:
   
   - no-stub provider
   - client streaming
   - many fast consecutive `onNext` calls
   - a very small first message
   - larger following messages, for example `128KB`
   - no artificial delay or logging inside the sending loop
   
   ### Fix
   
   The fix is to make `LazyFindMethodListener` use the transport's actual 
streaming decoder instead of creating a temporary decoder.
   
   So instead of decoding the first message with a separate `new 
GrpcStreamingDecoder()`, it now reuses `getStreamingDecoder()`.
   
   This preserves any buffered partial gRPC message across the transition from 
`LazyFindMethodListener` to the real method listener.
   
   ### Verification
   
   I added a regression test that simulates this exact shape:
   
   ```text
   DATA frame = [full first gRPC message][partial second gRPC message]
   ```
   
   The test verifies that after the listener switch, the remaining bytes of the 
second message are still decoded correctly.
   
   I also reproduced the issue against Dubbo `3.3.6` using a no-stub provider 
and client-streaming calls. With `20` messages of `128KB` sent quickly, one run 
failed at iteration 17 with server-side decode errors including 
`LengthFieldStreamingDecoder`, `InvalidProtocolBufferException`, and 
`IllegalReferenceCountException: refCnt: 0`.
   
   provider log:
   ````
   2026-08-08 02:34:09.244 ERROR 10324 --- [0051-thread-200] 
.r.p.t.h.AbstractServerTransportListener :  [DUBBO] An error occurred while 
processing the http request with GrpcHttp2ServerTransportListener, 
Http2MetadataFrame{method='POST', path='/repro.api.UploadFacade/upload', 
contentType='application/grpc+proto', streamId=3, endStream=false}, 
method=repro.api.UploadFacade.upload(StreamObserver), dubbo version: 3.3.6, 
current host: 192.168.0.115, error code: 99-0. This may be caused by , go to 
https://dubbo.apache.org/faq/99/0 to find instructions. 
   
   org.apache.dubbo.remoting.http12.exception.DecodeException: Internal Server 
Error
        at 
org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcCompositeCodec.decode(GrpcCompositeCodec.java:105)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcCompositeCodec.decode(GrpcCompositeCodec.java:111)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.remoting.http12.message.HttpMessageDecoder.decode(HttpMessageDecoder.java:56)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.remoting.http12.message.DefaultListeningDecoder.decode(DefaultListeningDecoder.java:41)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.remoting.http12.message.StreamingDecoder$DefaultFragmentListener.onFragmentMessage(StreamingDecoder.java:55)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.remoting.http12.message.LengthFieldStreamingDecoder.invokeListener(LengthFieldStreamingDecoder.java:180)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.remoting.http12.message.LengthFieldStreamingDecoder.processBody(LengthFieldStreamingDecoder.java:172)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.remoting.http12.message.LengthFieldStreamingDecoder.deliver(LengthFieldStreamingDecoder.java:121)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.remoting.http12.message.LengthFieldStreamingDecoder.decode(LengthFieldStreamingDecoder.java:69)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.rpc.protocol.tri.h12.http2.GenericHttp2ServerTransportListener$StreamingHttpMessageListener.onMessage(GenericHttp2ServerTransportListener.java:220)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.rpc.protocol.tri.h12.AbstractServerTransportListener.doOnData(AbstractServerTransportListener.java:183)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.rpc.protocol.tri.h12.AbstractServerTransportListener.lambda$onData$1(AbstractServerTransportListener.java:168)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.common.threadpool.serial.SerializingExecutor.run(SerializingExecutor.java:111)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
 ~[na:na]
        at 
java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
 ~[na:na]
        at 
org.apache.dubbo.common.threadlocal.InternalRunnable.run(InternalRunnable.java:39)
 ~[dubbo-3.3.6.jar:3.3.6]
        at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
   Caused by: java.io.IOException: 
com.google.protobuf.InvalidProtocolBufferException: While parsing a protocol 
message, the input ended unexpectedly in the middle of a field.  This could 
mean either that the input has been truncated or that an embedded message 
misreported its own length.
        at 
org.apache.dubbo.rpc.protocol.tri.SingleProtobufUtils.deserialize(SingleProtobufUtils.java:104)
 ~[dubbo-3.3.6.jar:3.3.6]
        at org.apache.dubbo.rpc.protocol.tri.PbUnpack.unpack(PbUnpack.java:35) 
~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.rpc.model.PackableMethod.parseRequest(PackableMethod.java:26) 
~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcCompositeCodec.decode(GrpcCompositeCodec.java:101)
 ~[dubbo-3.3.6.jar:3.3.6]
        ... 16 common frames omitted
   Caused by: com.google.protobuf.InvalidProtocolBufferException: While parsing 
a protocol message, the input ended unexpectedly in the middle of a field.  
This could mean either that the input has been truncated or that an embedded 
message misreported its own length.
        at 
com.google.protobuf.InvalidProtocolBufferException.truncatedMessage(InvalidProtocolBufferException.java:92)
 ~[protobuf-java-3.25.5.jar:na]
        at 
com.google.protobuf.CodedInputStream$StreamDecoder.readRawBytesSlowPathRemainingChunks(CodedInputStream.java:2950)
 ~[protobuf-java-3.25.5.jar:na]
        at 
com.google.protobuf.CodedInputStream$StreamDecoder.readBytesSlowPath(CodedInputStream.java:2987)
 ~[protobuf-java-3.25.5.jar:na]
        at 
com.google.protobuf.CodedInputStream$StreamDecoder.readBytes(CodedInputStream.java:2373)
 ~[protobuf-java-3.25.5.jar:na]
        at repro.upload.UploadFrame$Builder.mergeFrom(UploadFrame.java:452) 
~[classes/:na]
        at repro.upload.UploadFrame$1.parsePartialFrom(UploadFrame.java:648) 
~[classes/:na]
        at repro.upload.UploadFrame$1.parsePartialFrom(UploadFrame.java:640) 
~[classes/:na]
        at 
com.google.protobuf.AbstractParser.parsePartialFrom(AbstractParser.java:192) 
~[protobuf-java-3.25.5.jar:na]
        at 
com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:209) 
~[protobuf-java-3.25.5.jar:na]
        at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:25) 
~[protobuf-java-3.25.5.jar:na]
        at 
org.apache.dubbo.rpc.protocol.tri.SingleProtobufUtils$SingleMessageMarshaller.parse(SingleProtobufUtils.java:132)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.rpc.protocol.tri.SingleProtobufUtils.deserialize(SingleProtobufUtils.java:102)
 ~[dubbo-3.3.6.jar:3.3.6]
        ... 19 common frames omitted
   
   2026-08-08 02:34:09.253 ERROR 10324 --- [0051-thread-200] 
.r.p.t.h.AbstractServerTransportListener :  [DUBBO] An error occurred while 
processing the http request with GrpcHttp2ServerTransportListener, 
Http2MetadataFrame{method='POST', path='/repro.api.UploadFacade/upload', 
contentType='application/grpc+proto', streamId=3, endStream=false}, 
method=repro.api.UploadFacade.upload(StreamObserver), dubbo version: 3.3.6, 
current host: 192.168.0.115, error code: 99-0. This may be caused by , go to 
https://dubbo.apache.org/faq/99/0 to find instructions. 
   
   io.netty.util.IllegalReferenceCountException: refCnt: 0
        at 
io.netty.buffer.AbstractByteBuf.ensureAccessible(AbstractByteBuf.java:1454) 
~[netty-buffer-4.1.101.Final.jar:4.1.101.Final]
        at 
io.netty.buffer.AbstractByteBuf.checkReadableBytes0(AbstractByteBuf.java:1440) 
~[netty-buffer-4.1.101.Final.jar:4.1.101.Final]
        at 
io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1428) 
~[netty-buffer-4.1.101.Final.jar:4.1.101.Final]
        at io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:895) 
~[netty-buffer-4.1.101.Final.jar:4.1.101.Final]
        at io.netty.buffer.ByteBufInputStream.read(ByteBufInputStream.java:183) 
~[netty-buffer-4.1.101.Final.jar:4.1.101.Final]
        at 
org.apache.dubbo.remoting.http12.CompositeInputStream.read(CompositeInputStream.java:82)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.remoting.http12.message.LengthFieldStreamingDecoder.readRawMessage(LengthFieldStreamingDecoder.java:185)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcStreamingDecoder.readRawMessage(GrpcStreamingDecoder.java:54)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.remoting.http12.message.LengthFieldStreamingDecoder.processBody(LengthFieldStreamingDecoder.java:170)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.remoting.http12.message.LengthFieldStreamingDecoder.deliver(LengthFieldStreamingDecoder.java:121)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.remoting.http12.message.LengthFieldStreamingDecoder.decode(LengthFieldStreamingDecoder.java:69)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.rpc.protocol.tri.h12.http2.GenericHttp2ServerTransportListener$StreamingHttpMessageListener.onMessage(GenericHttp2ServerTransportListener.java:220)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.rpc.protocol.tri.h12.AbstractServerTransportListener.doOnData(AbstractServerTransportListener.java:183)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.rpc.protocol.tri.h12.AbstractServerTransportListener.lambda$onData$1(AbstractServerTransportListener.java:168)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
org.apache.dubbo.common.threadpool.serial.SerializingExecutor.run(SerializingExecutor.java:111)
 ~[dubbo-3.3.6.jar:3.3.6]
        at 
java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
 ~[na:na]
        at 
java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
 ~[na:na]
        at 
org.apache.dubbo.common.threadlocal.InternalRunnable.run(InternalRunnable.java:39)
 ~[dubbo-3.3.6.jar:3.3.6]
        at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
   
   2026-08-08 02:34:09.281  INFO 10324 --- [rverWorker-3-21] 
o.a.d.r.t.netty4.NettyServerHandler      :  [DUBBO] The connection [id: 
0xf714b008, L:/192.168.0.115:50051 ! R:/192.168.0.115:56989] of 
192.168.0.115:56989 -> 192.168.0.115:50051 is disconnected., dubbo version: 
3.3.6, current host: 192.168.0.115
   2026-08-08 02:34:09.282  INFO 10324 --- [rverWorker-3-21] 
o.a.d.r.t.n.NettyPortUnificationServer   :  [DUBBO] All clients has 
disconnected from /192.168.0.115:50051. You can graceful shutdown now., dubbo 
version: 3.3.6, current host: 192.168.0.115
   2026-08-08 02:34:09.282  INFO 10324 --- [rverWorker-3-21] 
o.a.d.r.t.netty4.NettyChannelHandler     :  [DUBBO] The connection [id: 
0xf714b008, L:/192.168.0.115:50051 ! R:/192.168.0.115:56989] of 
192.168.0.115:56989 -> 192.168.0.115:50051 is disconnected., dubbo version: 
3.3.6, current host: 192.168.0.115
   
   ````


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to