1 定义ngx_http_test_expect 函数 定义在 ./nginx-1.24.0/src/http/ngx_http_request_body.c2 目的HTTP 协议中的 Expect 头部 HTTP 请求由“请求头部”和可选的“请求体”组成。 请求头部里可以包含一个字段叫 Expect。 Expect 字段的作用是 客户端在真正发送请求体之前告诉服务器 “我期望你满足某个条件如果你能满足我再把请求体发过去 如果你不能满足就立刻告诉我我就不发请求体了免得浪费带宽和时间。” 最常见的用法就是 Expect: 100-continue。 它的工作流程分两步 客户端发送请求头部 其中包含 Expect: 100-continue 然后暂停等待服务器回应。此时客户端还没有发送请求体。 服务器收到这些头部后检查请求。如果服务器愿意接收请求体 就立刻发送一个临时响应 100 Continue。 客户端收到这个 100 Continue 后才开始发送请求体。 如果服务器不愿意接收比如权限不足、文件太大 服务器可以直接返回一个错误状态码比如 403 或 413 然后连接终止或忽略请求体客户端就不会再发送无用的请求体了。 这是一种优化机制特别适用于大的请求体。 如果服务器一看请求头部就决定拒绝就可以省去传输整个请求体的时间和资源。设计 ngx_http_test_expect 的核心目的 这个函数就是 Nginx 用来实现上面第 2 步中“服务器端的检查和回应”的。 具体来说它的任务是 检查当前请求是否带有 Expect: 100-continue 头部 并且是否满足需要处理的条件HTTP 版本、未测试过等。 响应如果确认客户端在期待一个 100 Continue 回应 就立即向客户端发送这个回应告诉客户端“你可以继续发送请求体了”。 记录状态 标记这个请求的 Expect 头部已经测试过防止重复发送 100 Continue。为什么需要把它设计成一个单独的函数 因为在 Nginx 中有多个地方可能需要触发这个“测试-回应”的动作。 因此它被封装成一个独立的函数供各处使用。3 详解1 函数签名staticngx_int_tngx_http_test_expect(ngx_http_request_t*r)1. 返回值类型ngx_int_t这个函数的返回值不是实际的业务数据而是一个状态码用来表示函数执行的结果。可能的返回值有NGX_OK处理成功这可能包括“不需要做任何事”的情况例如请求没有 Expect 头部或者成功发送了 100 Continue 响应。NGX_ERROR处理失败例如发送 100 Continue 响应时发生网络错误。调用者会根据这个返回值决定后续处理是正常继续还是向上传递错误。2. 函数名ngx_http_test_expect函数名由多个部分用下划线连接遵循 Nginx 的命名惯例ngx全局前缀表明这是 Nginx 源码的一部分。http表明该函数属于 HTTP 核心模块处理 HTTP 协议相关的逻辑。test动词意思是“测试、检查”。在这里指检查请求的 Expect 头部。expect直接对应 HTTP 协议中的 Expect 请求头部。整体名字的含义是在 HTTP 模块中测试并处理Expect 请求头部的函数。这个名字精确概括了它的功能。3.ngx_http_request_t *r参数 r是一个指针指向代表当前 HTTP 请求的结构体里面包含了所有相关的信息。2 逻辑流程1 检查 过滤无需发送 100 Continue 的情况直接返回 NGX_OK 1-1 如果请求已经测试过 或根本没有 Expect 头部 或 HTTP 版本低于 1.1 或这是 HTTP/2 请求 则直接返回 NGX_OK不做任何事。 1-2 设置 expect_tested 标志为 1防止重复处理。 1-3 获取 Expect 头部的值。 如果该值不是 100-continue长度不同或内容不匹配则忽略它返回 NGX_OK。 2 发送 100 Continue 2-1 否则发送 HTTP/1.1 100 Continue\r\n\r\n 到客户端。 2-2 如果发送完整返回 NGX_OK 2-3 否则标记连接错误返回 NGX_ERROR。 这个函数的核心意义在于 正确实现 HTTP/1.1 的 Expect: 100-continue 协议 并提供一个可被多个地方调用的标准化测试和响应过程。{ngx_int_tn;ngx_str_t*expect;局部变量声明1 检查if(r-expect_tested||r-headers_in.expectNULL||r-http_versionNGX_HTTP_VERSION_11#if(NGX_HTTP_V2)||r-stream!NULL#endif){returnNGX_OK;}r-expect_tested一个标志flag。如果它已经是非零值通常是 1表示该请求的 Expect 头部已经被测试过了无需再测试。目的防止重复处理同一个请求的 Expect 头部。r-headers_in.expect NULLr-headers_in是存储解析后请求头部的结构体。expect是其中一个指针成员指向解析出的 Expect 头部的信息。如果它为 NULL表示请求头部中根本没有 Expect 字段。目的如果不存在 Expect 头部就不需要做任何事。r-http_version NGX_HTTP_VERSION_11r-http_version是一个整数表示请求的 HTTP 版本。NGX_HTTP_VERSION_11是一个宏代表 HTTP/1.1 的版本号。如果请求版本低于 1.1比如 HTTP/1.0则不支持 Expect 机制直接跳过。目的遵循 HTTP 协议规范Expect 头部仅在 HTTP/1.1 或更高版本中有效。#if (NGX_HTTP_V2)这是一个预处理指令表示如果 Nginx 编译时包含了 HTTP/2 支持那么下面这一行才有效。#endif表示结束。|| r-stream ! NULL在 HTTP/2 下每个请求都对应一个流streamr-stream指向一个流结构体。如果非空说明这是一个 HTTP/2 请求。HTTP/2 不使用Expect: 100-continue机制它有自己的流量控制所以跳过。目的避免在 HTTP/2 请求中错误地应用 HTTP/1.1 的 Expect 处理逻辑快速返回NGX_OK如果上述任一条件为真函数直接返回NGX_OK表示“测试通过无需进一步动作”。调用者会认为一切正常继续后续流程。r-expect_tested1;标记已经测试过将r-expect_tested标志设置为 1。作用当这个函数被再次调用时比如不同的模块或不同的阶段第一个条件r-expect_tested就会为真从而直接返回NGX_OK避免重复执行后面的发送100 Continue的逻辑。这确保了100 Continue最多只发送一次。expectr-headers_in.expect-value;获取Expect头部的值r-headers_in.expect指向一个结构体表示Expect 头部。该结构体中的value成员是一个ngx_str_t类型的变量存储了Expect 头部的具体值例如 “100-continue”。是“取地址”操作符这里取得value这个结构体在内存中的地址然后赋值给指针变量expect作用现在我们可以通过expect指针方便地访问Expect值的长度expect-len和数据expect-data。if(expect-len!sizeof(100-continue)-1||ngx_strncasecmp(expect-data,(u_char*)100-continue,sizeof(100-continue)-1)!0){returnNGX_OK;}检查值是否为“100-continue”expect-lenExpect 值的长度字节数例如 100-continue 有 12 个字符长度为 12。sizeof(100-continue)在 C 语言中字符串字面量100-continue实际存储时会包含一个结尾的空字符\0所以sizeof(100-continue)返回的是 1312 个字符 1 个空字符。减 1 后得到 12即实际字符数。expect-len ! sizeof(100-continue) - 1首先比较长度。如果长度不是 12那么这个值肯定不是100-continue条件成立。如果长度相等再执行第二个条件ngx_strncasecmp(expect-data, (u_char *) 100-continue, sizeof(100-continue) - 1)ngx_strncasecmp是 Nginx 提供的字符串比较函数。它比较两个字符串的前 n 个字符不区分字母大小写。如果相等返回 0否则返回非 0 值表示第一个不同的字符的差值。第一个参数expect-dataExpect 值的字符串数据。第二个参数(u_char *) 100-continue将字符串字面量强制转换为u_char *类型以匹配函数参数类型。第三个参数sizeof(100-continue) - 1要比较的字节数这里就是 12。! 0如果比较结果不等于 0意味着字符串不匹配。综合起来如果长度不同或者字符串内容不同不区分大小写整个条件为真。此时 Expect 头部虽然存在但它的值不是100-continue不是“100-continue”则返回成功对于不支持的Expect 值Nginx 选择忽略该头部直接返回成功。根据 HTTP 规范服务器对无法识别或不满足的 Expect 期望应该返回 417 Expectation Failed但 Nginx 这里的实现方式是宽松的只处理100-continue对其他值当作没有 Expect 头部一样继续处理请求。ngx_log_debug0(NGX_LOG_DEBUG_HTTP,r-connection-log,0,send 100 Continue);调试日志2 发送响应nr-connection-send(r-connection,(u_char*)HTTP/1.1 100 ContinueCRLF CRLF,sizeof(HTTP/1.1 100 ContinueCRLF CRLF)-1);if(nsizeof(HTTP/1.1 100 ContinueCRLF CRLF)-1){returnNGX_OK;}发送“100 Continue”响应send函数执行后返回实际发送出去的字节数。这个返回值被赋给变量n检查发送是否完整如果实际发送的字节数n等于要发送的总字节数说明完整的响应已经被成功地送入发送缓冲区。此时函数执行成功。成功则返回返回NGX_OK表示“成功处理了 Expect 头部”客户端已经收到100 Continue可以继续发送请求体。/* we assume that such small packet should be send successfully */r-connection-error1;returnNGX_ERROR;}设置连接错误并返回