news 2026/9/17 14:41:22

Fluent Bit 内嵌 nghttp2:nghttp2_submit_headers() API 深入解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Fluent Bit 内嵌 nghttp2:nghttp2_submit_headers() API 深入解析

Fluent Bit 内嵌 nghttp2:nghttp2_submit_headers() API 深入解析

【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit

nghttp2_submit_headers()是 nghttp2 库中提交 HEADERS 帧的核心低层 API。Fluent Bit 在输出端启用 HTTP/2(如net.http_version = 2的 HTTP 客户端)时,其内嵌的 nghttp2 1.65.0(位于 lib/nghttp2-1.65.0/)正是通过这一系列 submit 接口把请求头组装成 HTTP/2 帧序列。读完本文,你将掌握该函数的完整签名语义、参数与标志位、伪头字段约束、内存拷贝策略、返回值与错误码体系,以及 Fluent Bit HTTP/2 客户端如何在上层封装调用链。

函数原型

声明位于 lib/nghttp2-1.65.0/lib/includes/nghttp2/nghttp2.h,实现在 lib/nghttp2-1.65.0/lib/nghttp2_submit.c:

#include <nghttp2/nghttp2.h> int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, void *stream_user_data);

它的作用是向会话提交一个 HEADERS 帧。各参数含义如下(对应文档 lib/nghttp2-1.65.0/doc/nghttp2_submit_headers.rst):

参数说明
session已初始化的 nghttp2 会话(客户端或服务端会话)
flags帧标志位,目前仅支持NGHTTP2_FLAG_END_STREAM(值0x01,见 nghttp2.h)
stream_id目标流 ID;传-1表示由库分配新的客户端流(请求 HEADERS,打开新流)
pri_spec优先级规格,当前实现中被忽略(源码中有显式(void)pri_spec;
nva/nvlen头字段 name/value 数组及其元素个数,类型为nghttp2_nv
stream_user_data与流关联的用户指针,仅当本帧把流从 idle/reserved 状态转为 open 时生效

参数语义详解

flags 与 CONTINUATION 的透明处理

应用层只需关心NGHTTP2_FLAG_END_STREAM(0x01):设置后该 HEADERS 帧携带 END_STREAM 标志,表示流在本帧结束。NGHTTP2_FLAG_END_HEADERS(0x04)对应用层不可见——库内部处理 CONTINUATION 帧拆分时,会在 PUSH_PROMISE 或最后一组 CONTINUATION 帧上正确设置 END_HEADERS。

源码印证了这一点:nghttp2_submit.c 中入口处先做flags &= NGHTTP2_FLAG_END_STREAM把非法标志位全部剔除,随后在submit_headers_shared()里强制 OR 上NGHTTP2_FLAG_END_HEADERS(nghttp2_submit.c)。

stream_id 的三种取值

  • -1:仅客户端会话可用。库把本帧归类为请求(NGHTTP2_HCAT_REQUEST),从session->next_stream_id取 ID 并使后续 ID 加 2(保持客户端流奇数编号),成功时返回新分配的流 ID(见 nghttp2_submit.c)。若next_stream_id已超出INT32_MAX,则无可用 ID。
  • 0:非法,返回NGHTTP2_ERR_INVALID_ARGUMENT(HTTP/2 规范中 0 是连接级流)。
  • 正奇数(具体值):对已存在的流提交 HEADERS(响应头、trailer 等),归类为NGHTTP2_HCAT_HEADERS,成功时返回 0。

注意文档中的 warning:stream_id = -1成功返回的流 ID 只是预分配,流尚未打开;在nghttp2_before_frame_send_callback对该帧被调用之前,不得向该流 ID 提交其他帧。

nva:伪头字段由应用负责

nghttp2_nv{flags, name, namelen, value, valuelen}五元组。文档明确要求:

  • 必需的伪头字段:前缀,如:method:scheme:path:status)必须由应用放入nva,且必须排在普通头字段之前
  • 函数会拷贝nva中所有 name/value,并自动将名称转为小写,元素顺序保持不变;
  • 若某个nghttp2_nv设置了NGHTTP2_NV_FLAG_NO_COPY_NAME(0x02)/NGHTTP2_NV_FLAG_NO_COPY_VALUE(0x04)(见 nghttp2.h),对应部分不拷贝;其中NO_COPY_NAME要求应用自行保证名称是小写。对于不拷贝的部分,应用必须保证指针引用在nghttp2_on_frame_send_callbacknghttp2_on_frame_not_send_callback被调用之前持续有效。

低层定位:何时用它,何时用替代品

文档明确把nghttp2_submit_headers()定位为低层函数——它允许应用直接指定 flags。对常规 HTTP 请求应优先用nghttp2_submit_request2(),对 HTTP 响应优先用nghttp2_submit_response2()(两者内部同样最终提交 HEADERS 帧)。此外nghttp2_submit_trailer()等价于flags = NGHTTP2_FLAG_END_STREAMpri_specstream_user_data为 NULL 的nghttp2_submit_headers(),见 nghttp2_submit.c 的实现——它正是直接调用submit_headers_shared_nva()复用同一条路径。

实现剖析:从 submit 到出站队列

