Skip to content

[BUG] GRPC Streaming message frames are not parsed explicitly #7721

@Dmcz

Description

@Dmcz

Execute the command and paste the result below.

Command: uname -a && php -v && composer info | grep hyperf && php --ri swoole

uname -a && php -v && composer info | grep hyperf && php --ri swoole
Linux 3dd7ae510298 6.8.0-64-generic #67-Ubuntu SMP PREEMPT_DYNAMIC Sun Jun 15 20:23:40 UTC 2025 aarch64 GNU/Linux
PHP 8.4.16 (cli) (built: Jan  6 2026 20:21:19) (ZTS)
Copyright (c) The PHP Group
Zend Engine v4.4.16, Copyright (c) Zend Technologies
dmcz/hyperf-rocketmq               dev-main 358e33c  
hyperf/cache                       3.1.63             A cache component for hyperf.
hyperf/code-parser                 3.1.63             A code parser component for Hyperf.
hyperf/codec                       3.1.63             A codec component for Hyperf.
hyperf/collection                  3.1.64             Hyperf Collection package which come from illuminate/collections
hyperf/command                     3.1.64             Command for hyperf
hyperf/conditionable               3.1.63             Hyperf Macroable package which come from illuminate/conditionable
hyperf/config                      3.1.63             An independent component that provides configuration container.
hyperf/context                     3.1.63             A coroutine/application context library.
hyperf/contract                    3.1.63             The contracts of Hyperf.
hyperf/coordinator                 3.1.63             Hyperf Coordinator
hyperf/coroutine                   3.1.63             Hyperf Coroutine
hyperf/database                    3.1.63             A flexible database library.
hyperf/db-connection               3.1.63             A hyperf db connection handler for hyperf/database.
hyperf/devtool                     3.1.63             A Devtool for Hyperf.
hyperf/di                          3.1.63             A DI for Hyperf.
hyperf/dispatcher                  3.1.63             A HTTP Server for Hyperf.
hyperf/engine                      2.14.1             Coroutine engine provided by swoole.
hyperf/engine-contract             1.13.0             Contract for Coroutine Engine
hyperf/event                       3.1.63             an event manager that implements PSR-14.
hyperf/exception-handler           3.1.63             Exception handler for hyperf
hyperf/filesystem                  3.1.63             flysystem integration for hyperf
hyperf/framework                   3.1.63             A coroutine framework that focuses on hyperspeed and flexible, specifically use for build microservices and middlewares.
hyperf/grpc                        dev-master 1b4f1ed A GRPC basic library for Hyperf.
hyperf/grpc-client                 dev-master 05c767f A GRPC Client for Hyperf.
hyperf/guzzle                      3.1.63             Swoole coroutine handler for guzzle
hyperf/http-message                3.1.63             microservice framework base on swoole
hyperf/http-server                 3.1.63             A HTTP Server for Hyperf.
hyperf/laminas-mime                3.0.0              Create and parse MIME messages and parts
hyperf/logger                      3.1.63             A logger component for hyperf.
hyperf/macroable                   3.1.63             Hyperf Macroable package which come from illuminate/macroable
hyperf/memory                      3.1.63             An independent component that use to operate and manage memory.
hyperf/model-listener              3.1.63             A model listener for Hyperf.
hyperf/pipeline                    3.1.63             Hyperf Macroable package which come from illuminate/pipeline
hyperf/pool                        3.1.63             An independent universal connection pool component.
hyperf/process                     3.1.63             A process component for hyperf.
hyperf/redis                       3.1.63             A redis component for hyperf.
hyperf/serializer                  3.1.63             A serializer component for Hyperf.
hyperf/server                      3.1.63             A base server library for Hyperf.
hyperf/snowflake                   3.1.63             A snowflake library
hyperf/stdlib                      3.1.63             A stdlib component for Hyperf.
hyperf/stringable                  3.1.63             Hyperf Stringable package which come from illuminate/support
hyperf/support                     3.1.63             A support component for Hyperf.
hyperf/tappable                    3.1.63             Hyperf Macroable package which come from illuminate/tappable
hyperf/testing                     3.1.63             Testing for hyperf
hyperf/translation                 3.1.63             An independent translation component, forked by illuminate/translation.
hyperf/validation                  3.1.64             hyperf validation

swoole

Swoole => enabled
Author => Swoole Team <[email protected]>
Version => 6.1.5
Built => Jan  6 2026 20:30:22
host byte order => little endian
coroutine => enabled with boost asm context
epoll => enabled
eventfd => enabled
thread => enabled
signalfd => enabled
cpu_affinity => enabled
spinlock => enabled
rwlock => enabled
openssl => OpenSSL 3.5.4 30 Sep 2025
dtls => enabled
http2 => enabled
json => enabled
curl-native => enabled
curl-version => 8.14.1
c-ares => 1.34.5
zlib => 1.3.1
brotli => E16781312/D16781312
mutex_timedlock => enabled
pthread_barrier => enabled
futex => enabled
execinfo => enabled

Directive => Local Value => Master Value
swoole.enable_library => On => On
swoole.enable_fiber_mock => Off => Off
swoole.enable_preemptive_scheduler => Off => Off
swoole.display_errors => On => On
swoole.use_shortname => Off => Off
swoole.unixsock_buffer_size => 8388608 => 8388608

Description:

当前 streaming RPC 的处理存在两个问题:

  1. Streaming message 没有按 gRPC message framing 解析在 streaming RPC 中,一个 HTTP/2 DATA frame 中可能包含 多个 gRPC message。根据 gRPC over HTTP/2 协议,每个 message 的格式为:
    > DATA frame boundaries have no relation to Length-Prefixed-Message boundaries.
    
    Compressed-Flag (1 byte)
    Message-Length (4 bytes)
    Message-Payload
    
    因此实现需要按 gRPC message framing 从 buffer 中解析 message,而不能依赖 HTTP/2 DATA frame 的边界。当前实现中 Parser::deserializeMessage() 并没有按照 gRPC message framing 对 buffer 再次拆解 frame,因此在streaming RPC 场景下:如果一个 buffer 中包含多个 message,当前实现只会解析出 第一条消息,剩余消息会被忽略。在 unary RPC 场景下通常不会暴露这个问题,因为 unary 语义上只返回一个 message。
  2. Stream 结束时没有检查 gRPC status,因此服务端返回的非 0 状态可能会被忽略。

理论上更合理的做法是修改 Parser::deserializeMessage(),使其按照 gRPC message framing 解析 buffer 并支持多个 message。但改动范围相对较大,另外由于返回数据多为[message|string, grpc-status, resp] 不好追踪需要多测试。因此我目前的修复方式是新增 Parser::parseStreamMessage(),仅在 streaming 场景中使用,从而仅在streaming recv 行为上产生 BC,用于快速修复。

另外当前 gRPC 组件中很多方法直接返回数组结构,对外暴露了协议细节也增加后续维护成本。这里是否有考虑做封装和优化?

Steps To Reproduce:

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions