注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

偶有所得,记录在此

有分享交流才有进步,永远不要固步自封

 
 
 

日志

 
 

[Nginx 源码分析] Fastcgi 模块(上)  

2011-01-20 16:39:04|  分类: 默认分类 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
这一部分需熟悉 FASTCGI 协议

关键函数::ngx_http_fastcgi_create_request

大概处理流程

1) 计算出 FASTCgI params 和 request_headers 这两部分所需的空间,然后分配分配一个足够大的 buffer 来准备 copy 实际 params 和 header 内容。buffer 结构大概如下:

size = sizeof(ngx_http_fastcgi_header_t)
       + sizeof(ngx_http_fastcgi_begin_request_t)

       + sizeof(ngx_http_fastcgi_header_t/* NGX_HTTP_FASTCGI_PARAMS */
       + len + padding
       + sizeof(ngx_http_fastcgi_header_t/* NGX_HTTP_FASTCGI_PARAMS */

       + sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */


b = ngx_create_temp_buf(r->pool, size);
if (b == NULL) {
    return NGX_ERROR;
}

2) 填充相应的 Header,并复制  FASTCgI params 和 request_headers 这两部分内容到 buffer中。

3) 如果有 request body,填充好 FASTCGI_STDIN Header,创建新的 buffer 来复制 body;并将 buffer 链接到 buffer chain 里面

4) 发送整个 buffer chain,fastcgi 请求结束。


详细源码分析:

计算 params 长度
----------------
len = 0;
header_params = 0;
ignored = NULL;

flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
// 开始计算 params 长度
// params_len 是一个所有 params 长度的 array
if (flcf->params_len) {
    ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
   
    ngx_http_script_flush_no_cacheable_variables(r, flcf->flushes);
    le.flushed = 1;

    le.ip = flcf->params_len->elts;
    le.request = r;

    // 循环处理每个 params_len item
    while (*(uintptr_t *) le.ip) {

        // le.ip 为函数指针
        lcode = *(ngx_http_script_len_code_pt *) le.ip;
       
        // 取得 item key 的长度
        // 函数 lcode 取得 le 的 len 成员,并把 le->ip 指向下一个 array item
        key_len = lcode(&le);

        // 计算对应 value 的长度
        for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
            lcode = *(ngx_http_script_len_code_pt *) le.ip;
        }
       
        // 不同 params_len item 以一个 NULL uintptr_t 指针分隔
        // 这里把指针移动到下一个 params_len item,next item
        le.ip += sizeof(uintptr_t);

        // 计算传输这个 params item 所需要创建的 FASTCGI 报文的长度
        len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len;
    }
}
这段代码不太好理解的地方就是 params_len,及其用法

params_len 是一个 nginx 的 Array,起成员结构如下:
typedef struct {
    ngx_http_script_code_pt     code;
    uintptr_t                   len;
} ngx_http_script_copy_code_t;

成员 code 是一个函数指针,该函数如下:
size_t 
ngx_http_script_copy_len_code(ngx_http_script_engine_t *e)
{      
    ngx_http_script_copy_code_t  *code;
       
    code = (ngx_http_script_copy_code_t *) e->ip;
       
    e->ip += sizeof(ngx_http_script_copy_code_t);
       
    return code->len;
}
了解这些,params_len 部分的计算,就很好理解了。
这种数据结构和用法相当之神秘,找时间好好揣摩下。


