"
4 | #define get_route_p_s "/ngx-stats?r_p_s=1"
5 |
6 | int is_admin(ngx_http_request_t* r, char* admin_ip){
7 |
8 | if ( (r->uri.len - 1 )< ADMIN_PAGE_LEN )
9 | return 0;
10 |
11 | if ( strncmp ( admin_ip, (char *)r->connection->addr_text.data, r->connection->addr_text.len ) != 0)
12 | return 0;
13 |
14 | if (strncmp ((char*)r->uri.data, get_route_p_s, 18) == 0)
15 | return 2;
16 |
17 | if (r->uri.len - 2 == ADMIN_PAGE_LEN && r->uri.data[ADMIN_PAGE_LEN+1] != '/' )
18 | return 0;
19 |
20 | if ( (r->uri.len - 2) > ADMIN_PAGE_LEN && r->uri.data[ADMIN_PAGE_LEN+1] != '?')
21 | return 0;
22 |
23 | if (strncmp ((char*)r->uri.data, ADMIN_PAGE, ADMIN_PAGE_LEN + 1) != 0)
24 | return 0;
25 |
26 | return 1;
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/lib/init_shm.h:
--------------------------------------------------------------------------------
1 | typedef struct {
2 | unsigned int time;
3 | long int count;
4 | } req;
5 |
6 | typedef struct{
7 | req r_per_s[9];
8 | } ngx_stats_shm_count_t;
9 |
10 | ngx_slab_pool_t *shpool;
11 |
12 | static ngx_int_t ngx_stats_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data);
13 | void init_r_p_s(req *data, int size);
14 |
15 | static ngx_int_t ngx_stats_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data){
16 | ngx_stats_shm_count_t *shm_data;
17 | if(data){
18 | shm_zone->data = data;
19 | return NGX_OK;
20 | }
21 | shpool = (ngx_slab_pool_t *)shm_zone->shm.addr;
22 | shm_data = ngx_slab_alloc(shpool, sizeof *shm_data );
23 | init_r_p_s(shm_data->r_per_s , 9);
24 | shm_zone->data = shm_data;
25 |
26 | return NGX_OK;
27 | }
28 |
29 | void init_r_p_s(req *data, int size){
30 | for (int i=0; i<= size; i++){
31 | data[i].count = 0;
32 | data[i].time = 0 ;
33 | }
34 | }
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ngx_http_stats.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #include "lib/admin.h"
8 | #include "lib/init_shm.h"
9 |
10 | typedef struct {
11 | ngx_shm_zone_t *shm_zone;
12 | ngx_flag_t enabled;
13 | ngx_str_t IP;
14 | } ngx_http_stats_loc_conf_t;
15 |
16 |
17 | static ngx_int_t ngx_http_stats(ngx_conf_t *cf);
18 | static void *ngx_http_stats_create_loc_conf(ngx_conf_t *cf);
19 | static char *ngx_http_stats_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);
20 | static ngx_int_t ngx_http_stats_handler(ngx_http_request_t *r);
21 |
22 | static ngx_int_t server_html_stats(ngx_http_request_t *r);
23 | static ngx_int_t server_html_stats_r_p_s(ngx_http_request_t *r);
24 |
25 |
26 |
27 |
28 | static ngx_command_t ngx_http_stats_commands[] = {
29 | {
30 | ngx_string("ngx_stats"),
31 | NGX_HTTP_LOC_CONF | NGX_HTTP_LIF_CONF | NGX_HTTP_SIF_CONF | NGX_HTTP_SRV_CONF | NGX_CONF_FLAG,
32 | ngx_conf_set_flag_slot,
33 | NGX_HTTP_LOC_CONF_OFFSET,
34 | offsetof(ngx_http_stats_loc_conf_t, enabled),
35 | NULL
36 | },
37 | {
38 | ngx_string("ngx_stats_admin_ip"),
39 | NGX_HTTP_LOC_CONF | NGX_HTTP_SRV_CONF | NGX_CONF_TAKE1,
40 | ngx_conf_set_str_slot,
41 | NGX_HTTP_LOC_CONF_OFFSET,
42 | offsetof(ngx_http_stats_loc_conf_t, IP),
43 | NULL
44 | },
45 | ngx_null_command
46 | };
47 |
48 |
49 | static ngx_http_module_t ngx_http_stats_module_ctx = {
50 | NULL,
51 | ngx_http_stats,
52 | NULL,
53 | NULL,
54 | NULL,
55 | NULL,
56 | ngx_http_stats_create_loc_conf,
57 | ngx_http_stats_merge_loc_conf
58 | };
59 |
60 | ngx_module_t ngx_http_stats_module = {
61 | NGX_MODULE_V1,
62 | &ngx_http_stats_module_ctx,
63 | ngx_http_stats_commands,
64 | NGX_HTTP_MODULE,
65 | NULL,
66 | NULL,
67 | NULL,
68 | NULL,
69 | NULL,
70 | NULL,
71 | NULL,
72 | NGX_MODULE_V1_PADDING
73 | };
74 |
75 |
76 | static void *ngx_http_stats_create_loc_conf(ngx_conf_t *cf) {
77 | ngx_http_stats_loc_conf_t *conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_stats_loc_conf_t));
78 | if (conf == NULL) {
79 | return NGX_CONF_ERROR;
80 | }
81 | conf->IP = (ngx_str_t) {0, NULL};
82 | conf->enabled = NGX_CONF_UNSET;
83 |
84 | return conf;
85 | }
86 |
87 |
88 | static char *ngx_http_stats_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) {
89 | ngx_http_stats_loc_conf_t *prev = parent;
90 | ngx_http_stats_loc_conf_t *conf = child;
91 |
92 | ngx_conf_merge_value(conf->enabled, prev->enabled, 0)
93 |
94 | ngx_conf_merge_str_value(conf->IP, prev->IP, NULL)
95 | ngx_shm_zone_t *shm_zone;
96 | ngx_str_t *shm_name;
97 | shm_name = ngx_palloc(cf->pool, sizeof *shm_name);
98 | shm_name->len = sizeof("shared_memory") - 1;
99 | shm_name->data = (unsigned char *) "shared_memory";
100 | shm_zone = ngx_shared_memory_add(cf, shm_name, 8*ngx_pagesize, &ngx_http_stats_module);
101 | if(shm_zone == NULL){
102 | return NGX_CONF_ERROR;
103 | }
104 |
105 | shm_zone->init = ngx_stats_init_shm_zone;
106 | conf->shm_zone = shm_zone;
107 |
108 | ngx_conf_merge_ptr_value(conf->shm_zone, prev->shm_zone, NULL);
109 |
110 | return NGX_CONF_OK;
111 | }
112 |
113 |
114 | static ngx_int_t ngx_http_stats(ngx_conf_t *cf) {
115 | ngx_http_handler_pt *h;
116 | ngx_http_core_main_conf_t *main_conf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
117 |
118 | h = ngx_array_push(&main_conf->phases[NGX_HTTP_PRECONTENT_PHASE].handlers);
119 | if (h == NULL) {
120 | ngx_log_error(NGX_LOG_ERR, cf->log, 0, "null");
121 | return NGX_ERROR;
122 | }
123 |
124 | *h = ngx_http_stats_handler;
125 |
126 | return NGX_OK;
127 | }
128 |
129 |
130 | static ngx_int_t ngx_http_stats_handler(ngx_http_request_t *r) {
131 |
132 | ngx_http_stats_loc_conf_t *conf = ngx_http_get_module_loc_conf(r, ngx_http_stats_module);
133 |
134 | if (!conf->enabled) {
135 | return NGX_DECLINED;
136 | }
137 |
138 | unsigned char key_s;
139 | unsigned int mTime;
140 | ngx_http_stats_loc_conf_t *lccf;
141 | ngx_shm_zone_t *shm_zone;
142 |
143 | lccf = ngx_http_get_module_loc_conf(r, ngx_http_stats_module);
144 |
145 | if(lccf->shm_zone == NULL){
146 | return NGX_DECLINED;
147 | }
148 | shm_zone = lccf->shm_zone;
149 | mTime = time(NULL);
150 | key_s = mTime % 10;
151 |
152 | ngx_shmtx_lock(&shpool->mutex);
153 | //count req/s
154 | if ((mTime - 9 )> ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[key_s].time ){
155 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[key_s].time = mTime;
156 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[key_s].count = 0;
157 | }
158 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[key_s].count ++;
159 | ngx_shmtx_unlock(&shpool->mutex);
160 |
161 | //check if is admin
162 | int isAdm = is_admin(r, (char *)conf->IP.data);
163 | switch ( isAdm ) {
164 | case 1:
165 | return server_html_stats(r);
166 |
167 | case 2:
168 | return server_html_stats_r_p_s( r);
169 | }
170 |
171 | return NGX_DECLINED;
172 |
173 | }
174 |
175 |
176 |
177 | static ngx_int_t server_html_stats(ngx_http_request_t *r) {
178 | ngx_http_stats_loc_conf_t *lccf;
179 | ngx_shm_zone_t *shm_zone;
180 | unsigned char key;
181 | unsigned int mTime;
182 | ngx_buf_t* b;
183 | ngx_chain_t out;
184 | char html[8192] =" ";
185 |
186 | lccf = ngx_http_get_module_loc_conf(r, ngx_http_stats_module);
187 | if(lccf->shm_zone == NULL){
188 | return NGX_DECLINED;
189 | }
190 |
191 | shm_zone = lccf->shm_zone;
192 | mTime = time(NULL);
193 | key = (mTime - 9 ) % 10;
194 |
195 | sprintf(html,ADMIN_HTML,
196 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ key].time,
197 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+1)%10].time,
198 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+2)%10].time,
199 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+3)%10].time,
200 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+4)%10].time,
201 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+5)%10].time,
202 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+6)%10].time,
203 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+7)%10].time,
204 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+8)%10].time,
205 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+9)%10].time,
206 |
207 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ key].count,
208 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+1)%10].count,
209 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+2)%10].count,
210 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+3)%10].count,
211 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+4)%10].count,
212 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+5)%10].count,
213 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+6)%10].count,
214 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+7)%10].count,
215 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+8)%10].count,
216 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[ (key+9)%10].count
217 | );
218 |
219 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
220 | out.buf = b;
221 | out.next = NULL;
222 | ngx_str_t count_str = ngx_string(html);
223 |
224 | b->pos = count_str.data;
225 | b->last = count_str.data + count_str.len;
226 | b->memory = 1;
227 | b->last_buf = 1;
228 | r->headers_out.content_type.len = sizeof("text/html") - 1;
229 | r->headers_out.content_type.data = (u_char *) "text/html";
230 | r->headers_out.status = NGX_HTTP_OK;
231 | r->headers_out.content_length_n = count_str.len;
232 |
233 | ngx_http_send_header(r);
234 | ngx_http_output_filter(r, &out);
235 | ngx_http_finalize_request(r, 0);
236 |
237 | return NGX_DONE;
238 | }
239 |
240 |
241 | static ngx_int_t server_html_stats_r_p_s(ngx_http_request_t *r) {
242 | char json[64], *json_response;
243 | unsigned char key;
244 | unsigned int mTime;
245 | ngx_http_stats_loc_conf_t *lccf;
246 | ngx_shm_zone_t *shm_zone;
247 | ngx_buf_t* b;
248 | ngx_chain_t out;
249 |
250 | lccf = ngx_http_get_module_loc_conf(r, ngx_http_stats_module);
251 |
252 | if(lccf->shm_zone == NULL){
253 | return NGX_DECLINED;
254 | }
255 | shm_zone = lccf->shm_zone;
256 | mTime = time(NULL);
257 |
258 | key = (mTime - 1 )% 10 ; // -1 second
259 | //create JSON
260 | sprintf( json, "{\"req\":%ld, \"time\":%u}\n",
261 | (long int)(((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[key].count),
262 | ((ngx_stats_shm_count_t *)shm_zone->data)->r_per_s[key].time
263 | );
264 | json_response = malloc(strlen(json)+1);
265 | strncpy(json_response, json, strlen(json));
266 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
267 | out.buf = b;
268 | out.next = NULL;
269 |
270 | b->pos = (u_char *)json_response;
271 | b->last = (u_char *)json_response + strlen(json_response);
272 | b->memory = 1;
273 | b->last_buf = 1;
274 |
275 | r->headers_out.content_type.len = sizeof("application/json") - 1;
276 | r->headers_out.content_type.data = (u_char *) "application/json";
277 | r->headers_out.status = NGX_HTTP_OK;
278 | r->headers_out.content_length_n = strlen(json_response);
279 |
280 | ngx_http_send_header(r);
281 | ngx_http_output_filter(r, &out);
282 | ngx_http_finalize_request(r, 0);
283 |
284 | free(json_response);
285 | return NGX_DONE;
286 | }
287 |
288 |
289 |
290 |
--------------------------------------------------------------------------------