開發一個HTTP模塊

開發一個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
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/42329.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/42329.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/42329.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

什么時候考慮將mysql數據遷移到ES?

文章目錄 對ES的一些疑問問題1:ES相比mysql本身有哪些優勢&#xff1f;問題2:哪些場景適合用ES而不是mysql&#xff1f;問題3:mysql逐行掃描&#xff0c;根據過濾條件檢查記錄中對應字段是否滿足要求屬于正排索引&#xff0c;根據二叉樹索引檢索記錄的方式屬于正排索引還是倒排…

SpringBoot整合DataX數據同步(自動生成job文件)

SpringBoot整合Datax數據同步 文章目錄 SpringBoot整合Datax數據同步1.簡介設計理念 DataX3.0框架設計DataX3.0核心架構核心模塊介紹DataX調度流程 2.DataX3.0插件體系3.數據同步1.編寫job的json文件2.進入bin目錄下&#xff0c;執行文件 4.SpringBoot整合DataX生成Job文件并執…

生產力工具|VS Code安裝及使用指南

一、VS Code介紹 &#xff08;一&#xff09;軟件介紹 Visual Studio Code&#xff08;簡稱VS Code&#xff09;是由Microsoft開發的免費開源代碼編輯器&#xff0c;適用于Windows、macOS和Linux操作系統。它支持多種編程語言&#xff0c;如JavaScript、Python、C等&#xff0…

知識社區在線提問小程序模板源碼

藍色的知識問答&#xff0c;問答交流&#xff0c;知識社區&#xff0c;在線提問手機app小程序網頁模板。包含&#xff1a;社區主頁、提問、我的、綁定手機&#xff0c;實名認證等。 知識社區在線提問小程序模板源碼

ubuntu 檢查硬盤的通電時長、健康度

ubuntu 檢查硬盤的通電時長、健康度 在Ubuntu系統中&#xff0c;檢查硬盤的通電時長和健康度通常需要使用SMART&#xff08;Self-Monitoring, Analysis, and Reporting Technology&#xff09;工具。SMART是硬盤制造商內置的一套硬盤保護技術&#xff0c;用于監控硬盤的健康狀況…

品質至上!中國星坤連接器的發展之道!

在電子連接技術領域&#xff0c;中國星坤以其卓越的創新能力和對品質的不懈追求&#xff0c;贏得了業界的廣泛認可。憑借在高精度連接器設計和制造上的領先地位&#xff0c;星坤不僅獲得了多項實用新型專利&#xff0c;更通過一系列國際質量管理體系認證&#xff0c;彰顯了其產…

【Qt5.12.9】程序無法顯示照片問題(已解決)

問題記錄&#xff1a;Qt5.12.9下無法顯示照片 我的工程名為03_qpainter&#xff0c;照片cd.png存放在工程目錄下的image文件夾中。 /03_qpainter/image/cd.png 因為這是正點原子Linux下Qt書籍中的例程&#xff0c;在通過學習其配套的例程中的項目&#xff0c;發現我的項目少…

【Python】搭建屬于自己 AI 機器人

目錄 前言 1 準備工作 1.1 環境搭建 1.2 獲取 API KEY 2 寫代碼 2.1 引用庫 2.2 創建用戶 2.3 創建對話 2.4 輸出內容 2.5 調試 2.6 全部代碼 2.7 簡短的總結 3 優化代碼 3.1 規范代碼 3.1.1 引用庫 3.1.2 創建提示詞 3.1.3 創建模型 3.1.4 規范輸出&#xf…

在線調試網絡接口的免費網站

免費接口網站 GET接口 https://httpbin.org/get https://httpbin.org/ip https://publicobject.com/helloworld.txt POST接口 https://httpbin.org/post 調試網站 Postman需要下載安裝&#xff0c;還要登錄賬號。對于簡單測試&#xff0c;麻煩&#xff01; http://coolaf.…

西門子1200高速計數器編碼器的應用 接線 組態 編程 調試 測距測速

