1 #include <nchan_module.h>
2 #include <assert.h>
3
4 #if (NGX_ZLIB)
5 #include <zlib.h>
6 #endif
7
8 #ifndef NCHAN_HAVE_MEMRCHR
memrchr(const void * s,int c,size_t n)9 void *memrchr(const void *s, int c, size_t n) {
10 const char *start = s;
11 const char *end = &start[n]; //one too much on purpose
12 while(--end >= start) { //and this is why
13 if(c == *end) {
14 return (void *)end;
15 }
16 }
17 return NULL;
18 }
19 #endif
20
nchan_ngx_str_match(ngx_str_t * str1,ngx_str_t * str2)21 int nchan_ngx_str_match(ngx_str_t *str1, ngx_str_t *str2) {
22 if(str1 == str2) {
23 return 1;
24 }
25 if(str1->len != str2->len) {
26 return 0;
27 }
28 if(str1->len == 0) {
29 return 1;
30 }
31 return memcmp(str1->data, str2->data, str1->len) == 0;
32 }
33
34
nchan_ngx_str_nonzero_match(ngx_str_t * str1,ngx_str_t * str2)35 int nchan_ngx_str_nonzero_match(ngx_str_t *str1, ngx_str_t *str2) {
36 if(str1->len == 0) {
37 return 0;
38 }
39 else {
40 return nchan_ngx_str_match(str1, str2);
41 }
42 }
43
nchan_strscanstr(u_char ** cur,ngx_str_t * find,u_char * last)44 int nchan_strscanstr(u_char **cur, ngx_str_t *find, u_char *last) {
45 //inspired by ngx_strnstr
46 char *s2 = (char *)find->data;
47 u_char *s1 = *cur;
48 size_t len = last - s1;
49 u_char c1, c2;
50 size_t n;
51 c2 = *(u_char *) s2++;
52 n = find->len - 1;
53 do {
54 do {
55 if (len-- == 0) {
56 return 0;
57 }
58 c1 = *s1++;
59 if (c1 == 0) {
60 return 0;
61 }
62 } while (c1 != c2);
63 if (n > len) {
64 return 0;
65 }
66 } while (ngx_strncmp(s1, (u_char *) s2, n) != 0);
67 *cur = s1 + n;
68 return 1;
69 }
70
ngx_http_complex_value_noalloc(ngx_http_request_t * r,ngx_http_complex_value_t * val,ngx_str_t * value,size_t maxlen)71 ngx_int_t ngx_http_complex_value_noalloc(ngx_http_request_t *r, ngx_http_complex_value_t *val, ngx_str_t *value, size_t maxlen) {
72 size_t len;
73 ngx_http_script_code_pt code;
74 ngx_http_script_len_code_pt lcode;
75 ngx_http_script_engine_t e;
76
77 if (val->lengths == NULL) {
78 *value = val->value;
79 return NGX_OK;
80 }
81
82 ngx_http_script_flush_complex_value(r, val);
83
84 ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
85
86 e.ip = val->lengths;
87 e.request = r;
88 e.flushed = 1;
89
90 len = 0;
91
92 while (*(uintptr_t *) e.ip) {
93 lcode = *(ngx_http_script_len_code_pt *) e.ip;
94 len += lcode(&e);
95 }
96
97 if(len > maxlen) {
98 return NGX_ERROR;
99 }
100
101 value->len = len;
102
103 e.ip = val->values;
104 e.pos = value->data;
105 e.buf = *value;
106
107 while (*(uintptr_t *) e.ip) {
108 code = *(ngx_http_script_code_pt *) e.ip;
109 code((ngx_http_script_engine_t *) &e);
110 }
111
112 *value = e.buf;
113
114 return NGX_OK;
115 }
116
ngx_http_complex_value_alloc(ngx_http_request_t * r,ngx_http_complex_value_t * val,ngx_str_t * value,size_t maxlen)117 ngx_int_t ngx_http_complex_value_alloc(ngx_http_request_t *r, ngx_http_complex_value_t *val, ngx_str_t *value, size_t maxlen) {
118 size_t len;
119 ngx_http_script_code_pt code;
120 ngx_http_script_len_code_pt lcode;
121 ngx_http_script_engine_t e;
122
123 if (val->lengths == NULL) {
124 *value = val->value;
125 return NGX_OK;
126 }
127
128 ngx_http_script_flush_complex_value(r, val);
129
130 ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
131
132 e.ip = val->lengths;
133 e.request = r;
134 e.flushed = 1;
135
136 len = 0;
137
138 while (*(uintptr_t *) e.ip) {
139 lcode = *(ngx_http_script_len_code_pt *) e.ip;
140 len += lcode(&e);
141 }
142
143 if(len > maxlen) {
144 return NGX_ERROR;
145 }
146
147 if((value->data = ngx_alloc(len, r->connection->log)) == NULL) {
148 return NGX_ERROR;
149 }
150
151 value->len = len;
152
153 e.ip = val->values;
154 e.pos = value->data;
155 e.buf = *value;
156
157 while (*(uintptr_t *) e.ip) {
158 code = *(ngx_http_script_code_pt *) e.ip;
159 code((ngx_http_script_engine_t *) &e);
160 }
161
162 *value = e.buf;
163
164 return NGX_OK;
165 }
166
ngx_http_complex_value_free(ngx_str_t * value)167 ngx_int_t ngx_http_complex_value_free(ngx_str_t *value) {
168 ngx_free(value->data);
169 value->data = NULL;
170 value->len = 0;
171 return NGX_OK;
172 }
173
ngx_http_complex_value_custom_pool(ngx_http_request_t * r,ngx_http_complex_value_t * val,ngx_str_t * value,ngx_pool_t * pool)174 ngx_int_t ngx_http_complex_value_custom_pool(ngx_http_request_t *r, ngx_http_complex_value_t *val, ngx_str_t *value, ngx_pool_t *pool) {
175 size_t len;
176 ngx_http_script_code_pt code;
177 ngx_http_script_len_code_pt lcode;
178 ngx_http_script_engine_t e;
179
180 if (val->lengths == NULL) {
181 *value = val->value;
182 return NGX_OK;
183 }
184
185 ngx_http_script_flush_complex_value(r, val);
186
187 ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
188
189 e.ip = val->lengths;
190 e.request = r;
191 e.flushed = 1;
192
193 len = 0;
194
195 while (*(uintptr_t *) e.ip) {
196 lcode = *(ngx_http_script_len_code_pt *) e.ip;
197 len += lcode(&e);
198 }
199
200 value->data = ngx_palloc(pool, len);
201 if(value->data == NULL) {
202 nchan_log_error("couldn't palloc for ngx_http_complex_value_custom_pool");
203 return NGX_ERROR;
204 }
205 value->len = len;
206
207 e.ip = val->values;
208 e.pos = value->data;
209 e.buf = *value;
210
211 while (*(uintptr_t *) e.ip) {
212 code = *(ngx_http_script_code_pt *) e.ip;
213 code((ngx_http_script_engine_t *) &e);
214 }
215
216 *value = e.buf;
217
218 return NGX_OK;
219 }
220
nchan_strsplit(u_char ** s1,ngx_str_t * sub,u_char * last_char)221 u_char *nchan_strsplit(u_char **s1, ngx_str_t *sub, u_char *last_char) {
222 u_char *delim = sub->data;
223 size_t delim_sz = sub->len;
224 u_char *last = last_char - delim_sz;
225 u_char *cur;
226
227 for(cur = *s1; cur < last; cur++) {
228 if(ngx_strncmp(cur, delim, delim_sz) == 0) {
229 *s1 = cur + delim_sz;
230 return cur;
231 }
232 }
233 *s1 = last_char;
234 if(cur == last) {
235 return last_char;
236 }
237 else if(cur > last) {
238 return NULL;
239 }
240 assert(0);
241 return NULL;
242 }
243
nchan_get_header_value(ngx_http_request_t * r,ngx_str_t header_name)244 ngx_str_t *nchan_get_header_value(ngx_http_request_t * r, ngx_str_t header_name) {
245 ngx_uint_t i;
246 ngx_list_part_t *part = &r->headers_in.headers.part;
247 ngx_table_elt_t *header= part->elts;
248
249 for (i = 0; /* void */ ; i++) {
250 if (i >= part->nelts) {
251 if (part->next == NULL) {
252 break;
253 }
254 part = part->next;
255 header = part->elts;
256 i = 0;
257 }
258 if (header[i].key.len == header_name.len
259 && ngx_strncasecmp(header[i].key.data, header_name.data, header[i].key.len) == 0) {
260 return &header[i].value;
261 }
262 }
263 return NULL;
264 }
265
nchan_get_header_value_origin(ngx_http_request_t * r,nchan_request_ctx_t * ctx)266 ngx_str_t *nchan_get_header_value_origin(ngx_http_request_t *r, nchan_request_ctx_t *ctx) {
267 ngx_str_t *origin_header;
268 static ngx_str_t empty_str = ngx_string("");
269 if(!ctx) {
270 ctx = ngx_http_get_module_ctx(r, ngx_nchan_module);
271 }
272
273 if(!ctx->request_origin_header) {
274 if((origin_header = nchan_get_header_value(r, NCHAN_HEADER_ORIGIN)) != NULL) {
275 ctx->request_origin_header = origin_header;
276 }
277 else {
278 ctx->request_origin_header = &empty_str;
279 }
280 }
281
282 return ctx->request_origin_header == &empty_str ? NULL : ctx->request_origin_header;
283 }
284
nchan_get_accept_header_value(ngx_http_request_t * r)285 ngx_str_t *nchan_get_accept_header_value(ngx_http_request_t *r) {
286 #if (NGX_HTTP_HEADERS)
287 if(r->headers_in.accept == NULL) {
288 return NULL;
289 }
290 else {
291 return &r->headers_in.accept->value;
292 }
293 #else
294 ngx_str_t accept_header_name = ngx_string("Accept");
295 return nchan_get_header_value(r, accept_header_name);
296 #endif
297 }
298
nchan_strmatch_va_list(ngx_str_t * val,ngx_int_t n,va_list args)299 static int nchan_strmatch_va_list(ngx_str_t *val, ngx_int_t n, va_list args) {
300 u_char *match;
301 ngx_int_t i;
302 for(i=0; i<n; i++) {
303 match = va_arg(args, u_char *);
304 if(ngx_strncasecmp(val->data, match, val->len)==0) {
305 return 1;
306 }
307 }
308 return 0;
309 }
310
nchan_strmatch(ngx_str_t * val,ngx_int_t n,...)311 int nchan_strmatch(ngx_str_t *val, ngx_int_t n, ...) {
312 int rc;
313 va_list args;
314 va_start(args, n);
315 rc = nchan_strmatch_va_list(val, n, args);
316 va_end(args);
317 return rc;
318 }
319
nchan_cstrmatch(char * cstr,ngx_int_t n,...)320 int nchan_cstrmatch(char *cstr, ngx_int_t n, ...) {
321 int rc;
322 va_list args;
323 ngx_str_t str;
324 str.data = (u_char *)cstr;
325 str.len = strlen(cstr);
326 va_start(args, n);
327 rc = nchan_strmatch_va_list(&str, n, args);
328 va_end(args);
329 return rc;
330 }
331
nchan_cstr_startswith(char * cstr,char * match)332 int nchan_cstr_startswith(char *cstr, char *match) {
333 for(/*void*/; *match != '\0'; cstr++, match++) {
334 if(*cstr == '\0' || *cstr != *match)
335 return 0;
336 }
337 return 1;
338 }
nchan_str_startswith(ngx_str_t * str,const char * match)339 int nchan_str_startswith(ngx_str_t *str, const char *match) {
340 size_t sz = strlen(match);
341 if(sz > str->len) {
342 return 0;
343 }
344 if(memcmp(str->data, match, sz) == 0) {
345 return 1;
346 }
347 return 0;
348 }
349
nchan_str_after(ngx_str_t ** str,const char * match)350 int nchan_str_after(ngx_str_t **str, const char *match) {
351 if(nchan_str_startswith(*str, match)) {
352 size_t sz = strlen(match);
353 (*str)->data+=sz;
354 (*str)->len-=sz;
355 return 1;
356 }
357 else {
358 return 0;
359 }
360 }
361
362
nchan_scan_split_by_chr(u_char ** cur,size_t max_len,ngx_str_t * str,u_char chr)363 void nchan_scan_split_by_chr(u_char **cur, size_t max_len, ngx_str_t *str, u_char chr) {
364 u_char *shortest = NULL;
365 u_char *start = *cur;
366 u_char *tmp_cur;
367
368 for(tmp_cur = *cur; shortest == NULL && (tmp_cur == *cur || tmp_cur - start < (ssize_t )max_len); tmp_cur++) {
369 if(*tmp_cur == chr) {
370 shortest = tmp_cur;
371 }
372 }
373 if(shortest) {
374 str->data = (u_char *)*cur;
375 str->len = shortest - *cur;
376 *cur = shortest + 1;
377 }
378 else if(tmp_cur - start == (ssize_t )max_len) {
379 str->data = start;
380 str->len = max_len;
381 *cur = start + max_len;
382 }
383 else {
384 str->data = NULL;
385 str->len = 0;
386 }
387 }
388
nchan_scan_until_chr_on_line(ngx_str_t * line,ngx_str_t * str,u_char chr)389 void nchan_scan_until_chr_on_line(ngx_str_t *line, ngx_str_t *str, u_char chr) {
390 u_char *cur;
391 //ERR("rest_line: \"%V\"", line);
392 cur = (u_char *)memchr(line->data, chr, line->len);
393 if(!cur) {
394 if(str) {
395 *str = *line;
396 }
397 line->data += line->len;
398 line->len = 0;
399 }
400 else {
401 if(str) {
402 str->data = line->data;
403 str->len = (cur - line->data);
404 }
405 line->len -= (cur - line->data) + 1;
406 line->data += (cur - line->data) + 1;
407 }
408 //ERR("str: \"%V\"", str);
409 }
410
nchan_strcpy(ngx_str_t * dst,ngx_str_t * src,size_t maxlen)411 void nchan_strcpy(ngx_str_t *dst, ngx_str_t *src, size_t maxlen) {
412 size_t len = src->len > maxlen && maxlen > 0 ? maxlen : src->len;
413 memcpy(dst->data, src->data, len);
414 dst->len = len;
415 }
416
ensure_last_buf(ngx_pool_t * pool,ngx_buf_t * buf)417 static ngx_buf_t *ensure_last_buf(ngx_pool_t *pool, ngx_buf_t *buf) {
418 ngx_buf_t *cbuf;
419 if(buf->last_buf == 1) {
420 return buf;
421 }
422 else {
423 cbuf = ngx_create_temp_buf(pool,sizeof(*cbuf));
424 *cbuf = *buf;
425 cbuf->last_buf = 1;
426 return cbuf;
427 }
428 }
429
nchan_get_allow_origin_value(ngx_http_request_t * r,nchan_loc_conf_t * cf,nchan_request_ctx_t * ctx)430 ngx_str_t *nchan_get_allow_origin_value(ngx_http_request_t *r, nchan_loc_conf_t *cf, nchan_request_ctx_t *ctx) {
431 if(!ctx) ctx = ngx_http_get_module_ctx(r, ngx_nchan_module);
432 if(!cf) cf = ngx_http_get_module_loc_conf(r, ngx_nchan_module);
433 if(!ctx->allow_origin && cf->allow_origin) {
434 ngx_str_t *allow_origin = ngx_palloc(r->pool, sizeof(*allow_origin));
435 ngx_http_complex_value(r, cf->allow_origin, allow_origin);
436 ctx->allow_origin = allow_origin;
437 }
438
439 return ctx->allow_origin;
440 }
441
nchan_match_origin_header(ngx_http_request_t * r,nchan_loc_conf_t * cf,nchan_request_ctx_t * ctx)442 int nchan_match_origin_header(ngx_http_request_t *r, nchan_loc_conf_t *cf, nchan_request_ctx_t *ctx) {
443 ngx_str_t *origin_header;
444 ngx_str_t *allow_origin;
445 ngx_str_t curstr;
446 u_char *cur, *end;
447
448 if(cf->allow_origin == NULL) { //default is to always match
449 return 1;
450 }
451
452 if((origin_header = nchan_get_header_value_origin(r, ctx)) == NULL) {
453 return 1;
454 }
455
456 allow_origin = nchan_get_allow_origin_value(r, cf, ctx);
457
458 cur = allow_origin->data;
459 end = cur + allow_origin->len;
460
461 while(cur < end) {
462 nchan_scan_split_by_chr(&cur, end - cur, &curstr, ' ');
463 if(curstr.len == 1 && curstr.data[0] == '*') {
464 return 1;
465 }
466 if(nchan_ngx_str_match(&curstr, origin_header)) {
467 return 1;
468 }
469 }
470
471 return 0;
472 }
473
474 // this function adapted from push stream module. thanks Wandenberg Peixoto <wandenberg@gmail.com> and Rogério Carvalho Schneider <stockrt@gmail.com>
nchan_chain_to_single_buffer(ngx_pool_t * pool,ngx_chain_t * chain,size_t content_length)475 ngx_buf_t * nchan_chain_to_single_buffer(ngx_pool_t *pool, ngx_chain_t *chain, size_t content_length) {
476 ngx_buf_t *buf = NULL;
477 ssize_t n;
478 size_t len;
479
480 if (chain->next == NULL) {
481 return ensure_last_buf(pool, chain->buf);
482 }
483 //nchan_log_error("multiple buffers in request, need memcpy :(");
484 if (chain->buf->in_file) {
485 if (ngx_buf_in_memory(chain->buf)) {
486 nchan_log_error("can't handle a buffer in a temp file and in memory ");
487 }
488 if (chain->next != NULL) {
489 nchan_log_error("error reading request body with multiple ");
490 }
491 return ensure_last_buf(pool, chain->buf);
492 }
493 buf = ngx_create_temp_buf(pool, content_length + 1);
494 if (buf != NULL) {
495 ngx_memset(buf->start, '\0', content_length + 1);
496 while ((chain != NULL) && (chain->buf != NULL)) {
497 len = ngx_buf_size(chain->buf);
498 // if buffer is equal to content length all the content is in this buffer
499 if (len >= content_length) {
500 buf->start = buf->pos;
501 buf->last = buf->pos;
502 len = content_length;
503 }
504 if (chain->buf->in_file) {
505 n = ngx_read_file(chain->buf->file, buf->start, len, 0);
506 if (n == NGX_FILE_ERROR) {
507 nchan_log_error("cannot read file with request body");
508 return NULL;
509 }
510 buf->last = buf->last + len;
511 ngx_delete_file(chain->buf->file->name.data);
512 chain->buf->file->fd = NGX_INVALID_FILE;
513 } else {
514 buf->last = ngx_copy(buf->start, chain->buf->pos, len);
515 }
516
517 chain = chain->next;
518 buf->start = buf->last;
519 }
520 buf->last_buf = 1;
521 }
522 return buf;
523 }
524
nchan_init_timer(ngx_event_t * ev,void (* cb)(ngx_event_t *),void * pd)525 ngx_int_t nchan_init_timer(ngx_event_t *ev, void (*cb)(ngx_event_t *), void *pd) {
526 #if nginx_version >= 1008000
527 ev->cancelable = 1;
528 #endif
529 ev->handler = cb;
530 ev->data = pd;
531 ev->log = ngx_cycle->log;
532 return NGX_OK;
533 }
534
535
536 typedef struct {
537 ngx_event_t ev;
538 void (*cb)(void *pd);
539 } oneshot_timer_t;
540
oneshot_timer_callback(ngx_event_t * ev)541 void oneshot_timer_callback(ngx_event_t *ev) {
542 oneshot_timer_t *timer = container_of(ev, oneshot_timer_t, ev);
543 timer->cb(ev->data);
544 ngx_free(timer);
545 }
546
nchan_add_oneshot_timer(void (* cb)(void *),void * pd,ngx_msec_t delay)547 void *nchan_add_oneshot_timer(void (*cb)(void *), void *pd, ngx_msec_t delay) {
548 oneshot_timer_t *timer = ngx_alloc(sizeof(*timer), ngx_cycle->log);
549 ngx_memzero(&timer->ev, sizeof(timer->ev));
550 timer->cb = cb;
551 nchan_init_timer(&timer->ev, oneshot_timer_callback, pd);
552 ngx_add_timer(&timer->ev, delay);
553 return timer;
554 }
555
nchan_abort_oneshot_timer(void * t)556 void nchan_abort_oneshot_timer(void *t) {
557 oneshot_timer_t *timer = t;
558 if(timer->ev.timer_set) {
559 ngx_del_timer(&timer->ev);
560 }
561 ngx_free(timer);
562 }
563
564 typedef struct {
565 ngx_event_t ev;
566 ngx_msec_t wait;
567 ngx_int_t (*cb)(void *pd);
568 } interval_timer_t;
569
interval_timer_callback(ngx_event_t * ev)570 void interval_timer_callback(ngx_event_t *ev) {
571 interval_timer_t *timer = container_of(ev, interval_timer_t, ev);
572 ngx_int_t rc = timer->cb(ev->data);
573 if((rc == NGX_OK || rc == NGX_AGAIN) && ev->timedout) {
574 ev->timedout=0;
575 ngx_add_timer(&timer->ev, timer->wait);
576 }
577 else if(rc > NGX_OK && ev->timedout) {
578 timer->wait = rc;
579 ev->timedout=0;
580 ngx_add_timer(&timer->ev, timer->wait);
581 }
582 else { //rc < 0, != NGX_EAGAIN
583 //NGX_DONE, for example, or NGX_ABORT
584 ngx_free(timer);
585 }
586 }
587
nchan_add_interval_timer(ngx_int_t (* cb)(void *),void * pd,ngx_msec_t interval)588 void *nchan_add_interval_timer(ngx_int_t (*cb)(void *), void *pd, ngx_msec_t interval) {
589 interval_timer_t *timer = ngx_alloc(sizeof(*timer), ngx_cycle->log);
590 ngx_memzero(&timer->ev, sizeof(timer->ev));
591 timer->cb = cb;
592 timer->wait = interval;
593 nchan_init_timer(&timer->ev, interval_timer_callback, pd);
594 ngx_add_timer(&timer->ev, interval);
595 return timer;
596 }
nchan_abort_interval_timer(void * t)597 void nchan_abort_interval_timer(void *t) {
598 interval_timer_t *timer = t;
599 if(timer->ev.timer_set) {
600 ngx_del_timer(&timer->ev);
601 }
602 ngx_free(timer);
603 }
604
605
nchan_urldecode_str(ngx_http_request_t * r,ngx_str_t * str)606 ngx_str_t *nchan_urldecode_str(ngx_http_request_t *r, ngx_str_t *str) {
607 ngx_str_t *out;
608 u_char *dst, *src;
609 if(memchr(str->data, '%', str->len) == NULL) {
610 return str;
611 }
612
613 out = ngx_palloc(r->pool, sizeof(*out) + str->len);
614 out->data = (u_char *)&out[1];
615
616 dst = out->data;
617 src = str->data;
618
619 ngx_unescape_uri(&dst, &src, str->len, 0);
620 out->len = dst - out->data;
621
622 return out;
623 }
624
nchan_ngx_str_char_substr(ngx_str_t * str,char * substr,size_t sz)625 int nchan_ngx_str_char_substr(ngx_str_t *str, char *substr, size_t sz) {
626 //naive non-null-terminated string matcher. don't use it in tight loops!
627 char *cur = (char *)str->data;
628 size_t len;
629 for(len = str->len; len >= sz; cur++, len--) {
630 if(strncmp(cur, substr, sz) == 0) {
631 return 1;
632 }
633 }
634 return 0;
635 }
636
nchan_cstr_match_line(const char * cstr,const char * line)637 int nchan_cstr_match_line(const char *cstr, const char *line) {
638 ngx_str_t rest;
639 if(nchan_get_rest_of_line_in_cstr(cstr, line, &rest)) {
640 return rest.len == 0 ? 1 : 0;
641 }
642 return 0;
643 }
644
nchan_get_rest_of_line_in_cstr(const char * cstr,const char * line_start,ngx_str_t * rest)645 int nchan_get_rest_of_line_in_cstr(const char *cstr, const char *line_start, ngx_str_t *rest) {
646 const char *cur = cstr;
647 const char *end = cur + strlen(cur);
648
649 while(cur && cur < end) {
650 char *first = strstr(cstr, line_start);
651 if(!first) { //not found at all
652 if(rest) rest->len = 0;
653 return 0;
654 }
655
656 if(first == cstr || first[-1]=='\n') { //start of line or start of string
657 const char *last = strchr(first, '\n');
658 if(!last) { // end of string
659 last = end;
660 }
661 else if(last > first && last[-1]=='\r') {
662 last--;
663 }
664 if(rest) {
665 rest->len = last - first - strlen(line_start);
666 rest->data = (u_char *)first + strlen(line_start);
667 }
668 return 1;
669 }
670 else {
671 //we found a match in the middle of the string
672 cur = strchr(cur, '\n'); //move on, redo
673 }
674 }
675 return 0;
676 }
677
678 //converts string to positive double float
nchan_atof(u_char * line,ssize_t n)679 static double nchan_atof(u_char *line, ssize_t n) {
680 ssize_t cutoff, cutlim;
681 double value = 0;
682
683 u_char *decimal, *cur, *last = line + n;
684
685 if (n == 0) {
686 return NGX_ERROR;
687 }
688
689 cutoff = NGX_MAX_SIZE_T_VALUE / 10;
690 cutlim = NGX_MAX_SIZE_T_VALUE % 10;
691
692 decimal = memchr(line, '.', n);
693
694 if(decimal == NULL) {
695 decimal = line + n;
696 }
697
698 for (n = decimal - line; n-- > 0; line++) {
699 if (*line < '0' || *line > '9') {
700 return NGX_ERROR;
701 }
702
703 if (value >= cutoff && (value > cutoff || (*line - '0') > cutlim)) {
704 return NGX_ERROR;
705 }
706
707 value = value * 10 + (*line - '0');
708 }
709
710 double decval = 0;
711
712
713
714 for(cur = (decimal - last) > 10 ? decimal + 10 : last-1; cur > decimal && cur < last; cur--) {
715 if (*cur < '0' || *cur > '9') {
716 return NGX_ERROR;
717 }
718 decval = decval / 10 + (*cur - '0');
719 }
720 value = value + decval/10;
721
722 return value;
723 }
724
nchan_parse_size(ngx_str_t * line)725 ssize_t nchan_parse_size(ngx_str_t *line) {
726 u_char unit;
727 size_t len;
728 ssize_t size, scale, max;
729 double floaty;
730
731 len = line->len;
732 unit = line->data[len - 1];
733
734 switch (unit) {
735 case 'K':
736 case 'k':
737 len--;
738 max = NGX_MAX_SIZE_T_VALUE / 1024;
739 scale = 1024;
740 break;
741
742 case 'M':
743 case 'm':
744 len--;
745 max = NGX_MAX_SIZE_T_VALUE / (1024 * 1024);
746 scale = 1024 * 1024;
747 break;
748
749 case 'G':
750 case 'g':
751 len--;
752 max = NGX_MAX_SIZE_T_VALUE / (1024 * 1024 * 1024);
753 scale = 1024 * 1024 * 1024;
754 break;
755
756 default:
757 max = NGX_MAX_SIZE_T_VALUE;
758 scale = 1;
759 }
760
761 floaty = nchan_atof(line->data, len);
762
763 if (floaty == NGX_ERROR || floaty > max) {
764 return NGX_ERROR;
765 }
766
767 size = floaty * scale;
768
769 return size;
770 }
771
nchan_conf_set_size_slot(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)772 char *nchan_conf_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
773 char *p = conf;
774
775 size_t *sp;
776 ngx_str_t *value;
777 ngx_conf_post_t *post;
778
779
780 sp = (size_t *) (p + cmd->offset);
781 if (*sp != NGX_CONF_UNSET_SIZE) {
782 return "is duplicate";
783 }
784
785 value = cf->args->elts;
786
787 *sp = nchan_parse_size(&value[1]);
788 if (*sp == (size_t) NGX_ERROR) {
789 return "invalid value";
790 }
791
792 if (cmd->post) {
793 post = cmd->post;
794 return post->post_handler(cf, post, sp);
795 }
796
797 return NGX_CONF_OK;
798 }
799
ngx_init_set_membuf(ngx_buf_t * buf,u_char * start,u_char * end)800 void ngx_init_set_membuf(ngx_buf_t *buf, u_char *start, u_char *end) {
801 ngx_memzero(buf, sizeof(*buf));
802 buf->start = start;
803 buf->pos = start;
804 buf->end = end;
805 buf->last = end;
806 buf->memory = 1;
807 }
808
ngx_init_set_membuf_str(ngx_buf_t * buf,ngx_str_t * str)809 void ngx_init_set_membuf_str(ngx_buf_t *buf, ngx_str_t *str) {
810 ngx_memzero(buf, sizeof(*buf));
811 buf->start = str->data;
812 buf->pos = str->data;
813 buf->end = str->data + str->len;
814 buf->last = buf->end;
815 buf->memory = 1;
816 }
817
818
819 #if (NGX_ZLIB)
820 static z_stream *deflate_zstream = NULL;
821 static z_stream *deflate_dummy_zstream = NULL;
822
823 static ngx_path_t *message_temp_path = NULL;
824
nchan_common_deflate_init(nchan_main_conf_t * mcf)825 ngx_int_t nchan_common_deflate_init(nchan_main_conf_t *mcf) {
826 int rc;
827 int windowBits;
828 message_temp_path = mcf->message_temp_path;
829
830 if((deflate_zstream = ngx_calloc(sizeof(*deflate_zstream), ngx_cycle->log)) == NULL) {
831 nchan_log_error("couldn't allocate deflate stream.");
832 return NGX_ERROR;
833 }
834
835 windowBits = -mcf->zlib_params.windowBits; //negative to disable headers and use a raw stream
836
837 deflate_zstream->zalloc = Z_NULL;
838 deflate_zstream->zfree = Z_NULL;
839 deflate_zstream->opaque = Z_NULL;
840
841 rc = deflateInit2(deflate_zstream, (int) mcf->zlib_params.level, Z_DEFLATED, windowBits, mcf->zlib_params.memLevel, mcf->zlib_params.strategy);
842 if(rc != Z_OK) {
843 nchan_log_error("couldn't initialize deflate stream.");
844 deflate_zstream = NULL;
845 return NGX_ERROR;
846 }
847
848 if((deflate_dummy_zstream = ngx_calloc(sizeof(*deflate_dummy_zstream), ngx_cycle->log)) == NULL) {
849 nchan_log_error("couldn't allocate dummy deflate stream.");
850 return NGX_ERROR;
851 }
852
853 deflate_dummy_zstream->zalloc = Z_NULL;
854 deflate_dummy_zstream->zfree = Z_NULL;
855 deflate_dummy_zstream->opaque = Z_NULL;
856
857 rc = deflateInit2(deflate_dummy_zstream, 0, Z_DEFLATED, -9, 1, Z_DEFAULT_STRATEGY);
858 if(rc != Z_OK) {
859 nchan_log_error("couldn't initialize deflate stream.");
860 deflate_dummy_zstream = NULL;
861 return NGX_ERROR;
862 }
863
864 return NGX_OK;
865 }
866
nchan_common_deflate_shutdown(void)867 ngx_int_t nchan_common_deflate_shutdown(void) {
868 if(deflate_zstream) {
869 deflateEnd(deflate_zstream);
870 ngx_free(deflate_zstream);
871 deflate_zstream = NULL;
872 }
873
874 if(deflate_dummy_zstream) {
875 deflateEnd(deflate_dummy_zstream);
876 ngx_free(deflate_dummy_zstream);
877 deflate_dummy_zstream = NULL;
878 }
879 return NGX_OK;
880 }
881
882 #define ZLIB_CHUNK 16384
883
make_temp_file(ngx_http_request_t * r,ngx_pool_t * pool)884 static ngx_temp_file_t *make_temp_file(ngx_http_request_t *r, ngx_pool_t *pool) {
885 ngx_temp_file_t *tf;
886 ngx_path_t *temp_path;
887 if(r) {
888 ngx_http_core_loc_conf_t *clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
889 temp_path = clcf->client_body_temp_path;
890 }
891 else {
892 temp_path = message_temp_path;
893 }
894 tf = ngx_pcalloc(pool, sizeof(*tf));
895 if(!tf) {
896 if(r) {
897 nchan_log_request_error(r, "failed to allocate space for temp_file struct.");
898 } else {
899 nchan_log_error("failed to allocate space for temp_file struct.");
900 }
901 return NULL;
902 }
903 tf->file.fd = NGX_INVALID_FILE;
904 if(r) {
905 tf->file.log = r->connection->log;
906 }
907 else if(pool && pool->log) {
908 tf->file.log = pool->log;
909 }
910 else {
911 tf->file.log = ngx_cycle->log;
912 }
913 tf->path = temp_path;
914 tf->pool = pool;
915 tf->persistent = 1;
916 tf->clean = 0; //this will close the file on pool cleanup
917 tf->access = 0;
918 if(ngx_create_temp_file(&tf->file, tf->path, tf->pool, tf->persistent, tf->clean, tf->access) != NGX_OK) {
919 if(r) {
920 nchan_log_request_error(r, "failed to create temp file for deflated message");
921 } else {
922 nchan_log_error("failed to create temp file for deflated message");
923 }
924 return NULL;
925 }
926 return tf;
927 }
928
nchan_common_deflate(ngx_buf_t * in,ngx_http_request_t * r,ngx_pool_t * pool)929 ngx_buf_t *nchan_common_deflate(ngx_buf_t *in, ngx_http_request_t *r, ngx_pool_t *pool) {
930 ngx_str_t mm_instr;
931 int mmapped = 0;
932 ngx_temp_file_t *tf = NULL;
933
934 int rc;
935 ngx_buf_t *out = NULL;
936 u_char outbuf[ZLIB_CHUNK];
937 unsigned have = 0;
938 off_t written = 0;
939
940 //input
941
942 if(ngx_buf_in_memory(in)) {
943 deflate_zstream->avail_in = ngx_buf_size(in);
944 deflate_zstream->next_in = in->pos;
945 }
946 else {
947 ngx_fd_t fd = in->file->fd == NGX_INVALID_FILE ? nchan_fdcache_get(&in->file->name) : in->file->fd;
948 mm_instr.len = in->file_last - in->file_pos;
949 mm_instr.data = mmap(NULL, mm_instr.len, PROT_READ, MAP_SHARED, fd, in->file_pos);
950 if (mm_instr.data == MAP_FAILED) {
951 nchan_log_request_error(r, "failed to mmap input file for deflated message");
952 return NULL;
953 }
954 deflate_zstream->avail_in = mm_instr.len;
955 deflate_zstream->next_in = mm_instr.data;
956 mmapped = 1;
957 }
958
959 //output
960
961 do {
962 deflate_zstream->avail_out = ZLIB_CHUNK;
963 deflate_zstream->next_out = outbuf;
964
965 rc = deflate(deflate_zstream, Z_SYNC_FLUSH);
966 assert(rc != Z_STREAM_ERROR);
967
968 have = ZLIB_CHUNK - deflate_zstream->avail_out;
969
970 if(deflate_zstream->avail_out == 0 && tf == NULL) {
971 //if we filled up the buffer, let's start dumping to a file.
972 tf = make_temp_file(r, pool);
973 if(tf == NULL) { //couldn't make temp file for some reason
974 nchan_log_request_error(r, "failed to allocate output buf for deflated message");
975 deflateReset(deflate_zstream);
976 return NULL;
977 }
978 }
979 if(tf) {
980 ngx_write_file(&tf->file, outbuf, have, written);
981 }
982
983 written += have;
984 } while(rc != Z_BUF_ERROR);
985
986 if(mmapped) {
987 munmap(mm_instr.data, mm_instr.len);
988 }
989
990 if((out = ngx_palloc(pool, sizeof(*out))) == NULL) {
991 nchan_log_request_error(r, "failed to allocate output buf for deflated message");
992 deflateReset(deflate_zstream);
993 return NULL;
994 }
995
996 if(written > 4) { //there will be a 00 00 FF FF chunk at the end. remove it as the permessage-deflate spec demands
997 written -= 4;
998 }
999
1000 if(tf) { //using a tempfile
1001 //thanks to tf->clean = 0, file will be closed on pool cleanup
1002 ngx_memzero(out, sizeof(*out));
1003 out->file_pos = 0;
1004 out->file_last = written;
1005 out->in_file = 1;
1006 out->file = &tf->file;
1007 }
1008 else {
1009 u_char *outpooled = ngx_palloc(pool, written);
1010 if(!outpooled) {
1011 nchan_log_request_error(r, "failed to allocate output data for deflated message");
1012 deflateReset(deflate_zstream);
1013 return NULL;
1014 }
1015 ngx_memcpy(outpooled, outbuf, written);
1016 ngx_init_set_membuf(out, outpooled, outpooled + written);
1017 }
1018 out->last_buf = 1;
1019
1020 deflateReset(deflate_zstream);
1021 return out;
1022 }
1023
nchan_inflate(z_stream * stream,ngx_buf_t * in,ngx_http_request_t * r,ngx_pool_t * pool)1024 ngx_buf_t *nchan_inflate(z_stream *stream, ngx_buf_t *in, ngx_http_request_t *r, ngx_pool_t *pool) {
1025 ngx_str_t mm_instr = {0, NULL};
1026 int mmapped = 0;
1027 ngx_temp_file_t *tf = NULL;
1028
1029 int rc;
1030 ngx_buf_t *out = NULL;
1031 u_char outbuf[ZLIB_CHUNK];
1032 unsigned have = 0;
1033 off_t written = 0;
1034 int trailer_appended = 0;
1035
1036 //input
1037 if(ngx_buf_in_memory(in)) {
1038 stream->avail_in = ngx_buf_size(in);
1039 stream->next_in = in->pos;
1040 }
1041 else {
1042 ngx_fd_t fd = in->file->fd == NGX_INVALID_FILE ? nchan_fdcache_get(&in->file->name) : in->file->fd;
1043 mm_instr.len = in->file_last - in->file_pos;
1044 mm_instr.data = mmap(NULL, mm_instr.len, PROT_READ, MAP_SHARED, fd, in->file_pos);
1045 if (mm_instr.data == MAP_FAILED) {
1046 nchan_log_request_error(r, "failed to mmap input file for deflated message");
1047 return NULL;
1048 }
1049 stream->avail_in = mm_instr.len;
1050 stream->next_in = mm_instr.data;
1051 mmapped = 1;
1052 }
1053
1054 //output
1055
1056 do {
1057 stream->avail_out = ZLIB_CHUNK;
1058 stream->next_out = outbuf;
1059
1060 if(stream->avail_in == 0 && !trailer_appended) {
1061 stream->avail_in = 4;
1062 stream->next_in = (u_char *)"\x00\x00\xFF\xFF";
1063 trailer_appended = 1;
1064 }
1065 rc = inflate(stream, trailer_appended ? Z_SYNC_FLUSH : Z_NO_FLUSH);
1066 assert(rc != Z_STREAM_ERROR);
1067 switch (rc) {
1068 case Z_DATA_ERROR:
1069 nchan_log_request_error(r, "inflate error %d: %s", rc, stream->msg);
1070 break;
1071 case Z_NEED_DICT:
1072 case Z_MEM_ERROR:
1073 nchan_log_request_error(r, "inflate error %d", rc);
1074 break;
1075 }
1076
1077 have = ZLIB_CHUNK - stream->avail_out;
1078
1079 if(stream->avail_out == 0 && tf == NULL) {
1080 //if we filled up the buffer, let's start dumping to a file.
1081 tf = make_temp_file(r, pool);
1082 }
1083 if(tf) {
1084 ngx_write_file(&tf->file, outbuf, have, written);
1085 }
1086 written += have;
1087 } while(rc == Z_OK);
1088
1089 if(mmapped) {
1090 munmap(mm_instr.data, mm_instr.len);
1091 }
1092
1093 if((out = ngx_palloc(pool, sizeof(*out))) == NULL) {
1094 nchan_log_request_error(r, "failed to allocate output buf for deflated message");
1095 deflateReset(deflate_zstream);
1096 return NULL;
1097 }
1098
1099 if(tf) { //using a tempfile
1100 //thanks to tf->clean = 0, file will be closed on pool cleanup
1101 ngx_memzero(out, sizeof(*out));
1102 out->file_pos = 0;
1103 out->file_last = written;
1104 out->in_file = 1;
1105 out->file = &tf->file;
1106 }
1107 else {
1108 u_char *outpooled = ngx_palloc(pool, written);
1109 if(!outpooled) {
1110 nchan_log_request_error(r, "failed to allocate output data for deflated message");
1111 deflateReset(deflate_zstream);
1112 return NULL;
1113 }
1114 ngx_memcpy(outpooled, outbuf, written);
1115 ngx_init_set_membuf(out, outpooled, outpooled + written);
1116 }
1117 out->last_buf = 1;
1118
1119 deflateReset(deflate_zstream);
1120 return out;
1121 }
1122
1123
nchan_common_simple_deflate_internal(z_stream * strm,ngx_str_t * in,ngx_str_t * out)1124 static ngx_int_t nchan_common_simple_deflate_internal(z_stream *strm, ngx_str_t *in, ngx_str_t *out) {
1125 int rc;
1126 strm->avail_in = in->len;
1127 strm->next_in = in->data;
1128
1129 //output
1130 strm->avail_out = out->len;
1131 strm->next_out = out->data;
1132
1133 rc = deflate(strm, Z_SYNC_FLUSH);
1134 if(rc != Z_STREAM_ERROR) {
1135 out->len = strm->total_out;
1136 }
1137
1138 deflateReset(strm);
1139
1140 return rc != Z_STREAM_ERROR ? NGX_OK : NGX_ERROR;
1141 }
1142
nchan_common_simple_deflate(ngx_str_t * in,ngx_str_t * out)1143 ngx_int_t nchan_common_simple_deflate(ngx_str_t *in, ngx_str_t *out) {
1144 return nchan_common_simple_deflate_internal(deflate_zstream, in, out);
1145 }
1146
nchan_common_simple_deflate_raw_block(ngx_str_t * in,ngx_str_t * out)1147 ngx_int_t nchan_common_simple_deflate_raw_block(ngx_str_t *in, ngx_str_t *out) {
1148 return nchan_common_simple_deflate_internal(deflate_dummy_zstream, in, out);
1149 }
1150
1151 #endif
1152
nchan_need_to_deflate_message(nchan_loc_conf_t * cf)1153 ngx_flag_t nchan_need_to_deflate_message(nchan_loc_conf_t *cf) {
1154 #if (NGX_ZLIB)
1155 if(cf->redis.enabled && cf->redis.storage_mode != REDIS_MODE_BACKUP) {
1156 //redis mode ns non-backup storage mode gets the messages deflated later on
1157 return 0;
1158 }
1159 return cf->message_compression == NCHAN_MSG_COMPRESSION_WEBSOCKET_PERMESSAGE_DEFLATE;
1160 #else
1161 return 0;
1162 #endif
1163 }
1164
nchan_deflate_message_if_needed(nchan_msg_t * msg,nchan_loc_conf_t * cf,ngx_http_request_t * r,ngx_pool_t * pool)1165 ngx_int_t nchan_deflate_message_if_needed(nchan_msg_t *msg, nchan_loc_conf_t *cf, ngx_http_request_t *r, ngx_pool_t *pool) {
1166 #if (NGX_ZLIB)
1167 if(nchan_need_to_deflate_message(cf)) {
1168 msg->compressed = ngx_pcalloc(pool, sizeof(*msg->compressed));
1169 if(!msg->compressed) {
1170 if(r) {
1171 nchan_log_request_error(r, "no memory to compress message");
1172 }
1173 else {
1174 nchan_log_error("no memory to compress message");
1175 }
1176 }
1177 else {
1178 ngx_buf_t *compressed_buf = nchan_common_deflate(&msg->buf, r, pool);
1179 if(!compressed_buf) {
1180 if(r) {
1181 nchan_log_request_error(r, "failed to compress message");
1182 }
1183 else {
1184 nchan_log_error("failed to compress message");
1185 }
1186 }
1187 else {
1188 msg->compressed->compression = cf->message_compression;
1189 msg->compressed->buf = *compressed_buf;
1190 }
1191 }
1192 }
1193 return NGX_OK;
1194 #else
1195 return NGX_DECLINED;
1196 #endif
1197 }
1198
flip_uint64_if_little_endian(uint64_t value)1199 static uint64_t flip_uint64_if_little_endian(uint64_t value) {
1200 int num = 42;
1201 if (*(char *)&num == 42) {
1202 uint32_t high_part = htonl((uint32_t)(value >> 32));
1203 uint32_t low_part = htonl((uint32_t)(value & 0xFFFFFFFFLL));
1204 return (((uint64_t)low_part) << 32) | high_part;
1205 } else {
1206 return value;
1207 }
1208 }
1209
nchan_htonll(uint64_t value)1210 uint64_t nchan_htonll(uint64_t value) {
1211 return flip_uint64_if_little_endian(value);
1212 }
nchan_ntohll(uint64_t value)1213 uint64_t nchan_ntohll(uint64_t value) {
1214 return flip_uint64_if_little_endian(value);
1215 }
1216
1217 #if (NGX_DEBUG_POOL)
1218 //Copyright (C) 2015 Alibaba Group Holding Limited
1219 static ngx_str_t debug_pool_str;
ngx_http_debug_pool_str(ngx_pool_t * pool)1220 ngx_str_t *ngx_http_debug_pool_str(ngx_pool_t *pool) {
1221 u_char *p, *unit;
1222 size_t s, n, cn, ln;
1223 ngx_uint_t i;
1224 ngx_pool_stat_t *stat;
1225 static u_char charbuf[512];
1226
1227 debug_pool_str.len = 0;
1228 debug_pool_str.data = charbuf;
1229
1230 #define NGX_POOL_PID_SIZE (NGX_TIME_T_LEN + sizeof("pid:\n") - 1) /* sizeof pid_t equals time_t */
1231 #define NGX_POOL_PID_FORMAT "pid:%P\n"
1232 #define NGX_POOL_ENTRY_SIZE (48 /* func */ + 12 * 4 + sizeof("size: num: cnum: lnum: \n") - 1)
1233 #define NGX_POOL_ENTRY_FORMAT "size:%12z num:%12z cnum:%12z lnum:%12z %s\n"
1234 #define NGX_POOL_SUMMARY_SIZE (12 * 4 + sizeof("size: num: cnum: lnum: [SUMMARY]\n") - 1)
1235 #define NGX_POOL_SUMMARY_FORMAT "size:%10z%2s num:%12z cnum:%12z lnum:%12z [SUMMARY]\n"
1236
1237 p = charbuf;
1238 p = ngx_sprintf(p, NGX_POOL_PID_FORMAT, ngx_pid);
1239
1240 /* lines of entry */
1241
1242 s = n = cn = ln = 0;
1243
1244 for (i = 0; i < NGX_POOL_STATS_MAX; i++) {
1245 for (stat = ngx_pool_stats[i]; stat != NULL; stat = stat->next) {
1246 p = ngx_snprintf(p, NGX_POOL_ENTRY_SIZE, NGX_POOL_ENTRY_FORMAT,
1247 stat->size, stat->num, stat->cnum, stat->lnum,
1248 stat->func);
1249 s += stat->size;
1250 n += stat->num;
1251 cn += stat->cnum;
1252 ln += stat->lnum;
1253 }
1254 }
1255
1256 /* summary line */
1257
1258 unit = (u_char *) " B";
1259 if (s > 1024 * 1024) {
1260 s = s / (1024 * 1024);
1261 unit = (u_char *) "MB";
1262 } else if (s > 1024) {
1263 s = s / 1024;
1264 unit = (u_char *) "KB";
1265 }
1266
1267 p = ngx_snprintf(p, NGX_POOL_SUMMARY_SIZE, NGX_POOL_SUMMARY_FORMAT, s, unit, n, cn, ln);
1268
1269 debug_pool_str.len = p - debug_pool_str.data;
1270
1271 return &debug_pool_str;
1272 }
1273 #endif
1274