leevis.com
leevis.com copied to clipboard
nginx http2.0的实现
在ls回调函数ngx_http_init_connection中。如果支持http2.0协议,则可读事件的回调函数覆写为ngx_http_v2_init。当连接可读时,回调该函数。
ngx_http_v2_init
-
创建连接结构体h2c,类型为ngx_http_v2_connection_t,一个连接对应一个结构体。
-
创建h2c->streams_index stream列表。
-
调用ngx_http_v2_send_settings构件SETTINGS帧,设置SETTINGS_MAX_CONCURRENT_STREAMS、SETTINGS_INITIAL_WINDOW_SIZE、SETTINGS_MAX_FRAME_SIZE。因为该帧是面向链接的而不是流,因此流标识必须为0(ngx_http_v2_write_sid(buf->last, 0))。 构件好的帧被保存到h2c->last_out上,h2c类型为ngx_http_v2_connection_s。
-
调用ngx_http_v2_send_window_update函数构件WINDOW_UPDATE帧,结果被保存到h2c->last_out上。
-
设置state回调函数为ngx_http_v2_state_preface。
-
可读事件回调函数为ngx_http_v2_read_handler,可写事件回调函数为ngx_http_v2_write_handler。
-
调用可读事件的回调函数。
ngx_http_v2_read_handler
-
调用recv封装的函数读取socket缓存区内容。
-
调用state回调函数ngx_http_v2_state_preface。判断读取的内容是否为
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
,不是则返回Error Codes帧错误码为PROTOCOL_ERROR。 -
调用ngx_http_v2_state_head处理帧,或添加回调。
-
调用ngx_http_v2_send_output_queue发送应答帧。
ngx_http_v2_send_settings
拼装settings帧。 每一个帧由ngx_http_v2_out_frame_t来表示,其中first指向帧的缓存链,调用ngx_http_v2_write_len_and_type向缓存链的第一个buf写入Length(24)和Type(8),细节请参考帧格式。Length+Type共占32bit,低8bit是Type,所以Length+Type的内容就是Length左移8bit与上低8位的Type。buf->last是u_char类型长度1字节8bit,复制Flag后++。接着调用ngx_http_v2_write_sid写31bit的Stream Identifier了和1bit的R。 接着该设置Payload了。我们看下settings帧的Payload格式,16bit的Identifier和32bit的Value,调用ngx_http_v2_write_uint16写Identifier,调用ngx_http_v2_write_uint32写Value。 最后调用ngx_http_v2_queue_blocked_frame把帧挂到h2c->last_out上。
ngx_http_v2_state_head
解析帧头。 调用ngx_http_v2_parse_uint32解析帧的Length(24)和Type(8)。调用ngx_http_v2_parse_length从上述解析出的Length和Type中解析Length。调用ngx_http_v2_parse_sid解析Stream Identifier,32位中最高1bit是R位。 调用ngx_http_v2_parse_type从上述解析出的Length和Type解析Type。自此帧头已经解析完了。
ngx_http_v2_frame_states
解析不同的帧的payload。目前有10种类型的帧格式。
ngx_http_v2_state_settings
解析settings帧。 先判断settings帧的格式是否正确,然后调用ngx_http_v2_send_settings拼settings帧的ack,最后调用ngx_http_v2_state_settings_params。
ngx_http_v2_run_request
ngx_http_v2_state_headers
解析headers帧。
static u_char *
ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
size_t size;
ngx_uint_t padded, priority, depend, dependency, excl, weight;
ngx_uint_t status;
ngx_http_v2_node_t *node;
ngx_http_v2_stream_t *stream;
ngx_http_v2_srv_conf_t *h2scf;
// padded标识
padded = h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG;
// priority标识
priority = h2c->state.flags & NGX_HTTP_V2_PRIORITY_FLAG;
// payload 长度
size = 0;
// 设置了padded标识,payload包涵了1个字节的pad
if (padded) {
size++;
}
// 设置了priority标识,payload包涵了1bit的E,31bit的Stream Dependency和8bit的Weight。共5个字节。
if (priority) {
size += sizeof(uint32_t) + 1;
}
// 当帧头的length小于等于size说明该帧payload格式错误
if (h2c->state.length < size) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent HEADERS frame with incorrect length %uz",
h2c->state.length);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (h2c->state.length == size) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent HEADERS frame with empty header block");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (h2c->goaway) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"skipping http2 HEADERS frame");
return ngx_http_v2_state_skip(h2c, pos, end);
}
if ((size_t) (end - pos) < size) {
return ngx_http_v2_state_save(h2c, pos, end,
ngx_http_v2_state_headers);
}
h2c->state.length -= size;
// 解析pad,填充长度
if (padded) {
h2c->state.padding = *pos++;
if (h2c->state.padding > h2c->state.length) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent padded HEADERS frame "
"with incorrect length: %uz, padding: %uz",
h2c->state.length, h2c->state.padding);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
h2c->state.length -= h2c->state.padding;
}
depend = 0;
excl = 0;
weight = 16;
if (priority) {
dependency = ngx_http_v2_parse_uint32(pos);
// depend 该流所依赖的流
depend = dependency & 0x7fffffff;
// E标识
excl = dependency >> 31;
// 该流的优先级权重
weight = pos[4] + 1;
pos += sizeof(uint32_t) + 1;
}
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 HEADERS frame sid:%ui on %ui excl:%ui weight:%ui",
h2c->state.sid, depend, excl, weight);
// 客户端发起的流标识是基数的流
if (h2c->state.sid % 2 == 0 || h2c->state.sid <= h2c->last_sid) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent HEADERS frame with incorrect identifier "
"%ui, the last was %ui", h2c->state.sid, h2c->last_sid);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
}
h2c->last_sid = h2c->state.sid;
h2c->state.pool = ngx_create_pool(1024, h2c->connection->log);
if (h2c->state.pool == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
if (depend == h2c->state.sid) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent HEADERS frame for stream %ui "
"with incorrect dependency", h2c->state.sid);
status = NGX_HTTP_V2_PROTOCOL_ERROR;
goto rst_stream;
}
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
h2c->state.header_limit = h2scf->max_header_size;
if (h2c->processing >= h2scf->concurrent_streams) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"concurrent streams exceeded %ui", h2c->processing);
status = NGX_HTTP_V2_REFUSED_STREAM;
goto rst_stream;
}
if (!h2c->settings_ack
&& !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG)
&& h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW)
{
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent stream with data "
"before settings were acknowledged");
status = NGX_HTTP_V2_REFUSED_STREAM;
goto rst_stream;
}
node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1);
if (node == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
if (node->parent) {
ngx_queue_remove(&node->reuse);
h2c->closed_nodes--;
}
stream = ngx_http_v2_create_stream(h2c);
if (stream == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
h2c->state.stream = stream;
stream->pool = h2c->state.pool;
h2c->state.keep_pool = 1;
stream->request->request_length = h2c->state.length;
stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG;
stream->node = node;
node->stream = stream;
if (priority || node->parent == NULL) {
node->weight = weight;
ngx_http_v2_set_dependency(h2c, node, depend, excl);
}
if (h2c->connection->requests >= h2scf->max_requests) {
h2c->goaway = 1;
if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR) == NGX_ERROR) {
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
}
return ngx_http_v2_state_header_block(h2c, pos, end);
rst_stream:
if (ngx_http_v2_send_rst_stream(h2c, h2c->state.sid, status) != NGX_OK) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
return ngx_http_v2_state_header_block(h2c, pos, end);
}
http2 头部编码: https://imququ.com/post/header-compression-in-http2.html
代码分析
组装帧头
// 一个帧结构体
struct ngx_http_v2_out_frame_s {
ngx_http_v2_out_frame_t *next;
ngx_chain_t *first;
ngx_chain_t *last;
ngx_int_t (*handler)(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame);
ngx_http_v2_stream_t *stream;
size_t length;
unsigned blocked:1;
unsigned fin:1;
};
static ngx_http_v2_out_frame_t *
ngx_http_v2_get_frame(ngx_http_v2_connection_t *h2c, size_t length,
ngx_uint_t type, u_char flags, ngx_uint_t sid)
{
ngx_buf_t *buf;
ngx_pool_t *pool;
ngx_http_v2_out_frame_t *frame;
frame = h2c->free_frames;
if (frame) {
h2c->free_frames = frame->next;
buf = frame->first->buf;
buf->pos = buf->start;
frame->blocked = 0;
} else {
pool = h2c->pool ? h2c->pool : h2c->connection->pool;
frame = ngx_pcalloc(pool, sizeof(ngx_http_v2_out_frame_t));
if (frame == NULL) {
return NULL;
}
frame->first = ngx_alloc_chain_link(pool);
if (frame->first == NULL) {
return NULL;
}
buf = ngx_create_temp_buf(pool, NGX_HTTP_V2_FRAME_BUFFER_SIZE);
if (buf == NULL) {
return NULL;
}
buf->last_buf = 1;
frame->first->buf = buf;
frame->last = frame->first;
frame->handler = ngx_http_v2_frame_handler;
}
#if (NGX_DEBUG)
if (length > NGX_HTTP_V2_FRAME_BUFFER_SIZE - NGX_HTTP_V2_FRAME_HEADER_SIZE)
{
ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
"requested control frame is too large: %uz", length);
return NULL;
}
frame->length = length;
#endif
// length(24) + type(8) 共4B 以网络字节序写入buf这块内存
buf->last = ngx_http_v2_write_len_and_type(buf->pos, length, type);
// flags(8)
*buf->last++ = flags;
// R(1) + stream ID(31) = 32bit
buf->last = ngx_http_v2_write_sid(buf->last, sid);
return frame;
}
组装settings帧
static ngx_int_t
ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c)
{
size_t len;
ngx_buf_t *buf;
ngx_chain_t *cl;
ngx_http_v2_srv_conf_t *h2scf;
ngx_http_v2_out_frame_t *frame;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 send SETTINGS frame");
frame = ngx_palloc(h2c->pool, sizeof(ngx_http_v2_out_frame_t));
if (frame == NULL) {
return NGX_ERROR;
}
cl = ngx_alloc_chain_link(h2c->pool);
if (cl == NULL) {
return NGX_ERROR;
}
// 一个settings帧payload的大小是48bit = 6B。
// Identifier (16) + Value (32) = 48bit
// 设置3个
len = NGX_HTTP_V2_SETTINGS_PARAM_SIZE * 3;
// NGX_HTTP_V2_FRAME_HEADER_SIZE frame header 9B = 72bit
// Length(24) + Type(8) + Flags(8)+ R(1) + Stream Identifier(31) = 72bit
// 9B + 6B * 3 = 27B
buf = ngx_create_temp_buf(h2c->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE + len);
if (buf == NULL) {
return NGX_ERROR;
}
buf->last_buf = 1;
cl->buf = buf;
cl->next = NULL;
frame->first = cl;
frame->last = cl;
frame->handler = ngx_http_v2_settings_frame_handler;
frame->stream = NULL;
#if (NGX_DEBUG)
frame->length = len;
#endif
frame->blocked = 0;
buf->last = ngx_http_v2_write_len_and_type(buf->last, len,
NGX_HTTP_V2_SETTINGS_FRAME);
*buf->last++ = NGX_HTTP_V2_NO_FLAG;
buf->last = ngx_http_v2_write_sid(buf->last, 0);
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
buf->last = ngx_http_v2_write_uint16(buf->last,
NGX_HTTP_V2_MAX_STREAMS_SETTING);
buf->last = ngx_http_v2_write_uint32(buf->last,
h2scf->concurrent_streams);
buf->last = ngx_http_v2_write_uint16(buf->last,
NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING);
buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size);
buf->last = ngx_http_v2_write_uint16(buf->last,
NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING);
buf->last = ngx_http_v2_write_uint32(buf->last,
NGX_HTTP_V2_MAX_FRAME_SIZE);
// 把该frame挂载到h2c->last_out 这个链表的后面。
ngx_http_v2_queue_blocked_frame(h2c, frame);
return NGX_OK;
}
可读回调函数:
static void
ngx_http_v2_read_handler(ngx_event_t *rev)
{
u_char *p, *end;
size_t available;
ssize_t n;
ngx_connection_t *c;
ngx_http_v2_main_conf_t *h2mcf;
ngx_http_v2_connection_t *h2c;
c = rev->data;
h2c = c->data;
if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
return;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 read handler");
h2c->blocked = 1;
if (c->close) {
c->close = 0;
if (!h2c->goaway) {
h2c->goaway = 1;
if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR)
== NGX_ERROR)
{
ngx_http_v2_finalize_connection(h2c, 0);
return;
}
if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
ngx_http_v2_finalize_connection(h2c, 0);
return;
}
}
h2c->blocked = 0;
return;
}
h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE;
do {
p = h2mcf->recv_buffer;
ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE);
end = p + h2c->state.buffer_used;
n = c->recv(c, end, available);
if (n == NGX_AGAIN) {
break;
}
if (n == 0
&& (h2c->state.incomplete || h2c->processing || h2c->pushing))
{
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client prematurely closed connection");
}
if (n == 0 || n == NGX_ERROR) {
c->error = 1;
ngx_http_v2_finalize_connection(h2c, 0);
return;
}
end += n;
// 把读取到未解析的内容保存在state.buffer字符数组中
h2c->state.buffer_used = 0;
h2c->state.incomplete = 0;
do {
// 如果没有四层代理协议,则handler是ngx_http_v2_state_preface函数
// ngx_http_v2_state_preface 和 ngx_http_v2_state_preface_end 是处理
// h2 connection preface 见:http://httpwg.org/specs/rfc7540.html#ConnectionHeader
// 如果剩余的内容不是一个完整的帧,没有处理完毕,剩余的内容会调用
// ngx_http_v2_state_save函数,保存在state这个结构体中。
// 接着调用ngx_http_v2_state_head 处理frame head
p = h2c->state.handler(h2c, p, end);
if (p == NULL) {
return;
}
} while (p != end);
} while (rev->ready);
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
return;
}
if (h2c->last_out && ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
ngx_http_v2_finalize_connection(h2c, 0);
return;
}
h2c->blocked = 0;
if (h2c->processing || h2c->pushing) {
if (rev->timer_set) {
ngx_del_timer(rev);
}
return;
}
ngx_http_v2_handle_connection(h2c);
}
// 处理帧头
static u_char *
ngx_http_v2_state_head(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
{
uint32_t head;
ngx_uint_t type;
// 如果长度不够保存下来
if (end - pos < NGX_HTTP_V2_FRAME_HEADER_SIZE) {
return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_head);
}
// 32bit 包含了length(24) 和type(8)
head = ngx_http_v2_parse_uint32(pos);
h2c->state.length = ngx_http_v2_parse_length(head);
h2c->state.flags = pos[4]; // 第4字节 [32bit-40bit)
h2c->state.sid = ngx_http_v2_parse_sid(&pos[5]);
pos += NGX_HTTP_V2_FRAME_HEADER_SIZE;
// 帧头处理结束
// 32bit 高8位
// type定义:http://httpwg.org/specs/rfc7540.html#iana-frames
type = ngx_http_v2_parse_type(head);
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 frame type:%ui f:%Xd l:%uz sid:%ui",
type, h2c->state.flags, h2c->state.length, h2c->state.sid);
if (type >= NGX_HTTP_V2_FRAME_STATES) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent frame with unknown type %ui", type);
return ngx_http_v2_state_skip(h2c, pos, end);
}
// ngx_http_v2_frame_states 是定义了帧处理回调函数的数组
return ngx_http_v2_frame_states[type](h2c, pos, end);
}
ngx_http_v2_state_headers HEADERS FRAME 处理函数
static u_char *
ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
size_t size;
ngx_uint_t padded, priority, depend, dependency, excl, weight;
ngx_uint_t status;
ngx_http_v2_node_t *node;
ngx_http_v2_stream_t *stream;
ngx_http_v2_srv_conf_t *h2scf;
// 是否有payload填充
padded = h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG;
// 是否有依赖的stream
priority = h2c->state.flags & NGX_HTTP_V2_PRIORITY_FLAG;
size = 0;
// 填充长度占1字节
if (padded) {
size++;
}
// 依赖stream 占5字节
if (priority) {
size += sizeof(uint32_t) + 1;
}
if (h2c->state.length < size) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent HEADERS frame with incorrect length %uz",
h2c->state.length);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (h2c->state.length == size) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent HEADERS frame with empty header block");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (h2c->goaway) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"skipping http2 HEADERS frame");
return ngx_http_v2_state_skip(h2c, pos, end);
}
if ((size_t) (end - pos) < size) {
return ngx_http_v2_state_save(h2c, pos, end,
ngx_http_v2_state_headers);
}
h2c->state.length -= size;
if (padded) {
// 解析填充字段长度字节为单位
// 填充字节大小
h2c->state.padding = *pos++;
if (h2c->state.padding > h2c->state.length) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent padded HEADERS frame "
"with incorrect length: %uz, padding: %uz",
h2c->state.length, h2c->state.padding);
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_PROTOCOL_ERROR);
}
// 剩余的真实的paylo大小
h2c->state.length -= h2c->state.padding;
}
depend = 0;
excl = 0;
weight = NGX_HTTP_V2_DEFAULT_WEIGHT;
if (priority) {
dependency = ngx_http_v2_parse_uint32(pos);
depend = dependency & 0x7fffffff;
excl = dependency >> 31;
weight = pos[4] + 1;
pos += sizeof(uint32_t) + 1;
}
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 HEADERS frame sid:%ui "
"depends on %ui excl:%ui weight:%ui",
h2c->state.sid, depend, excl, weight);
// 客户端发起的请求的stream ID 是奇数
if (h2c->state.sid % 2 == 0 || h2c->state.sid <= h2c->last_sid) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent HEADERS frame with incorrect identifier "
"%ui, the last was %ui", h2c->state.sid, h2c->last_sid);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
}
h2c->last_sid = h2c->state.sid;
h2c->state.pool = ngx_create_pool(1024, h2c->connection->log);
if (h2c->state.pool == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
if (depend == h2c->state.sid) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent HEADERS frame for stream %ui "
"with incorrect dependency", h2c->state.sid);
status = NGX_HTTP_V2_PROTOCOL_ERROR;
goto rst_stream;
}
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
h2c->state.header_limit = h2scf->max_header_size;
// processing 当前处理的流的数目
if (h2c->processing >= h2scf->concurrent_streams) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"concurrent streams exceeded %ui", h2c->processing);
status = NGX_HTTP_V2_REFUSED_STREAM;
goto rst_stream;
}
if (!h2c->settings_ack
&& !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG)
&& h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW)
{
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent stream with data "
"before settings were acknowledged");
status = NGX_HTTP_V2_REFUSED_STREAM;
goto rst_stream;
}
// 获取一个节点,
node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1);
if (node == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
if (node->parent) {
ngx_queue_remove(&node->reuse);
h2c->closed_nodes--;
}
// 创建一个请求流结构体,同时对应一个请求
stream = ngx_http_v2_create_stream(h2c, 0);
if (stream == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
h2c->state.stream = stream;
stream->pool = h2c->state.pool;
h2c->state.keep_pool = 1;
stream->request->request_length = h2c->state.length;
stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG;
stream->node = node;
node->stream = stream;
if (priority || node->parent == NULL) {
node->weight = weight;
ngx_http_v2_set_dependency(h2c, node, depend, excl);
}
if (h2c->connection->requests >= h2scf->max_requests) {
h2c->goaway = 1;
if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR) == NGX_ERROR) {
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
}
return ngx_http_v2_state_header_block(h2c, pos, end);
rst_stream:
if (ngx_http_v2_send_rst_stream(h2c, h2c->state.sid, status) != NGX_OK) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
return ngx_http_v2_state_header_block(h2c, pos, end);
}
// 解析header的[HPACK](http://httpwg.org/specs/rfc7541.html#detailed.format)格式的头
static u_char *
ngx_http_v2_state_header_block(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
u_char ch;
ngx_int_t value;
ngx_uint_t indexed, size_update, prefix;
if (end - pos < 1) {
return ngx_http_v2_state_headers_save(h2c, pos, end,
ngx_http_v2_state_header_block);
}
if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)
&& h2c->state.length < NGX_HTTP_V2_INT_OCTETS)
{
return ngx_http_v2_handle_continuation(h2c, pos, end,
ngx_http_v2_state_header_block);
}
size_update = 0;
indexed = 0;
ch = *pos;
if (ch >= (1 << 7)) {
// http://httpwg.org/specs/rfc7541.html#indexed.header.representation
/* indexed header field */
indexed = 1;
// (1 << 7) -1 6bit全是1.
prefix = ngx_http_v2_prefix(7);
} else if (ch >= (1 << 6)) {
/* literal header field with incremental indexing */
h2c->state.index = 1;
prefix = ngx_http_v2_prefix(6);
} else if (ch >= (1 << 5)) {
/* dynamic table size update */
size_update = 1;
prefix = ngx_http_v2_prefix(5);
} else if (ch >= (1 << 4)) {
/* literal header field never indexed */
prefix = ngx_http_v2_prefix(4);
} else {
/* literal header field without indexing */
prefix = ngx_http_v2_prefix(4);
}
// 解析HPACK 的整数编码
// value 是表中的下标
value = ngx_http_v2_parse_int(h2c, &pos, end, prefix);
if (value < 0) {
if (value == NGX_AGAIN) {
return ngx_http_v2_state_headers_save(h2c, pos, end,
ngx_http_v2_state_header_block);
}
if (value == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent header block with too long %s value",
size_update ? "size update" : "header index");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
}
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent header block with incorrect length");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (indexed) {
// 静态table 索引
if (ngx_http_v2_get_indexed_header(h2c, value, 0) != NGX_OK) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
}
return ngx_http_v2_state_process_header(h2c, pos, end);
}
if (size_update) {
if (ngx_http_v2_table_size(h2c, value) != NGX_OK) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
}
return ngx_http_v2_state_header_complete(h2c, pos, end);
}
if (value == 0) {
h2c->state.parse_name = 1;
} else if (ngx_http_v2_get_indexed_header(h2c, value, 1) != NGX_OK) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
}
h2c->state.parse_value = 1;
return ngx_http_v2_state_field_len(h2c, pos, end);
}
创建一个流,一个流对应一个请求。 一个连接包含多个流,一个流包含多个帧。 这个函数是适配nginx http 框架的核心
static ngx_http_v2_stream_t *
ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t push)
{
ngx_log_t *log;
ngx_event_t *rev, *wev;
ngx_connection_t *fc;
ngx_http_log_ctx_t *ctx;
ngx_http_request_t *r;
ngx_http_v2_stream_t *stream;
ngx_http_v2_srv_conf_t *h2scf;
ngx_http_core_srv_conf_t *cscf;
fc = h2c->free_fake_connections;
if (fc) {
h2c->free_fake_connections = fc->data;
rev = fc->read;
wev = fc->write;
log = fc->log;
ctx = log->data;
} else {
// 创建一个 虚拟连接
fc = ngx_palloc(h2c->pool, sizeof(ngx_connection_t));
if (fc == NULL) {
return NULL;
}
// 虚拟连接可读回调
rev = ngx_palloc(h2c->pool, sizeof(ngx_event_t));
if (rev == NULL) {
return NULL;
}
wev = ngx_palloc(h2c->pool, sizeof(ngx_event_t));
if (wev == NULL) {
return NULL;
}
log = ngx_palloc(h2c->pool, sizeof(ngx_log_t));
if (log == NULL) {
return NULL;
}
ctx = ngx_palloc(h2c->pool, sizeof(ngx_http_log_ctx_t));
if (ctx == NULL) {
return NULL;
}
ctx->connection = fc;
ctx->request = NULL;
ctx->current_request = NULL;
}
ngx_memcpy(log, h2c->connection->log, sizeof(ngx_log_t));
log->data = ctx;
if (push) {
log->action = "processing pushed request headers";
} else {
log->action = "reading client request headers";
}
ngx_memzero(rev, sizeof(ngx_event_t));
rev->data = fc;
rev->ready = 1;
rev->handler = ngx_http_v2_close_stream_handler;
rev->log = log;
ngx_memcpy(wev, rev, sizeof(ngx_event_t));
wev->write = 1;
ngx_memcpy(fc, h2c->connection, sizeof(ngx_connection_t));
fc->data = h2c->http_connection;
fc->read = rev;
fc->write = wev;
fc->sent = 0;
fc->log = log;
fc->buffered = 0;
fc->sndlowat = 1;
fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
// 和http1 就一样了,创建一个请求结构体
r = ngx_http_create_request(fc);
if (r == NULL) {
return NULL;
}
// 强制设置http2.0协议
ngx_str_set(&r->http_protocol, "HTTP/2.0");
r->http_version = NGX_HTTP_VERSION_20;
r->valid_location = 1;
fc->data = r;
h2c->connection->requests++;
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
// 创建一个保存请求的buffer
r->header_in = ngx_create_temp_buf(r->pool,
cscf->client_header_buffer_size);
if (r->header_in == NULL) {
ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NULL;
}
// 创建请求头
if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NULL;
}
// 对于请求而言没有长连接
r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
stream = ngx_pcalloc(r->pool, sizeof(ngx_http_v2_stream_t));
if (stream == NULL) {
ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NULL;
}
r->stream = stream;
stream->request = r;
stream->connection = h2c;
h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
stream->send_window = h2c->init_window;
stream->recv_window = h2scf->preread_size;
if (push) {
h2c->pushing++;
} else {
h2c->processing++;
}
return stream;
}
文件 ngx_http_v2_table.c
// 解析HPACK协议的header头
ngx_int_t
ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c, ngx_uint_t index,
ngx_uint_t name_only)
{
u_char *p;
size_t rest;
ngx_http_v2_header_t *entry;
if (index == 0) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent invalid hpack table index 0");
return NGX_ERROR;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 get indexed %s: %ui",
name_only ? "name" : "header", index);
index--;
// 是否是静态表的头
if (index < NGX_HTTP_V2_STATIC_TABLE_ENTRIES) {
h2c->state.header = ngx_http_v2_static_table[index];
return NGX_OK;
}
index -= NGX_HTTP_V2_STATIC_TABLE_ENTRIES;
if (index < h2c->hpack.added - h2c->hpack.deleted) {
index = (h2c->hpack.added - index - 1) % h2c->hpack.allocated;
entry = h2c->hpack.entries[index];
p = ngx_pnalloc(h2c->state.pool, entry->name.len + 1);
if (p == NULL) {
return NGX_ERROR;
}
h2c->state.header.name.len = entry->name.len;
h2c->state.header.name.data = p;
rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->name.data;
if (entry->name.len > rest) {
p = ngx_cpymem(p, entry->name.data, rest);
p = ngx_cpymem(p, h2c->hpack.storage, entry->name.len - rest);
} else {
p = ngx_cpymem(p, entry->name.data, entry->name.len);
}
*p = '\0';
if (name_only) {
return NGX_OK;
}
p = ngx_pnalloc(h2c->state.pool, entry->value.len + 1);
if (p == NULL) {
return NGX_ERROR;
}
h2c->state.header.value.len = entry->value.len;
h2c->state.header.value.data = p;
rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->value.data;
if (entry->value.len > rest) {
p = ngx_cpymem(p, entry->value.data, rest);
p = ngx_cpymem(p, h2c->hpack.storage, entry->value.len - rest);
} else {
p = ngx_cpymem(p, entry->value.data, entry->value.len);
}
*p = '\0';
return NGX_OK;
}
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent out of bound hpack table index: %ui", index);
return NGX_ERROR;
}
修改nginx返回的server:
在ngx_http_v2_filter_module.c 文件中有以下定义: static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7"; 修改一下。
package main
import (
"fmt"
"golang.org/x/net/http2/hpack"
)
func main() {
fmt.Println("nginx", "→", Encode("nginx"))
fmt.Println("apache", "→", Encode("apache"))
fmt.Println("-----")
fmt.Println("\\x84\\xaa\\x63\\x55\\xe7", "→", Decode("\x84\xaa\x63\x55\xe7"))
fmt.Println("\\x84\\x1d\\x63\\x24\\xe5", "→", Decode("\x84\x1d\x63\x24\xe5"))
}
func Encode(s string) string {
var result string
hd := hpack.AppendHuffmanString(nil, s)
hl := hpack.HuffmanEncodeLength(s) | 0x80
result += RenderByte(byte(hl))
for _, b := range hd {
result += RenderByte(b)
}
return result
}
func Decode(s string) string {
data := []byte(s)
result, _ := hpack.HuffmanDecodeToString(data[1:])
return result
}
func RenderByte(b byte) string {
return fmt.Sprintf("\\x%x", b)
}