編碼器的應用、接線、組態、博途1200編程與調試&#xff1a;高速計數器&#xff0c;用于給PLC發高速脈沖&#xff0c;接I點 用來例如&#xff1a;檢測電機轉速&#xff0c;皮帶輸送機運行的距離 &#xff08;粗略定位&#xff09; 360&#xff1a;代表轉一圈會對外發360個脈沖&…

系統化學習 H264視頻編碼(02) I幀 P幀 B幀 引入及相關概念解讀

說明&#xff1a;我們參考黃金圈學習法&#xff08;什么是黃金圈法則?->模型 黃金圈法則&#xff0c;本文使用&#xff1a;why-what&#xff09;來學習音H264視頻編碼。本系列文章側重于理解視頻編碼的知識體系和實踐方法&#xff0c;理論方面會更多地講清楚 音視頻中概念的…

Python類實例的json

web開發中有這么一個場景&#xff0c;我們從數據庫中查詢某一數據的時候&#xff0c;往往需要對數據進行一些轉化之后才能傳給前端。 當然我們可以根據查詢出來的實例對象&#xff0c;構建一個dict返回&#xff0c;這樣會導致我們的代碼非常的臃腫。但是這也確實是一種最直接的…

網絡空間測繪是什么?

網絡空間測繪是一種技術過程&#xff0c;用于探測、分析和可視化互聯網及其他網絡環境中的各種資源和連接。這個概念在2016年開始廣泛使用&#xff0c;它涉及到收集有關網絡節點&#xff08;如服務器、路由器、個人電腦和其他設備&#xff09;的信息&#xff0c;并建立這些節點…

C++ STL 多線程庫用法介紹

目錄 一:Atomic: 二:Thread 1. 創建線程 2. 小心移動(std::move)線程 3. 如何創建帶參數的線程 4. 線程參數是引用類型時,要小心謹慎。 5. 獲取線程ID 6. jthread 7. 如何在線程中使用中斷 stop_token 三:如何解決數據競爭 1.有問題的代碼 2.使用互斥 3.預防…

Vue3+.NET6前后端分離式管理后臺實戰(二十八)

1&#xff0c;Vue3.NET6前后端分離式管理后臺實戰(二十八)

【Linux進階】文件系統6——理解文件操作

目錄 1.文件的讀取 1.1.目錄 1.2.文件 1.3.目錄樹讀取 1.4.文件系統大小與磁盤讀取性能 2.增添文件 2.1.數據的不一致&#xff08;Inconsistent&#xff09;狀態 2.2.日志式文件系統&#xff08;Journaling filesystem&#xff09; 3.Linux文件系統的運行 4、文件的刪…

動態規劃算法-以中學排班管理系統為例

1.動態規劃算法介紹 1.算法思路 動態規劃算法通常用于求解具有某種最優性質的問題。在這類問題中&#xff0c;可能會有許多可行解。每一個解都對應于一個值&#xff0c;我們希望找到具有最優值的解。動態規劃算法與分治法類似&#xff0c;其基本思想也是將待求解問題分解成若…

干貨 | 2024大模型場景下智算平臺的設計與優化實踐(免費下載)

誠摯邀請您微信掃描以下二維碼加入方案驛站知識星球&#xff0c;獲取上萬份PPT/WORD解決方案&#xff01;&#xff01;&#xff01;感謝支持&#xff01;&#xff01;&#xff01;

android pdf框架-11,查看圖片

前10篇文章,9章關于pdf的,pdf解析后,里面也是有各種圖片,于是利用pdf的view來展示圖片,似乎也是個不錯的想法. android手機中的圖片查看功能,有的可以展示,有的不能.比如華為,榮耀對大體積的png是可以顯示的,小米是不顯示,只有縮略圖. 一張png50m大,比如清明上河圖,原圖是tif…

【C++】string的底層原理及實現

文章目錄 string類的存儲結構默認成員函數構造函數析構函數拷貝構造函數賦值重載 容量操作size()capacity()reserve()resize()clear() 遍歷與訪問operator[ ]迭代器范圍與for 增刪查改push_back()pop_back()append()operatorinsert()erase()c_str()find()substr() 非成員函數op…