leevis.com icon indicating copy to clipboard operation
leevis.com copied to clipboard

http2.0协议建立

Open vislee opened this issue 8 years ago • 0 comments

http2.0 协议是一个二进制协议。具体特性就不再多介绍了。

http2.0是应用层协议,传输层还是tcp协议。http2.0协议支持http的和https的,http的需要多一次协议协商。https的需要ssl支持alpn。

明文协议:

  • client -> server:
GET / HTTP1.1
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
  • server -> client:
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c

如果服务端不支持http2.0直接返回http1.1的resp

加密协议:

在建立安全握手阶段,客户端把支持的协议传递给服务端,服务端选择一个协议传递给客户端,优先选择http2.0。双方都支持http2.0的情况下,客户端发送的直接就是http2.0的协议。

http2.0

http2.0协议是二进制协议,一帧一帧的传递,客户端发送给服务端的第一个帧必须是: PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n

服务端收到该帧,回复setting帧。格式见:http://httpwg.org/specs/rfc7540.html#SETTINGS

  • SETTINGS SETTINGS帧(type=0x4)传达影响端点通信方式的配置参数。也用于确认收到这些参数。 标志:ack(0x1) 若设置:settings帧有效载荷必须为空。 流标志为 0x0,适用于连接。 settings 有效载荷由零个或多个参数组成,每个参数由一个无符号16位标识符 和 一个无符号32位值组成。 +--------------------------+ | Identifier(16)
    +--------------------------+------------------------------------+ | Value(32) +---------------------------------------------------------------+ 参数定义: SETTINGS_HEADER_TABLE_SIZE (0x1):允许发送者以八位字节的形式通知远程端点用于解码头块的头压缩表的最大尺寸。编码器可以通过使用特定于头部块内头部压缩格式的信令来选择等于或小于此值的任何大小(请参见[压缩])。初始值是4,096个八位字节。 SETTINGS_ENABLE_PUSH (0x2):此设置可用于禁用服务器推送(第8.2节)。如果一个端点接收到这个参数设置为0的值,它不应该发送一个PUSH_PROMISE帧。一个端点既将这个参数设置为0,并且确认它也必须将PUSH_PROMISE帧的接收视为连接错误(见5.4节)。 1)类型PROTOCOL_ERROR。初始值为1,表示允许服务器推送。除0或1以外的任何值必须视为PROTOCOL_ERROR类型的连接错误(第5.4.1节)。 SETTINGS_MAX_CONCURRENT_STREAMS (0x3):表示发件人允许的最大并发流数。这个限制是有方向性的:它适用于发送者允许接收者创建的数据流。最初,这个值没有限制。建议此值不小于100,以免不必要地限制并行性。值为0的SETTINGS_MAX_CONCURRENT_STREAMS不应被视为特殊的端点。零值确实会阻止创建新的流;然而,这也可能发生在活动流所耗尽的任何限制上。服务器应该只在短时间内设置一个零值;如果服务器不希望接受请求,关闭连接更合适。 SETTINGS_INITIAL_WINDOW_SIZE (0x4):指示发送者的流级别流控制的初始窗口大小(以八位字节为单位)。初始值是2 ^ 16-1(65,535)个八位组。该设置会影响所有流的窗口大小(请参阅第6.9.2节)。高于最大流量控制窗口大小2 ^ 31-1的值必须视为FLOW_CONTROL_ERROR类型的连接错误(见第5.4.1节)。 SETTINGS_MAX_FRAME_SIZE (0x5):指示发送者愿意接收的最大帧有效载荷的大小,以八位字节为单位。初始值是2 ^ 14(16,384)个八位字节。端点通告的值必须在该初始值和最大允许帧大小之间(2 ^ 24-1或16,777,215个八位字节),包括在内。此范围之外的值务必视为PROTOCOL_ERROR类型的连接错误(第5.4.1节)。 SETTINGS_MAX_HEADER_LIST_SIZE (0x6):此通报设置以八位字节的形式通知对等方发送方准备接受的标题列表的最大大小。该值基于头字段的未压缩大小,包括名称和八位字节的值的长度,以及每个头字段的开销32个字节。对于任何给定的请求,可能会强制实施一个比所宣传的更低的限制。 nginx中调用ngx_http_v2_send_settings函数发送settings帧