完整调用链为nghttp2_submit_headers()submit_headers_shared_nva()submit_headers_shared(),源码在 lib/nghttp2-1.65.0/lib/nghttp2_submit.c:

  1. 参数校验(L149-L155):stream_id == -1session->server为真 →NGHTTP2_ERR_PROTOstream_id <= 0且非 -1 →NGHTTP2_ERR_INVALID_ARGUMENT
  2. 深拷贝头数组submit_headers_shared_nva()(L112-L130)通过nghttp2_nv_array_copy()复制nva,此后所有权交给nghttp2_frame_headers_init()(源码注释:nghttp2_frame_headers_init() takes ownership of nva_copy,L104),失败路径负责释放。
  3. 分配出站项并初始化 HEADERS 帧nghttp2_mem_malloc()分配nghttp2_outbound_item(内存不足返回NGHTTP2_ERR_NOMEM),nghttp2_outbound_item_init()初始化,stream_user_data存入item->aux_data.headers.stream_user_data
  4. 入队nghttp2_session_add_item()将项挂入会话的 outbound 队列;此后帧何时真正写包、是否被拆为 CONTINUATION,都由nghttp2_session_send()驱动。
  5. 返回值:请求类(新流)返回新流 ID,其余情况返回 0。

需要强调:submit 只是入队,并不发送字节。应用随后调用nghttp2_session_send()才会触发nghttp2_before_frame_send_callback并真正序列化帧。

返回值与错误码

成功且stream_id == -1时返回新分配的流 ID;否则成功返回 0。失败返回以下负值错误码(常量取值见 nghttp2.h):

错误码常量值触发条件
NGHTTP2_ERR_NOMEM-901内存分配失败
NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE-509next_stream_id达到INT32_MAX,无可用流 ID
NGHTTP2_ERR_INVALID_ARGUMENT-501stream_id为 0
NGHTTP2_ERR_DATA_EXIST-529该流的 DATA/HEADERS 已提交且尚未处理完(例如流处于 reserved 状态时重复提交)
NGHTTP2_ERR_PROTO-505stream_id为 -1 但session是服务端会话(服务端不能"打开"新流)

判断错误码是否致命时,可配合nghttp2_is_fatal()辅助函数(见 lib/nghttp2-1.65.0/doc/nghttp2_is_fatal.rst)。

Fluent Bit 中的实际使用模式

Fluent Bit 的 HTTP/2 客户端位于 src/flb_http_client_http2.c。从源码结构看,它并没有直接调用裸的nghttp2_submit_headers(),而是按上文建议采用了更高层的封装:

  • flb_http2_request_submit()中组装nghttp2_nv头数组(用户头 +Host+Content-length),并绑定data_provider.read_callback(即http2_data_source_read_callback)提供请求体,然后调用nghttp2_submit_request(session->inner_session, NULL, headers, header_count, &data_provider, stream)(src/flb_http_client_http2.c)。最后一个参数stream_user_data正是stream指针——对应本文参数表中"仅当流从 idle 变为 open 时生效"的用户指针语义;
  • 提交后立刻调用nghttp2_session_send()驱动帧发送,并把流状态机推进到HTTP_STREAM_STATUS_PROCESSINGHTTP_STREAM_STATUS_RECEIVING_HEADERS
  • 会话建立阶段还通过nghttp2_submit_settings()下发 SETTINGS 帧(src/flb_http_client_http2.c)。

这正体现了文档给出的选型指引:常规 HTTP 请求走nghttp2_submit_request2()系列,nghttp2_submit_headers()则留给需要直接操控 flags 的场景(如服务器推送 trailer、自定义扩展头块等)。理解这条低层 API 的完整语义,有助于阅读 Fluent Bit HTTP/2 路径的调试与二次开发。

小结

  • nghttp2_submit_headers()提交的是逻辑 HEADERS 帧,入队于 outbound 队列,END_HEADERS/CONTINUATION 拆分与 END_STREAM 之外的标志位全部由库接管;
  • stream_id = -1仅客户端可用且返回新流 ID,但该流在nghttp2_before_frame_send_callback触发前不可复用;
  • 伪头字段由应用负责按序提供;名称默认被拷贝并小写化,NO_COPY_*标志下生命周期责任转移到应用;
  • 五个错误码(-901/-509/-501/-529/-505)分别对应内存、流 ID 耗尽、非法 0、重复提交与服务端误用,可直接用于错误处理分支;
  • Fluent Bit 在实际请求路径上选择nghttp2_submit_request()+stream_user_data回调的组合,与该 API 文档的低层/高层定位一致。

【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 14:40:33

MongoDB聚合管道:查询统计与性能优化实战

第一次被聚合管道教做人&#xff0c;是在一个"订单看板"的需求上。当时订单集合里也就七八万条数据&#xff0c;我用了最朴素的做法&#xff1a;find({status: "paid"})把全部订单捞回应用层&#xff0c;然后用一个 for 循环累加出总额、订单数&#xff0c…

作者头像 李华
网站建设 2026/9/17 14:38:46

MCP 服务里的 DeepSeek 请求走 TaoToken,uv 天气查询流程不用改

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 14:38:17

Notepad-- 文件对比:文本逐行高亮差异,10M 内二进制也能比

Notepad-- 文件对比&#xff1a;文本逐行高亮差异&#xff0c;10M 内二进制也能比 【免费下载链接】notepad-- 一个支持windows/linux/mac的文本编辑器&#xff0c;目标是做中国人自己的编辑器&#xff0c;来自中国。 项目地址: https://gitcode.com/GitHub_Trending/no/note…

作者头像 李华
网站建设 2026/9/17 14:37:26

自然连接⋈的真相:不是自动匹配,而是隐式多条件陷阱

1. 项目概述&#xff1a;为什么“自然连接”是数据库里最常被误解、也最该被吃透的操作&#xff1f;“土话笔记&#xff1a;数据库——自然连接(符号⋈)”这个标题&#xff0c;乍看像学生课后随手记的潦草笔记&#xff0c;但恰恰是这种带点烟火气的命名&#xff0c;戳中了数据库…

作者头像 李华