├── config ├── LICENSE ├── t └── 001-ngx-lfqueue-enq-deq-test.t ├── README.md └── src └── ngx_http_lfqueue_module.c /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_lfqueue_module 2 | 3 | HTTP_LFQUEUE_SRCS="$ngx_addon_dir/src/ngx_http_lfqueue_module.c \ 4 | " 5 | 6 | HTTP_LFQUEUE_DEPS="$NGX_ADDON_DEPS \ 7 | " 8 | 9 | 10 | ngx_feature="ngx_http_lfqueue" 11 | ngx_feature_name="NGX_HAVE_LFQUEUE_HEADERS" 12 | ngx_feature_run=no 13 | ngx_feature_incs="#include " 14 | ngx_feature_path= 15 | ngx_feature_libs="-llfqueue" 16 | # ngx_feature_exit_if_not_found=yes 17 | ngx_feature_test="lfqueue_t q; lfqueue_init(&q);" 18 | . auto/feature 19 | 20 | if [ $ngx_found != yes ]; then 21 | echo "lfqueue library not found, this module depends on lfqueue. install library via https://github.com/Taymindis/lfqueue " 22 | echo 23 | exit 1 24 | fi 25 | 26 | if test -n "$ngx_module_link"; then 27 | ngx_module_type=HTTP 28 | ngx_module_name=$ngx_addon_name 29 | ngx_module_deps="$HTTP_LFQUEUE_DEPS" 30 | ngx_module_srcs="$HTTP_LFQUEUE_SRCS" 31 | #ngx_module_libs=" -rdynamic -ldl -lpthread -lpcre -Wl,-E" 32 | ngx_module_libs="-ldl -lm -lpthread -lpcre -llfqueue" 33 | #ngx_module_libs="-lunwind-coredump -lunwind-generic -lunwind-ptrace -lunwind" 34 | 35 | . auto/module 36 | else 37 | HTTP_MODULES="$HTTP_MODULES ngx_http_lfqueue_module" 38 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $HTTP_LFQUEUE_SRCS" 39 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS $HTTP_LFQUEUE_DEPS" 40 | fi -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2018, Taymindis Woon 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /t/001-ngx-lfqueue-enq-deq-test.t: -------------------------------------------------------------------------------- 1 | # vi:filetype=perl 2 | 3 | use lib '/home/booking/nginx_build/test-nginx/inc'; 4 | use lib '/home/booking/nginx_build/test-nginx/lib'; 5 | use Test::Nginx::Socket 'no_plan'; 6 | 7 | 8 | our $http_config = <<'_EOC_'; 9 | ngx_lfqueue_memory_allocate 10m; 10 | ngx_lfqueue_name q1; 11 | ngx_lfqueue_name q2; 12 | ngx_lfqueue_name q3; 13 | ngx_lfqueue_backup "|@|" /tmp/ngx_lfqueue_data.txt; 14 | _EOC_ 15 | 16 | no_shuffle(); 17 | run_tests(); 18 | 19 | 20 | __DATA__ 21 | 22 | 23 | 24 | === TEST 1: enqueue q1 25 | --- http_config eval: $::http_config 26 | --- config 27 | location /processQueue { 28 | ngx_lfqueue_target $arg_target; 29 | } 30 | --- request 31 | POST /processQueue?target=q1 32 | {"data":"MESSAGE1"} 33 | --- error_code: 202 34 | --- timeout: 3 35 | --- response_headers 36 | Content-Type: text/plain 37 | 38 | 39 | 40 | 41 | === TEST 2: enqueue q1 second time 42 | --- http_config eval: $::http_config 43 | --- config 44 | location /processQueue { 45 | ngx_lfqueue_target $arg_target; 46 | } 47 | --- request 48 | POST /processQueue?target=q1 49 | {"data":"MESSAGE2"} 50 | --- error_code: 202 51 | --- timeout: 3 52 | --- response_headers 53 | Content-Type: text/plain 54 | 55 | 56 | === TEST 1: enqueue q2 57 | --- http_config eval: $::http_config 58 | --- config 59 | location /processQueue { 60 | ngx_lfqueue_target $arg_target; 61 | } 62 | --- request 63 | POST /processQueue?target=q2 64 | {"data":"MESSAGE1"} 65 | --- error_code: 202 66 | --- timeout: 3 67 | --- response_headers 68 | Content-Type: text/plain 69 | 70 | 71 | 72 | === TEST 1: enqueue q3 73 | --- http_config eval: $::http_config 74 | --- config 75 | location /processQueue { 76 | ngx_lfqueue_target $arg_target; 77 | } 78 | --- request 79 | POST /processQueue?target=q3 80 | {"data":"MESSAGE1"} 81 | --- error_code: 202 82 | --- timeout: 3 83 | --- response_headers 84 | Content-Type: text/plain 85 | 86 | 87 | 88 | 89 | === TEST 2: enqueue q3 second time 90 | --- http_config eval: $::http_config 91 | --- config 92 | location /processQueue { 93 | ngx_lfqueue_target $arg_target; 94 | } 95 | --- request 96 | POST /processQueue?target=q3 97 | {"data":"MESSAGE2"} 98 | --- error_code: 202 99 | --- timeout: 3 100 | --- response_headers 101 | Content-Type: text/plain 102 | 103 | 104 | 105 | === TEST 5: dequeue q1 106 | --- http_config eval: $::http_config 107 | --- config 108 | location /processQueue { 109 | ngx_lfqueue_target $arg_target; 110 | } 111 | --- request 112 | GET /processQueue?target=q1 113 | --- error_code: 200 114 | --- timeout: 10 115 | --- response_headers 116 | Content-Type: text/plain 117 | --- response_body_like eval chomp 118 | qr/.*?\"MESSAGE1\".*/ 119 | 120 | 121 | 122 | === TEST 6: dequeue q1 second time 123 | --- http_config eval: $::http_config 124 | --- config 125 | location /processQueue { 126 | ngx_lfqueue_target $arg_target; 127 | } 128 | --- request 129 | GET /processQueue?target=q1 130 | --- error_code: 200 131 | --- timeout: 10 132 | --- response_headers 133 | Content-Type: text/plain 134 | --- response_body_like eval chomp 135 | qr/.*?\"MESSAGE2\".*/ 136 | 137 | 138 | 139 | === TEST 5: dequeue q2 140 | --- http_config eval: $::http_config 141 | --- config 142 | location /processQueue { 143 | ngx_lfqueue_target $arg_target; 144 | } 145 | --- request 146 | GET /processQueue?target=q2 147 | --- error_code: 200 148 | --- timeout: 10 149 | --- response_headers 150 | Content-Type: text/plain 151 | --- response_body_like eval chomp 152 | qr/.*?\"MESSAGE1\".*/ 153 | 154 | 155 | 156 | === TEST 5: dequeue q3 157 | --- http_config eval: $::http_config 158 | --- config 159 | location /processQueue { 160 | ngx_lfqueue_target $arg_target; 161 | } 162 | --- request 163 | GET /processQueue?target=q3 164 | --- error_code: 200 165 | --- timeout: 10 166 | --- response_headers 167 | Content-Type: text/plain 168 | --- response_body_like eval chomp 169 | qr/.*?\"MESSAGE1\".*/ 170 | 171 | 172 | 173 | === TEST 6: dequeue q3 second time 174 | --- http_config eval: $::http_config 175 | --- config 176 | location /processQueue { 177 | ngx_lfqueue_target $arg_target; 178 | } 179 | --- request 180 | GET /processQueue?target=q3 181 | --- error_code: 200 182 | --- timeout: 10 183 | --- response_headers 184 | Content-Type: text/plain 185 | --- response_body_like eval chomp 186 | qr/.*?\"MESSAGE2\".*/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ngx_lfqueue 2 | 3 | 4 | Table of Contents 5 | ================= 6 | 7 | * [Introduction](#introduction) 8 | * [Usage](#usage) 9 | * [Installation](#installation) 10 | * [Test](#test) 11 | * [Support](#support) 12 | * [Copyright & License](#copyright--license) 13 | 14 | Introduction 15 | ============ 16 | 17 | ngx_lfqueue is the lock free queue container running on nginx share memory and it read/write across multiple threads and multiple workers without any lock! 18 | 19 | ngx_lfqueue is zero downtime nginx reloadable, data is on share memory, data is backupable in case nginx stop and start. 20 | 21 | 22 | Usage 23 | ======= 24 | ### 1. Setup your lfqueue 25 | 26 | #### There are 4 commands in this module 27 | 28 | **_ngx_lfqueue_memory_allocate_** - main config scope (1 argument) 29 | allocate the suitable share memory size for all the queue. 30 | 31 | **_ngx_lfqueue_name_** - main config scope (1 argument) 32 | init or reload the queue with a queue name, if the queue name existed on backup data, it will load from backup data. 33 | 34 | **_ngx_lfqueue_backup_** - main config scope (2 arguments) 35 | backup the data with special unique split key and file path, if file path not mentioned, it will stored under same directory of nginx config file. 36 | 37 | **_ngx_lfqueue_target_** - location config scope (1 argument) 38 | target which queue name to process data enqueue or dequeue. 39 | _POST METHOD_ - Enqueue request body data 40 | _GET METHOD_ - Dequeue queue message 41 | _HEAD METHOD_ - Get the queue Info 42 | 43 | ```nginx 44 | # nginx.conf 45 | http { 46 | ngx_lfqueue_memory_allocate 10m; 47 | ngx_lfqueue_name q1; 48 | ngx_lfqueue_name q2; 49 | ngx_lfqueue_name q3; 50 | ngx_lfqueue_backup |@| /tmp/ngx_lfqueue_data.txt; 51 | ... 52 | } 53 | ``` 54 | 55 | ### 2. Enqueue the message to specific queue name `ngx_lfqueue_target` by using POST/PUT method only, the request_body will be taken as queue message, response code 202 56 | ```nginx 57 | # nginx.conf 58 | 59 | server { 60 | .... 61 | location /processQueue { 62 | ngx_lfqueue_target q1; 63 | } 64 | 65 | location /processQueueWithArgVariable { 66 | ngx_lfqueue_target $arg_target; 67 | } 68 | } 69 | ``` 70 | 71 | ### 3. Dequeue the message by using GET method only, response code 200 72 | ```nginx 73 | # nginx.conf 74 | 75 | server { 76 | .... 77 | location /processQueue { 78 | ngx_lfqueue_target q1; 79 | } 80 | } 81 | ``` 82 | 83 | 84 | ### 4. Get the queue info by using HEAD method only, response code 204, the headers response queue_size, total_enq, total_deq 85 | ```nginx 86 | # nginx.conf 87 | 88 | server { 89 | .... 90 | location /processQueue { 91 | ngx_lfqueue_target q1; 92 | } 93 | } 94 | ``` 95 | 96 | ### Calling the service via using http call 97 | 98 | ```bash 99 | 100 | # for enqueue 101 | POST /processQueue HTTP/1.1 102 | Host: 127.0.0.1 103 | Cache-Control: no-cache 104 | Postman-Token: 858bacc8-4826-9d8e-5ec5-fde220351b5d 105 | 106 | {"Data":"MESSAGE 1......."} 107 | 108 | # for dequeue 109 | 110 | GET /processQueue HTTP/1.1 111 | Host: 127.0.0.1 112 | Cache-Control: no-cache 113 | Postman-Token: a0f89290-981a-2e18-cd99-40fa7fe734a0 114 | 115 | # for dequeue by using variable pass in 116 | 117 | 118 | GET /processQueueWithArgVariable?target=q2 HTTP/1.1 119 | Host: 127.0.0.1 120 | Cache-Control: no-cache 121 | Postman-Token: a0f89290-981a-2e18-cd99-40fa7fe734a0 122 | 123 | # for queue info 124 | 125 | HEAD /processQueue HTTP/1.1 126 | Host: 127.0.0.1 127 | Cache-Control: no-cache 128 | Postman-Token: 487251d4-51e9-33ee-58cf-343f259df27b 129 | 130 | ``` 131 | 132 | Installation 133 | ============ 134 | 135 | ngx_lfqueue is depends on [lfqueue](https://github.com/Taymindis/lfqueue) , install lfqueue as .so library before install ngx_lfqueue. 136 | 137 | 138 | ```bash 139 | wget 'http://nginx.org/download/nginx-1.13.7.tar.gz' 140 | tar -xzvf nginx-1.13.7.tar.gz 141 | cd nginx-1.13.7/ 142 | 143 | ./configure --add-module=/path/to/ngx_lfqueue 144 | 145 | make -j2 146 | sudo make install 147 | ``` 148 | 149 | [Back to TOC](#table-of-contents) 150 | 151 | 152 | Test 153 | ===== 154 | 155 | It depends on nginx test suite libs, please refer [test-nginx](https://github.com/openresty/test-nginx) for installation. 156 | 157 | 158 | ```bash 159 | cd /path/to/ngx_lfqueue 160 | export PATH=/path/to/nginx-dirname:$PATH 161 | sudo prove t 162 | ``` 163 | 164 | [Back to TOC](#table-of-contents) 165 | 166 | Support 167 | ======= 168 | 169 | Please do not hesitate to contact minikawoon2017@gmail.com for any queries or development improvement. 170 | 171 | 172 | [Back to TOC](#table-of-contents) 173 | 174 | Copyright & License 175 | =================== 176 | 177 | Copyright (c) 2018, Taymindis 178 | 179 | This module is licensed under the terms of the BSD license. 180 | 181 | All rights reserved. 182 | 183 | Redistribution and use in source and binary forms, with or without 184 | modification, are permitted provided that the following conditions are met: 185 | 186 | 1. Redistributions of source code must retain the above copyright notice, this 187 | list of conditions and the following disclaimer. 188 | 2. Redistributions in binary form must reproduce the above copyright notice, 189 | this list of conditions and the following disclaimer in the documentation 190 | and/or other materials provided with the distribution. 191 | 192 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 193 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 194 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 195 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 196 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 197 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 198 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 199 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 200 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 201 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 202 | 203 | [Back to TOC](#table-of-contents) 204 | 205 | 206 | 207 | ## You may also like nginx lock free stack 208 | 209 | [ngx_lfstack](https://github.com/Taymindis/ngx_lfstack) 210 | -------------------------------------------------------------------------------- /src/ngx_http_lfqueue_module.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ngx_http_lfqueue_module.c 3 | * @author taymindis 4 | * @date Sun JAN 28 12:06:52 2018 5 | * 6 | * @brief A ngx_lfqueue module for Nginx. 7 | * 8 | * @section LICENSE 9 | * 10 | * Copyright (c) 2018, Taymindis 11 | * 12 | * This module is licensed under the terms of the BSD license. 13 | * 14 | * Redistribution and use in source and binary forms, with or without 15 | * modification, are permitted provided that the following conditions are met: 16 | * 17 | * 1. Redistributions of source code must retain the above copyright notice, this 18 | * list of conditions and the following disclaimer. 19 | * 2. Redistributions in binary form must reproduce the above copyright notice, 20 | * this list of conditions and the following disclaimer in the documentation 21 | * and/or other materials provided with the distribution. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 27 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 28 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 30 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #define MODULE_NAME "ngx_lfqueue" 41 | #define MAX_DEQ_TRY 1000 42 | #define MAX_SIZE_DIGIT_TRNFM 128 43 | #define LFQUEUE_DATA_FILE "ngx_lfqueue.txt" 44 | 45 | /*Put data above to make malloc in one time*/ 46 | typedef struct { 47 | u_char *data; 48 | size_t len; 49 | } ngx_lfqueue_msg_t; 50 | 51 | typedef struct { 52 | ssize_t enq_cnt; 53 | ssize_t deq_cnt; 54 | lfqueue_t q; 55 | } ngx_lfqueue_t; 56 | 57 | typedef struct { 58 | ngx_str_node_t sn; 59 | void *value; 60 | } ngx_http_lfqueue_value_node_t; 61 | 62 | typedef struct { 63 | ngx_rbtree_t rbtree; 64 | ngx_rbtree_node_t sentinel; 65 | ngx_slab_pool_t *shpool; 66 | } ngx_http_lfqueue_shm_t; 67 | 68 | typedef struct { 69 | ngx_str_t name; 70 | ngx_http_lfqueue_shm_t *shared_mem; 71 | } ngx_http_lfqueue_shm_ctx_t; 72 | 73 | typedef struct { 74 | ngx_flag_t is_cache_defined; 75 | ngx_http_lfqueue_shm_ctx_t *shm_ctx; 76 | ngx_array_t *_queue_names; 77 | #ifndef NGX_LFQUEUE_DISABLE_STORING 78 | ngx_str_t saved_path; 79 | ngx_str_t split_delim; 80 | ngx_array_t *datachain; 81 | #endif 82 | } ngx_http_lfqueue_main_conf_t; 83 | 84 | typedef struct { 85 | ngx_http_complex_value_t target_q_name$; 86 | /*HEAD METHOD for get queue info*/ 87 | /*GET METHOD for dequeue*/ 88 | /*POST/PUT METHOD for enqueue*/ 89 | } ngx_http_lfqueue_loc_conf_t; 90 | 91 | typedef struct { 92 | unsigned done: 1; 93 | unsigned waiting_more_body: 1; 94 | ngx_int_t rc; 95 | ngx_http_lfqueue_shm_t *shared_mem; 96 | union { 97 | ngx_str_t payload; 98 | ngx_str_t response; 99 | }; 100 | ngx_http_request_t *r; 101 | ngx_str_t target_q_name; 102 | ngx_lfqueue_t *_targeted_q; 103 | } ngx_http_lfqueue_ctx_t; 104 | 105 | static ngx_int_t ngx_http_lfqueue_pre_configuration(ngx_conf_t *cf); 106 | static ngx_int_t ngx_http_lfqueue_post_configuration(ngx_conf_t *cf); 107 | static void *ngx_http_lfqueue_create_main_conf(ngx_conf_t *cf); 108 | static char *ngx_http_lfqueue_init_main_conf(ngx_conf_t *cf, void *conf); 109 | static void * ngx_http_lfqueue_create_loc_conf(ngx_conf_t *cf); 110 | static char * ngx_http_lfqueue_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); 111 | static char* ngx_http_lfqueue_set_shm_sz_cmd(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 112 | static char *ngx_http_lfqueue_target_cmd(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 113 | static char *ngx_http_lfqueue_data_backup_cmd(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 114 | static ngx_int_t ngx_http_lfqueue_module_init(ngx_cycle_t *cycle); 115 | static void ngx_http_lfqueue_module_exit(ngx_cycle_t *cycle); 116 | static ngx_int_t ngx_http_lfqueue_rewrite_handler(ngx_http_request_t *r); 117 | static ngx_int_t ngx_http_lfqueue_precontent_handler(ngx_http_request_t *r); 118 | ngx_int_t ngx_http_lfqueue_shm_init(ngx_shm_zone_t *shm_zone, void *data); 119 | 120 | static void ngx_http_lfqueue_client_body_handler(ngx_http_request_t *r); 121 | static void ngx_http_lfqueue_output_filter(ngx_http_request_t *r); 122 | static void ngx_http_lfqueue_process(ngx_http_request_t *r, ngx_http_lfqueue_ctx_t *ctx); 123 | #if (NGX_THREADS) //&& (nginx_version > 1013003) 124 | static void ngx_http_lfqueue_process_t_handler(void *data, ngx_log_t *log); 125 | static void ngx_http_lfqueue_after_t_handler(ngx_event_t *ev); 126 | #endif 127 | 128 | // static ngx_int_t ngx_lfqueue_check_create_dir(const u_char *path); 129 | static inline void* ngx_lfqueue_alloc(void *pl, size_t sz) { 130 | return ngx_slab_alloc( ((ngx_http_lfqueue_shm_t*)pl)->shpool, sz); 131 | } 132 | 133 | static inline void ngx_lfqueue_free(void *pl, void *ptr) { 134 | ngx_slab_free( ((ngx_http_lfqueue_shm_t*)pl)->shpool, ptr); 135 | } 136 | 137 | /** strstr with known length **/ 138 | static u_char* ngx_lfqueue_get_if_contain(u_char *s1, u_char *e1, u_char *s2, size_t s2_len ) { 139 | u_char *s3; 140 | while ( ( s3 = ngx_strlchr(s1, e1, *s2) ) ) { 141 | if ( ngx_strncmp(s3, s2, s2_len) == 0) { 142 | return s3; 143 | } 144 | s1 = ++s3; 145 | } 146 | return NULL; 147 | } 148 | 149 | /** 150 | * This module provided directive. 151 | */ 152 | static ngx_command_t ngx_http_lfqueue_commands[] = { 153 | { 154 | ngx_string("ngx_lfqueue_memory_allocate"), /* For Share memory Capacity */ 155 | NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, 156 | ngx_http_lfqueue_set_shm_sz_cmd, 157 | NGX_HTTP_MAIN_CONF_OFFSET, 158 | 0, 159 | NULL 160 | }, 161 | { 162 | ngx_string("ngx_lfqueue_name"), 163 | NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, 164 | ngx_conf_set_str_array_slot, 165 | NGX_HTTP_MAIN_CONF_OFFSET, 166 | offsetof(ngx_http_lfqueue_main_conf_t, _queue_names), 167 | NULL 168 | }, 169 | { 170 | ngx_string("ngx_lfqueue_target"), 171 | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 172 | ngx_http_lfqueue_target_cmd, 173 | NGX_HTTP_LOC_CONF_OFFSET, 174 | 0, 175 | NULL 176 | }, 177 | { ngx_string("ngx_lfqueue_backup"), 178 | NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE12, 179 | ngx_http_lfqueue_data_backup_cmd, 180 | NGX_HTTP_MAIN_CONF_OFFSET, 181 | 0, 182 | NULL 183 | }, 184 | ngx_null_command /* command termination */ 185 | }; 186 | 187 | // static ngx_shm_zone_t *bk_shm_zone; 188 | static const char* lfqueue_head_keys[] = {"queue_name", "queue_size", "enqueue_cnt", "dequeue_cnt", NULL}; 189 | #define HEADER_KEY_QUEUE_NAME 0 190 | #define HEADER_KEY_QUEUE_SIZE 1 191 | #define HEADER_KEY_QUEUE_ENQ 2 192 | #define HEADER_KEY_QUEUE_DEQ 3 193 | 194 | /* The module context. */ 195 | static ngx_http_module_t ngx_http_lfqueue_module_ctx = { 196 | ngx_http_lfqueue_pre_configuration, /* preconfiguration */ 197 | ngx_http_lfqueue_post_configuration, /* postconfiguration */ 198 | 199 | ngx_http_lfqueue_create_main_conf, /* create main configuration */ 200 | ngx_http_lfqueue_init_main_conf, /* init main configuration */ 201 | 202 | NULL, /* create server configuration */ 203 | NULL, /* merge server configuration */ 204 | 205 | ngx_http_lfqueue_create_loc_conf, /* create location configuration */ 206 | ngx_http_lfqueue_merge_loc_conf /* merge location configuration */ 207 | }; 208 | 209 | /* Module definition. */ 210 | ngx_module_t ngx_http_lfqueue_module = { 211 | NGX_MODULE_V1, 212 | &ngx_http_lfqueue_module_ctx, /* module context */ 213 | ngx_http_lfqueue_commands, /* module directives */ 214 | NGX_HTTP_MODULE, /* module type */ 215 | NULL, /* init master */ 216 | ngx_http_lfqueue_module_init, /* init module */ 217 | NULL, /* init process */ 218 | NULL, /* init thread */ 219 | NULL, /* exit thread */ 220 | NULL, /* exit process */ 221 | ngx_http_lfqueue_module_exit, /* exit master */ 222 | NGX_MODULE_V1_PADDING 223 | }; 224 | 225 | 226 | static ngx_int_t 227 | ngx_http_lfqueue_pre_configuration(ngx_conf_t *cf) { 228 | #if (NGX_THREADS) 229 | ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "lfqueue, %s", " with aio threads feature"); 230 | #endif 231 | return NGX_OK; 232 | } 233 | 234 | static ngx_int_t 235 | ngx_http_lfqueue_post_configuration(ngx_conf_t *cf) { 236 | ngx_http_lfqueue_main_conf_t *mcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lfqueue_module); 237 | 238 | if (mcf != NULL ) { 239 | ngx_http_handler_pt *h; 240 | ngx_http_core_main_conf_t *cmcf; 241 | 242 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 243 | 244 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers); 245 | if (h == NULL) { 246 | return NGX_ERROR; 247 | } 248 | 249 | *h = ngx_http_lfqueue_rewrite_handler; 250 | 251 | /***Enable pre content phase for apps concurrent processing request layer, NGX_DONE and wait for finalize request ***/ 252 | #if (nginx_version > 1013003) 253 | ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "lfqueue, %s", "USING NGX_HTTP_PRECONTENT_PHASE"); 254 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_PRECONTENT_PHASE].handlers); 255 | #else /**Access Phase is the only last phase for multi thread**/ 256 | ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "lfqueue, %s", "USING NGX_HTTP_ACCESS_PHASE"); 257 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); 258 | #endif 259 | if (h == NULL) { 260 | return NGX_ERROR; 261 | } 262 | 263 | *h = ngx_http_lfqueue_precontent_handler; 264 | } 265 | 266 | /*** Default Init for shm with 1M if pool is empty***/ 267 | if (mcf != NULL && !mcf->is_cache_defined ) { 268 | ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "lfqueue, %s", "Init Default Share memory with 10mb"); 269 | ngx_str_t default_size = ngx_string("10M"); 270 | 271 | ngx_shm_zone_t *shm_zone = ngx_shared_memory_add(cf, &mcf->shm_ctx->name, ngx_parse_size(&default_size), &ngx_http_lfqueue_module); 272 | if (shm_zone == NULL) { 273 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "lfqueue, %s", "Unable to allocate size"); 274 | return NGX_ERROR; 275 | } 276 | 277 | shm_zone->init = ngx_http_lfqueue_shm_init; 278 | shm_zone->data = mcf->shm_ctx; 279 | } 280 | 281 | return NGX_OK; 282 | } 283 | 284 | static void * 285 | ngx_http_lfqueue_create_main_conf(ngx_conf_t *cf) { 286 | ngx_http_lfqueue_main_conf_t *mcf; 287 | mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_lfqueue_main_conf_t)); 288 | if (mcf == NULL) { 289 | return NGX_CONF_ERROR; 290 | } 291 | 292 | mcf->shm_ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_lfqueue_shm_ctx_t)); 293 | 294 | if (mcf->shm_ctx == NULL) { 295 | return NGX_CONF_ERROR; 296 | } 297 | 298 | ngx_str_set(&mcf->shm_ctx->name , "ngx_lfqueue_shm_capacity"); 299 | 300 | mcf->shm_ctx->shared_mem = NULL; 301 | mcf->is_cache_defined = 0; 302 | mcf->_queue_names = NGX_CONF_UNSET_PTR; 303 | 304 | #ifndef NGX_LFQUEUE_DISABLE_STORING 305 | mcf->datachain = NGX_CONF_UNSET_PTR; 306 | /* Although by default is 0, just in case */ 307 | mcf->split_delim.len = 0; 308 | mcf->saved_path.len = 0; 309 | #endif 310 | 311 | return mcf; 312 | } 313 | 314 | static char * 315 | ngx_http_lfqueue_init_main_conf(ngx_conf_t *cf, void *conf) { 316 | return NGX_CONF_OK; 317 | } 318 | 319 | 320 | static char* 321 | ngx_http_lfqueue_set_shm_sz_cmd(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { 322 | ngx_str_t *values; 323 | ngx_http_lfqueue_main_conf_t *mcf = conf; 324 | ngx_shm_zone_t *shm_zone; 325 | ngx_int_t pg_size; 326 | 327 | values = cf->args->elts; 328 | 329 | pg_size = ngx_parse_size(&values[1]); 330 | 331 | if (pg_size == NGX_ERROR) { 332 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "lfqueue, %s", "Invalid cache size, please specify like 1m, 1000m, 9000M or etc."); 333 | return NGX_CONF_ERROR; 334 | } 335 | 336 | 337 | shm_zone = ngx_shared_memory_add(cf, &mcf->shm_ctx->name, pg_size, &ngx_http_lfqueue_module); 338 | if (shm_zone == NULL) { 339 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "lfqueue, %s", "Unable to allocate apps defined size"); 340 | return NGX_CONF_ERROR; 341 | } 342 | mcf->is_cache_defined = 1; 343 | shm_zone->init = ngx_http_lfqueue_shm_init; 344 | shm_zone->data = mcf->shm_ctx; 345 | 346 | return NGX_CONF_OK; 347 | } 348 | 349 | static void * 350 | ngx_http_lfqueue_create_loc_conf(ngx_conf_t *cf) { 351 | ngx_http_lfqueue_loc_conf_t *conf; 352 | 353 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_lfqueue_loc_conf_t)); 354 | if (conf == NULL) { 355 | return NULL; 356 | } 357 | 358 | /*lfqueue Init*/ 359 | ngx_memzero(&conf->target_q_name$, sizeof(ngx_http_complex_value_t)); 360 | 361 | return conf; 362 | } 363 | 364 | 365 | static char * 366 | ngx_http_lfqueue_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { 367 | // ngx_http_lfqueue_loc_conf_t *prev = parent; 368 | // ngx_http_lfqueue_loc_conf_t *conf = child; 369 | 370 | // if (conf->target_q_name$.len == 0) { 371 | // conf->target_q_name$ = prev->target_q_name$; 372 | // } 373 | 374 | return NGX_CONF_OK; 375 | } 376 | 377 | ngx_int_t 378 | ngx_http_lfqueue_shm_init(ngx_shm_zone_t *shm_zone, void *data) { 379 | size_t len; 380 | ngx_http_lfqueue_shm_ctx_t *oshm = data; 381 | ngx_http_lfqueue_shm_ctx_t *nshm = shm_zone->data; 382 | ngx_slab_pool_t *shpool; 383 | 384 | if (oshm) { 385 | nshm->name = oshm->name; 386 | nshm->shared_mem = oshm->shared_mem; 387 | return NGX_OK; 388 | } 389 | 390 | shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; 391 | 392 | if (shm_zone->shm.exists) { 393 | shm_zone->data = shpool->data; 394 | return NGX_OK; 395 | } 396 | 397 | nshm->shared_mem = ngx_slab_alloc(shpool, sizeof(ngx_http_lfqueue_shm_t)); 398 | ngx_rbtree_init(&nshm->shared_mem->rbtree, &nshm->shared_mem->sentinel, ngx_str_rbtree_insert_value); 399 | 400 | nshm->shared_mem->shpool = shpool; 401 | 402 | len = sizeof(" in nginx lfqueue session shared cache \"\"") + shm_zone->shm.name.len; 403 | 404 | nshm->shared_mem->shpool->log_ctx = ngx_slab_alloc(nshm->shared_mem->shpool, len); 405 | if (nshm->shared_mem->shpool->log_ctx == NULL) { 406 | return NGX_ERROR; 407 | } 408 | 409 | ngx_sprintf(nshm->shared_mem->shpool->log_ctx, " in nginx lfqueue session shared cache \"%V\"%Z", 410 | &shm_zone->shm.name); 411 | 412 | nshm->shared_mem->shpool->log_nomem = 0; 413 | 414 | return NGX_OK; 415 | } 416 | 417 | static char * 418 | ngx_http_lfqueue_target_cmd(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { 419 | ngx_http_lfqueue_loc_conf_t *lflcf = conf; 420 | ngx_str_t *value; 421 | ngx_http_compile_complex_value_t ccv; 422 | 423 | if (lflcf->target_q_name$.value.len != 0) { 424 | return "is duplicate"; 425 | } 426 | 427 | value = cf->args->elts; 428 | 429 | if (value[1].len == 0) { 430 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "lfqueue, %s", "no queue name given "); 431 | return NGX_CONF_ERROR; 432 | } 433 | 434 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 435 | 436 | ccv.cf = cf; 437 | ccv.value = &value[1]; 438 | ccv.complex_value = &lflcf->target_q_name$; 439 | 440 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 441 | return NGX_CONF_ERROR; 442 | } 443 | 444 | return NGX_CONF_OK; 445 | } 446 | 447 | static char * 448 | ngx_http_lfqueue_data_backup_cmd(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { 449 | ngx_http_lfqueue_main_conf_t *lfmcf = conf; 450 | ngx_str_t *value; 451 | 452 | value = cf->args->elts; 453 | 454 | if (lfmcf->datachain == NGX_CONF_UNSET_PTR) { 455 | lfmcf->datachain = ngx_array_create(cf->pool, 1024 /*Initial buffer*/, sizeof(u_char)); 456 | } else { 457 | return "is duplicate"; 458 | } 459 | 460 | if ( cf->args->nelts == 3 ) { 461 | lfmcf->saved_path.data = value[2].data; 462 | lfmcf->saved_path.len = ngx_strlen(value[2].data); 463 | } 464 | 465 | lfmcf->split_delim.data = value[1].data; 466 | lfmcf->split_delim.len = ngx_strlen(value[1].data); 467 | 468 | return NGX_CONF_OK; 469 | } 470 | 471 | static ngx_int_t 472 | ngx_http_lfqueue_rewrite_handler(ngx_http_request_t *r) { 473 | ngx_http_lfqueue_loc_conf_t *lcf = ngx_http_get_module_loc_conf(r, ngx_http_lfqueue_module); 474 | ngx_http_lfqueue_main_conf_t *mcf = ngx_http_get_module_main_conf(r, ngx_http_lfqueue_module); 475 | ngx_http_lfqueue_ctx_t *ctx; 476 | ngx_int_t rc; 477 | ngx_str_t target_queue_key; 478 | ngx_lfqueue_t *targeted_q; 479 | 480 | if (mcf == NULL) { 481 | // ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "lfqueue config not found"); 482 | targeted_q = NULL; 483 | } else if (ngx_http_complex_value(r, &lcf->target_q_name$, &target_queue_key) != NGX_OK) { 484 | // ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "%s", "No target queue set"); 485 | targeted_q = NULL; 486 | } else if (target_queue_key.len == 0) { 487 | // ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "%s", " No queue found "); 488 | targeted_q = NULL; 489 | } else { 490 | uint32_t hash = ngx_crc32_long(target_queue_key.data, target_queue_key.len); 491 | ngx_http_lfqueue_shm_t *_shm = mcf->shm_ctx->shared_mem; 492 | ngx_http_lfqueue_value_node_t *vnt = (ngx_http_lfqueue_value_node_t *) 493 | ngx_str_rbtree_lookup(&_shm->rbtree, &target_queue_key, hash); 494 | if (vnt) { 495 | targeted_q = vnt->value; 496 | } else { 497 | targeted_q = NULL; 498 | } 499 | } 500 | 501 | if (r->method & (NGX_HTTP_POST | NGX_HTTP_PUT | NGX_HTTP_PATCH)) { 502 | // r->request_body_in_single_buf = 1; 503 | // r->request_body_in_clean_file = 1; 504 | // r->request_body_in_persistent_file = 1; 505 | ctx = ngx_http_get_module_ctx(r, ngx_http_lfqueue_module); 506 | 507 | if (ctx != NULL) { 508 | if (ctx->done) { 509 | /***Done Reading***/ 510 | return NGX_DECLINED; 511 | } 512 | return NGX_DONE; 513 | } 514 | 515 | /* calloc, has init with 0 value*/ 516 | ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_lfqueue_ctx_t)); 517 | 518 | if (ctx == NULL) { 519 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Insufficient Memory to create ngx_http_lfqueue_ctx_t"); 520 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 521 | } 522 | 523 | ctx->r = r; 524 | ctx->rc = NGX_CONF_UNSET; 525 | ctx->_targeted_q = targeted_q; 526 | ctx->target_q_name.data = target_queue_key.data; 527 | ctx->target_q_name.len = target_queue_key.len; 528 | ngx_http_set_ctx(r, ctx, ngx_http_lfqueue_module); 529 | 530 | if (ctx->_targeted_q == NULL) { 531 | return NGX_DECLINED; 532 | } 533 | 534 | /****Reading Body Request ****/ 535 | rc = ngx_http_read_client_request_body(r, ngx_http_lfqueue_client_body_handler); 536 | 537 | if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { 538 | #if (nginx_version < 1002006) || \ 539 | (nginx_version >= 1003000 && nginx_version < 1003009) 540 | r->main->count--; 541 | #endif 542 | return rc; 543 | } 544 | 545 | if (rc == NGX_AGAIN) { 546 | ctx->waiting_more_body = 1; 547 | return NGX_DONE; 548 | } 549 | 550 | return NGX_DECLINED; 551 | } else { 552 | ctx = ngx_http_get_module_ctx(r, ngx_http_lfqueue_module); 553 | if (ctx == NULL) { 554 | /* calloc, has init with 0 value*/ 555 | ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_lfqueue_ctx_t)); 556 | if (ctx == NULL) { 557 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Insufficient Memory to create ngx_http_lfqueue_ctx_t"); 558 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 559 | } 560 | 561 | ctx->r = r; 562 | ctx->rc = NGX_CONF_UNSET; 563 | ctx->_targeted_q = targeted_q; 564 | ctx->target_q_name.data = target_queue_key.data; 565 | ctx->target_q_name.len = target_queue_key.len; 566 | ngx_http_set_ctx(r, ctx, ngx_http_lfqueue_module); 567 | if (ctx->_targeted_q == NULL) { 568 | return NGX_DECLINED; 569 | } 570 | } 571 | return NGX_DECLINED; 572 | } 573 | } 574 | 575 | static void 576 | ngx_http_lfqueue_client_body_handler(ngx_http_request_t *r) { 577 | ngx_http_lfqueue_ctx_t *ctx; 578 | ctx = ngx_http_get_module_ctx(r, ngx_http_lfqueue_module); 579 | ctx->done = 1; 580 | 581 | #if defined(nginx_version) && nginx_version >= 8011 582 | r->main->count--; 583 | #endif 584 | /* waiting_more_body my rewrite phase handler */ 585 | if (ctx->waiting_more_body) { 586 | ctx->waiting_more_body = 0; 587 | ngx_http_core_run_phases(r); 588 | } 589 | } 590 | 591 | /** 592 | * Pre Content handler. 593 | * @param r 594 | * Pointer to the request structure. See http_request.h. 595 | * @return 596 | * The status of the response generation. 597 | */ 598 | static ngx_int_t 599 | ngx_http_lfqueue_precontent_handler(ngx_http_request_t *r) { 600 | // ngx_http_lfqueue_loc_conf_t *lcf = ngx_http_get_module_loc_conf(r, ngx_http_lfqueue_module); 601 | ngx_http_lfqueue_main_conf_t *mcf = ngx_http_get_module_main_conf(r, ngx_http_lfqueue_module); 602 | ngx_http_lfqueue_ctx_t *ctx; 603 | 604 | ctx = ngx_http_get_module_ctx(r, ngx_http_lfqueue_module); 605 | 606 | if (ctx == NULL) { 607 | ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "error while processing request"); 608 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 609 | } else if (ctx->_targeted_q == NULL) { 610 | /** Not for ngx lfqueue handler, decline it**/ 611 | return NGX_DECLINED; 612 | } 613 | 614 | if (ctx->rc == NGX_CONF_UNSET) { 615 | goto new_task; 616 | } 617 | 618 | ngx_http_lfqueue_output_filter(r); 619 | // #if (nginx_version > 1013003) 620 | return NGX_DONE; 621 | // #else 622 | // return NGX_OK; 623 | // #endif 624 | 625 | new_task: 626 | 627 | ctx->shared_mem = mcf->shm_ctx->shared_mem; 628 | 629 | /***Set to default incase link library does not return anything ***/ 630 | ctx->rc = NGX_HTTP_INTERNAL_SERVER_ERROR; 631 | 632 | if (r->method & (NGX_HTTP_POST | NGX_HTTP_PUT | NGX_HTTP_PATCH)) { 633 | u_char *p, *buf = NULL; 634 | ngx_chain_t *cl; 635 | size_t len; 636 | ngx_buf_t *b; 637 | 638 | if (r->request_body == NULL || r->request_body->bufs == NULL) { 639 | goto REQUEST_BODY_DONE; 640 | } 641 | 642 | if (r->request_body->bufs->next != NULL) { 643 | len = 0; 644 | for (cl = r->request_body->bufs; cl; cl = cl->next) { 645 | b = cl->buf; 646 | if (b->in_file) { 647 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "insufficient client_body_buffer_size"); 648 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 649 | } 650 | len += b->last - b->pos; 651 | } 652 | if (len == 0) { 653 | goto REQUEST_BODY_DONE; 654 | } 655 | 656 | buf = ngx_palloc(r->pool, len ); 657 | if (buf == NULL) { 658 | ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "insufficient memory."); 659 | goto REQUEST_BODY_DONE; 660 | } 661 | 662 | p = buf; 663 | for (cl = r->request_body->bufs; cl; cl = cl->next) { 664 | p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos); 665 | } 666 | } else { 667 | b = r->request_body->bufs->buf; 668 | if ((len = ngx_buf_size(b)) == 0) { 669 | goto REQUEST_BODY_DONE; 670 | } 671 | buf = ngx_palloc(r->pool, len ); 672 | ngx_memcpy(buf, b->pos, len); 673 | } 674 | /************End REading ****************/ 675 | 676 | REQUEST_BODY_DONE: 677 | if (buf /*If got request body*/) { 678 | // ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "request_content=%*s \n", len, buf); 679 | ctx->payload.data = buf; 680 | ctx->payload.len = len; 681 | } else { 682 | // ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "%s\n", "No data to enqueue"); 683 | return NGX_HTTP_BAD_REQUEST; 684 | } 685 | } else { //if (!(r->method & (NGX_HTTP_POST | NGX_HTTP_PUT | NGX_HTTP_PATCH))) { 686 | if (ngx_http_discard_request_body(r) != NGX_OK) { 687 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 688 | } 689 | } 690 | 691 | #if (NGX_THREADS) //&& (nginx_version > 1013003) 692 | ngx_thread_pool_t *tp; 693 | ngx_http_core_loc_conf_t *clcf; 694 | 695 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 696 | 697 | tp = clcf->thread_pool; 698 | 699 | if (tp == NULL) { 700 | ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "lfqueue is processing single thread only, specify \"aio threads;\" in server/loc block for concurrent request"); 701 | goto single_thread; 702 | } 703 | 704 | ngx_thread_task_t *task = ngx_thread_task_alloc(r->pool, sizeof(ngx_http_request_t)); 705 | ngx_memcpy(task->ctx, r, sizeof(ngx_http_request_t)); 706 | task->handler = ngx_http_lfqueue_process_t_handler; 707 | task->event.data = r; 708 | task->event.handler = ngx_http_lfqueue_after_t_handler; 709 | 710 | if (ngx_thread_task_post(tp, task) != NGX_OK) { 711 | return NGX_ERROR; 712 | } 713 | r->main->blocked++; 714 | r->aio = 1; 715 | return NGX_DONE; 716 | single_thread: 717 | #endif 718 | 719 | // ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, " Processing lfqueue "); 720 | 721 | ngx_http_lfqueue_process(r, ctx); 722 | 723 | ngx_http_lfqueue_output_filter(r); 724 | 725 | // #if (nginx_version > 1013003) 726 | return NGX_DONE; 727 | // #else 728 | // return NGX_OK; 729 | // #endif 730 | } 731 | 732 | static void 733 | ngx_http_lfqueue_process(ngx_http_request_t *r, ngx_http_lfqueue_ctx_t *ctx) 734 | { 735 | ngx_lfqueue_msg_t *qmsg; 736 | u_char *rs; 737 | ngx_uint_t i; 738 | ngx_str_t *payload = &ctx->payload; 739 | ngx_table_elt_t *h; 740 | 741 | if (r->method & (NGX_HTTP_POST | NGX_HTTP_PUT | NGX_HTTP_PATCH)) { 742 | 743 | qmsg = ngx_slab_alloc(ctx->shared_mem->shpool, sizeof(ngx_lfqueue_msg_t) + payload->len ); 744 | 745 | if (qmsg == NULL) { 746 | ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, " No enough share memory given, expand the share memory capacity"); 747 | ctx->rc = NGX_HTTP_INTERNAL_SERVER_ERROR; 748 | return; 749 | } 750 | 751 | qmsg->data = ((u_char*)qmsg) + sizeof(ngx_lfqueue_msg_t); 752 | qmsg->len = payload->len; 753 | ngx_memcpy(qmsg->data, payload->data, payload->len); 754 | payload->len = 0; // clear the data, enqueu does not need to response any content 755 | 756 | ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, " %s", "enqueueing" ); 757 | lfqueue_enq(&ctx->_targeted_q->q, qmsg); 758 | ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, " %s", "enqueueing done" ); 759 | ngx_atomic_fetch_add(&ctx->_targeted_q->enq_cnt, 1); 760 | 761 | ctx->rc = NGX_HTTP_ACCEPTED; 762 | 763 | } else if ( r->method & NGX_HTTP_HEAD ) { 764 | for (i = 0; lfqueue_head_keys[i]; i++) { 765 | h = ngx_list_push(&r->headers_out.headers); 766 | if (h == NULL) { 767 | ctx->rc = NGX_HTTP_INTERNAL_SERVER_ERROR; 768 | return; 769 | } 770 | h->hash = 1; /*to mark HTTP output headers show set 1, show missing set 0*/ 771 | h->key.len = ngx_strlen(lfqueue_head_keys[i]); 772 | h->key.data = ngx_palloc(r->pool, h->key.len * sizeof(u_char)); 773 | ngx_memcpy(h->key.data, lfqueue_head_keys[i], h->key.len); 774 | 775 | switch (i) { 776 | case HEADER_KEY_QUEUE_NAME: 777 | h->value.data = ctx->target_q_name.data; 778 | h->value.len = ctx->target_q_name.len; 779 | break; 780 | case HEADER_KEY_QUEUE_SIZE: 781 | h->value.data = ngx_pcalloc(r->pool, MAX_SIZE_DIGIT_TRNFM * sizeof(u_char)); 782 | ngx_snprintf(h->value.data, MAX_SIZE_DIGIT_TRNFM - 1, "%z", lfqueue_size(&ctx->_targeted_q->q) ); 783 | h->value.len = ngx_strlen(h->value.data); 784 | break; 785 | case HEADER_KEY_QUEUE_ENQ: 786 | h->value.data = ngx_pcalloc(r->pool, MAX_SIZE_DIGIT_TRNFM * sizeof(u_char)); 787 | ngx_snprintf(h->value.data, MAX_SIZE_DIGIT_TRNFM - 1, "%z", ctx->_targeted_q->enq_cnt ); 788 | h->value.len = ngx_strlen(h->value.data); 789 | break; 790 | case HEADER_KEY_QUEUE_DEQ: 791 | h->value.data = ngx_pcalloc(r->pool, MAX_SIZE_DIGIT_TRNFM * sizeof(u_char)); 792 | ngx_snprintf(h->value.data, MAX_SIZE_DIGIT_TRNFM - 1, "%z", ctx->_targeted_q->deq_cnt ); 793 | h->value.len = ngx_strlen(h->value.data); 794 | break; 795 | } 796 | } 797 | 798 | ctx->rc = NGX_HTTP_NO_CONTENT; 799 | 800 | } else { 801 | /** PROCESSING Dequeue, 10 sec trying**/ 802 | ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, " %s", "dequeueing" ); 803 | for (i = 0; i < MAX_DEQ_TRY; i++) { 804 | if ( (qmsg = lfqueue_deq(&ctx->_targeted_q->q)) ) { 805 | ngx_atomic_fetch_add(&ctx->_targeted_q->deq_cnt, 1); 806 | goto QMSG_FOUND; 807 | } 808 | ngx_msleep(10); 809 | } 810 | 811 | ctx->rc = NGX_HTTP_NO_CONTENT; 812 | return; 813 | QMSG_FOUND: 814 | // ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, " Content %*s ", qmsg->len, qmsg->data); 815 | if (qmsg->len) { 816 | rs = ngx_palloc(r->pool, qmsg->len); 817 | ngx_memcpy(rs, qmsg->data, qmsg->len); 818 | ctx->response.data = rs; 819 | ctx->response.len = qmsg->len; 820 | ngx_slab_free(ctx->shared_mem->shpool, qmsg); 821 | ctx->rc = NGX_HTTP_OK; 822 | } 823 | } 824 | } 825 | 826 | #if (NGX_THREADS) //&& (nginx_version > 1013003) 827 | static void 828 | ngx_http_lfqueue_process_t_handler(void *data, ngx_log_t *log) 829 | { 830 | ngx_http_request_t *r = data; 831 | ngx_http_lfqueue_ctx_t *ctx; 832 | ngx_lfqueue_msg_t *qmsg; 833 | u_char *rs; 834 | ngx_uint_t i; 835 | ngx_str_t *payload; 836 | ngx_table_elt_t *h; 837 | 838 | ctx = ngx_http_get_module_ctx(r, ngx_http_lfqueue_module); 839 | payload = &ctx->payload; 840 | 841 | if (r->method & (NGX_HTTP_POST | NGX_HTTP_PUT | NGX_HTTP_PATCH)) { 842 | 843 | qmsg = ngx_slab_alloc(ctx->shared_mem->shpool, sizeof(ngx_lfqueue_msg_t) + payload->len ); 844 | 845 | if (qmsg == NULL) { 846 | ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, " No enough share memory given, expand the share memory capacity"); 847 | ctx->rc = NGX_HTTP_INTERNAL_SERVER_ERROR; 848 | return; 849 | } 850 | 851 | qmsg->data = ((u_char*)qmsg) + sizeof(ngx_lfqueue_msg_t); 852 | qmsg->len = payload->len; 853 | ngx_memcpy(qmsg->data, payload->data, payload->len); 854 | payload->len = 0; 855 | 856 | ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, " %s", "enqueueing" ); 857 | lfqueue_enq(&ctx->_targeted_q->q, qmsg); 858 | ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, " %s", "enqueueing done" ); 859 | ngx_atomic_fetch_add(&ctx->_targeted_q->enq_cnt, 1); 860 | 861 | ctx->rc = NGX_HTTP_ACCEPTED; 862 | 863 | } else if ( r->method & NGX_HTTP_HEAD ) { 864 | for (i = 0; lfqueue_head_keys[i]; i++) { 865 | h = ngx_list_push(&r->headers_out.headers); 866 | if (h == NULL) { 867 | ctx->rc = NGX_HTTP_INTERNAL_SERVER_ERROR; 868 | return; 869 | } 870 | h->hash = 1; /*to mark HTTP output headers show set 1, show missing set 0*/ 871 | h->key.len = ngx_strlen(lfqueue_head_keys[i]); 872 | h->key.data = ngx_palloc(r->pool, h->key.len * sizeof(u_char)); 873 | ngx_memcpy(h->key.data, lfqueue_head_keys[i], h->key.len); 874 | 875 | switch (i) { 876 | case HEADER_KEY_QUEUE_NAME: 877 | h->value.data = ctx->target_q_name.data; 878 | h->value.len = ctx->target_q_name.len; 879 | break; 880 | case HEADER_KEY_QUEUE_SIZE: 881 | h->value.data = ngx_pcalloc(r->pool, MAX_SIZE_DIGIT_TRNFM * sizeof(u_char)); 882 | ngx_snprintf(h->value.data, MAX_SIZE_DIGIT_TRNFM - 1, "%z", (ssize_t) lfqueue_size(&ctx->_targeted_q->q) ); 883 | h->value.len = ngx_strlen(h->value.data); 884 | break; 885 | case HEADER_KEY_QUEUE_ENQ: 886 | h->value.data = ngx_pcalloc(r->pool, MAX_SIZE_DIGIT_TRNFM * sizeof(u_char)); 887 | ngx_snprintf(h->value.data, MAX_SIZE_DIGIT_TRNFM - 1, "%z", ctx->_targeted_q->enq_cnt ); 888 | h->value.len = ngx_strlen(h->value.data); 889 | break; 890 | case HEADER_KEY_QUEUE_DEQ: 891 | h->value.data = ngx_pcalloc(r->pool, MAX_SIZE_DIGIT_TRNFM * sizeof(u_char)); 892 | ngx_snprintf(h->value.data, MAX_SIZE_DIGIT_TRNFM - 1, "%z", ctx->_targeted_q->deq_cnt ); 893 | h->value.len = ngx_strlen(h->value.data); 894 | break; 895 | } 896 | } 897 | 898 | ctx->rc = NGX_HTTP_NO_CONTENT; 899 | 900 | } else { 901 | /** PROCESSING Dequeue, 10 sec trying**/ 902 | ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, " %s", "dequeueing" ); 903 | for (i = 0; i < MAX_DEQ_TRY; i++) { 904 | if ( (qmsg = lfqueue_deq(&ctx->_targeted_q->q)) ) { 905 | ngx_atomic_fetch_add(&ctx->_targeted_q->deq_cnt, 1); 906 | goto QMSG_FOUND; 907 | } 908 | ngx_msleep(10); 909 | } 910 | 911 | ctx->rc = NGX_HTTP_NO_CONTENT; 912 | return; 913 | 914 | 915 | QMSG_FOUND: 916 | ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, " Message found "); 917 | ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, " Content %*s ", qmsg->len, qmsg->data); 918 | 919 | 920 | if (qmsg->len) { 921 | rs = ngx_palloc(r->pool, qmsg->len); 922 | ngx_memcpy(rs, qmsg->data, qmsg->len); 923 | ctx->response.data = rs; 924 | ctx->response.len = qmsg->len; 925 | ngx_slab_free(ctx->shared_mem->shpool, qmsg); 926 | ctx->rc = NGX_HTTP_OK; 927 | } 928 | } 929 | } 930 | 931 | static void 932 | ngx_http_lfqueue_after_t_handler(ngx_event_t *ev) { 933 | ngx_connection_t *c; 934 | ngx_http_request_t *r; 935 | 936 | r = ev->data; 937 | c = r->connection; 938 | 939 | ngx_http_set_log_request(c->log, r); 940 | 941 | r->main->blocked--; 942 | r->aio = 0; 943 | 944 | r->write_event_handler(r); 945 | ngx_http_run_posted_requests(c); 946 | } 947 | #endif 948 | 949 | static void 950 | ngx_http_lfqueue_output_filter(ngx_http_request_t *r) { 951 | ngx_int_t rc; 952 | ngx_chain_t out; 953 | ngx_http_lfqueue_ctx_t *ctx; 954 | ngx_str_t *response; 955 | size_t resp_len; 956 | ngx_buf_t *b; 957 | 958 | ctx = ngx_http_get_module_ctx(r, ngx_http_lfqueue_module); 959 | 960 | if (ctx == NULL) { 961 | ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "Session is not valid"); 962 | ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); 963 | return; 964 | } 965 | 966 | if (ctx->rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { 967 | ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "Internal Server error"); 968 | ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); 969 | return; 970 | } 971 | 972 | response = &ctx->response; 973 | 974 | r->headers_out.status = ctx->rc; 975 | 976 | r->headers_out.content_type.len = sizeof("text/plain") - 1; 977 | r->headers_out.content_type.data = (u_char *) "text/plain"; 978 | 979 | /**Response Content***/ 980 | if ( (resp_len = response->len) ) { 981 | r->headers_out.content_length_n = resp_len; 982 | rc = ngx_http_send_header(r); /* Send the headers */ 983 | if (rc == NGX_ERROR) { 984 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "response processing failed."); 985 | // ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); 986 | rc = NGX_HTTP_INTERNAL_SERVER_ERROR; 987 | ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); 988 | return; 989 | 990 | } 991 | b = ngx_create_temp_buf(r->pool, resp_len); 992 | b->last = ngx_copy(b->last, response->data, resp_len); 993 | b->memory = 1; /* content is in read-only memory */ 994 | b->last_buf = 1; /* there will be no more buffers in the request */ 995 | 996 | /* Insertion in the buffer chain. */ 997 | out.buf = b; 998 | out.next = NULL; /* just one buffer */ 999 | 1000 | /* Send the body, and return the status code of the output filter chain. */ 1001 | ngx_http_finalize_request(r, ngx_http_output_filter(r, &out)); 1002 | } else { 1003 | r->headers_out.content_length_n = 0; 1004 | r->header_only = 1; 1005 | ngx_http_finalize_request(r, ngx_http_send_header(r)); 1006 | } 1007 | } 1008 | 1009 | static ngx_int_t 1010 | ngx_http_lfqueue_module_init(ngx_cycle_t *cycle) { 1011 | ngx_core_conf_t *ccf; 1012 | ngx_uint_t i; 1013 | ngx_http_lfqueue_main_conf_t *mcf; 1014 | ngx_http_conf_ctx_t *ctx = (ngx_http_conf_ctx_t *)ngx_get_conf(cycle->conf_ctx, ngx_http_module); 1015 | ngx_str_t *s, *qstr; 1016 | ngx_lfqueue_t *_queues; 1017 | uint32_t hash; 1018 | ngx_http_lfqueue_value_node_t *vnt; 1019 | ngx_http_lfqueue_shm_t *shm; 1020 | ngx_uint_t has_lfqueue_init = 0; 1021 | ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); 1022 | 1023 | if (ccf->worker_processes > 1) { 1024 | ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "%s", "support more than 1 worker_processes may slow down the lfqueue performance"); 1025 | } 1026 | 1027 | mcf = ctx->main_conf[ngx_http_lfqueue_module.ctx_index]; 1028 | 1029 | if (mcf->_queue_names == NGX_CONF_UNSET_PTR) { 1030 | /** No lfqueue triggered **/ 1031 | return NGX_OK; 1032 | } 1033 | 1034 | shm = mcf->shm_ctx->shared_mem; 1035 | 1036 | #if (NGX_THREADS) 1037 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, " enabled aio threads for lfqueue module "); 1038 | #endif 1039 | 1040 | if (mcf->_queue_names->nelts > 0) { 1041 | qstr = mcf->_queue_names->elts; 1042 | 1043 | /**Check the first queue whether has initiliazed lfqueue**/ 1044 | for (i = 0; i < mcf->_queue_names->nelts; i++) { 1045 | s = qstr + i; 1046 | hash = ngx_crc32_long(s->data, s->len); 1047 | vnt = (ngx_http_lfqueue_value_node_t *) ngx_str_rbtree_lookup(&shm->rbtree, s, hash); 1048 | if (vnt) { 1049 | _queues = vnt->value; 1050 | if (_queues != NULL) { 1051 | has_lfqueue_init = 1; 1052 | _queues->q.pl = shm; 1053 | } 1054 | } else { 1055 | break; 1056 | } 1057 | } 1058 | 1059 | if (has_lfqueue_init) { 1060 | goto LFQUEUE_INIT_DONE; 1061 | } 1062 | 1063 | /*** Init lfqueue ***/ 1064 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, " Initializing lfqueue "); 1065 | _queues = ngx_slab_calloc(shm->shpool, mcf->_queue_names->nelts * sizeof(ngx_lfqueue_t)); 1066 | if (_queues == NULL) { 1067 | ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, " share memory allocation error "); 1068 | return NGX_ERROR; 1069 | } 1070 | 1071 | for (i = 0; i < mcf->_queue_names->nelts; i++) { 1072 | s = qstr + i; 1073 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, " Queue name \"%V\"\n", s); 1074 | 1075 | if (lfqueue_init_mf(&_queues[i].q, shm, ngx_lfqueue_alloc, ngx_lfqueue_free) == -1) { 1076 | ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, " lfqueue Initializing error... "); 1077 | return NGX_ERROR; 1078 | } 1079 | _queues[i].enq_cnt = 0; 1080 | _queues[i].deq_cnt = 0; 1081 | 1082 | ngx_http_lfqueue_value_node_t *vnt = (ngx_http_lfqueue_value_node_t *) 1083 | ngx_slab_alloc(shm->shpool, sizeof(ngx_http_lfqueue_value_node_t)); 1084 | 1085 | if (vnt == NULL) { 1086 | ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, " share memory allocation error "); 1087 | return NGX_ERROR; 1088 | } 1089 | 1090 | ngx_str_t *str_key = &(vnt->sn.str); 1091 | str_key->len = s->len; 1092 | str_key->data = (u_char*) ngx_slab_alloc(shm->shpool, sizeof(u_char) * (str_key->len + 1) ); 1093 | ngx_memcpy(str_key->data, s->data, str_key->len); 1094 | str_key->data[str_key->len] = 0; 1095 | 1096 | uint32_t hash = ngx_crc32_long(str_key->data, str_key->len); 1097 | vnt->value = _queues + i; 1098 | vnt->sn.node.key = hash; 1099 | ngx_rbtree_insert(&shm->rbtree, &vnt->sn.node); 1100 | } 1101 | 1102 | #ifndef NGX_LFQUEUE_DISABLE_STORING 1103 | u_char *filecontent, *p, *pflip, *pend, *store_file_path; 1104 | uintptr_t *arrp; 1105 | ngx_array_t *qarr; 1106 | ngx_str_t delim, delim_qkey, delim_msgkey; 1107 | ngx_fd_t readfd; 1108 | ngx_file_info_t fi; 1109 | off_t store_sz; 1110 | ngx_uint_t n; 1111 | ngx_lfqueue_msg_t *qmsg; 1112 | 1113 | if (mcf->saved_path.len == 0) { 1114 | p = store_file_path = (u_char*) ngx_pcalloc(cycle->pool, cycle->conf_prefix.len + sizeof(LFQUEUE_DATA_FILE)); 1115 | p = ngx_copy(p, cycle->conf_prefix.data, cycle->conf_prefix.len); 1116 | p = ngx_copy(p, LFQUEUE_DATA_FILE, sizeof(LFQUEUE_DATA_FILE)); 1117 | } else { 1118 | store_file_path = mcf->saved_path.data; 1119 | } 1120 | 1121 | readfd = ngx_open_file(store_file_path, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); 1122 | if (readfd != NGX_INVALID_FILE) { 1123 | if (ngx_fd_info(readfd, &fi) != NGX_FILE_ERROR) { 1124 | if ( (store_sz = ngx_file_size(&fi) ) ) { 1125 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, " read file size %O bytes ", store_sz); 1126 | filecontent = (u_char*) ngx_pcalloc(cycle->pool, store_sz); 1127 | if ( read(readfd, filecontent, store_sz) == -1 ) { 1128 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "read backup file \"%s\" failed", store_file_path); 1129 | goto LFQUEUE_INIT_DONE; 1130 | } else if (ngx_close_file(readfd) == NGX_FILE_ERROR || ngx_delete_file(store_file_path) == NGX_FILE_ERROR) { 1131 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "unable to close / remove data file %s", store_file_path); 1132 | } 1133 | 1134 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "restoring... "); 1135 | /**DECODING**/ 1136 | ngx_str_t plain_data; 1137 | ngx_str_t encoded_data; 1138 | encoded_data.len = store_sz; 1139 | encoded_data.data = filecontent; 1140 | ngx_uint_t declen = ngx_base64_decoded_length(store_sz); 1141 | plain_data.len = declen; 1142 | plain_data.data = (u_char*) ngx_pcalloc(cycle->pool, declen ); 1143 | ngx_decode_base64(&plain_data, &encoded_data); 1144 | /**DECODING END**/ 1145 | // ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "DATA RESTORE \n\n%V ", &plain_data); 1146 | pflip = plain_data.data; 1147 | pend = pflip + plain_data.len; 1148 | 1149 | /** GET DELIMETER **/ 1150 | if ( (p = ngx_lfqueue_get_if_contain(pflip, pend, (u_char*) "k@", sizeof("k@") - 1) ) ) { 1151 | delim.len = (p - pflip); 1152 | delim.data = (u_char*) ngx_pcalloc(cycle->pool, delim.len ); 1153 | ngx_memcpy(delim.data, pflip, delim.len); 1154 | } else { 1155 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "backup restore failed, no key found"); 1156 | goto LFQUEUE_INIT_DONE; 1157 | } 1158 | 1159 | delim_msgkey.len = delim_qkey.len = delim.len + (sizeof("k@") - 1); 1160 | delim_qkey.data = ngx_pcalloc(cycle->pool, delim.len + (sizeof("k@") - 1)); 1161 | delim_msgkey.data = ngx_pcalloc(cycle->pool, delim.len + (sizeof("m@") - 1)); 1162 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "delim %V", &delim); 1163 | 1164 | ngx_memcpy(delim_qkey.data, delim.data, delim.len); 1165 | ngx_memcpy(delim_qkey.data + delim.len, "k@", (sizeof("k@") - 1)); 1166 | ngx_memcpy(delim_msgkey.data, delim.data, delim.len); 1167 | ngx_memcpy(delim_msgkey.data + delim.len, "m@", (sizeof("m@") - 1)); 1168 | 1169 | qarr = ngx_array_create(cycle->pool, 128, sizeof(uintptr_t)); 1170 | 1171 | p = pflip; 1172 | while ( (p = ngx_lfqueue_get_if_contain(p, pend, delim_qkey.data, delim_qkey.len ) ) ) { 1173 | arrp = ngx_array_push(qarr); 1174 | p = p + delim_qkey.len; 1175 | *arrp = (uintptr_t) (u_char*) p; 1176 | } 1177 | 1178 | arrp = (uintptr_t*) qarr->elts; 1179 | 1180 | for ( n = 0; n < qarr->nelts; n++ ) { 1181 | pflip = (u_char*) arrp[n]; 1182 | if ( (n + 1) == qarr->nelts ) { 1183 | pend = plain_data.data + plain_data.len; 1184 | } else { 1185 | pend = (u_char*) arrp[n + 1]; 1186 | pend -= delim_qkey.len; 1187 | } 1188 | 1189 | for (i = 0; i < mcf->_queue_names->nelts; i++) { 1190 | if ( (p = ngx_lfqueue_get_if_contain(pflip, pend, delim_msgkey.data, delim_msgkey.len) ) ) { 1191 | s = qstr + i; 1192 | if ( s->len == (size_t) (p - pflip) && ngx_strncmp(s->data, pflip, (p - pflip) ) == 0 ) { 1193 | hash = ngx_crc32_long(s->data, s->len); 1194 | vnt = (ngx_http_lfqueue_value_node_t *) ngx_str_rbtree_lookup(&shm->rbtree, s, hash); 1195 | if (vnt) { 1196 | _queues = vnt->value; 1197 | if (_queues != NULL) { 1198 | pflip = p + delim_msgkey.len; 1199 | while ( (p = ngx_lfqueue_get_if_contain(pflip, pend, 1200 | delim_msgkey.data, delim_msgkey.len ) ) ) { 1201 | qmsg = ngx_slab_alloc(shm->shpool, sizeof(ngx_lfqueue_msg_t) + (p - pflip) ); 1202 | 1203 | if (qmsg == NULL) { 1204 | ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, " No enough share memory given, expand the share memory capacity"); 1205 | return NGX_ERROR; 1206 | } 1207 | 1208 | qmsg->data = ((u_char*)qmsg) + sizeof(ngx_lfqueue_msg_t); 1209 | qmsg->len = (p - pflip); 1210 | ngx_memcpy(qmsg->data, pflip, (p - pflip)); 1211 | lfqueue_enq(&_queues->q, qmsg); 1212 | pflip = p + delim_msgkey.len; 1213 | } 1214 | qmsg = ngx_slab_alloc(shm->shpool, sizeof(ngx_lfqueue_msg_t) + (pend - pflip) ); 1215 | if (qmsg == NULL) { 1216 | ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, " No enough share memory given, expand the share memory capacity"); 1217 | return NGX_ERROR; 1218 | } 1219 | qmsg->data = ((u_char*)qmsg) + sizeof(ngx_lfqueue_msg_t); 1220 | qmsg->len = (pend - pflip); 1221 | ngx_memcpy(qmsg->data, pflip, (pend - pflip)); 1222 | lfqueue_enq(&_queues->q, qmsg); 1223 | } 1224 | } 1225 | } 1226 | } 1227 | } 1228 | } 1229 | 1230 | ngx_pfree(cycle->pool, delim.data); 1231 | ngx_pfree(cycle->pool, delim_qkey.data); 1232 | ngx_pfree(cycle->pool, delim_msgkey.data); 1233 | ngx_pfree(cycle->pool, filecontent); 1234 | ngx_pfree(cycle->pool, plain_data.data); 1235 | ngx_array_destroy(qarr); 1236 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, " backup data %s has been restored ", store_file_path); 1237 | } 1238 | } 1239 | } 1240 | ngx_pfree(cycle->pool, store_file_path); 1241 | #endif 1242 | } else { 1243 | ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, " No queue names specified "); 1244 | return NGX_ERROR; 1245 | } 1246 | 1247 | LFQUEUE_INIT_DONE: 1248 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, " lfqueue has successfully Initialized"); 1249 | 1250 | return NGX_OK; 1251 | } 1252 | 1253 | static void 1254 | ngx_http_lfqueue_module_exit(ngx_cycle_t *cycle) { 1255 | #ifndef NGX_LFQUEUE_DISABLE_STORING 1256 | ngx_http_lfqueue_main_conf_t *mcf; 1257 | ngx_http_conf_ctx_t *ctx; 1258 | ngx_http_lfqueue_shm_t *shm; 1259 | u_char *p, *store_file_path; 1260 | ngx_str_t *s, *qstr, delim_qkey, delim_msgkey; 1261 | ngx_lfqueue_t *_queues; 1262 | lfqueue_t *q; 1263 | ngx_lfqueue_msg_t *qmsg; 1264 | uint32_t hash; 1265 | ngx_http_lfqueue_value_node_t *vnt; 1266 | ngx_uint_t i; 1267 | // off_t store_sz = 0; 1268 | ngx_array_t *datachain; 1269 | 1270 | ctx = (ngx_http_conf_ctx_t *)ngx_get_conf(cycle->conf_ctx, ngx_http_module); 1271 | if (ctx == NULL) { 1272 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "ngx_http_lfqueue_module_exit in error, unable to get config context"); 1273 | return; 1274 | } 1275 | 1276 | mcf = ctx->main_conf[ngx_http_lfqueue_module.ctx_index]; 1277 | shm = mcf->shm_ctx->shared_mem; 1278 | if (shm == NULL) { 1279 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "ngx_http_lfqueue_module_exit in error, lfqueue data not found"); 1280 | return; 1281 | } else if (mcf->datachain == NGX_CONF_UNSET_PTR) { 1282 | goto LFQUEUE_MASTER_EXIT; 1283 | } else if ( mcf->split_delim.len == 0) { 1284 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "backup failed, no key split found"); 1285 | return; 1286 | } else if (mcf->_queue_names->nelts > 0) { 1287 | datachain = mcf->datachain; 1288 | 1289 | delim_msgkey.len = delim_qkey.len = mcf->split_delim.len + 2; 1290 | delim_qkey.data = ngx_pcalloc(cycle->pool, mcf->split_delim.len + 2); 1291 | delim_msgkey.data = ngx_pcalloc(cycle->pool, mcf->split_delim.len + 2); 1292 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "mcf->split_delim %V", &mcf->split_delim); 1293 | 1294 | ngx_memcpy(delim_qkey.data, mcf->split_delim.data, mcf->split_delim.len); 1295 | ngx_memcpy(delim_qkey.data + mcf->split_delim.len, "k@", 2); 1296 | ngx_memcpy(delim_msgkey.data, mcf->split_delim.data, mcf->split_delim.len); 1297 | ngx_memcpy(delim_msgkey.data + mcf->split_delim.len, "m@", 2); 1298 | 1299 | /** Backup queue message **/ 1300 | qstr = mcf->_queue_names->elts; 1301 | for (i = 0; i < mcf->_queue_names->nelts; i++) { 1302 | s = qstr + i; 1303 | hash = ngx_crc32_long(s->data, s->len); 1304 | vnt = (ngx_http_lfqueue_value_node_t *) ngx_str_rbtree_lookup(&shm->rbtree, s, hash); 1305 | if (vnt) { 1306 | _queues = vnt->value; 1307 | if (_queues != NULL) { 1308 | q = &_queues->q; 1309 | if ( (lfqueue_size(q)) ) { 1310 | if ( ngx_lfqueue_get_if_contain(s->data, s->data + s->len, mcf->split_delim.data, mcf->split_delim.len ) ) { 1311 | goto LFQUEUE_MASTER_EXIT_WITH_DELIM_CRASH; 1312 | } 1313 | p = ngx_array_push_n(datachain, delim_qkey.len + s->len); 1314 | p = ngx_copy(p, delim_qkey.data, delim_qkey.len); 1315 | p = ngx_copy(p, s->data, s->len); 1316 | } else { 1317 | continue; 1318 | } 1319 | while ( (qmsg = lfqueue_deq(q)) ) { 1320 | if ( ngx_lfqueue_get_if_contain(qmsg->data, qmsg->data + qmsg->len, mcf->split_delim.data, mcf->split_delim.len ) ) { 1321 | goto LFQUEUE_MASTER_EXIT_WITH_DELIM_CRASH; 1322 | } 1323 | p = ngx_array_push_n(datachain, delim_msgkey.len + qmsg->len); 1324 | p = ngx_copy(p, delim_msgkey.data, delim_msgkey.len); 1325 | p = ngx_copy(p, qmsg->data, qmsg->len); 1326 | } 1327 | } 1328 | } 1329 | } 1330 | } else { 1331 | goto LFQUEUE_MASTER_EXIT; 1332 | } 1333 | 1334 | if ( 0 == datachain->nelts ) { 1335 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "no data backup requied"); 1336 | goto LFQUEUE_MASTER_EXIT; 1337 | } 1338 | 1339 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "backup size %O bytes", mcf->datachain->nelts); 1340 | /** Storing data **/ 1341 | if (mcf->saved_path.len == 0) { 1342 | p = store_file_path = (u_char*) ngx_pcalloc(cycle->pool, cycle->conf_prefix.len + sizeof(LFQUEUE_DATA_FILE)); 1343 | p = ngx_copy(p, cycle->conf_prefix.data, cycle->conf_prefix.len); 1344 | p = ngx_copy(p, LFQUEUE_DATA_FILE, sizeof(LFQUEUE_DATA_FILE)); 1345 | } else { 1346 | store_file_path = mcf->saved_path.data; 1347 | } 1348 | 1349 | FILE *stored_file; 1350 | stored_file = fopen ((char*) store_file_path, "w"); 1351 | if (stored_file == NULL) { 1352 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "Error while storing backup data file, unable to create file"); 1353 | goto LFQUEUE_MASTER_EXIT; 1354 | } 1355 | 1356 | 1357 | /** ENCODING **/ 1358 | ngx_str_t encoded_data; 1359 | ngx_str_t plain_data; 1360 | plain_data.data = (u_char*) mcf->datachain->elts; 1361 | plain_data.len = mcf->datachain->nelts; 1362 | ngx_uint_t enclen = ngx_base64_encoded_length(plain_data.len); 1363 | encoded_data.data = (u_char*) ngx_pcalloc(cycle->pool, enclen ); 1364 | encoded_data.len = enclen; 1365 | ngx_encode_base64(&encoded_data, &plain_data); 1366 | /** END ENCODING **/ 1367 | 1368 | if ( fwrite (encoded_data.data, encoded_data.len, 1, stored_file) != 1 ) { 1369 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "Error while storing backup data file, unable to write to file"); 1370 | } 1371 | 1372 | ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "Data has been successfully saved to %s", store_file_path); 1373 | LFQUEUE_MASTER_EXIT: 1374 | #endif 1375 | // ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "Share memory size is %z", shm->shpool->end - shm->shpool->start); 1376 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "ngx_http_lfqueue_module_exit"); 1377 | return; 1378 | LFQUEUE_MASTER_EXIT_WITH_DELIM_CRASH: 1379 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "backup failed, queue message data has contain split key, suggest to change unique split key e.g ngx_lfqueue_backup "); 1380 | } 1381 | 1382 | // static ngx_int_t 1383 | // ngx_lfqueue_check_create_dir(const u_char *path) { 1384 | // ngx_str_t str_path = { ngx_strlen(path) - 1, (u_char *) path }; 1385 | // ngx_dir_t dir; 1386 | 1387 | // if (ngx_open_dir(&str_path, &dir) == NGX_OK) { 1388 | // if (ngx_close_dir(&dir) == NGX_ERROR) { 1389 | // return NGX_ERROR; 1390 | // } 1391 | // } else if ( ngx_create_dir(path, 0700) == NGX_FILE_ERROR ) { 1392 | // return NGX_ERROR; 1393 | // } 1394 | // return NGX_OK 1395 | // } --------------------------------------------------------------------------------