開發一個HTTP模塊
- HTTP模塊的數據結構
- ngx_module_t模塊的數據結構
- ngx_http_module_t數據結構
- ngx_command_s 數據結構
- 定義一個HTTP模塊
- 處理用戶請求
- 返回值
- 獲取URI和參數
- 方法名
- URI
- URL
- 協議版本
- 獲取HTTP頭
- 獲取HTTP包體
- 發送響應
- 發送HTTP頭
- 發送內存中的字符串作為包體
- 返回一個Hello world
- 磁盤文件作為包體發送
HTTP模塊的數據結構
ngx_module_t模塊的數據結構
// core.ngx_core.h
typedef struct ngx_module_s ngx_module_t;// core.ngx_module.h
struct ngx_module_s {/* 表示當前模塊在這個模塊中的序號,* 既表示模塊的優先級,也表明模塊的位置,* 借以幫助Nginx框架快速獲得某個模塊的數據*/ngx_uint_t ctx_index;/** 表示當前模塊在ngx_modules數組中的序號,* ctx_index表示當前模塊在一類模塊中的序號,* index是在所有模塊中的序號*/ngx_uint_t index;// 模塊名稱char *name;// spare系列的保留變量ngx_uint_t spare0;ngx_uint_t spare1;// 模塊的版本號ngx_uint_t version;const char *signature; ///** 用于指向一類模塊的上下文結構體,* Nginx模塊有很多種類的模塊,不同模塊之間功能差距很大* 事件類型的模塊主要處理IO事件,HTTP類型模塊主要處理HTTP應用層的功能* ctx指向特定類型模塊的公共接口,例如HTTP模塊中,指向ngx_http_module_t*/void *ctx;ngx_command_t *commands;// 將處理nginx.conf中的配置項/** 模塊的類型,有5種* NGX_HTTP_MODULE,NGX_CORE_MODULE,NGX_CONF_MODULE,NGX_EVENT_MODULE,NGX_MALL_MODULE* 實際上可以自己定義類型* */ngx_uint_t type;/** 如下7個函數指針表示有7個執行點會分別調用這7個方法,對于任何一個方法* 如果不需要Nginx在某個時刻執行它,那么可以簡單的設置為NULL空指針即可* */// 當master進程啟動時進行回調init_master,但是目前為止,框架代碼從來不會調用,設置為NULLngx_int_t (*init_master)(ngx_log_t *log);// init_module初始化所有模塊時被調用,在master/worker模式下,這個階段將在啟動worker子進程前完成ngx_int_t (*init_module)(ngx_cycle_t *cycle);// 在正常服務前被調用,在master/worker模式下,多個worker子進程已經產生,在每個worker進程的初始化過程會調用所有模塊的init_processngx_int_t (*init_process)(ngx_cycle_t *cycle);// Nginx不支持多線程模式,所以init_thread在框架代碼中沒有被調用過,設置為NULLngx_int_t (*init_thread)(ngx_cycle_t *cycle);// exit_thread也不支持,設置為NULLvoid (*exit_thread)(ngx_cycle_t *cycle);// 在服務停止前調用,master/worker模式下,worker進程會在退出前調用它void (*exit_process)(ngx_cycle_t *cycle);// 在master進程退出前被調用void (*exit_master)(ngx_cycle_t *cycle);/** 以下8個變量也是保留字段,目前沒有使用,但可用Nginx提供的NGX_MODULE_V1_PADDING宏來填充* */uintptr_t spare_hook0;uintptr_t spare_hook1;uintptr_t spare_hook2;uintptr_t spare_hook3;uintptr_t spare_hook4;uintptr_t spare_hook5;uintptr_t spare_hook6;uintptr_t spare_hook7;
};
當定義HTTP模塊時,type設置為NGX_HTTP_MODULE,對于回調方法:init_module,init_process,exit_process,exit_master,調用它們的是Nginx代碼,和HTTP框架無關,即使nginx.conf中沒有定義http{…},這種開啟HTTP功能的配置項,這些毀掉方法仍然會被調用。因此,通常開發HTTP模塊時會都設為NULL,當不作為Web服務器使用時,不會執行HTTP模塊的任何代碼。
定義HTTP模塊時,最終要的是設置ctx和commands兩個成員,對于HTTP類型的模塊來說ctx指向的是ngx_http_module_t接口。
ngx_http_module_t數據結構
// http.ngx_http_config.h
typedef struct {// 解析配置文件前被調用ngx_int_t (*preconfiguration)(ngx_conf_t *cf);// 完成配置文件的解析后調用ngx_int_t (*postconfiguration)(ngx_conf_t *cf);//當需要創建數據結構用于存儲main級別的全局配置項時,可以通過create_main_conf回調方法創建存儲全局配置項的結構體void *(*create_main_conf)(ngx_conf_t *cf);// 常用與初始化main級別配置項char *(*init_main_conf)(ngx_conf_t *cf, void *conf);// 當需要創建數據結構用于存儲srv級別的配置項時,可以通過create_srv_conf回調方法創建存儲srv級別配置項的結構體void *(*create_srv_conf)(ngx_conf_t *cf);// 合并main級別和srv級別的同名配置項char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);// 當需要創建結構體用于存儲loc級別的配置項時,可以實現create_loc_conf回調方法void *(*create_loc_conf)(ngx_conf_t *cf);// 合并srv級別和loc級別下的同名配置項char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
上述八個階段的調用順序與定義順序不同,Nginx啟動過程中,HTTP框架調用這些回調方法的實際順序有可能是:(與nginx.conf配置項有關)
1)create_main_conf
2)create_srv_conf
3)create_loc_conf
4)preconfiguration
5)init_main_conf
6)merge_srv_conf
7)merge_loc_conf
8)postconfiguration
commands數組用于定義模塊的配置文件參數,每個數組元素都是ngx_command_t類型,數組的結尾用ngx_null_command表示,Nginx在解析配置文件中的一個配置項時首先會編譯所有的模塊,對于每一個模塊而言,即通過遍歷commands數組進行,在數組中檢查到ngx_null_command時,會停止使用當前模塊解析該配置項
ngx_command_s 數據結構
// core.ngx_core.h
typedef struct ngx_command_s ngx_command_t;// core.ngx_conf_file.h
struct ngx_command_s {ngx_str_t name;// 配置項名稱,列入:gzipngx_uint_t type;// 配置項類型,指定可以出現的位置,例如server,location中以及它可以攜帶的參數個數// 出現了name中指定的配置項后,將會調用set方法處理配置項的參數char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);// 在配置文件中的偏移量ngx_uint_t conf;// 通常用于預設的解析方法解析配置項ngx_uint_t offset;// 配置項讀取后的處理方法,必須是ngx_conf_post_t結構體指針void *post;
};#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL }
定義一個HTTP模塊
// http.modules.ngx_http_mytest_module.c
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);static ngx_command_t ngx_http_mytest_commands[] = {{ngx_string("mytest"),NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,ngx_http_mytest,NGX_HTTP_LOC_CONF_OFFSET,0,NULL},ngx_null_command
};
static ngx_http_module_t ngx_http_mytest_module_ctx = {NULL, /* preconfiguration */NULL, /* postconfiguration */NULL, /* create main configuration */NULL, /* init main configuration */NULL, /* create server configuration */NULL, /* merge server configuration */NULL, /* create location configuration */NULL /* merge location configuration */
};ngx_module_t ngx_http_mytest_module = {NGX_MODULE_V1,&ngx_http_mytest_module_ctx, /* module context */ngx_http_mytest_commands, /* module directives */NGX_HTTP_MODULE, /* module type */NULL, /* init master */NULL, /* init module */NULL, /* init process */NULL, /* init thread */NULL, /* exit thread */NULL, /* exit process */NULL, /* exit master */NGX_MODULE_V1_PADDING
};static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {ngx_http_core_loc_conf_t *clcf;clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);clcf->handler = ngx_http_mytest_handler;return NGX_CONF_OK;
}
處理用戶請求
// http.ngx_http_core_module.h
typedef struct ngx_http_core_loc_conf_s ngx_http_core_loc_conf_t;
struct ngx_http_core_loc_conf_s {ngx_str_t name; /* location name */ngx_str_t escaped_name;#if (NGX_PCRE)ngx_http_regex_t *regex;
#endifunsigned noname:1; /* "if () {}" block or limit_except */unsigned lmt_excpt:1;unsigned named:1;unsigned exact_match:1;unsigned noregex:1;unsigned auto_redirect:1;
#if (NGX_HTTP_GZIP)unsigned gzip_disable_msie6:2;unsigned gzip_disable_degradation:2;
#endifngx_http_location_tree_node_t *static_locations;
#if (NGX_PCRE)ngx_http_core_loc_conf_t **regex_locations;
#endif/* pointer to the modules' loc_conf */void **loc_conf;uint32_t limit_except;void **limit_except_loc_conf;ngx_http_handler_pt handler;/* location name length for inclusive location with inherited alias */size_t alias;ngx_str_t root; /* root, alias */ngx_str_t post_action;ngx_array_t *root_lengths;ngx_array_t *root_values;ngx_array_t *types;ngx_hash_t types_hash;ngx_str_t default_type;off_t client_max_body_size; /* client_max_body_size */off_t directio; /* directio */off_t directio_alignment; /* directio_alignment */size_t client_body_buffer_size; /* client_body_buffer_size */size_t send_lowat; /* send_lowat */size_t postpone_output; /* postpone_output */size_t sendfile_max_chunk; /* sendfile_max_chunk */size_t read_ahead; /* read_ahead */size_t subrequest_output_buffer_size;/* subrequest_output_buffer_size */ngx_http_complex_value_t *limit_rate; /* limit_rate */ngx_http_complex_value_t *limit_rate_after; /* limit_rate_after */ngx_msec_t client_body_timeout; /* client_body_timeout */ngx_msec_t send_timeout; /* send_timeout */ngx_msec_t keepalive_time; /* keepalive_time */ngx_msec_t keepalive_timeout; /* keepalive_timeout */ngx_msec_t lingering_time; /* lingering_time */ngx_msec_t lingering_timeout; /* lingering_timeout */ngx_msec_t resolver_timeout; /* resolver_timeout */ngx_msec_t auth_delay; /* auth_delay */ngx_resolver_t *resolver; /* resolver */time_t keepalive_header; /* keepalive_timeout */ngx_uint_t keepalive_requests; /* keepalive_requests */ngx_uint_t keepalive_disable; /* keepalive_disable */ngx_uint_t satisfy; /* satisfy */ngx_uint_t lingering_close; /* lingering_close */ngx_uint_t if_modified_since; /* if_modified_since */ngx_uint_t max_ranges; /* max_ranges */ngx_uint_t client_body_in_file_only; /* client_body_in_file_only */ngx_flag_t client_body_in_single_buffer;/* client_body_in_singe_buffer */ngx_flag_t internal; /* internal */ngx_flag_t sendfile; /* sendfile */ngx_flag_t aio; /* aio */ngx_flag_t aio_write; /* aio_write */ngx_flag_t tcp_nopush; /* tcp_nopush */ngx_flag_t tcp_nodelay; /* tcp_nodelay */ngx_flag_t reset_timedout_connection; /* reset_timedout_connection */ngx_flag_t absolute_redirect; /* absolute_redirect */ngx_flag_t server_name_in_redirect; /* server_name_in_redirect */ngx_flag_t port_in_redirect; /* port_in_redirect */ngx_flag_t msie_padding; /* msie_padding */ngx_flag_t msie_refresh; /* msie_refresh */ngx_flag_t log_not_found; /* log_not_found */ngx_flag_t log_subrequest; /* log_subrequest */ngx_flag_t recursive_error_pages; /* recursive_error_pages */ngx_uint_t server_tokens; /* server_tokens */ngx_flag_t chunked_transfer_encoding; /* chunked_transfer_encoding */ngx_flag_t etag; /* etag */#if (NGX_HTTP_GZIP)ngx_flag_t gzip_vary; /* gzip_vary */ngx_uint_t gzip_http_version; /* gzip_http_version */ngx_uint_t gzip_proxied; /* gzip_proxied */#if (NGX_PCRE)ngx_array_t *gzip_disable; /* gzip_disable */
#endif
#endif#if (NGX_THREADS || NGX_COMPAT)ngx_thread_pool_t *thread_pool;ngx_http_complex_value_t *thread_pool_value;
#endif#if (NGX_HAVE_OPENAT)ngx_uint_t disable_symlinks; /* disable_symlinks */ngx_http_complex_value_t *disable_symlinks_from;
#endifngx_array_t *error_pages; /* error_page */ngx_path_t *client_body_temp_path; /* client_body_temp_path */ngx_open_file_cache_t *open_file_cache;time_t open_file_cache_valid;ngx_uint_t open_file_cache_min_uses;ngx_flag_t open_file_cache_errors;ngx_flag_t open_file_cache_events;ngx_log_t *error_log;ngx_uint_t types_hash_max_size;ngx_uint_t types_hash_bucket_size;ngx_queue_t *locations;#if 0ngx_http_core_loc_conf_t *prev_location;
#endif
};typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
返回值
// http.ngx_http_request.h#define NGX_HTTP_CONTINUE 100
#define NGX_HTTP_SWITCHING_PROTOCOLS 101
#define NGX_HTTP_PROCESSING 102#define NGX_HTTP_OK 200
#define NGX_HTTP_CREATED 201
#define NGX_HTTP_ACCEPTED 202
#define NGX_HTTP_NO_CONTENT 204
#define NGX_HTTP_PARTIAL_CONTENT 206#define NGX_HTTP_SPECIAL_RESPONSE 300
#define NGX_HTTP_MOVED_PERMANENTLY 301
#define NGX_HTTP_MOVED_TEMPORARILY 302
#define NGX_HTTP_SEE_OTHER 303
#define NGX_HTTP_NOT_MODIFIED 304
#define NGX_HTTP_TEMPORARY_REDIRECT 307
#define NGX_HTTP_PERMANENT_REDIRECT 308#define NGX_HTTP_BAD_REQUEST 400
#define NGX_HTTP_UNAUTHORIZED 401
#define NGX_HTTP_FORBIDDEN 403
#define NGX_HTTP_NOT_FOUND 404
#define NGX_HTTP_NOT_ALLOWED 405
#define NGX_HTTP_REQUEST_TIME_OUT 408
#define NGX_HTTP_CONFLICT 409
#define NGX_HTTP_LENGTH_REQUIRED 411
#define NGX_HTTP_PRECONDITION_FAILED 412
#define NGX_HTTP_REQUEST_ENTITY_TOO_LARGE 413
#define NGX_HTTP_REQUEST_URI_TOO_LARGE 414
#define NGX_HTTP_UNSUPPORTED_MEDIA_TYPE 415
#define NGX_HTTP_RANGE_NOT_SATISFIABLE 416
#define NGX_HTTP_MISDIRECTED_REQUEST 421
#define NGX_HTTP_TOO_MANY_REQUESTS 429/* Our own HTTP codes *//* The special code to close connection without any response */
#define NGX_HTTP_CLOSE 444#define NGX_HTTP_NGINX_CODES 494#define NGX_HTTP_REQUEST_HEADER_TOO_LARGE 494#define NGX_HTTPS_CERT_ERROR 495
#define NGX_HTTPS_NO_CERT 496/** We use the special code for the plain HTTP requests that are sent to* HTTPS port to distinguish it from 4XX in an error page redirection*/
#define NGX_HTTP_TO_HTTPS 497/* 498 is the canceled code for the requests with invalid host name *//** HTTP does not define the code for the case when a client closed* the connection while we are processing its request so we introduce* own code to log such situation when a client has closed the connection* before we even try to send the HTTP header to it*/
#define NGX_HTTP_CLIENT_CLOSED_REQUEST 499#define NGX_HTTP_INTERNAL_SERVER_ERROR 500
#define NGX_HTTP_NOT_IMPLEMENTED 501
#define NGX_HTTP_BAD_GATEWAY 502
#define NGX_HTTP_SERVICE_UNAVAILABLE 503
#define NGX_HTTP_GATEWAY_TIME_OUT 504
#define NGX_HTTP_VERSION_NOT_SUPPORTED 505
#define NGX_HTTP_INSUFFICIENT_STORAGE 507
以上返回值除了RFC2616規范中定義的返回碼外,還有Nginx自身定義的HTTP返回碼,例如:NGX_HTTP_CLOSE就是用于要求HTTP框架直接關閉用戶連接的
Nginx全局定義的錯誤碼
// core.ngx_core.h
#define NGX_OK 0
#define NGX_ERROR -1
#define NGX_AGAIN -2
#define NGX_BUSY -3
#define NGX_DONE -4
#define NGX_DECLINED -5
#define NGX_ABORT -6
當最后調用ngx_http_output_filter向用戶發送響應包時,可以將ngx_http_output_filter的返回值作為ngx_http_mytest_handler方法的返回值使用。
獲取URI和參數
// http.ngx_http.h
typedef struct ngx_http_request_s ngx_http_request_t;// http.ngx_http_request.hstruct ngx_http_request_s {...ngx_uint_t method;ngx_uint_t http_version;ngx_str_t request_line;ngx_str_t uri;ngx_str_t args;ngx_str_t exten;ngx_str_t unparsed_uri;ngx_str_t method_name;ngx_str_t http_protocol;ngx_str_t schema;...u_char *uri_start;u_char *uri_end;u_char *uri_ext;u_char *args_start;u_char *request_start;u_char *request_end;u_char *method_end;u_char *schema_start;u_char *schema_end;...
}
方法名
// http.ngx_http_request.h
ngx_uint_t method;// http.ngx_http_request.h
#define NGX_HTTP_UNKNOWN 0x00000001
#define NGX_HTTP_GET 0x00000002
#define NGX_HTTP_HEAD 0x00000004
#define NGX_HTTP_POST 0x00000008
#define NGX_HTTP_PUT 0x00000010
#define NGX_HTTP_DELETE 0x00000020
#define NGX_HTTP_MKCOL 0x00000040
#define NGX_HTTP_COPY 0x00000080
#define NGX_HTTP_MOVE 0x00000100
#define NGX_HTTP_OPTIONS 0x00000200
#define NGX_HTTP_PROPFIND 0x00000400
#define NGX_HTTP_PROPPATCH 0x00000800
#define NGX_HTTP_LOCK 0x00001000
#define NGX_HTTP_UNLOCK 0x00002000
#define NGX_HTTP_PATCH 0x00004000
#define NGX_HTTP_TRACE 0x00008000
#define NGX_HTTP_CONNECT 0x00010000
獲取方法的方式
- method為上述定義的宏,直接比較(如果使用method_name成員與字符串比較,效率會差很多)
- 如果需要獲取方法名,可以使用method_name,與可以聯合request_start與method_end指針獲取
URI
ngx_str_t uri;ngx_str_t unparsed_uri;u_char *uri_start;u_char *uri_end;u_char *uri_ext;
uri表示請求中的URI,uri_start和uri_end也是可以獲取到。
exten成員變量指向用戶請求的文件擴展名。
unparsed_uri表示沒有進行URL解碼的原始請求。例如,uri為/a b時,unparsed_uri為/a%20b
URL
args指向用戶請求中的URL參數。同理:args_start和uri_end也可以獲取URL
協議版本
http_protocol的data成員指向用戶請求中的HTTP協議版本字符串,len成員為協議版本字符串長度
http_version是Nginx解析過的協議版本
ngx_uint_t http_version;#define NGX_HTTP_VERSION_9 9
#define NGX_HTTP_VERSION_10 1000
#define NGX_HTTP_VERSION_11 1001
#define NGX_HTTP_VERSION_20 2000
#define NGX_HTTP_VERSION_30 3000
建議使用http_version分析HTTP協議版本。
使用request_start和request_end獲取原始的用戶請求行
獲取HTTP頭
struct ngx_http_request_s {...ngx_buf_t *header_in;ngx_http_headers_in_t headers_in;...
}
header_in指向Nginx收到的未經解析的HTTP頭部,這里暫不關注,header_in是接受HTTP頭部的緩沖區,ngx_http_headers_in_t類型的headers_in是存儲已經解析過的HTTP頭部。
// http.ngx_http_request.h
typedef struct {//所有解析過的HTTP頭部都在headers鏈表中ngx_list_t headers;// 每個ngx_table_elt_t成員都是RFC2616規范定義的HTTP頭部,// 它們實際上指向headers鏈表中相應成員,當為NULL時表示沒有解析到相應的HTTP頭部ngx_table_elt_t *host;ngx_table_elt_t *connection;ngx_table_elt_t *if_modified_since;ngx_table_elt_t *if_unmodified_since;ngx_table_elt_t *if_match;ngx_table_elt_t *if_none_match;ngx_table_elt_t *user_agent;ngx_table_elt_t *referer;ngx_table_elt_t *content_length;ngx_table_elt_t *content_range;ngx_table_elt_t *content_type;ngx_table_elt_t *range;ngx_table_elt_t *if_range;ngx_table_elt_t *transfer_encoding;ngx_table_elt_t *te;ngx_table_elt_t *expect;ngx_table_elt_t *upgrade;#if (NGX_HTTP_GZIP || NGX_HTTP_HEADERS)ngx_table_elt_t *accept_encoding;ngx_table_elt_t *via;
#endifngx_table_elt_t *authorization;ngx_table_elt_t *keep_alive;#if (NGX_HTTP_X_FORWARDED_FOR)ngx_table_elt_t *x_forwarded_for;
#endif#if (NGX_HTTP_REALIP)ngx_table_elt_t *x_real_ip;
#endif#if (NGX_HTTP_HEADERS)ngx_table_elt_t *accept;ngx_table_elt_t *accept_language;
#endif#if (NGX_HTTP_DAV)ngx_table_elt_t *depth;ngx_table_elt_t *destination;ngx_table_elt_t *overwrite;ngx_table_elt_t *date;
#endifngx_table_elt_t *cookie;// 只有ngx_http_auth_basic_module才會用到的成員ngx_str_t user;ngx_str_t passwd;// server名稱ngx_str_t server;// 計算出的HTTP包體大小(ngx_table_elt_t * content_length)off_t content_length_n;time_t keep_alive_n;// HTTP連接類型,取值范圍是0,// #define NGX_HTTP_CONNECTION_CLOSE 1//#define NGX_HTTP_CONNECTION_KEEP_ALIVE 2unsigned connection_type:2;unsigned chunked:1;unsigned multi:1;unsigned multi_linked:1;// 以下7個標志位是 HTTP框架根據瀏覽器傳來的useragent頭部,可以判斷瀏覽器的類型,只為1表示相應的瀏覽器發來的請求unsigned msie:1;unsigned msie6:1;unsigned opera:1;unsigned gecko:1;unsigned chrome:1;unsigned safari:1;unsigned konqueror:1;
} ngx_http_headers_in_t;
例如:對于不常見的HTTP請求頭如何尋找,只能通過遍歷headers_in.headers
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r) {...ngx_list_part_t *part = &r->header_in.headers.part;ngx_table_elt_t *header = part->elts;for (i = 0;; ++i) {if (i >= part->nelts) {if (part->next == NULL) {break;}part = part->next;header = part->elts;i = 0;}if (header[i].hash == 0){continue;}if (0 == ngx_strncasecmp(header[i].key.data,(u_char*) "Rpc-Description",header[i].key.len)){if (0 == ngx_strncmp(header[i].value.data,"uploadFile",header[i].value.len)){// 找到了正確的頭,繼續向下執行}}}...
}
獲取HTTP包體
HTTP包體的長度有可能非常大,如果嘗試一次性調用并讀取完所有的包體,那么多半阻塞nginx進程,HTTP框架提供一種異步接收包體方法:
ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r,ngx_http_client_body_handler_pt post_handler);
這是一個異步方法,調用只能說明要求Nginx開始接受請求的包體,并不表示是否已經接受完,當接收完所有的包體內容后,post_handler指向的回調方法會被回調。
發送響應
響應包含響應行,響應頭部,包體三部分。
發送HTTP頭
ngx_int_t ngx_http_send_header(ngx_http_request_t *r)
struct ngx_http_request_s {ngx_http_headers_out_t headers_out;
}typedef struct {ngx_list_t headers;ngx_list_t trailers;ngx_uint_t status;ngx_str_t status_line;ngx_table_elt_t *server;ngx_table_elt_t *date;ngx_table_elt_t *content_length;ngx_table_elt_t *content_encoding;ngx_table_elt_t *location;ngx_table_elt_t *refresh;ngx_table_elt_t *last_modified;ngx_table_elt_t *content_range;ngx_table_elt_t *accept_ranges;ngx_table_elt_t *www_authenticate;ngx_table_elt_t *expires;ngx_table_elt_t *etag;ngx_table_elt_t *cache_control;ngx_table_elt_t *link;ngx_str_t *override_charset;size_t content_type_len;ngx_str_t content_type;ngx_str_t charset;u_char *content_type_lowcase;ngx_uint_t content_type_hash;off_t content_length_n;off_t content_offset;time_t date_time;time_t last_modified_time;
} ngx_http_headers_out_t;
添加請求頭
ngx_table_elt_t h = ngx_list_push(&r->headers_out.headers);if (h == NULL){return NGX_ERROR}h.hash = 1;h.key.len = sizeof("TestHeader") - 1;h.key.data = (u_char *) "TestHeader";h.value.len = sizeof("TestValue") - 1;h.value.data = (u_char *) "TestValue" ;
發送內存中的字符串作為包體
ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
向客戶端發送HTTP響應包體
ngx_buf_t *b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));b->start = (u_char*)ngx_pcalloc(r->pool,128);b->pos = b->start;b->last = b->start;b->end = b->last + 128;b->temporary = 1;// 等同于如下ngx_buf_t *b = ngx_create_temp_buf(r->pool,128)
返回一個Hello world
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r) {// 必須為GET或者HEAD,否則返回405if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))){return NGX_HTTP_NOT_ALLOWED;}// 丟棄請求中的包體ngx_int_t rc = ngx_http_discard_request_body(r);if (rc != NGX_OK){return rc;}// 設置返回類型ngx_str_t type = ngx_string("text/plain");// 設置返回體ngx_str_t response = ngx_string("Hello World!");// 設置響應碼r->headers_out.status = NGX_HTTP_OK;// 設置響應體大小r->headers_out.content_length_n = response.len;// 設置類型r->headers_out.content_type = type;// 發送HTTP頭部rc = ngx_http_send_header(r);if (rc == NGX_ERROR || rc > NGX_OK || r->header_only){return rc;}// 構造ngx_buf_t結構體發送包體ngx_buf_t *b;b = ngx_create_temp_buf(r->pool,response.len);if (b == NULL){return NGX_HTTP_INTERNAL_SERVER_ERROR;}// 將hello world復制到ngx_buf_t指向的內存中ngx_memcpy(b->pos,response.data,response.len);// 注意設置好last指針b->last = b->pos + response.len;// 聲明這是最后一塊緩沖區b->last_buf = 1;// 構造發送時的ngx_chain_t結構體ngx_chain_t out;out.buf = b;out.next = NULL;// 發送包體return ngx_http_output_filter(r,&out);
}
磁盤文件作為包體發送
設置ngx_buf_t的in_file為1,表示緩沖區是文件,不是內存。file為ngx_file_t,結構如下:
struct ngx_file_s {ngx_fd_t fd;// 文件句柄描述符ngx_str_t name;// 文件名稱ngx_file_info_t info;// 文件大小等基本信息,就是Linux系統定義的stat結構off_t offset;// 表示處理到文件何處了off_t sys_offset;// 當前文件系統偏移量ngx_log_t *log;// 日志對象,相關的日志會輸出到log指定的日志文件中#if (NGX_THREADS || NGX_COMPAT)ngx_int_t (*thread_handler)(ngx_thread_task_t *task,ngx_file_t *file);void *thread_ctx;ngx_thread_task_t *thread_task;
#endif#if (NGX_HAVE_FILE_AIO || NGX_COMPAT)ngx_event_aio_t *aio;
#endifunsigned valid_info:1;unsigned directio:1;// 在發送大文件時可以設置為1
};