计算 request_headers 长度
------------------------------------
// 计算 request header 的长度
if (flcf->upstream.pass_request_headers) {

    allocated = 0;
    lowcase_key = NULL;

    // 创建一个 ignored 数组
    if (flcf->header_params) {
        ignored = ngx_palloc(r->pool, flcf->header_params * sizeof(void *));
        if (ignored == NULL) {
            return NGX_ERROR;
        }
    }

    // 取得 headers 的第一个 part
    part = &r->headers_in.headers.part;
    // 取得 header 元素的指针,array 类型
    header = part->elts;

    for (i = 0; /* void */; i++) {

        if (i >= part->nelts) {
            if (part->next == NULL) {
                break;
            }

            // 如果当前 part 处理完毕,继续 next
            part = part->next;
            header = part->elts;
            i = 0;
        }

        if (flcf->header_params) {
       
            // 为 hash key 分配空间
            if (allocated < header[i].key.len) {
                allocated = header[i].key.len + 16;
                lowcase_key = ngx_pnalloc(r->pool, allocated);
                if (lowcase_key == NULL) {
                    return NGX_ERROR;
                }
            }

            hash = 0;

            // 把 key 转成小写,并计算出最后一个 ch 的 hash 值
            for (n = 0; n < header[i].key.len; n++) {
                ch = header[i].key.data[n];

                if (ch >= 'A' && ch <= 'Z') {
                    ch |= 0x20;

                } else if (ch == '-') {
                    ch = '_';
                }

                hash = ngx_hash(hash, ch);
                lowcase_key[n] = ch;
            }

            // 查找这个 header 是否在 ignore 名单之内
            // yes ,把这个 header 指针放在 ignore 数组内,后面有用,然后继续处理下一个
            if (ngx_hash_find(&flcf->headers_hash, hash, lowcase_key, n)) {
                ignored[header_params++] = &header[i];
                continue;
            }

            // n 的值的计算和下面的其实一样
            // 至于 sizeof 后再减一,是因为只需要附加个 "HTTP" 到 Header 上去,不需要 "_"
            n += sizeof("HTTP_") - 1;

        } else {
            n = sizeof("HTTP_") - 1 + header[i].key.len;
        }

        // 计算 FASTCGI 报文长度
        len += ((n > 127) ? 4 : 1) + ((header[i].value.len > 127) ? 4 : 1)
            + n + header[i].value.len;
    }
}

创建 buffer 和 buffer chain
-----------------------------------
// FASTCGI 协议规定,数据必须 8 bit 对齐
padding = 8 - len % 8;
padding = (padding == 8) ? 0 : padding;


// 计算总的所需空间大小
size = sizeof(ngx_http_fastcgi_header_t)
       + sizeof(ngx_http_fastcgi_begin_request_t)

       + sizeof(ngx_http_fastcgi_header_t/* NGX_HTTP_FASTCGI_PARAMS */
       + len + padding
       + sizeof(ngx_http_fastcgi_header_t/* NGX_HTTP_FASTCGI_PARAMS */

       + sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */


// 创建 buffer
b = ngx_create_temp_buf(r->pool, size);
if (b == NULL) {
    return NGX_ERROR;
}

// 创建 buffer chain,把刚创建的 buffer 链进去
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
    return NGX_ERROR;
}

cl->buf = b;

// 前两个 header 的内容是已经定义好的,这里简单复制过来
ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start,
           sizeof(ngx_http_fastcgi_request_start_t));

// 取得第三个 header 的指针
h = (ngx_http_fastcgi_header_t *)
         (b->pos + sizeof(ngx_http_fastcgi_header_t)
                 + sizeof(ngx_http_fastcgi_begin_request_t));

// 设置 fastcgi content len 域
h->content_length_hi = (u_char) ((len >> 8) & 0xff);
h->content_length_lo = (u_char) (len & 0xff);
h->padding_length = (u_char) padding;
h->reserved = 0;


