/* 一个例子模块,简单的将http请求的内容返输出 V4 版本: 使用upstream来实现的版本 需要设置的参数出了 echov4_times 之外, 还有echov4_pass :指明echo服务的upstream地址 by RainX */ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> /* 函数的声明 */ static ngx_int_t ngx_http_echo_handler(ngx_http_request_t* r); static char* ngx_conf_set_echo(ngx_conf_t *cf, ngx_command_t* cmd, void* conf); static void* ngx_http_echo_create_loc_conf(ngx_conf_t *cf); static char* ngx_http_echo_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child); static size_t ngx_http_dump_request_body(ngx_http_request_t* r, void** post_content_ptr); static void ngx_http_dump_request_headers(ngx_http_request_t* r); static ngx_int_t ngx_http_echo_filter_init(ngx_conf_t* cf); static ngx_int_t ngx_http_echo_header_filter(ngx_http_request_t* r); static ngx_int_t ngx_http_echo_body_filter(ngx_http_request_t* r, ngx_chain_t* in); /* upstream 相关的callback */ static ngx_int_t ngx_http_echo_create_request(ngx_http_request_t *r); static ngx_int_t ngx_http_echo_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_echo_process_header(ngx_http_request_t *r); static void ngx_http_echo_abort_request(ngx_http_request_t *r); static void ngx_http_echo_finalize_request(ngx_http_request_t *r, ngx_int_t rc); /* 必须要包含的两个变量 */ static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt ngx_http_next_body_filter; /* 定义一个loc的上下文结构体,保存配置信息等 */ typedef struct ngx_http_echov4_loc_conf_s { ngx_int_t echo_times; /* echo要被显示的次数 */ ngx_http_upstream_conf_t upstream; } ngx_http_echov4_loc_conf_t; /* 定义一个request life cycle的结构体,用来保存per request的运行时状态等信息 保存在 r->ctx[index of our module] 中 */ typedef struct ngx_http_echov4_request_ctx_s { ngx_int_t current_echo_times; /* 已经被显示的次数 */ ngx_str_t* post_content; /* 要发送的内容 */ } ngx_http_echov4_request_ctx_t ; void ngx_http_echov4_request_post_handler (ngx_http_request_t *r) { } /* 定义echo的conf文件指令集 */ static ngx_command_t ngx_http_echo_commands[] = { { ngx_string("echov4_pass"), /* 设定echo的handler */ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, &ngx_conf_set_echo, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, { ngx_string("echov4_times"), /* 设置echo的次数 */ NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_echov4_loc_conf_t, echo_times), NULL }, ngx_null_command }; /* echo 指令的处理,主要是进行handler的注册 */ static char* ngx_conf_set_echo(ngx_conf_t *cf, ngx_command_t *cmd, void* conf) { ngx_http_core_loc_conf_t* clcf; ngx_str_t *value; ngx_url_t u; ngx_http_echov4_loc_conf_t* elcf = (ngx_http_echov4_loc_conf_t*) conf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, "ngx_conf_set_echo called - [rainx]"); clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_echo_handler; value = cf->args->elts; u.url = value[1]; u.no_resolve = 1; elcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); if (elcf->upstream.upstream == NULL){ return NGX_CONF_ERROR; } return NGX_CONF_OK; } /* create conf for loc */ static void* ngx_http_echo_create_loc_conf(ngx_conf_t *cf) { ngx_http_echov4_loc_conf_t *conf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, "ngx_http_echo_create_loc_conf called - [rainx]"); conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echov4_loc_conf_t)); if (conf == NULL) { return NGX_CONF_ERROR; } /* from memcached module */ conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC; conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC; conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC; conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE; /* the hardcoded values */ conf->upstream.cyclic_temp_file = 0; conf->upstream.buffering = 0; conf->upstream.ignore_client_abort = 0; conf->upstream.send_lowat = 0; conf->upstream.bufs.num = 0; conf->upstream.busy_buffers_size = 0; conf->upstream.max_temp_file_size = 0; conf->upstream.temp_file_write_size = 0; conf->upstream.intercept_errors = 1; conf->upstream.intercept_404 = 1; conf->upstream.pass_request_headers = 0; conf->upstream.pass_request_body = 0; conf->echo_times = NGX_CONF_UNSET; return conf; } /* merge conf for loc */ static char* ngx_http_echo_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child) { ngx_http_echov4_loc_conf_t* prev = parent; ngx_http_echov4_loc_conf_t* conf = child; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, "ngx_http_echo_merge_loc_conf called - [rainx]"); if (conf->echo_times == NGX_CONF_UNSET) { conf->echo_times = prev->echo_times; } /* from memcached */ ngx_conf_merge_msec_value(conf->upstream.connect_timeout, prev->upstream.connect_timeout, 60000); ngx_conf_merge_msec_value(conf->upstream.send_timeout, prev->upstream.send_timeout, 60000); ngx_conf_merge_msec_value(conf->upstream.read_timeout, prev->upstream.read_timeout, 60000); ngx_conf_merge_size_value(conf->upstream.buffer_size, prev->upstream.buffer_size, (size_t) ngx_pagesize); ngx_conf_merge_bitmask_value(conf->upstream.next_upstream, prev->upstream.next_upstream, (NGX_CONF_BITMASK_SET |NGX_HTTP_UPSTREAM_FT_ERROR |NGX_HTTP_UPSTREAM_FT_TIMEOUT)); if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) { conf->upstream.next_upstream = NGX_CONF_BITMASK_SET |NGX_HTTP_UPSTREAM_FT_OFF; } if (conf->upstream.upstream == NULL) { conf->upstream.upstream = prev->upstream.upstream; } return NGX_CONF_OK; } /* HTTP 上下文 */ static ngx_http_module_t ngx_http_echo_module_ctx = { NULL, /* preconfiguration */ ngx_http_echo_filter_init, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_echo_create_loc_conf, /* create location configuration */ ngx_http_echo_merge_loc_conf /* merge location configuration */ }; /* 定义模块 */ ngx_module_t ngx_http_echov4_module = { NGX_MODULE_V1, &ngx_http_echo_module_ctx, /* 模块的上下文 */ ngx_http_echo_commands, /* 模块的指令 */ NGX_HTTP_MODULE, /* 模块的类型 */ NULL, /* 初始化master */ NULL, /* 初始化模块 */ NULL, /* 初始化process*/ NULL, /* 初始化线程 */ NULL, /* 退出线程 */ NULL, /* 退出process */ NULL, /* 退出master */ NGX_MODULE_V1_PADDING }; /* echo 处理handler */ static ngx_int_t ngx_http_echo_handler(ngx_http_request_t* r) { ngx_http_echov4_loc_conf_t* echo_conf; ngx_int_t rc; u_char* post_content = NULL; size_t post_content_len = 0; ngx_http_echov4_request_ctx_t* echo_ctx; ngx_http_upstream_t* u; echo_conf = ngx_http_get_module_loc_conf(r, ngx_http_echov4_module); // 我们模块运行时的上下文,如果没有的话,我们将进行初始化工作 echo_ctx = (ngx_http_echov4_request_ctx_t*) ngx_http_get_module_ctx(r, ngx_http_echov4_module); if (!echo_ctx){ //初始化 echo_ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_echov4_request_ctx_t)); /* 实际上calloc 已经把结构体里的数值设置为0 */ echo_ctx->current_echo_times = 0; echo_ctx->post_content = ngx_pcalloc(r->pool, sizeof(ngx_str_t)); ngx_http_set_ctx(r, echo_ctx, ngx_http_echov4_module); } rc = ngx_http_read_client_request_body(r, ngx_http_echov4_request_post_handler); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } if (!(r->method &(NGX_HTTP_POST))) { return NGX_HTTP_NOT_ALLOWED; } ngx_http_dump_request_headers(r); // 将post_content的数值赋值给结构体 post_content_len = ngx_http_dump_request_body(r, /* out */(void*) &post_content); echo_ctx->post_content->len = post_content_len; echo_ctx->post_content->data = post_content; u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t)); if (u == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } u->schema.len = sizeof("echo://") - 1; u->schema.data = (u_char *) "echo://"; u->peer.log = r->connection->log; u->peer.log_error = NGX_ERROR_ERR; #if (NGX_THREADS) u->peer.lock = &r->connection->lock; #endif u->output.tag = (ngx_buf_tag_t) &ngx_http_echov4_module; u->conf = &echo_conf->upstream; u->create_request = ngx_http_echo_create_request; u->reinit_request = ngx_http_echo_reinit_request; u->process_header = ngx_http_echo_process_header; u->abort_request = ngx_http_echo_abort_request; u->finalize_request = ngx_http_echo_finalize_request; r->upstream = u; ngx_http_upstream_init(r); return NGX_OK; } // 将request body 的数据打印出来 static size_t ngx_http_dump_request_body(ngx_http_request_t* r, void** post_content_ptr) { ngx_http_request_body_t* rb; u_char* post_content = NULL; rb = r->request_body; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_dump_request_body entried - [rainx]"); if (!rb) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "request body is empty - [rainx]"); return 0; } // check a bit ngx_log_debug7(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "nParameters dump is : [ rb->temp_file = %p ]n" " [ rb->bufs = %p ]n" " [ rb->bufs->buf = %p ]n" " [ rb->buf = %p ]n" " [ rb->rest = %d ]n" " [ rb->to_write = %d ]n" " [ buf size = %d ]n", " [ buf pos = %d ]n", " [ buf last = %d ]n", rb->temp_file, rb->bufs, rb->bufs->buf, rb->buf, rb->rest, rb->to_write, rb->buf->end - rb->buf->start ); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "n [ buf pos = %p ]n" " [ buf last = %p ]n", rb->buf->pos, rb->buf->last ); post_content = ngx_palloc(r->pool, rb->buf->last-rb->buf->pos + 1); ngx_cpystrn(post_content, rb->buf->pos, rb->buf->last-rb->buf->pos); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "n [ buf pos data = %s ]", post_content ); *post_content_ptr = post_content; return rb->buf->last-rb->buf->pos; } static void ngx_http_dump_request_headers(ngx_http_request_t* r) { // ngx_http_headers_in_t h; // dump some data ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[rainx] request_line is : %V", &r->request_line); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[rainx] uri is : %V", &r->uri); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[rainx] args is : %V", &r->args); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[rainx] exten is : %V", &r->exten); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[rainx] unparsed_uri is : %V", &r->unparsed_uri); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[rainx] method_name is : %V", &r->method_name); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[rainx] http_protocol is : %V", &r->http_protocol); return ; } // 注册filter static ngx_int_t ngx_http_echo_filter_init(ngx_conf_t* cf) { ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_echo_header_filter; ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_echo_body_filter; return NGX_OK; } // 修改header头的size static ngx_int_t ngx_http_echo_header_filter(ngx_http_request_t* r) { /* 获取echo_times和current_echo_times */ ngx_http_echov4_loc_conf_t* echo_conf; ngx_http_echov4_request_ctx_t* echo_ctx; echo_conf = ngx_http_get_module_loc_conf(r, ngx_http_echov4_module); // 我们模块运行时的上下文,如果没有的话,我们将不进行处理 echo_ctx = (ngx_http_echov4_request_ctx_t*) ngx_http_get_module_ctx(r, ngx_http_echov4_module); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_echo_header_filter called - [rainx]"); // 如果当前的输出次数已经比设定的大 if (!echo_ctx || echo_ctx->current_echo_times >= echo_conf->echo_times){ return ngx_http_next_header_filter(r); } // 将content_length的数值设定为原来的长度乘以 echo_times; if (r->headers_out.content_length_n) r->headers_out.content_length_n = r->headers_out.content_length_n * echo_conf->echo_times; return ngx_http_next_header_filter(r); } static ngx_int_t ngx_http_echo_body_filter(ngx_http_request_t* r, ngx_chain_t* in) { /* 获取echo_times和current_echo_times */ ngx_http_echov4_loc_conf_t* echo_conf; ngx_chain_t* chain_link; ngx_chain_t* new_link; ngx_buf_t* new_buf; ngx_http_echov4_request_ctx_t* echo_ctx; int i; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_echo_body_filter called - [rainx]"); echo_conf = ngx_http_get_module_loc_conf(r, ngx_http_echov4_module); // 我们模块运行时的上下文,如果没有的话,我们将不进行处理 echo_ctx = (ngx_http_echov4_request_ctx_t*) ngx_http_get_module_ctx(r, ngx_http_echov4_module); if (!echo_ctx || echo_ctx->current_echo_times >= echo_conf->echo_times){ return ngx_http_next_body_filter(r, in); } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ngx_http_echo_handler called[v2] - [rainx]n" "Dump of ngx_http_echov4_loc_ctx_t current_echo_times=[%d]n" "Dump of ngx_http_echov4_loc_conf_t echo_times=[%d]", echo_ctx->current_echo_times, echo_conf->echo_times ); chain_link = in; for ( i = 0 ; i < echo_conf->echo_times - echo_ctx->current_echo_times; i++) { new_link = ngx_alloc_chain_link(r->pool); new_buf = ngx_calloc_buf(r->pool); // 创建buffer并赋值 new_buf->pos = echo_ctx->post_content->data; new_buf->last = echo_ctx->post_content->data + echo_ctx->post_content->len; new_buf->memory = 1; new_buf->last_buf = 1; new_link->buf = new_buf; // 修改chain_link,将,新增的chain放到chain link的最前面 new_link->next = chain_link; chain_link = new_link; } echo_ctx->current_echo_times = echo_conf->echo_times; return ngx_http_next_body_filter(r, chain_link); } static ngx_int_t ngx_http_echo_create_request(ngx_http_request_t* r) { ngx_http_echov4_loc_conf_t* elcf; ngx_http_echov4_request_ctx_t* ctx; ngx_buf_t* b; ngx_chain_t* cl; elcf = ngx_http_get_module_loc_conf(r, ngx_http_echov4_module); ctx = ngx_http_get_module_ctx(r, ngx_http_echov4_module); b = ngx_create_temp_buf(r->pool, ctx->post_content->len); if (b == NULL) { return NGX_ERROR; } cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = b; cl->next = NULL; r->upstream->request_bufs = cl; b->last = ngx_copy(b->last, ctx->post_content->data, ctx->post_content->len); return NGX_OK; } static ngx_int_t ngx_http_echo_reinit_request(ngx_http_request_t* r) { return NGX_OK; } static ngx_int_t ngx_http_echo_process_header(ngx_http_request_t* r) { u_char *p; ngx_http_upstream_t *u; u = r->upstream; for (p = u->buffer.pos; p < u->buffer.last; p++) { if (*p == LF || *p == '