static ngx_int_t
ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c)
{
    ...

    // 分配帧结构体
    frame = ngx_palloc(h2c->pool, sizeof(ngx_http_v2_out_frame_t));
    ...

    // 帧二进制内容
    buf = ngx_create_temp_buf(h2c->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE + len);
    if (buf == NULL) {
        return NGX_ERROR;
    }

    buf->last_buf = 1;

    // 通过buf chain 挂到帧结构体上
    cl->buf = buf;
    cl->next = NULL;

    frame->first = cl;
    frame->last = cl;
    ...

    // 帧头 24位长度 + 8位 type
    buf->last = ngx_http_v2_write_len_and_type(buf->last, len,
                                               NGX_HTTP_V2_SETTINGS_FRAME);
    // 帧头8位flag
    *buf->last++ = NGX_HTTP_V2_NO_FLAG;

    // 帧头1位R 31位流标识ID
    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);

    // settings帧payload
    // 16位标识符
    buf->last = ngx_http_v2_write_uint16(buf->last,
                                         NGX_HTTP_V2_MAX_STREAMS_SETTING);
    // 32位无符号值
    buf->last = ngx_http_v2_write_uint32(buf->last,
                                         h2scf->concurrent_streams);
    // 多个16位标识符 + 32位无符号值
    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);
    ...
}
  • GOAWAY GOAWAY(type=0x7)用于启动连接关闭或发出严重错误状态信号。允许端点正常停止接受新的流,同时仍然完成对先前建立的流的处理。 +--+-----------------------------------------------+ |R|Last-Stream-ID(31) +--+-----------------------------------------------+ | Error Code(32) +--------------------------------------------------+ | Additional Debug Data(*) +--------------------------------------------------+ 在nginx中调用ngx_http_v2_send_goaway 函数发送GOAWAY帧
static ngx_int_t
ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c, ngx_uint_t status)
{
    ...
    frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_GOAWAY_SIZE,
                                  NGX_HTTP_V2_GOAWAY_FRAME,
                                  NGX_HTTP_V2_NO_FLAG, 0);
    if (frame == NULL) {
        return NGX_ERROR;
    }

    buf = frame->first->buf;

    // 1位保留位 + 31位Last-stream-ID
    buf->last = ngx_http_v2_write_sid(buf->last, h2c->last_sid);
    // 32位错误码
    buf->last = ngx_http_v2_write_uint32(buf->last, status);

    ngx_http_v2_queue_blocked_frame(h2c, frame);

    return NGX_OK;
}
  • WINDOW_UPDATE WINDOW_UPDATE帧(type=0x8) 用于实现流控。 两个层面运行: 单独的流 和 整个连接。 流控只在两个端点之间。 WINDOW_UPDATE帧的有效载荷是一个保留位加上一个无符号的31位整数,指出除了现有的流量控制窗口之外,发送者可以传输的八位字节数。 +--+----------------------------------------------+ |R| Window Size Increment(31) +--+----------------------------------------------+ nginx中调用ngx_http_v2_send_window_update 发送窗口变更帧
static ngx_int_t
ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c, ngx_uint_t sid,
    size_t window)
{
    ...
    // 调用函数分配一个帧结构,并初始化帧头
    frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_WINDOW_UPDATE_SIZE,
                                  NGX_HTTP_V2_WINDOW_UPDATE_FRAME,
                                  NGX_HTTP_V2_NO_FLAG, sid);
    if (frame == NULL) {
        return NGX_ERROR;
    }

    buf = frame->first->buf;

    // 无符号31位窗口大小
    buf->last = ngx_http_v2_write_uint32(buf->last, window);

    ...

    return NGX_OK;
}

错误码

错误代码32位,用于RST_STREAM 和 GOAWAY帧来传达或连接错误的原因。 错误代码:

  • NO_ERROR(0x0):相关的条件不是错误的结果。例如,GOAWAY可能包含此代码以指示正常关闭连接。
  • PROTOCOL_ERROR(0x1):端点检测到非特定协议错误。此错误用于更具体的错误代码不可用时。
  • INTERNAL_ERROR(0x2):端点遇到意外的内部错误。
  • FLOW_CONTROL_ERROR(0x3):端点检测到它的对等方违反了流量控制协议。
  • SETTINGS_TIMEOUT(0x4):端点发送了一个SETTINGS帧,但没有及时收到响应。参见6.5.3节(“设置同步”)。
  • STREAM_CLOSED(0x5):流在半封闭后收到一个帧。
  • FRAME_SIZE_ERROR(0x6):端点收到一个无效大小的帧。
  • REFUSED_STREAM(0x7):端点在执行任何应用程序处理之前拒绝流(详情请参见第8.1.4节)。
  • CANCEL(0x8):由端点使用,表示不再需要该流。
  • COMPRESSION_ERROR(0x9):端点无法维护连接的头压缩上下文。
  • CONNECT_ERROR(0xa):响应CONNECT请求建立的连接(第8.3节)被重置或异常关闭。
  • ENHANCE_YOUR_CALM(0xb):端点检测到它的对等体表现出可能产生过度负载的行为。
  • INADEQUATE_SECURITY(0xc):底层传输具有不符合最低安全要求的属性(参见第9.2节)。
  • HTTP_1_1_REQUIRED(0xd):端点要求使用HTTP/1.1而不是HTTP/2。

vislee avatar Mar 02 '17 10:03 vislee