复制 params 内容
----------------------
if (flcf->params_len) {
    ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

    // 取得实际的 params
    e.ip = flcf->params->elts;
    // 把目前 buffer 的位置赋值给 e.pos
    e.pos = b->last;
    e.request = r;
    e.flushed = 1;

    le.ip = flcf->params_len->elts;

    while (*(uintptr_t *) le.ip) {

        // 这里依然是函数指针,和前面讲的类似
        // 依然是,取得 key_len 和 var_len
        lcode = *(ngx_http_script_len_code_pt *) le.ip;
        key_len = (u_char) lcode(&le);

        for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
            lcode = *(ngx_http_script_len_code_pt *) le.ip;
        }
        le.ip += sizeof(uintptr_t);

        // 开始构建 FASTCGI Name-Value Pairs
       
        // 设置 key len
        *e.pos++ = (u_char) key_len;

        // 设置 val len
        if (val_len > 127) {
            *e.pos++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);
            *e.pos++ = (u_char) ((val_len >> 16) & 0xff);
            *e.pos++ = (u_char) ((val_len >> 8) & 0xff);
            *e.pos++ = (u_char) (val_len & 0xff);

        } else {
            *e.pos++ = (u_char) val_len;
        }

        // 复制 key 和 value 到 buffer
        // code 属于函数指针,下面会有解释
        while (*(uintptr_t *) e.ip) {
            code = *(ngx_http_script_code_pt *) e.ip;
            code((ngx_http_script_engine_t *) &e);
        }
        // 继续下一个
        e.ip += sizeof(uintptr_t);

        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "fastcgi param: \"%*s: %*s\"",
                       key_len, e.pos - (key_len + val_len),
                       val_len, e.pos - val_len);
    }

    // 更新 buffer last 值
    b->last = e.pos;
}

这里最难理解的就是如何复制具体的 key 和 value 到 buffer 的,还是函数指针问题。
code 函数定义如下:
void   
ngx_http_script_copy_code(ngx_http_script_engine_t *e)
{      
    u_char                       *p;
    ngx_http_script_copy_code_t  *code;
       
    code = (ngx_http_script_copy_code_t *) e->ip;
         
    p = e->pos;
    // 复制相应 key 和 value 的内容到 buffer
    if (!e->skip) {
        e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_script_copy_code_t),
                          code->len);
    }  
   
    // 移动指针,等待处理下一个       
    e->ip += sizeof(ngx_http_script_copy_code_t)
          + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
           
    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
                   "http script copy: \"%*s\"", e->pos - p, p);
}

复制 request header
----------------------------
if (flcf->upstream.pass_request_headers) {

     part = &r->headers_in.headers.part;
     header = part->elts;

     for (i = 0; /* void */; i++) {

         if (i >= part->nelts) {
             if (part->next == NULL) {
                 break;
             }

             part = part->next;
             header = part->elts;
             i = 0;
         }

         // 如果是 ignored,继续下一个
         for (n = 0; n < header_params; n++) {
             if (&header[i] == ignored[n]) {
                 goto next;
             }
         }

         // 开始创建 FASTCGI Name-Value Pairs
        
         // 设置 key len
         key_len = sizeof("HTTP_") - 1 + header[i].key.len;
         if (key_len > 127) {
             *b->last++ = (u_char) (((key_len >> 24) & 0x7f) | 0x80);
             *b->last++ = (u_char) ((key_len >> 16) & 0xff);
             *b->last++ = (u_char) ((key_len >> 8) & 0xff);
             *b->last++ = (u_char) (key_len & 0xff);

         } else {
             *b->last++ = (u_char) key_len;
         }

         // 设置 val len
         val_len = header[i].value.len;
         if (val_len > 127) {
             *b->last++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);
             *b->last++ = (u_char) ((val_len >> 16) & 0xff);
             *b->last++ = (u_char) ((val_len >> 8) & 0xff);
             *b->last++ = (u_char) (val_len & 0xff);

         } else {
             *b->last++ = (u_char) val_len;
         }

         // 先给 Key 加一个 HTTP 的头
         b->last = ngx_cpymem(b->last, "HTTP_", sizeof("HTTP_") - 1);

         // 把 Heaer Name 转成 大写,然后复制到 buffer 中
         for (n = 0; n < header[i].key.len; n++) {
             ch = header[i].key.data[n];

             if (ch >= 'a' && ch <= 'z') {
                 ch &= ~0x20;

             } else if (ch == '-') {
                 ch = '_';
             }

             *b->last++ = ch;
         }

         // 复制该 Header 对应的值
         b->last = ngx_copy(b->last, header[i].value.data, val_len);

         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "fastcgi param: \"%*s: %*s\"",
                        key_len, b->last - (key_len + val_len),
                        val_len, b->last - val_len);
     next:

         continue;
     }
}
这部分比较容易看懂。

