├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── body_filter.c ├── conf ├── handlers.js ├── nginx.conf ├── njs_add_header.conf └── njs_nginx.conf ├── config ├── content_handler.c ├── header_filter.c └── njs_add_header_module.patch /.gitignore: -------------------------------------------------------------------------------- 1 | nginx/ 2 | test-root/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Vasiliy Soshnikov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY = all 2 | 3 | NGX_CONFIGURE = ./auto/configure 4 | ## Some versions of nginx have different path of the configure, 5 | ## following lines are handle it {{ 6 | ifeq ($(shell [ -e "$(NGX_PATH)/configure" ] && echo 1 || echo 0 ), 1) 7 | NGX_CONFIGURE=./configure 8 | endif 9 | ## }} 10 | 11 | NGX_PATH = nginx 12 | PREFIX_PATH = $(PWD)/test-root 13 | MODULE_PATH = $(PWD) 14 | WZ2 = $(MODULE_PATH)/../wz2 15 | PROJ_DIR = $(PWD)/.. 16 | 17 | DEV_CFLAGS += -ggdb3 -O0 -Wall -Werror 18 | 19 | 20 | all: build 21 | 22 | build: 23 | $(MAKE) -C $(NGX_PATH) 24 | 25 | configure: 26 | cd $(NGX_PATH) && \ 27 | CFLAGS="$(DEV_CFLAGS)" $(NGX_CONFIGURE) \ 28 | --with-http_addition_module \ 29 | --prefix=$(PREFIX_PATH) \ 30 | --add-module=$(MODULE_PATH) \ 31 | --with-debug 32 | mkdir -p $(PREFIX_PATH)/conf $(PREFIX_PATH)/logs 33 | cp -Rf $(NGX_PATH)/conf/* $(PREFIX_PATH)/conf 34 | cp -f $(MODULE_PATH)/conf/nginx.conf $(PREFIX_PATH)/conf/nginx.conf 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nginx-tutorials 2 | 3 | # The tutorials of creating modules of nginx. 4 | 5 | About nginx: https://nginx.org 6 | 7 | ## Content 8 | ---------- 9 | * [Compilation and install](#compilation-and-install) 10 | * [Tutorials](#tutorials) 11 | * [See also](#see-also) 12 | 13 | ## Compilation and install 14 | -------------------------- 15 | ```bash 16 | git clone https://github.com/dedok/nginx-tutorials 17 | cd nginx-tutorials 18 | git clone https://github.com/nginx/nginx.git nginx 19 | sudo apt-get install libpcre-dev zlib1-dev # install dependencies to build nginx 20 | make configure 21 | make 22 | ./nginx/objs/nginx 23 | wget --server-response --header "X-My-header:1" "127.0.0.1:8081/example" 24 | ``` 25 | [Back to content](#content) 26 | 27 | ## Tutorials 28 | ------------ 29 | ### Header filter, a file: body_filter.c 30 | 31 | A header filter consists of three basic steps: 32 | - Decide whether to operate on this response 33 | - Operate on the response 34 | - Call the next filter. 35 | 36 | ### Body filter, a file: header_filter.c or it's filtering the nginx chain buffer. 37 | 38 | The buffer chain makes it a little tricky to write a body filter, 39 | because the body filter can only operate on one buffer (chain link) at a time. 40 | The module must decide whether to overwrite the input buffer, replace the buffer with a newly allocated buffer, 41 | or insert a new buffer before or after the buffer in question. To complicate things, 42 | sometimes a module will receive several buffers so that it has an incomplete buffer chain that it must operate on. 43 | 44 | ### Handlers 45 | 46 | Content handler (Non-proxying), a file: content_handler.c 47 | Handlers typically do four things: 48 | - get the location configuration; 49 | - generate an appropriate response; 50 | - send the header, and send the body. 51 | A handler has one argument, the request struct. 52 | A request struct has a lot of useful information about the client request, such as the request method, URI, and headers. 53 | 54 | ### NJS 55 | 56 | #### NJS module example 57 | - Get NGINX NJS 58 | - Run NGINX with conf/njs_nginx.conf configuration 59 | #### NJS and add_header example 60 | - Get NJS sources 61 | - Patch original add_header sources and complie patched NGINX 62 | - Run compiled NGINX with conf/njs_add_header.conf configuration 63 | 64 | ## See also 65 | ------------ 66 | The links to the examples implementation of nginx upstream: 67 | https://github.com/tarantool/nginx_upstream_module 68 | https://github.com/openresty/redis2-nginx-module 69 | 70 | An old but the good an article about nginx anatomy: 71 | http://www.evanmiller.org/nginx-modules-guide.html#filters-body 72 | 73 | NginX wiki: 74 | https://www.nginx.com/resources/wiki/ 75 | ======= 76 | Tutorials of development the nginx-modules 77 | 78 | Developed for a conference HighLoad++ 2016 79 | 80 | if you have any questions, then reach me using email mailto: dedok.mad@gmail.com 81 | -------------------------------------------------------------------------------- /body_filter.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | typedef struct { 7 | ngx_str_t word_delimiter; 8 | } ngx_http_example_body_conf_t; 9 | 10 | 11 | typedef struct { 12 | ngx_int_t words; 13 | } ngx_http_example_body_ctx_t; 14 | 15 | 16 | static void *ngx_http_example_body_create_conf(ngx_conf_t *cf); 17 | static char *ngx_http_example_body_merge_conf(ngx_conf_t *cf, void *parent, 18 | void *child); 19 | static ngx_int_t ngx_http_example_body_filter_init(ngx_conf_t *cf); 20 | 21 | 22 | static ngx_command_t ngx_http_example_body_commands[] = { 23 | 24 | { ngx_string("example_word_delimiter"), 25 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 26 | ngx_conf_set_str_slot, 27 | NGX_HTTP_LOC_CONF_OFFSET, 28 | offsetof(ngx_http_example_body_conf_t, word_delimiter), 29 | NULL }, 30 | 31 | ngx_null_command 32 | }; 33 | 34 | 35 | static ngx_http_module_t ngx_http_example_body_filter_module_ctx = { 36 | NULL, /* preconfiguration */ 37 | ngx_http_example_body_filter_init, /* postconfiguration */ 38 | 39 | NULL, /* create main configuration */ 40 | NULL, /* init main configuration */ 41 | 42 | NULL, /* create server configuration */ 43 | NULL, /* merge server configuration */ 44 | 45 | ngx_http_example_body_create_conf, /* create location configuration */ 46 | ngx_http_example_body_merge_conf /* merge location configuration */ 47 | }; 48 | 49 | 50 | ngx_module_t ngx_http_example_body_filter_module = { 51 | NGX_MODULE_V1, 52 | &ngx_http_example_body_filter_module_ctx, /* module context */ 53 | ngx_http_example_body_commands, /* module directives */ 54 | NGX_HTTP_MODULE, /* module type */ 55 | NULL, /* init master */ 56 | NULL, /* init module */ 57 | NULL, /* init process */ 58 | NULL, /* init thread */ 59 | NULL, /* exit thread */ 60 | NULL, /* exit process */ 61 | NULL, /* exit master */ 62 | NGX_MODULE_V1_PADDING 63 | }; 64 | 65 | 66 | static ngx_http_output_body_filter_pt ngx_http_next_body_filter; 67 | 68 | 69 | static ngx_int_t 70 | ngx_http_example_body_body_filter(ngx_http_request_t *r, ngx_chain_t *in) 71 | { 72 | size_t i, end; 73 | ngx_buf_t *b; 74 | ngx_chain_t *cl; 75 | ngx_http_example_body_ctx_t *ctx; 76 | ngx_http_example_body_conf_t *lc; 77 | 78 | if (in == NULL || r->header_only) { 79 | return ngx_http_next_body_filter(r, in); 80 | } 81 | 82 | ctx = ngx_http_get_module_ctx(r, ngx_http_example_body_filter_module); 83 | if (ctx == NULL) { 84 | 85 | ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_example_body_ctx_t)); 86 | if (ctx == NULL) { 87 | return NGX_ERROR; 88 | } 89 | 90 | ngx_http_set_ctx(r, ctx, ngx_http_example_body_filter_module); 91 | } 92 | 93 | lc = ngx_http_get_module_loc_conf(r, ngx_http_example_body_filter_module); 94 | 95 | for (cl = in; cl; cl = cl->next) { 96 | 97 | b = cl->buf; 98 | 99 | i = 0, end = b->last - b->pos; 100 | for (; i < end; ++i) { 101 | if (b->pos[i] == lc->word_delimiter.data[0]) { 102 | ++ctx->words; 103 | } 104 | } 105 | 106 | if (b->last_buf) { 107 | ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, 108 | "body_filter -> words: %d", ctx->words); 109 | ctx->words = 0; 110 | } 111 | } 112 | 113 | return ngx_http_next_body_filter(r, in); 114 | } 115 | 116 | 117 | static ngx_int_t 118 | ngx_http_example_body_filter_init(ngx_conf_t *cf) 119 | { 120 | ngx_http_next_body_filter = ngx_http_top_body_filter; 121 | ngx_http_top_body_filter = ngx_http_example_body_body_filter; 122 | 123 | return NGX_OK; 124 | } 125 | 126 | 127 | static void * 128 | ngx_http_example_body_create_conf(ngx_conf_t *cf) 129 | { 130 | ngx_http_example_body_conf_t *conf; 131 | 132 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_example_body_conf_t)); 133 | if (conf == NULL) { 134 | return NULL; 135 | } 136 | 137 | return conf; 138 | } 139 | 140 | 141 | static char * 142 | ngx_http_example_body_merge_conf(ngx_conf_t *cf, void *parent, void *child) 143 | { 144 | ngx_http_example_body_conf_t *prev = parent; 145 | ngx_http_example_body_conf_t *conf = child; 146 | 147 | ngx_conf_merge_str_value(conf->word_delimiter, 148 | prev->word_delimiter, ","); 149 | 150 | return NGX_CONF_OK; 151 | } 152 | -------------------------------------------------------------------------------- /conf/handlers.js: -------------------------------------------------------------------------------- 1 | 2 | // js_set 3 | function get_cache_key(r) { 4 | // See also: 5 | // http://nginx.org/en/docs/njs/reference.html#http 6 | // 7 | if (r.headersIn['Header-A']) { 8 | 9 | if (r.headersIn['Header-A'] == 'xyz') { 10 | r.return(500, "Sorry, bad request\n"); 11 | return ""; 12 | } 13 | 14 | return r.headersIn['Header-A'] + '_default_key'; 15 | } 16 | return 'default_key'; 17 | } 18 | 19 | 20 | // js_content 21 | function subrequests(r) { 22 | // See also: 23 | // 1. http://nginx.org/en/docs/njs/reference.html#http 24 | // 2. subrequests 25 | // 26 | var result = ''; 27 | 28 | r.subrequest('/a', null, function(resp) { 29 | result = resp.responseBody; 30 | 31 | r.subrequest('/b', null, function(resp) { 32 | result += "; " + resp.responseBody + '\n'; 33 | r.return(200, result); 34 | }); 35 | 36 | }); 37 | } 38 | 39 | 40 | // NJS + add_header 41 | function if_status_200(r) { 42 | if (r.status == 200) { 43 | return true; 44 | } 45 | return false; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /conf/nginx.conf: -------------------------------------------------------------------------------- 1 | daemon off; 2 | master_process off; 3 | #worker_processes 4; 4 | 5 | pid logs/nginx.pid; 6 | 7 | error_log logs/notice.log notice; 8 | error_log logs/info.log info; 9 | error_log logs/crit.log crit; 10 | error_log logs/debug.log debug; 11 | error_log stderr; 12 | 13 | events {} 14 | 15 | http { 16 | 17 | root www; 18 | 19 | access_log off; 20 | 21 | include mime.types; 22 | default_type application/octet-stream; 23 | 24 | client_body_buffer_size 10K; 25 | client_header_buffer_size 1k; 26 | client_max_body_size 8m; 27 | large_client_header_buffers 2 1k; 28 | 29 | server { 30 | listen 8081 default; 31 | server_name tnt_test; 32 | 33 | location /example { 34 | example_if_header "X-My-header"; 35 | example_set_header_value "My-Value"; 36 | example_word_delimiter ","; 37 | example_set_content " ...,word, second, last ... "; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /conf/njs_add_header.conf: -------------------------------------------------------------------------------- 1 | daemon off; #on; 2 | master_process off; #on; 3 | worker_processes 1; #2; 4 | 5 | pid logs/nginx.pid; 6 | 7 | error_log stderr info; 8 | 9 | events { } 10 | 11 | 12 | http { 13 | 14 | default_type application/octet-stream; 15 | 16 | proxy_cache_path 17 | /Users/vasilysoshnikov/workspace/tarantool-nginx-upstream-module/test-root/cache 18 | levels=1:2 keys_zone=cache:1m max_size=10m 19 | inactive=60m use_temp_path=off; 20 | 21 | js_include handlers.js; 22 | js_set $js_cache_key get_cache_key; 23 | 24 | # Example: js_content 25 | server { 26 | 27 | listen 127.0.0.1:8082 default; 28 | 29 | location = / { 30 | js_content subrequests; 31 | } 32 | 33 | location = /a { 34 | return 200 "Location-A"; 35 | } 36 | 37 | location = /b { 38 | return 200 "Location-B"; 39 | } 40 | 41 | location = /c { 42 | return 204 "Location-B"; 43 | } 44 | } 45 | 46 | # Example: add_header + NJS 47 | # Testing: 48 | # 49 | # 1. curl -vvv 127.0.0.1:8083/c 50 | # "My-Header" should not be added 51 | # 2. Any other request 52 | # "My-Header" should be added 53 | # 54 | js_ex_include handlers.js; 55 | 56 | server { 57 | 58 | listen 127.0.0.1:8083 default; 59 | 60 | location / { 61 | 62 | add_header "My-Header" "My-Value" always if_status_200; 63 | proxy_pass http://127.0.0.1:8082; 64 | } 65 | 66 | } 67 | 68 | } 69 | 70 | -------------------------------------------------------------------------------- /conf/njs_nginx.conf: -------------------------------------------------------------------------------- 1 | daemon off; #on; 2 | master_process off; #on; 3 | worker_processes 1; #2; 4 | 5 | pid logs/nginx.pid; 6 | 7 | error_log stderr info; 8 | 9 | events { } 10 | 11 | 12 | http { 13 | 14 | default_type application/octet-stream; 15 | 16 | proxy_cache_path 17 | /Users/vasilysoshnikov/workspace/tarantool-nginx-upstream-module/test-root/cache 18 | levels=1:2 keys_zone=cache:1m max_size=10m 19 | inactive=60m use_temp_path=off; 20 | 21 | js_include handlers.js; 22 | js_set $js_cache_key get_cache_key; 23 | 24 | # Example: js_content 25 | server { 26 | 27 | listen 127.0.0.1:8082 default; 28 | 29 | location = / { 30 | js_content subrequests; 31 | } 32 | 33 | location = /a { 34 | return 200 "Location-A"; 35 | } 36 | 37 | location = /b { 38 | return 200 "Location-B"; 39 | } 40 | 41 | location = /c { 42 | return 204 "Location-B"; 43 | } 44 | } 45 | 46 | # Example: js_set 47 | server { 48 | 49 | listen 127.0.0.1:8081 default; 50 | 51 | location = /js_set { 52 | 53 | proxy_cache cache; 54 | 55 | # You probably wish to have complex cache key sometimes. 56 | # This is the best case for NJS, you could have a very complex 57 | # evaluation 58 | proxy_cache_key $js_cache_key; 59 | proxy_cache_valid any 1m; 60 | 61 | add_header X-Cache-Status $upstream_cache_status; 62 | proxy_pass http://127.0.0.1:8082/; 63 | } 64 | 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ## header filter 2 | ngx_module_type=HTTP_AUX_FILTER 3 | ngx_module_name=ngx_http_example_header_filter_module 4 | ngx_module_srcs=$ngx_addon_dir/header_filter.c 5 | . auto/module 6 | 7 | ## body filter 8 | ngx_module_type=HTTP_FILTER 9 | ngx_module_name=ngx_http_example_body_filter_module 10 | ngx_module_srcs=$ngx_addon_dir/body_filter.c 11 | . auto/module 12 | 13 | ## handler 14 | ngx_module_type=HTTP 15 | ngx_module_name=ngx_http_content_handler_module 16 | ngx_module_srcs=$ngx_addon_dir/content_handler.c 17 | . auto/module 18 | 19 | ## upstream 20 | -------------------------------------------------------------------------------- /content_handler.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | typedef struct { 7 | ngx_str_t content; 8 | } ngx_http_content_loc_conf_t; 9 | 10 | 11 | static ngx_int_t ngx_http_content_init(ngx_conf_t *cf); 12 | 13 | static void* ngx_http_content_create_loc_conf(ngx_conf_t *cf); 14 | static char* ngx_http_content_merge_loc_conf(ngx_conf_t *cf, 15 | void *parent, void *child); 16 | 17 | 18 | static ngx_command_t ngx_http_content_commands[] = { 19 | 20 | { ngx_string("example_set_content"), 21 | NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 22 | ngx_conf_set_str_slot, 23 | NGX_HTTP_LOC_CONF_OFFSET, 24 | offsetof(ngx_http_content_loc_conf_t, content), 25 | NULL }, 26 | 27 | ngx_null_command 28 | }; 29 | 30 | 31 | static ngx_http_module_t ngx_http_content_module_ctx = { 32 | NULL, /* preconfiguration */ 33 | ngx_http_content_init, /* postconfiguration */ 34 | 35 | NULL, /* create main configuration */ 36 | NULL, /* init main configuration */ 37 | 38 | NULL, /* create server configuration */ 39 | NULL, /* merge server configuration */ 40 | 41 | ngx_http_content_create_loc_conf, /* create location configuration */ 42 | ngx_http_content_merge_loc_conf /* merge location configuration */ 43 | }; 44 | 45 | 46 | ngx_module_t ngx_http_content_handler_module = { 47 | NGX_MODULE_V1, 48 | &ngx_http_content_module_ctx, /* module context */ 49 | ngx_http_content_commands, /* module directives */ 50 | NGX_HTTP_MODULE, /* module type */ 51 | NULL, /* init master */ 52 | NULL, /* init module */ 53 | NULL, /* init process */ 54 | NULL, /* init thread */ 55 | NULL, /* exit thread */ 56 | NULL, /* exit process */ 57 | NULL, /* exit master */ 58 | NGX_MODULE_V1_PADDING 59 | }; 60 | 61 | /** 62 | * Module entry point 63 | */ 64 | static ngx_int_t 65 | ngx_http_content_handler(ngx_http_request_t *r) 66 | { 67 | ngx_buf_t *b; 68 | ngx_int_t rc; 69 | ngx_chain_t out; 70 | ngx_http_content_loc_conf_t *lc; 71 | 72 | lc = ngx_http_get_module_loc_conf(r, ngx_http_content_handler_module); 73 | 74 | if (lc->content.data == NULL || lc->content.len == 0) { 75 | return NGX_DECLINED; 76 | } 77 | 78 | rc = ngx_http_discard_request_body(r); 79 | if (rc != NGX_OK && rc != NGX_AGAIN) { 80 | return rc; 81 | } 82 | 83 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); 84 | if (b == NULL) { 85 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 86 | } 87 | 88 | r->headers_out.status = NGX_HTTP_OK; 89 | r->headers_out.content_length_n = lc->content.len; 90 | r->headers_out.content_type.len = sizeof("plain/text") - 1; 91 | r->headers_out.content_type.data = (u_char *) "plain/text"; 92 | 93 | rc = ngx_http_send_header(r); 94 | if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { 95 | return rc; 96 | } 97 | 98 | b->pos = b->start = lc->content.data; 99 | b->end = b->last = lc->content.data + lc->content.len; 100 | b->last_buf = b->memory = 1; 101 | 102 | out.buf = b; 103 | out.next = NULL; 104 | 105 | return ngx_http_output_filter(r, &out); 106 | } 107 | 108 | 109 | static ngx_int_t 110 | ngx_http_content_init(ngx_conf_t *cf) 111 | { 112 | ngx_http_handler_pt *h; 113 | ngx_http_core_main_conf_t *cmcf; 114 | 115 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 116 | 117 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); 118 | if (h == NULL) { 119 | return NGX_ERROR; 120 | } 121 | 122 | *h = ngx_http_content_handler; 123 | 124 | return NGX_OK; 125 | } 126 | 127 | 128 | static void * 129 | ngx_http_content_create_loc_conf(ngx_conf_t *cf) 130 | { 131 | (void)cf; 132 | 133 | ngx_http_content_loc_conf_t *conf; 134 | 135 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_content_loc_conf_t)); 136 | if (conf == NULL) { 137 | return NGX_CONF_ERROR; 138 | } 139 | 140 | return conf; 141 | } 142 | 143 | 144 | static char * 145 | ngx_http_content_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 146 | { 147 | (void)cf; 148 | 149 | ngx_http_content_loc_conf_t *prev = parent; 150 | ngx_http_content_loc_conf_t *conf = child; 151 | 152 | ngx_conf_merge_str_value(conf->content, prev->content, ""); 153 | 154 | return NGX_CONF_OK; 155 | } 156 | 157 | -------------------------------------------------------------------------------- /header_filter.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | typedef struct { 7 | ngx_str_t cond; 8 | ngx_str_t value; 9 | } ngx_http_example_header_filter_loc_conf_t; 10 | 11 | 12 | static ngx_int_t ngx_http_example_header_filter_init(ngx_conf_t *cf); 13 | static void* ngx_http_example_header_filter_create_loc_conf(ngx_conf_t *cf); 14 | static char* ngx_http_example_header_filter_merge_loc_conf(ngx_conf_t *cf, 15 | void *parent, void *child); 16 | 17 | static ngx_int_t ngx_header_get(ngx_http_request_t *r, 18 | const ngx_str_t *header_name, 19 | ngx_str_t *k, ngx_str_t *v); 20 | static ngx_int_t ngx_header_set(ngx_http_request_t *r, 21 | const ngx_str_t *n, const ngx_str_t *v); 22 | 23 | 24 | static ngx_command_t ngx_http_example_header_filter_commands[] = { 25 | 26 | { ngx_string("example_if_header"), 27 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 28 | ngx_conf_set_str_slot, 29 | NGX_HTTP_LOC_CONF_OFFSET, 30 | offsetof(ngx_http_example_header_filter_loc_conf_t, cond), 31 | NULL }, 32 | 33 | { ngx_string("example_set_header_value"), 34 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 35 | ngx_conf_set_str_slot, 36 | NGX_HTTP_LOC_CONF_OFFSET, 37 | offsetof(ngx_http_example_header_filter_loc_conf_t, value), 38 | NULL }, 39 | 40 | ngx_null_command 41 | }; 42 | 43 | 44 | static ngx_http_module_t ngx_http_example_header_filter_ctx = { 45 | NULL, /* preconfiguration */ 46 | ngx_http_example_header_filter_init, /* postconfiguration */ 47 | 48 | NULL, /* create main configuration */ 49 | NULL, /* init main configuration */ 50 | 51 | NULL, /* create server configuration */ 52 | NULL, /* merge server configuration */ 53 | 54 | ngx_http_example_header_filter_create_loc_conf, /* create location configuration */ 55 | ngx_http_example_header_filter_merge_loc_conf /* merge location configuration */ 56 | }; 57 | 58 | 59 | ngx_module_t ngx_http_example_header_filter_module = { 60 | NGX_MODULE_V1, 61 | &ngx_http_example_header_filter_ctx, /* module context */ 62 | ngx_http_example_header_filter_commands, /* module directives */ 63 | NGX_HTTP_MODULE, /* module type */ 64 | NULL, /* init master */ 65 | NULL, /* init module */ 66 | NULL, /* init process */ 67 | NULL, /* init thread */ 68 | NULL, /* exit thread */ 69 | NULL, /* exit process */ 70 | NULL, /* exit master */ 71 | NGX_MODULE_V1_PADDING 72 | }; 73 | 74 | 75 | static ngx_http_output_header_filter_pt ngx_http_next_header_filter; 76 | 77 | 78 | static ngx_int_t 79 | ngx_http_example_header_filter(ngx_http_request_t *r) 80 | { 81 | ngx_int_t rc; 82 | ngx_http_example_header_filter_loc_conf_t *lc; 83 | ngx_str_t k = { .data = NULL, .len = 0 }, 84 | v = { .data = NULL, .len = 0 }; 85 | 86 | lc = ngx_http_get_module_loc_conf(r, ngx_http_example_header_filter_module); 87 | 88 | if (r != r->main || !lc->cond.data) { 89 | return ngx_http_next_header_filter(r); 90 | } 91 | 92 | rc = ngx_header_get(r, &lc->cond, &k, &v); 93 | if (rc == NGX_ERROR) { 94 | return rc; 95 | } 96 | 97 | if (k.data == NULL) { 98 | rc = ngx_header_set(r, &lc->cond, &lc->value); 99 | if (rc == NGX_ERROR) { 100 | return rc; 101 | } 102 | } 103 | 104 | return ngx_http_next_header_filter(r); 105 | } 106 | 107 | 108 | static void * 109 | ngx_http_example_header_filter_create_loc_conf(ngx_conf_t *cf) 110 | { 111 | (void)cf; 112 | 113 | ngx_http_example_header_filter_loc_conf_t *conf; 114 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_example_header_filter_loc_conf_t)); 115 | if (conf == NULL) { 116 | return NGX_CONF_ERROR; 117 | } 118 | 119 | return conf; 120 | } 121 | 122 | 123 | static char * 124 | ngx_http_example_header_filter_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 125 | { 126 | (void)cf; 127 | 128 | ngx_http_example_header_filter_loc_conf_t *prev = parent; 129 | ngx_http_example_header_filter_loc_conf_t *conf = child; 130 | 131 | ngx_conf_merge_str_value(conf->cond, prev->cond, ""); 132 | ngx_conf_merge_str_value(conf->value, prev->value, ""); 133 | 134 | return NGX_CONF_OK; 135 | } 136 | 137 | 138 | static ngx_int_t 139 | ngx_http_example_header_filter_init(ngx_conf_t *cf) 140 | { 141 | (void)cf; 142 | 143 | ngx_http_next_header_filter = ngx_http_top_header_filter; 144 | ngx_http_top_header_filter = ngx_http_example_header_filter; 145 | 146 | return NGX_OK; 147 | } 148 | 149 | static ngx_int_t 150 | ngx_header_get(ngx_http_request_t *r, const ngx_str_t *header_name, 151 | ngx_str_t* k, ngx_str_t *v) 152 | { 153 | ngx_table_elt_t *h; 154 | ngx_list_part_t *part; 155 | 156 | part = &r->headers_in.headers.part; 157 | h = part->elts; 158 | 159 | size_t i = 0; 160 | for (;; i++) { 161 | 162 | if (i >= part->nelts) { 163 | if (part->next == NULL) { 164 | break; 165 | } 166 | part = part->next; 167 | h = part->elts; 168 | i = 0; 169 | } 170 | 171 | if (ngx_strncmp(h[i].key.data, header_name->data, header_name->len) 172 | == 0) 173 | { 174 | k->data = h[i].key.data; 175 | k->len = h[i].key.len; 176 | v->data = h[i].value.data; 177 | v->len = h[i].value.len; 178 | break; 179 | } 180 | } 181 | 182 | return NGX_OK; 183 | } 184 | 185 | 186 | static ngx_int_t 187 | ngx_header_set(ngx_http_request_t *r, const ngx_str_t *n, const ngx_str_t *v) 188 | { 189 | ngx_table_elt_t *header; 190 | u_char *name, *name_end, *value, *value_end; 191 | 192 | name = ngx_pnalloc(r->pool, n->len); 193 | if (name == NULL) { 194 | return NGX_ERROR; 195 | } 196 | 197 | value = ngx_pnalloc(r->pool, v->len); 198 | if (value == NULL) { 199 | return NGX_ERROR; 200 | } 201 | 202 | name_end = ngx_copy(name, n->data, n->len); 203 | value_end = ngx_copy(value, v->data, v->len); 204 | 205 | header = ngx_list_push(&r->headers_out.headers); 206 | if (header == NULL) { 207 | return NGX_ERROR; 208 | } 209 | 210 | header->hash = 1; 211 | header->key.data = name; 212 | header->key.len = name_end - name; 213 | header->value.data = value; 214 | header->value.len = value_end - value; 215 | 216 | return NGX_OK; 217 | } 218 | 219 | -------------------------------------------------------------------------------- /njs_add_header_module.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c 2 | index a4c8cc26..d99558c6 100644 3 | --- a/src/http/modules/ngx_http_headers_filter_module.c 4 | +++ b/src/http/modules/ngx_http_headers_filter_module.c 5 | @@ -9,6 +9,30 @@ 6 | #include 7 | #include 8 | 9 | +#include 10 | + 11 | +/*** Added as example to show embedded NJS {{{ */ 12 | +/** Copy from NJS module sources {{{ */ 13 | +typedef struct { 14 | + njs_vm_t *vm; 15 | + ngx_array_t *paths; 16 | + const njs_extern_t *req_proto; 17 | +} ngx_http_js_main_conf_t; 18 | + 19 | +typedef struct { 20 | + njs_vm_t *vm; 21 | + ngx_log_t *log; 22 | + njs_opaque_value_t request; 23 | +} ngx_http_js_ctx_t; 24 | + 25 | +static void *ngx_http_js_create_main_conf(ngx_conf_t *cf); 26 | +static char *ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, 27 | + void *conf); 28 | +static void ngx_http_js_cleanup_vm(void *data); 29 | +/** }}} */ 30 | + 31 | +static ngx_int_t ngx_http_js_ex_call(ngx_http_request_t *r, ngx_str_t *fname); 32 | +/** }}} */ 33 | 34 | typedef struct ngx_http_header_val_s ngx_http_header_val_t; 35 | 36 | @@ -29,6 +53,9 @@ struct ngx_http_header_val_s { 37 | ngx_http_set_header_pt handler; 38 | ngx_uint_t offset; 39 | ngx_uint_t always; /* unsigned always:1 */ 40 | + /** Added as example of embedded NJS {{{ */ 41 | + ngx_str_t js_fname; 42 | + /** }} */ 43 | }; 44 | 45 | 46 | @@ -74,7 +101,6 @@ static char *ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, 47 | static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, 48 | void *conf); 49 | 50 | - 51 | static ngx_http_set_header_t ngx_http_set_headers[] = { 52 | 53 | { ngx_string("Cache-Control"), 54 | @@ -99,6 +125,15 @@ static ngx_http_set_header_t ngx_http_set_headers[] = { 55 | 56 | static ngx_command_t ngx_http_headers_filter_commands[] = { 57 | 58 | + /** Added as example of embedded NJS {{{ */ 59 | + { ngx_string("js_ex_include"), 60 | + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, 61 | + ngx_http_js_include, 62 | + NGX_HTTP_MAIN_CONF_OFFSET, 63 | + 0, 64 | + NULL }, 65 | + /** }}} */ 66 | + 67 | { ngx_string("expires"), 68 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF 69 | |NGX_CONF_TAKE12, 70 | @@ -109,7 +144,7 @@ static ngx_command_t ngx_http_headers_filter_commands[] = { 71 | 72 | { ngx_string("add_header"), 73 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF 74 | - |NGX_CONF_TAKE23, 75 | + |(NGX_CONF_TAKE23|NGX_CONF_TAKE4), 76 | ngx_http_headers_add, 77 | NGX_HTTP_LOC_CONF_OFFSET, 78 | offsetof(ngx_http_headers_conf_t, headers), 79 | @@ -131,7 +166,9 @@ static ngx_http_module_t ngx_http_headers_filter_module_ctx = { 80 | NULL, /* preconfiguration */ 81 | ngx_http_headers_filter_init, /* postconfiguration */ 82 | 83 | - NULL, /* create main configuration */ 84 | + /** Copy from NJS moduke {{{ */ 85 | + ngx_http_js_create_main_conf, /* create main configuration */ 86 | + /** }}} */ 87 | NULL, /* init main configuration */ 88 | 89 | NULL, /* create server configuration */ 90 | @@ -217,6 +254,16 @@ ngx_http_headers_filter(ngx_http_request_t *r) 91 | continue; 92 | } 93 | 94 | + /** Added as example of embedded NJS {{{ */ 95 | + ngx_int_t rc = ngx_http_js_ex_call(r, &h->js_fname); 96 | + if (rc == NGX_ERROR) { 97 | + return NGX_ERROR; 98 | + } 99 | + else if (rc == NGX_DECLINED) { 100 | + continue; 101 | + } 102 | + /** }}} */ 103 | + 104 | if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) { 105 | return NGX_ERROR; 106 | } 107 | @@ -863,5 +910,376 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 108 | 109 | hv->always = 1; 110 | 111 | + /** Added as example of embedded NJS {{{ */ 112 | + if (cf->args->nelts == 4) { 113 | + return NGX_CONF_OK; 114 | + } 115 | + 116 | + hv->js_fname.data = value[4].data; 117 | + hv->js_fname.len = value[4].len; 118 | + /** }}} */ 119 | + 120 | return NGX_CONF_OK; 121 | } 122 | + 123 | +/** Added as example of embedded NJS {{{ */ 124 | +static njs_int_t 125 | +ngx_http_js_ext_get_status(njs_vm_t *vm, njs_value_t *value, void *obj, 126 | + uintptr_t data) 127 | +{ 128 | + ngx_http_request_t *r; 129 | + 130 | + r = (ngx_http_request_t *) obj; 131 | + 132 | + njs_value_number_set(value, r->headers_out.status); 133 | + 134 | + return NJS_OK; 135 | +} 136 | + 137 | +static njs_external_t ngx_http_js_ext_request[] = { 138 | + 139 | + { njs_str("status"), 140 | + NJS_EXTERN_PROPERTY, 141 | + NULL, 142 | + 0, 143 | + ngx_http_js_ext_get_status, 144 | + NULL, 145 | + NULL, 146 | + NULL, 147 | + NULL, 148 | + NULL, 149 | + offsetof(ngx_http_request_t, headers_out.status) }, 150 | + }; 151 | + 152 | +static njs_external_t ngx_http_js_externals[] = { 153 | + 154 | + { njs_str("request"), 155 | + NJS_EXTERN_OBJECT, 156 | + ngx_http_js_ext_request, 157 | + njs_nitems(ngx_http_js_ext_request), 158 | + NULL, 159 | + NULL, 160 | + NULL, 161 | + NULL, 162 | + NULL, 163 | + NULL, 164 | + 0 }, 165 | +}; 166 | + 167 | +static char * 168 | +ngx_http_js_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 169 | +{ 170 | + ngx_http_js_main_conf_t *jmcf = conf; 171 | + 172 | + size_t size; 173 | + u_char *start, *end; 174 | + ssize_t n; 175 | + ngx_fd_t fd; 176 | + ngx_str_t *m, *value, file; 177 | + njs_int_t rc; 178 | + njs_str_t text, path; 179 | + ngx_uint_t i; 180 | + njs_vm_opt_t options; 181 | + ngx_file_info_t fi; 182 | + ngx_pool_cleanup_t *cln; 183 | + 184 | + if (jmcf->vm) { 185 | + return "is duplicate"; 186 | + } 187 | + 188 | + value = cf->args->elts; 189 | + file = value[1]; 190 | + 191 | + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { 192 | + return NGX_CONF_ERROR; 193 | + } 194 | + 195 | + fd = ngx_open_file(file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); 196 | + if (fd == NGX_INVALID_FILE) { 197 | + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, 198 | + ngx_open_file_n " \"%s\" failed", file.data); 199 | + return NGX_CONF_ERROR; 200 | + } 201 | + 202 | + if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { 203 | + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, 204 | + ngx_fd_info_n " \"%s\" failed", file.data); 205 | + (void) ngx_close_file(fd); 206 | + return NGX_CONF_ERROR; 207 | + } 208 | + 209 | + size = ngx_file_size(&fi); 210 | + 211 | + start = ngx_pnalloc(cf->pool, size); 212 | + if (start == NULL) { 213 | + (void) ngx_close_file(fd); 214 | + return NGX_CONF_ERROR; 215 | + } 216 | + 217 | + n = ngx_read_fd(fd, start, size); 218 | + 219 | + if (n == -1) { 220 | + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, 221 | + ngx_read_fd_n " \"%s\" failed", file.data); 222 | + 223 | + (void) ngx_close_file(fd); 224 | + return NGX_CONF_ERROR; 225 | + } 226 | + 227 | + if ((size_t) n != size) { 228 | + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 229 | + ngx_read_fd_n " has read only %z of %O from \"%s\"", 230 | + n, size, file.data); 231 | + 232 | + (void) ngx_close_file(fd); 233 | + return NGX_CONF_ERROR; 234 | + } 235 | + 236 | + if (ngx_close_file(fd) == NGX_FILE_ERROR) { 237 | + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, 238 | + ngx_close_file_n " %s failed", file.data); 239 | + } 240 | + 241 | + end = start + size; 242 | + 243 | + ngx_memzero(&options, sizeof(njs_vm_opt_t)); 244 | + 245 | + options.backtrace = 1; 246 | + options.ops = NULL; 247 | + options.argv = ngx_argv; 248 | + options.argc = ngx_argc; 249 | + 250 | + file = value[1]; 251 | + options.file.start = file.data; 252 | + options.file.length = file.len; 253 | + 254 | + jmcf->vm = njs_vm_create(&options); 255 | + if (jmcf->vm == NULL) { 256 | + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to create JS VM"); 257 | + return NGX_CONF_ERROR; 258 | + } 259 | + 260 | + cln = ngx_pool_cleanup_add(cf->pool, 0); 261 | + if (cln == NULL) { 262 | + return NGX_CONF_ERROR; 263 | + } 264 | + 265 | + cln->handler = ngx_http_js_cleanup_vm; 266 | + cln->data = jmcf->vm; 267 | + 268 | + path.start = ngx_cycle->prefix.data; 269 | + path.length = ngx_cycle->prefix.len; 270 | + 271 | + rc = njs_vm_add_path(jmcf->vm, &path); 272 | + if (rc != NJS_OK) { 273 | + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to add path"); 274 | + return NGX_CONF_ERROR; 275 | + } 276 | + 277 | + if (jmcf->paths != NGX_CONF_UNSET_PTR) { 278 | + m = jmcf->paths->elts; 279 | + 280 | + for (i = 0; i < jmcf->paths->nelts; i++) { 281 | + if (ngx_conf_full_name(cf->cycle, &m[i], 0) != NGX_OK) { 282 | + return NGX_CONF_ERROR; 283 | + } 284 | + 285 | + path.start = m[i].data; 286 | + path.length = m[i].len; 287 | + 288 | + rc = njs_vm_add_path(jmcf->vm, &path); 289 | + if (rc != NJS_OK) { 290 | + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to add path"); 291 | + return NGX_CONF_ERROR; 292 | + } 293 | + } 294 | + } 295 | + 296 | + jmcf->req_proto = njs_vm_external_prototype(jmcf->vm, 297 | + &ngx_http_js_externals[0]); 298 | + if (jmcf->req_proto == NULL) { 299 | + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "failed to add request proto"); 300 | + return NGX_CONF_ERROR; 301 | + } 302 | + 303 | + rc = njs_vm_compile(jmcf->vm, &start, end); 304 | + 305 | + if (rc != NJS_OK) { 306 | + njs_vm_retval_string(jmcf->vm, &text); 307 | + 308 | + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 309 | + "%*s, included", 310 | + text.length, text.start); 311 | + return NGX_CONF_ERROR; 312 | + } 313 | + 314 | + if (start != end) { 315 | + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 316 | + "extra characters in js script: \"%*s\", included", 317 | + end - start, start); 318 | + return NGX_CONF_ERROR; 319 | + } 320 | + 321 | + return NGX_CONF_OK; 322 | +} 323 | + 324 | +static void 325 | +ngx_http_js_cleanup_vm(void *data) 326 | +{ 327 | + njs_vm_t *vm = data; 328 | + 329 | + njs_vm_destroy(vm); 330 | +} 331 | + 332 | +static void 333 | +ngx_http_js_cleanup_ctx(void *data) 334 | +{ 335 | + ngx_http_js_ctx_t *ctx = data; 336 | + 337 | + if (njs_vm_pending(ctx->vm)) { 338 | + ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "pending events"); 339 | + } 340 | + 341 | + njs_vm_destroy(ctx->vm); 342 | +} 343 | + 344 | + 345 | +static ngx_int_t 346 | +ngx_http_header_js_init_vm(ngx_http_request_t *r) 347 | +{ 348 | + njs_int_t rc; 349 | + njs_str_t exception; 350 | + ngx_http_js_ctx_t *ctx; 351 | + ngx_pool_cleanup_t *cln; 352 | + ngx_http_js_main_conf_t *jmcf; 353 | + 354 | + jmcf = ngx_http_get_module_main_conf(r, ngx_http_headers_filter_module); 355 | + if (jmcf->vm == NULL) { 356 | + return NGX_DECLINED; 357 | + } 358 | + 359 | + ctx = ngx_http_get_module_ctx(r, ngx_http_headers_filter_module); 360 | + 361 | + if (ctx == NULL) { 362 | + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_js_ctx_t)); 363 | + if (ctx == NULL) { 364 | + return NGX_ERROR; 365 | + } 366 | + 367 | + ngx_http_set_ctx(r, ctx, ngx_http_headers_filter_module); 368 | + } 369 | + 370 | + if (ctx->vm) { 371 | + return NGX_OK; 372 | + } 373 | + 374 | + ctx->vm = njs_vm_clone(jmcf->vm, r); 375 | + if (ctx->vm == NULL) { 376 | + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "can't clone"); 377 | + return NGX_ERROR; 378 | + } 379 | + 380 | + cln = ngx_pool_cleanup_add(r->pool, 0); 381 | + if (cln == NULL) { 382 | + return NGX_ERROR; 383 | + } 384 | + 385 | + ctx->log = r->connection->log; 386 | + 387 | + cln->handler = ngx_http_js_cleanup_ctx; 388 | + cln->data = ctx; 389 | + 390 | + if (njs_vm_start(ctx->vm) == NJS_ERROR) { 391 | + njs_vm_retval_string(ctx->vm, &exception); 392 | + 393 | + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 394 | + "js exception: %*s", exception.length, exception.start); 395 | + 396 | + return NGX_ERROR; 397 | + } 398 | + 399 | + rc = njs_vm_external_create(ctx->vm, njs_value_arg(&ctx->request), 400 | + jmcf->req_proto, r); 401 | + if (rc != NJS_OK) { 402 | + return NGX_ERROR; 403 | + } 404 | + 405 | + return NGX_OK; 406 | +} 407 | + 408 | +/** Added as example of embedded NJS {{{ */ 409 | +static ngx_int_t 410 | +ngx_http_js_ex_call(ngx_http_request_t *r, ngx_str_t *fname) 411 | +{ 412 | + ngx_int_t rc; 413 | + njs_str_t name, value, exception; 414 | + njs_function_t *func; 415 | + ngx_http_js_ctx_t *ctx; 416 | + 417 | + rc = ngx_http_header_js_init_vm(r); 418 | + 419 | + if (rc == NGX_ERROR) { 420 | + return NGX_ERROR; 421 | + } 422 | + 423 | + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 424 | + "http js variable call \"%V\"", fname); 425 | + 426 | + ctx = ngx_http_get_module_ctx(r, ngx_http_headers_filter_module); 427 | + 428 | + name.start = fname->data; 429 | + name.length = fname->len; 430 | + 431 | + func = njs_vm_function(ctx->vm, &name); 432 | + if (func == NULL) { 433 | + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 434 | + "js function \"%V\" not found", fname); 435 | + return NGX_OK; 436 | + } 437 | + 438 | + if (njs_vm_call(ctx->vm, func, njs_value_arg(&ctx->request), 1) 439 | + != NJS_OK) 440 | + { 441 | + njs_vm_retval_string(ctx->vm, &exception); 442 | + 443 | + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 444 | + "js exception: %*s", exception.length, exception.start); 445 | + return NGX_OK; 446 | + } 447 | + 448 | + if (njs_vm_retval_string(ctx->vm, &value) != NJS_OK) { 449 | + return NGX_ERROR; 450 | + } 451 | + 452 | + if (value.length == sizeof("false") - 1 && 453 | + ngx_strncmp(value.start, "false", sizeof("false") - 1) == 0) 454 | + { 455 | + return NGX_DECLINED; 456 | + } 457 | + 458 | + return NGX_OK; 459 | +} 460 | + 461 | +/** Copy from NJS module sources */ 462 | +static void * 463 | +ngx_http_js_create_main_conf(ngx_conf_t *cf) 464 | +{ 465 | + ngx_http_js_main_conf_t *conf; 466 | + 467 | + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_js_main_conf_t)); 468 | + if (conf == NULL) { 469 | + return NULL; 470 | + } 471 | + 472 | + /* 473 | + * set by ngx_pcalloc(): 474 | + * 475 | + * conf->vm = NULL; 476 | + * conf->req_proto = NULL; 477 | + */ 478 | + 479 | + conf->paths = NGX_CONF_UNSET_PTR; 480 | + 481 | + return conf; 482 | +} 483 | +/** }}} */ 484 | --------------------------------------------------------------------------------