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