复制 request body
-----------------------
if (flcf->upstream.pass_request_body) {
        body = r->upstream->request_bufs;
        // 把 buffer chain 赋值给 request_bufs
        r->upstream->request_bufs = cl;

#if (NGX_SUPPRESS_WARN)
        file_pos = 0;
        pos = NULL;
#endif

        while (body) {

            // 取得起始 pos
            if (body->buf->in_file) {
                file_pos = body->buf->file_pos;

            } else {
                pos = body->buf->pos;
            }

            next = 0;

            do {
                // 创建一个新的 buffer
                b = ngx_alloc_buf(r->pool);
                if (b == NULL) {
                    return NGX_ERROR;
                }

                // 用 body->buf 赋值
                ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));

                // 每次最多复制 32 * 1024 字节
                // 结束则设置 next 值,退出循环
                if (body->buf->in_file) {
                    b->file_pos = file_pos;
                    file_pos += 32 * 1024;

                    if (file_pos >= body->buf->file_last) {
                        file_pos = body->buf->file_last;
                        next = 1;
                    }

                    b->file_last = file_pos;
                    len = (ngx_uint_t) (file_pos - b->file_pos);

                } else {
                    b->pos = pos;
                    pos += 32 * 1024;

                    if (pos >= body->buf->last) {
                        pos = body->buf->last;
                        next = 1;
                    }

                    b->last = pos;
                    len = (ngx_uint_t) (pos - b->pos);
                }

                padding = 8 - len % 8;
                padding = (padding == 8) ? 0 : padding;

                // 创建 FASTCGI STDIN 报文
                h->version = 1;
                h->type = NGX_HTTP_FASTCGI_STDIN;
                h->request_id_hi = 0;
                h->request_id_lo = 1;
                h->content_length_hi = (u_char) ((len >> 8) & 0xff);
                h->content_length_lo = (u_char) (len & 0xff);
                h->padding_length = (u_char) padding;
                h->reserved = 0;

                // 创建一个新的 chain link,把刚才的 buffer 链接上去 
                cl->next = ngx_alloc_chain_link(r->pool);
                if (cl->next == NULL) {
                    return NGX_ERROR;
                }

                cl = cl->next;
                cl->buf = b;

                // 再创建一个 buffer,生成一个新的 FASTCGI header
                // 然后链接到 buffer chain 中
                b = ngx_create_temp_buf(r->pool,
                                        sizeof(ngx_http_fastcgi_header_t)
                                        + padding);
                if (b == NULL) {
                    return NGX_ERROR;
                }

                // 如果前一个需要 paddding,这里需要设置 padding
                if (padding) {
                    ngx_memzero(b->last, padding);
                    b->last += padding;
                }

                h = (ngx_http_fastcgi_header_t *) b->last;
                b->last += sizeof(ngx_http_fastcgi_header_t);

                cl->next = ngx_alloc_chain_link(r->pool);
                if (cl->next == NULL) {
                    return NGX_ERROR;
                }

                cl = cl->next;
                cl->buf = b;

            } while (!next);

            body = body->next;
        }

    } else {
        r->upstream->request_bufs = cl;
    }
body 部分,不会发生实际的数据 copy。仅仅是创建新的 ngx_buf_t 结构,然后修改 buffer 指针直接指向 body 的 buffer 地址,然后链入 buffer chain。

每个 buffer 最多管理 32 * 1024 字节内容,如果超过,则创建新的 ngx_buf_t 结构和相应的 FASTCGI Header 报文。


请求创建完毕后,会有 upstream 模块把 buffer chain 发送出去。

----
感谢代码发芽网提供的语法高亮服务。


  评论这张
 
阅读(2905)| 评论(1)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017