기술나눔

HTTP 모듈 개발

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

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,但是目前为止,框架代码从来不会调用,设置为NULL
    ngx_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_process
    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);
    // Nginx不支持多线程模式,所以init_thread在框架代码中没有被调用过,设置为NULL
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);
    // exit_thread也不支持,设置为NULL
    void                (*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;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

HTTP 모듈을 정의할 때 유형은 NGX_HTTP_MODULE로 설정됩니다. init_module, init_process,exit_process,exit_master 콜백 메소드는 Nginx 코드에 의해 호출되며 HTTP 프레임워크와 관련이 없습니다. nginx.conf에 정의되어 있지 않습니다. 이러한 종류의 열기는 HTTP 함수 구성 항목의 경우 이러한 파괴 메서드가 계속 호출됩니다. 따라서 HTTP 모듈을 개발할 때 일반적으로 NULL로 설정됩니다. 웹 서버로 사용되지 않으면 HTTP 모듈의 코드가 실행되지 않습니다.

HTTP 모듈을 정의할 때 마지막으로 해야 할 일은 ctx와 Command 두 멤버를 설정하는 것입니다. 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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

위 8개 단계의 호출 순서는 정의된 순서와 다릅니다. Nginx 시작 프로세스 중에 HTTP 프레임워크가 이러한 콜백 메서드를 호출하는 실제 순서는 다음과 같습니다. (nginx.conf 구성 항목과 관련됨)
1)create_main_conf
2) srv_conf를 만듭니다.
3) create_loc_conf
4) 사전 구성
5) init_main_conf
6)merge_srv_conf
7)merge_loc_conf
8) 구성 후

명령 배열은 모듈의 구성 파일 매개변수를 정의하는 데 사용됩니다. 각 배열 요소는 ngx_command_t 유형입니다. Nginx는 구성 파일의 구성 항목을 구문 분석할 때 먼저 모든 모듈을 컴파일합니다. 각 모듈에 대해 즉, 명령 배열을 순회하여 수행됩니다. 배열에서 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;// 配置项名称,列入:gzip
    ngx_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 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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;
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

사용자 요청 처리

// 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;
#endif

    unsigned      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;
#endif

    ngx_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;
#endif

    ngx_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 0
    ngx_http_core_loc_conf_t  *prev_location;
#endif
};

typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149

반환 값

// 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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

최종적으로 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.h

struct 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;
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

메소드 이름

// 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

방법을 얻는 방법

  • method는 위에서 정의한 매크로로 직접 비교합니다. (method_name 멤버를 사용하여 문자열과 비교하면 효율성이 훨씬 떨어집니다.)
  • 메소드 이름을 얻으려면 method_name을 사용하면 되고, request_start 포인터와 method_end 포인터를 결합하면 얻을 수 있습니다.

우리말

	ngx_str_t                         uri;
	
	ngx_str_t                         unparsed_uri;
	
    u_char                           *uri_start;
    u_char                           *uri_end;
    u_char                           *uri_ext;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

uri는 요청의 URI를 나타내며 uri_start 및 uri_end도 얻을 수 있습니다.
Exten 멤버 변수는 사용자가 요청한 파일 확장자를 가리킵니다.

unparsed_uri는 URL 디코딩이 없는 원래 요청을 나타냅니다. 예를 들어 uri가 /ab이면 unparsed_uri는 /a%20b입니다.

웹 주소

args는 사용자 요청의 URL 매개변수를 가리킵니다. 같은 방식으로: args_start 및 uri_end도 URL을 얻을 수 있습니다.

프로토콜 버전

http_protocol의 데이터 멤버는 사용자 요청의 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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

HTTP 프로토콜 버전을 분석하려면 http_version을 사용하는 것이 좋습니다.

원래 사용자 요청 라인을 얻으려면 request_start 및 request_end를 사용하십시오.

HTTP 헤더 가져오기


struct ngx_http_request_s {
	...
	ngx_buf_t                        *header_in;
	ngx_http_headers_in_t             headers_in;
	...
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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;
#endif

    ngx_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;
#endif
    ngx_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     2
    unsigned                          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;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

예: 일반적이지 않은 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)){
                // 找到了正确的头,继续向下执行
            }
        }
    }
    ...
}



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

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);
  • 1
  • 2

이는 비동기 메서드입니다. 호출은 요청된 패키지 본문을 수락하기 시작해야 한다는 점만 나타낼 수 있습니다. 모든 패키지 본문 내용이 수신되면 post_handler가 가리키는 콜백 메서드가 표시되지 않습니다. 다시 전화를 받다.

응답 보내기

응답에는 응답 줄, 응답 헤더 및 패키지 본문의 세 부분이 포함됩니다.

HTTP 헤더 보내기

ngx_int_t ngx_http_send_header(ngx_http_request_t *r)
  • 1

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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

요청 헤더 추가

   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" ;


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

패킷 본문으로 메모리의 문자열을 보냅니다.

ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
  • 1

클라이언트에 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) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Hello World 반환

static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r) {
    // 必须为GET或者HEAD,否则返回405
    if (!(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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

패키지 본문으로 전송된 디스크 파일

ngx_buf_t의 in_file을 1로 설정하면 버퍼가 메모리가 아닌 파일임을 나타냅니다. 파일은 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;
#endif

    unsigned                   valid_info:1;
    unsigned                   directio:1;// 在发送大文件时可以设置为1
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26