nginx自定义模块记录上游服务器特定响应头

功能,服务器通过扩展自定义命令,记录上游的服务器返回的特定响应头内容,记录到本地文件中

代码如下:

/*
 * Copyright (C) Ciaos
 */

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

typedef struct {
    ngx_str_t    header;
    ngx_str_t    savefile;
} ngx_http_tiaowuban_filter_conf_t;

static void *ngx_http_tiaowuban_filter_create_conf(ngx_conf_t *cf);
static char *ngx_http_tiaowuban_filter_merge_conf(ngx_conf_t *cf, void *parent,void *child);

static ngx_int_t ngx_http_tiaowuban_filter_init(ngx_conf_t *cf);

static ngx_command_t  ngx_http_tiaowuban_filter_commands[] = {
    { ngx_string("tiaowuban_header"),
        NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
        ngx_conf_set_str_slot,
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_tiaowuban_filter_conf_t, header),
        NULL },
    { ngx_string("tiaowuban_savefile"),
        NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
        ngx_conf_set_str_slot,
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_tiaowuban_filter_conf_t, savefile),
        NULL },

    ngx_null_command
};

static ngx_http_module_t  ngx_http_tiaowuban_filter_module_ctx = {
    NULL,                                  /* preconfiguration */
    ngx_http_tiaowuban_filter_init,              /* postconfiguration */

    NULL,                                  /* create main configuration */
    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */
    NULL,                                  /* merge server configuration */

    ngx_http_tiaowuban_filter_create_conf,       /* create location configration */
    ngx_http_tiaowuban_filter_merge_conf         /* merge location configration */
};


ngx_module_t  ngx_http_tiaowuban_filter_module = {
    NGX_MODULE_V1,
    &ngx_http_tiaowuban_filter_module_ctx,       /* module context */
    ngx_http_tiaowuban_filter_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 ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

static ngx_int_t 
ngx_http_tiaowuban_filter_header_filter(ngx_http_request_t *r)
{
    ngx_uint_t        buffsize = 1024, n;
    ngx_table_elt_t        *ho;
    ngx_buf_t        out_buff;
    u_char            buff[buffsize];
    ngx_file_t        file;
    ngx_list_t        *headers;
    ngx_http_tiaowuban_filter_conf_t *clcf;
    
    clcf = ngx_http_get_module_loc_conf(r, ngx_http_tiaowuban_filter_module);
    if(clcf == NULL) {
        return NGX_ERROR;
    }

    if(clcf->header.len != 0){
        headers = &r->headers_out.headers;
        for (n = 0; n < headers->part.nelts; n++) {
            ho = &((ngx_table_elt_t *)headers->part.elts)[n];
            if (ngx_strncmp(ho->key.data, clcf->header.data, clcf->header.len) == 0) {
                if (clcf->savefile.data == NULL){
                    return NGX_ERROR;
                }
                file.fd = ngx_open_file(clcf->savefile.data, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN, 0600);
                if (file.fd == NGX_INVALID_FILE) {
                    return NGX_ERROR;
                }
                file.log = r->connection->log;
            
                out_buff.pos = out_buff.last = buff;
                out_buff.last = ngx_http_time(out_buff.last, time(0));
                out_buff.last = ngx_snprintf(out_buff.last, r->connection->addr_text.len+1, "|%s", r->connection->addr_text.data);
                buffsize -= out_buff.last - out_buff.pos;
                out_buff.last = ngx_snprintf(out_buff.last, buffsize, "|%s
", (u_char *) ho->value.data);

                (void) ngx_write_file(&file, out_buff.pos, out_buff.last - out_buff.pos, 0);

                if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
                     ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, ngx_close_file_n " "%s" failed", clcf->savefile.data);
                }
            }
        }
    }

    return ngx_http_next_header_filter(r);
}

static ngx_int_t 
ngx_http_tiaowuban_filter_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    return ngx_http_next_body_filter(r, in);
}

static void *
ngx_http_tiaowuban_filter_create_conf(ngx_conf_t *cf)
{
    ngx_http_tiaowuban_filter_conf_t  *clcf;

    clcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_tiaowuban_filter_conf_t));
    if (clcf == NULL) {
        return NULL;
    }

    return clcf;
}


static char *
ngx_http_tiaowuban_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_tiaowuban_filter_conf_t *prev = parent;
    ngx_http_tiaowuban_filter_conf_t *clcf = child;

    ngx_conf_merge_str_value(clcf->header, prev->header, "");
    ngx_conf_merge_str_value(clcf->savefile, prev->savefile, "");

    return NGX_CONF_OK;
}

static ngx_int_t
ngx_http_tiaowuban_filter_init(ngx_conf_t *cf)
{
    ngx_http_next_header_filter = ngx_http_top_header_filter;
    ngx_http_top_header_filter = ngx_http_tiaowuban_filter_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;
    ngx_http_top_body_filter = ngx_http_tiaowuban_filter_body_filter;

    return NGX_OK;
}

测试用例如下

use Test::Nginx::Socket;
repeat_each(1);
plan tests => 3 * repeat_each() * blocks() - 1;

our $config = <<"_EOC_";
        location /test {
                tiaowuban_header "tiaowubanCtrl";
                tiaowuban_savefile /usr/local/nginx/logs/tiaowuban.bill;
                proxy_pass      http://127.0.0.1/;
        }
_EOC_

run_tests();

__DATA__

=== TEST 1: upstream header one
--- http_config eval
"
    server{
        listen  80;
        location / {
                add_header tiaowubanCtrl "testone";
                return 200 "this is test one";
        }
    }
"
--- config eval: $::config
--- request
GET /test
--- pre_remove_files eval
["/usr/local/nginx/logs/tiaowuban.bill"]
--- error_code: 200
--- response_headers
tiaowubanCtrl: testone
--- response_body_like: this is test one
--- output_files_like
/usr/local/nginx/logs/tiaowuban.bill: testone

=== TEST 2: upstream header two
--- http_config eval
"
    server{
        listen  80;
        location / {
                add_header tiaowubanCtrl "testtwo";
                return 200 "this is test two";
        }
    }
"
--- config eval: $::config
--- request
GET /test
--- pre_remove_files eval
["/usr/local/nginx/logs/tiaowuban.bill"]
--- error_code: 200
--- response_headers
tiaowubanCtrl: testtwo
--- response_body_like: this is test two
--- output_files_like
/usr/local/nginx/logs/tiaowuban.bill: testtwo

=== TEST 3: upstream no header
//--- ONLY
--- http_config eval
"
    server{
        listen  80;
        location / {
                return 200 "this is test three";
        }
    }
"
--- config eval: $::config
--- request
GET /test
--- pre_remove_files eval
["/usr/local/nginx/logs/tiaowuban.bill"]
--- error_code: 200
--- response_body_like: this is test three
--- files_not_exist eval
["/usr/local/nginx/logs/tiaowuban.bill"]
--- timeout: 10

文件内容如下:

Wed, 12 Nov 2014 08:29:30 GMT|10.0.2.2|hello tiaowuban
Wed, 12 Nov 2014 08:30:20 GMT|10.0.2.2|hello tiaowuban
Wed, 12 Nov 2014 08:30:24 GMT|10.0.2.2|hello tiaowuban
Wed, 12 Nov 2014 08:30:43 GMT|10.0.2.2|hello tiaowuban
Wed, 12 Nov 2014 08:30:46 GMT|10.0.2.2|hello tiaowuban
原文地址:https://www.cnblogs.com/ciaos/p/4092335.html