nginx静态模块分析
2017-01-15 17:12
387 查看
在上一篇文章中,已经分析了http框架是如何调度各个http模块,共同协作完成一个请求的。现在分析静态模块,看下这个具体的http模块做了些什么。ngx_http_static_module静态模块会从客户端发来的http请求行中获取到文件名(例如: /GET /home/abc.txt HTTP/1.1),在服务器中查找相应的文件名,并给客户端返回文件内容。在ngx_http_block函数中,将会把需要介入11个http请求阶段的所有http模块回调注册到数组中保存。
现在看下ngx_http_static_handler函数的处理流程。在这个函数里,会根据http请求行中的uri信息(例如:GET /home/abc.txt HTTP/1.1, 则文件扩展名为txt),在服务器中查找相应的文件并打开。然后调用各个过滤器模块,先给客户端发送http响应头部信息,在给客户端发送http响应包体,也就是文件内容。
//开始解析http块 static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { //http配置解析完成后的后续处理,使得各个http模块可以介入http阶段 for (m = 0; ngx_modules[m]; m++) { //只注册http模块 if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } module = ngx_modules[m]->ctx; //将模块注册到11个http处理阶段中的某个阶段 if (module->postconfiguration) { if (module->postconfiguration(cf) != NGX_OK) { return NGX_CONF_ERROR; } } } }静态模块的postconfiguration回调为:ngx_http_static_init,这个函数负责将静态模块的回调注册到数组中保存。
//静态模块将自己注册到11个http请求阶段中的NGX_HTTP_CONTENT_PHASE阶段 static ngx_int_t ngx_http_static_init(ngx_conf_t *cf) { ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); // h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } //静态模块在NGX_HTTP_CONTENT_PHASE阶段的处理方法 *h = ngx_http_static_handler; return NGX_OK; }而NGX_HTTP_CONTENT_PHASE处理阶段的checker回调为:ngx_http_core_content_phase, 这个回调会调用介入这个阶段的所有http模块。
//NGX_HTTP_CONTENT_PHASE阶段的checker方法 ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { //调用模块的处理方法 rc = ph->handler(r); return NGX_OK; }而这个checker回调又是被ngx_http_core_run_phases调用。 在上一篇文件已经分析过了,这里就不在重复了。
现在看下ngx_http_static_handler函数的处理流程。在这个函数里,会根据http请求行中的uri信息(例如:GET /home/abc.txt HTTP/1.1, 则文件扩展名为txt),在服务器中查找相应的文件并打开。然后调用各个过滤器模块,先给客户端发送http响应头部信息,在给客户端发送http响应包体,也就是文件内容。
//函数名:http静态模块处理函数 //功能: 1、根据http请求行中的uri信息,在服务器目录找到对应的文件并打开。 // 2、然后给客户端发送http响应头部 // 3、最后给客户端发送http响应包体,也就是文件内容 static ngx_int_t ngx_http_static_handler(ngx_http_request_t *r) { //根据请求的uri获取到完整的绝对路径,保存在path中 last = ngx_http_map_uri_to_path(r, &path, &root, 0); path.len = last - path.data; //打开path文件,并获取文件信息保存到of中 ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool //静态文件,不允许为post方法 if (r->method & NGX_HTTP_POST) { return NGX_HTTP_NOT_ALLOWED; } //丢弃包体,即便客户端发来了包体内容,这里也不会处理,把它丢弃。为什么呢?因为对于静态资源文件请求,nginx服务器在获取到请求行,请求头部后,就可以调用静态模块查找文件,并给客户端返回文件信息。 不需要接收任何来自客户端的请求包体内容 rc = ngx_http_discard_request_body(r); //构造http响应头部 r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = of.size; r->headers_out.last_modified_time = of.mtime; //根据http请求中的uri扩展名参数,从而获取到文件类型 if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } //创建http响应包体发送缓冲区 b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)); if (b->file == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } //调度各个过滤器模块互相协作,发送http头部。 过滤器模块将在下一篇文章进行分析 rc = ngx_http_send_header(r); //header_only为1,表示只有头部信息,不需要发送包体。通常在第一个过滤器中,如果客户端请求的 //文件没有被修改时,则第一个过滤器会设置响应码为304,表示不需要发送包体,在最后一个 //头部顾虑器中检测到状态响应码为304,会将header_only设为1 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } //设置发送缓冲区文件内容 b->file_pos = 0; b->file_last = of.size; b->in_file = b->file_last ? 1: 0; b->last_buf = (r == r->main) ? 1: 0; b->last_in_chain = 1; b->file->fd = of.fd; b->file->name = path; out.buf = b; out.next = NULL; //调度各个过滤器模块互相协作,发送http包体内容。 过滤器模块将在下一篇文章进行分析 return ngx_http_output_filter(r, &out); }ngx_open_cached_file函数涉及到文件缓存,这里暂时抛开文件缓存不管,以后专门有一篇文章来分析缓存管理。这个函数会调用ngx_open_and_stat_file函数,打开文件,并获取文件信息。
//打开文件并获取文件新,保存到of中 static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log) { //打开文件 fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, NGX_FILE_OPEN, 0); //获取文件信息 ngx_fd_info(fd, &fi); //保存文件信息 of->uniq = ngx_file_uniq(&fi); of->mtime = ngx_file_mtime(&fi); of->size = ngx_file_size(&fi); of->fs_size = ngx_file_fs_size(&fi); of->is_dir = ngx_is_dir(&fi); of->is_file = ngx_is_file(&fi); of->is_link = ngx_is_link(&fi); of->is_exec = ngx_is_exec(&fi); }其中ngx_http_set_content_type会根据http请求的uri扩展名,查找哈希表从而得到http响应内容的类型。
//根据http请求中的uri扩展名参数,从而获取到文件类型 //例如:GET /home/abc.txt HTTP/1.1,则exten=txt ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r) { //根据uri扩展名进行查找 if (r->exten.len) { hash = 0; for (i = 0; i < r->exten.len; i++) { c = r->exten.data[i]; //扩展名只有有一个字符为大小,则转换小写。否则扩展名仍然为小写 if (c >= 'A' && c <= 'Z') { exten = ngx_pnalloc(r->pool, r->exten.len); hash = ngx_hash_strlow(exten, r->exten.data, r->exten.len); r->exten.data = exten; break; } //计算哈希值 hash = ngx_hash(hash, c); } //根据扩展名查找哈希表,进而获取到文件类型 type = ngx_hash_find(&clcf->types_hash, hash, r->exten.data, r->exten.len); //设置文件类型 if (type) { r->headers_out.content_type_len = type->len; r->headers_out.content_type = *type; return NGX_OK; } } //执行到这里,说明没有找到文件类型,则使用默认的文件类型 r->headers_out.content_type_len = clcf->default_type.len; r->headers_out.content_type = clcf->default_type; return NGX_OK; }那这个扩展名哈希表什么时候创建的呢? 在解析配置文件后,并将http块与server块的location信息进行合并时,会创建这样的哈希表。ngx_http_core_merge_loc_conf函数负责合并,也是在这个函数里面将nginx服务器默认支持的文件扩展名加入到哈希表中保存。哈希表的处理流程还是挺复杂的,前面花了三篇文章来分析。如果不太清楚哈希表的操作流程,可以参考:nginx普通哈希表、nginx通配符哈希表、nginx哈希表数组的构造这三篇文章
//location合并 static char * ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { if (conf->types == NULL) { //创建哈希数组,存放每一种支持的文件扩展名类型 conf->types = ngx_array_create(cf->pool, 3, sizeof(ngx_hash_key_t)); if (conf->types == NULL) { return NGX_CONF_ERROR; } //将默认支持的文件扩展名类型插入到哈希表中 for (i = 0; ngx_http_core_default_types[i].key.len; i++) { type = ngx_array_push(conf->types); if (type == NULL) { return NGX_CONF_ERROR; } type->key = ngx_http_core_default_types[i].key; type->key_hash = ngx_hash_key_lc(ngx_http_core_default_types[i].key.data, ngx_http_core_default_types[i].key.len); type->value = ngx_http_core_default_types[i].value; } } if (conf->types_hash.buckets == NULL) { //用上面的数组初始化哈希表 types_hash.hash = &conf->types_hash; types_hash.key = ngx_hash_key_lc; types_hash.max_size = conf->types_hash_max_size; types_hash.bucket_size = conf->types_hash_bucket_size; types_hash.name = "types_hash"; types_hash.pool = cf->pool; types_hash.temp_pool = NULL; //初始化哈希表 if (ngx_hash_init(&types_hash, conf->types->elts, conf->types->nelts) != NGX_OK) { return NGX_CONF_ERROR; } } }至此,静态模块的处理流程已经分析完成了。在这个模块中,调用了ngx_http_send_header, ngx_http_output_filter这两个http框架实现的过滤器模块函数,分别用于向客户端发送http响应头部,http响应包体。下一篇文件将详细分析过滤器模块的实现,当然也会分析这两个函数的实现。
相关文章推荐
- 配置nginx图片服务器之模块分析
- nginx源码分析—core模块callback
- Nginx模块开发(10)—limit_req模块分析
- nginx代码分析-http模块-access
- Nginx Upstream模块源码分析(上)
- nginx源码分析(15)-模块分析(1)
- nginx事件模块分析(一)
- nginx源码分析(9)——filter模块
- nginx源码分析(17)-模块分析(3)
- nginx配置解析模块分析
- Nginx技术交流Q群:225942451(探讨安装、部署、模块开发、源码分析,及其他知名服务端开源软件)
- Nginx 源码分析-- 模块module 解析执行 nginx.conf 配置文件流程分析 二
- Nginx 源码分析-- 模块module 解析执行 nginx.conf 配置文件流程分析 一
- nginx模块分析
- nginx源码分析-nginx模块定义和各大模块的上下文模块定义
- nginx源码分析—模块及其初始化
- Nginx 源码分析-- 浅谈对模块module 的基本认知
- nginx源码分析-过滤模块
- Nginx 源码分析-- 浅谈对模块module 的基本认知
- nginx事件模块分析(一)