1 
2 /*
3  * Copyright (C) Yichun Zhang (agentzh)
4  */
5 
6 
7 #ifndef DDEBUG
8 #define DDEBUG 0
9 #endif
10 #include "ddebug.h"
11 
12 
13 #include "ngx_http_srcache_util.h"
14 #include "ngx_http_srcache_headers.h"
15 
16 
17 static ngx_int_t ngx_http_srcache_set_content_length_header(
18         ngx_http_request_t *r, off_t len);
19 
20 
21 static ngx_str_t ngx_http_status_lines[] = {
22 
23     ngx_string("200 OK"),
24     ngx_string("201 Created"),
25     ngx_string("202 Accepted"),
26     ngx_null_string,  /* "203 Non-Authoritative Information" */
27     ngx_string("204 No Content"),
28     ngx_null_string,  /* "205 Reset Content" */
29     ngx_string("206 Partial Content"),
30 
31     /* ngx_null_string, */  /* "207 Multi-Status" */
32 
33 #define NGX_HTTP_LAST_LEVEL_200  207
34 #define NGX_HTTP_LEVEL_200       (NGX_HTTP_LAST_LEVEL_200 - 200)
35 
36     /* ngx_null_string, */  /* "300 Multiple Choices" */
37 
38     ngx_string("301 Moved Permanently"),
39     ngx_string("302 Moved Temporarily"),
40     ngx_string("303 See Other"),
41     ngx_string("304 Not Modified"),
42 
43     /* ngx_null_string, */  /* "305 Use Proxy" */
44     /* ngx_null_string, */  /* "306 unused" */
45     /* ngx_null_string, */  /* "307 Temporary Redirect" */
46 
47 #define NGX_HTTP_LAST_LEVEL_300  305
48 #define NGX_HTTP_LEVEL_300       (NGX_HTTP_LAST_LEVEL_300 - 301)
49 
50     ngx_string("400 Bad Request"),
51     ngx_string("401 Unauthorized"),
52     ngx_string("402 Payment Required"),
53     ngx_string("403 Forbidden"),
54     ngx_string("404 Not Found"),
55     ngx_string("405 Not Allowed"),
56     ngx_string("406 Not Acceptable"),
57     ngx_null_string,  /* "407 Proxy Authentication Required" */
58     ngx_string("408 Request Time-out"),
59     ngx_string("409 Conflict"),
60     ngx_string("410 Gone"),
61     ngx_string("411 Length Required"),
62     ngx_string("412 Precondition Failed"),
63     ngx_string("413 Request Entity Too Large"),
64     ngx_null_string,  /* "414 Request-URI Too Large", but we never send it
65                        * because we treat such requests as the HTTP/0.9
66                        * requests and send only a body without a header
67                        */
68     ngx_string("415 Unsupported Media Type"),
69     ngx_string("416 Requested Range Not Satisfiable"),
70 
71     /* ngx_null_string, */  /* "417 Expectation Failed" */
72     /* ngx_null_string, */  /* "418 unused" */
73     /* ngx_null_string, */  /* "419 unused" */
74     /* ngx_null_string, */  /* "420 unused" */
75     /* ngx_null_string, */  /* "421 unused" */
76     /* ngx_null_string, */  /* "422 Unprocessable Entity" */
77     /* ngx_null_string, */  /* "423 Locked" */
78     /* ngx_null_string, */  /* "424 Failed Dependency" */
79 
80 #define NGX_HTTP_LAST_LEVEL_400  417
81 #define NGX_HTTP_LEVEL_400       (NGX_HTTP_LAST_LEVEL_400 - 400)
82 
83     ngx_string("500 Internal Server Error"),
84     ngx_string("501 Method Not Implemented"),
85     ngx_string("502 Bad Gateway"),
86     ngx_string("503 Service Temporarily Unavailable"),
87     ngx_string("504 Gateway Time-out"),
88 
89     ngx_null_string,        /* "505 HTTP Version Not Supported" */
90     ngx_null_string,        /* "506 Variant Also Negotiates" */
91     ngx_string("507 Insufficient Storage"),
92     /* ngx_null_string, */  /* "508 unused" */
93     /* ngx_null_string, */  /* "509 unused" */
94     /* ngx_null_string, */  /* "510 Not Extended" */
95 
96 #define NGX_HTTP_LAST_LEVEL_500  508
97 
98 };
99 
100 
101 ngx_str_t  ngx_http_srcache_content_length_header_key =
102         ngx_string("Content-Length");
103 ngx_str_t  ngx_http_srcache_get_method =
104         ngx_http_srcache_method_name("GET");
105 ngx_str_t  ngx_http_srcache_put_method =
106         ngx_http_srcache_method_name("PUT");
107 ngx_str_t  ngx_http_srcache_post_method =
108         ngx_http_srcache_method_name("POST");
109 ngx_str_t  ngx_http_srcache_head_method =
110         ngx_http_srcache_method_name("HEAD");
111 ngx_str_t  ngx_http_srcache_copy_method =
112         ngx_http_srcache_method_name("COPY");
113 ngx_str_t  ngx_http_srcache_move_method =
114         ngx_http_srcache_method_name("MOVE");
115 ngx_str_t  ngx_http_srcache_lock_method =
116         ngx_http_srcache_method_name("LOCK");
117 ngx_str_t  ngx_http_srcache_mkcol_method =
118         ngx_http_srcache_method_name("MKCOL");
119 ngx_str_t  ngx_http_srcache_trace_method =
120         ngx_http_srcache_method_name("TRACE");
121 ngx_str_t  ngx_http_srcache_delete_method =
122         ngx_http_srcache_method_name("DELETE");
123 ngx_str_t  ngx_http_srcache_unlock_method =
124         ngx_http_srcache_method_name("UNLOCK");
125 ngx_str_t  ngx_http_srcache_options_method =
126         ngx_http_srcache_method_name("OPTIONS");
127 ngx_str_t  ngx_http_srcache_propfind_method =
128         ngx_http_srcache_method_name("PROPFIND");
129 ngx_str_t  ngx_http_srcache_proppatch_method =
130         ngx_http_srcache_method_name("PROPPATCH");
131 
132 
133 void
ngx_http_srcache_discard_bufs(ngx_pool_t * pool,ngx_chain_t * in)134 ngx_http_srcache_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in)
135 {
136     ngx_chain_t         *cl;
137 
138     for (cl = in; cl; cl = cl->next) {
139         cl->buf->pos = cl->buf->last;
140     }
141 }
142 
143 
144 ngx_int_t
ngx_http_srcache_parse_method_name(ngx_str_t ** method_name_ptr)145 ngx_http_srcache_parse_method_name(ngx_str_t **method_name_ptr)
146 {
147     const ngx_str_t         *method_name = *method_name_ptr;
148 
149     switch (method_name->len) {
150 
151     case 3:
152         if (ngx_http_srcache_strcmp_const(method_name->data, "GET") == 0) {
153             *method_name_ptr = &ngx_http_srcache_get_method;
154             return NGX_HTTP_GET;
155         }
156 
157         if (ngx_http_srcache_strcmp_const(method_name->data, "PUT") == 0) {
158             *method_name_ptr = &ngx_http_srcache_put_method;
159             return NGX_HTTP_PUT;
160         }
161 
162         return NGX_HTTP_UNKNOWN;
163 
164     case 4:
165         if (ngx_http_srcache_strcmp_const(method_name->data, "POST") == 0) {
166             *method_name_ptr = &ngx_http_srcache_post_method;
167             return NGX_HTTP_POST;
168         }
169 
170         if (ngx_http_srcache_strcmp_const(method_name->data, "HEAD") == 0) {
171             *method_name_ptr = &ngx_http_srcache_head_method;
172             return NGX_HTTP_HEAD;
173         }
174 
175         if (ngx_http_srcache_strcmp_const(method_name->data, "COPY") == 0) {
176             *method_name_ptr = &ngx_http_srcache_copy_method;
177             return NGX_HTTP_COPY;
178         }
179 
180         if (ngx_http_srcache_strcmp_const(method_name->data, "MOVE") == 0) {
181             *method_name_ptr = &ngx_http_srcache_move_method;
182             return NGX_HTTP_MOVE;
183         }
184 
185         if (ngx_http_srcache_strcmp_const(method_name->data, "LOCK") == 0) {
186             *method_name_ptr = &ngx_http_srcache_lock_method;
187             return NGX_HTTP_LOCK;
188         }
189 
190         return NGX_HTTP_UNKNOWN;
191 
192     case 5:
193         if (ngx_http_srcache_strcmp_const(method_name->data, "MKCOL") == 0) {
194             *method_name_ptr = &ngx_http_srcache_mkcol_method;
195             return NGX_HTTP_MKCOL;
196         }
197 
198         if (ngx_http_srcache_strcmp_const(method_name->data, "TRACE") == 0) {
199             *method_name_ptr = &ngx_http_srcache_trace_method;
200             return NGX_HTTP_TRACE;
201         }
202 
203         return NGX_HTTP_UNKNOWN;
204 
205     case 6:
206         if (ngx_http_srcache_strcmp_const(method_name->data, "DELETE") == 0) {
207             *method_name_ptr = &ngx_http_srcache_delete_method;
208             return NGX_HTTP_DELETE;
209         }
210 
211         if (ngx_http_srcache_strcmp_const(method_name->data, "UNLOCK") == 0) {
212             *method_name_ptr = &ngx_http_srcache_unlock_method;
213             return NGX_HTTP_UNLOCK;
214         }
215 
216         return NGX_HTTP_UNKNOWN;
217 
218     case 7:
219         if (ngx_http_srcache_strcmp_const(method_name->data, "OPTIONS") == 0) {
220             *method_name_ptr = &ngx_http_srcache_options_method;
221             return NGX_HTTP_OPTIONS;
222         }
223 
224         return NGX_HTTP_UNKNOWN;
225 
226     case 8:
227         if (ngx_http_srcache_strcmp_const(method_name->data, "PROPFIND") == 0) {
228             *method_name_ptr = &ngx_http_srcache_propfind_method;
229             return NGX_HTTP_PROPFIND;
230         }
231 
232         return NGX_HTTP_UNKNOWN;
233 
234     case 9:
235         if (ngx_http_srcache_strcmp_const(method_name->data, "PROPPATCH")
236             == 0)
237         {
238             *method_name_ptr = &ngx_http_srcache_proppatch_method;
239             return NGX_HTTP_PROPPATCH;
240         }
241 
242         return NGX_HTTP_UNKNOWN;
243 
244     default:
245         return NGX_HTTP_UNKNOWN;
246     }
247 
248     return NGX_HTTP_UNKNOWN;
249 }
250 
251 
252 ngx_int_t
ngx_http_srcache_adjust_subrequest(ngx_http_request_t * sr,ngx_http_srcache_parsed_request_t * parsed_sr)253 ngx_http_srcache_adjust_subrequest(ngx_http_request_t *sr,
254     ngx_http_srcache_parsed_request_t *parsed_sr)
255 {
256     ngx_http_core_main_conf_t  *cmcf;
257     ngx_http_request_t         *r;
258     ngx_http_request_body_t    *body;
259     ngx_int_t                   rc;
260 
261     sr->method      = parsed_sr->method;
262     sr->method_name = parsed_sr->method_name;
263 
264     r = sr->parent;
265 
266     dd("subrequest method: %d %.*s", (int) sr->method,
267        (int) sr->method_name.len, sr->method_name.data);
268 
269     sr->header_in = r->header_in;
270 
271 #if 1
272     /* XXX work-around a bug in ngx_http_subrequest */
273     if (r->headers_in.headers.last == &r->headers_in.headers.part) {
274         sr->headers_in.headers.last = &sr->headers_in.headers.part;
275     }
276 #endif
277 
278     /* we do not inherit the parent request's variables */
279     cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module);
280     sr->variables = ngx_pcalloc(sr->pool, cmcf->variables.nelts
281                                 * sizeof(ngx_http_variable_value_t));
282 
283     if (sr->variables == NULL) {
284         return NGX_ERROR;
285     }
286 
287     body = parsed_sr->request_body;
288     if (body) {
289         sr->request_body = body;
290 
291         rc = ngx_http_srcache_set_content_length_header(sr,
292                                                  parsed_sr->content_length_n);
293 
294         if (rc != NGX_OK) {
295             return NGX_ERROR;
296         }
297     }
298 
299     return NGX_OK;
300 }
301 
302 
303 ngx_int_t
ngx_http_srcache_add_copy_chain(ngx_pool_t * pool,ngx_chain_t ** chain,ngx_chain_t * in,unsigned * plast)304 ngx_http_srcache_add_copy_chain(ngx_pool_t *pool, ngx_chain_t **chain,
305     ngx_chain_t *in, unsigned *plast)
306 {
307     ngx_chain_t     *cl, **ll;
308     size_t           len;
309 
310     ll = chain;
311 
312     for (cl = *chain; cl; cl = cl->next) {
313         ll = &cl->next;
314     }
315 
316     *plast = 0;
317 
318     while (in) {
319         cl = ngx_alloc_chain_link(pool);
320         if (cl == NULL) {
321             return NGX_ERROR;
322         }
323 
324         if (in->buf->last_buf || in->buf->last_in_chain) {
325             *plast = 1;
326         }
327 
328         if (ngx_buf_special(in->buf)) {
329             cl->buf = in->buf;
330 
331         } else {
332             if (ngx_buf_in_memory(in->buf)) {
333                 len = ngx_buf_size(in->buf);
334                 cl->buf = ngx_create_temp_buf(pool, len);
335                 if (cl->buf == NULL) {
336                     return NGX_ERROR;
337                 }
338 
339                 dd("buf: %.*s", (int) len, in->buf->pos);
340 
341                 cl->buf->last = ngx_copy(cl->buf->pos, in->buf->pos, len);
342 
343             } else {
344                 return NGX_ERROR;
345             }
346         }
347 
348         *ll = cl;
349         ll = &cl->next;
350         in = in->next;
351     }
352 
353     *ll = NULL;
354 
355     return NGX_OK;
356 }
357 
358 
359 ngx_int_t
ngx_http_srcache_post_request_at_head(ngx_http_request_t * r,ngx_http_posted_request_t * pr)360 ngx_http_srcache_post_request_at_head(ngx_http_request_t *r,
361     ngx_http_posted_request_t *pr)
362 {
363     dd_enter();
364 
365     if (pr == NULL) {
366         pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));
367         if (pr == NULL) {
368             return NGX_ERROR;
369         }
370     }
371 
372     pr->request = r;
373     pr->next = r->main->posted_requests;
374     r->main->posted_requests = pr;
375 
376     return NGX_OK;
377 }
378 
379 
380 static ngx_int_t
ngx_http_srcache_set_content_length_header(ngx_http_request_t * r,off_t len)381 ngx_http_srcache_set_content_length_header(ngx_http_request_t *r, off_t len)
382 {
383     ngx_table_elt_t                 *h, *header;
384     u_char                          *p;
385     ngx_list_part_t                 *part;
386     ngx_http_request_t              *pr;
387     ngx_uint_t                       i;
388 
389     r->headers_in.content_length_n = len;
390 
391     if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
392                       sizeof(ngx_table_elt_t))
393         != NGX_OK)
394     {
395         return NGX_ERROR;
396     }
397 
398     h = ngx_list_push(&r->headers_in.headers);
399     if (h == NULL) {
400         return NGX_ERROR;
401     }
402 
403     h->key = ngx_http_srcache_content_length_header_key;
404     h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
405     if (h->lowcase_key == NULL) {
406         return NGX_ERROR;
407     }
408 
409     ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
410 
411     r->headers_in.content_length = h;
412 
413     p = ngx_palloc(r->pool, NGX_OFF_T_LEN);
414     if (p == NULL) {
415         return NGX_ERROR;
416     }
417 
418     h->value.data = p;
419 
420     h->value.len = ngx_sprintf(h->value.data, "%O", len) - h->value.data;
421 
422     h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(
423             ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash(
424             ngx_hash('c', 'o'), 'n'), 't'), 'e'), 'n'), 't'), '-'), 'l'), 'e'),
425             'n'), 'g'), 't'), 'h');
426 
427     dd("r content length: %.*s",
428        (int) r->headers_in.content_length->value.len,
429        r->headers_in.content_length->value.data);
430 
431     pr = r->parent;
432 
433     if (pr == NULL) {
434         return NGX_OK;
435     }
436 
437     /* forward the parent request's all other request headers */
438 
439     part = &pr->headers_in.headers.part;
440     header = part->elts;
441 
442     for (i = 0; /* void */; i++) {
443 
444         if (i >= part->nelts) {
445             if (part->next == NULL) {
446                 break;
447             }
448 
449             part = part->next;
450             header = part->elts;
451             i = 0;
452         }
453 
454         if (header[i].key.len == sizeof("Content-Length") - 1
455             && ngx_strncasecmp(header[i].key.data,
456                                (u_char *) "Content-Length",
457                                sizeof("Content-Length") - 1) == 0)
458         {
459             continue;
460         }
461 
462         h = ngx_list_push(&r->headers_in.headers);
463         if (h == NULL) {
464             return NGX_ERROR;
465         }
466 
467         *h = header[i];
468     }
469 
470     /* XXX maybe we should set those built-in header slot in
471      * ngx_http_headers_in_t too? */
472 
473     return NGX_OK;
474 }
475 
476 
477 ngx_int_t
ngx_http_srcache_request_no_cache(ngx_http_request_t * r,unsigned * no_store)478 ngx_http_srcache_request_no_cache(ngx_http_request_t *r, unsigned *no_store)
479 {
480     ngx_table_elt_t                 *h;
481     ngx_list_part_t                 *part;
482     u_char                          *p;
483     u_char                          *last;
484     ngx_uint_t                       i;
485     unsigned                         no_cache;
486 
487     part = &r->headers_in.headers.part;
488     h = part->elts;
489 
490     *no_store = 0;
491     no_cache = 0;
492 
493     for (i = 0; /* void */; i++) {
494 
495         if (i >= part->nelts) {
496             if (part->next == NULL) {
497                 break;
498             }
499 
500             part = part->next;
501             h = part->elts;
502             i = 0;
503         }
504 
505         if (h[i].key.len == sizeof("Cache-Control") - 1
506             && ngx_strncasecmp(h[i].key.data, (u_char *) "Cache-Control",
507                                sizeof("Cache-Control") - 1) == 0)
508         {
509             p = h[i].value.data;
510             last = p + h[i].value.len;
511 
512             if (!*no_store
513                 && ngx_strlcasestrn(p, last, (u_char *) "no-store", 8 - 1)
514                    != NULL)
515             {
516                 *no_store = 1;
517             }
518 
519             if (ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL)
520             {
521                 no_cache = 1;
522             }
523 
524             continue;
525         }
526 
527         if (h[i].key.len == sizeof("Pragma") - 1
528             && ngx_strncasecmp(h[i].key.data, (u_char *) "Pragma",
529                                sizeof("Pragma") - 1) == 0)
530         {
531             p = h[i].value.data;
532             last = p + h[i].value.len;
533 
534             if (ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL)
535             {
536                 no_cache = 1;
537             }
538         }
539     }
540 
541     return no_cache ? NGX_OK : NGX_DECLINED;
542 }
543 
544 
545 ngx_int_t
ngx_http_srcache_response_no_cache(ngx_http_request_t * r,ngx_http_srcache_loc_conf_t * conf,ngx_http_srcache_ctx_t * ctx)546 ngx_http_srcache_response_no_cache(ngx_http_request_t *r,
547     ngx_http_srcache_loc_conf_t *conf, ngx_http_srcache_ctx_t *ctx)
548 {
549     ngx_table_elt_t   **ccp;
550     ngx_table_elt_t    *h;
551     ngx_uint_t          i;
552     u_char             *p, *last;
553     ngx_int_t           n;
554     time_t              expires;
555 
556     dd("checking response cache control settings");
557 
558     ccp = r->headers_out.cache_control.elts;
559 
560     if (ccp == NULL) {
561         goto check_expires;
562     }
563 
564     for (i = 0; i < r->headers_out.cache_control.nelts; i++) {
565         if (!ccp[i]->hash) {
566             continue;
567         }
568 
569         p = ccp[i]->value.data;
570         last = p + ccp[i]->value.len;
571 
572         if (!conf->store_private
573             && ngx_strlcasestrn(p, last, (u_char *) "private", 7 - 1) != NULL)
574         {
575             return NGX_OK;
576         }
577 
578         if (!conf->store_no_store
579             && ngx_strlcasestrn(p, last, (u_char *) "no-store", 8 - 1) != NULL)
580         {
581             return NGX_OK;
582         }
583 
584         if (!conf->store_no_cache
585             && ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL)
586         {
587             return NGX_OK;
588         }
589 
590         if (ctx->valid_sec != 0) {
591             continue;
592         }
593 
594         p = ngx_strlcasestrn(p, last, (u_char *) "max-age=", 8 - 1);
595 
596         if (p == NULL) {
597             continue;
598         }
599 
600         n = 0;
601 
602         for (p += 8; p < last; p++) {
603             if (*p == ',' || *p == ';' || *p == ' ') {
604                 break;
605             }
606 
607             if (*p >= '0' && *p <= '9') {
608                 n = n * 10 + *p - '0';
609                 continue;
610             }
611 
612             return NGX_OK;
613         }
614 
615         if (n == 0) {
616             return NGX_OK;
617         }
618 
619         ctx->valid_sec = ngx_time() + n;
620     }
621 
622 check_expires:
623 
624     dd("valid_sec after processing cache-control: %d", (int) ctx->valid_sec);
625 
626     if (ctx->valid_sec == 0) {
627         h = r->headers_out.expires;
628 
629         dd("expires header: %p", h);
630 
631         if (h != NULL && h->hash != 0) {
632             expires = ngx_http_parse_time(h->value.data, h->value.len);
633 
634             if (expires == NGX_ERROR || expires <= ngx_time()) {
635                 return NGX_OK;
636             }
637 
638             ctx->valid_sec = expires;
639         }
640     }
641 
642     return NGX_DECLINED;
643 }
644 
645 
646 ngx_int_t
ngx_http_srcache_process_status_line(ngx_http_request_t * r,ngx_buf_t * b)647 ngx_http_srcache_process_status_line(ngx_http_request_t *r, ngx_buf_t *b)
648 {
649     ngx_int_t                    rc;
650     ngx_http_srcache_ctx_t      *ctx;
651     ngx_http_request_t          *pr;
652     ngx_http_srcache_loc_conf_t *conf;
653 
654     ctx = ngx_http_get_module_ctx(r, ngx_http_srcache_filter_module);
655 
656     rc = ngx_http_parse_status_line(r, b, &ctx->status);
657 
658     if (rc == NGX_AGAIN) {
659         return NGX_AGAIN;
660     }
661 
662     if (rc == NGX_ERROR) {
663         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
664                       "srcache_fetch: cache sent invalid status line");
665         return NGX_ERROR;
666     }
667 
668     /* rc == NGX_OK */
669 
670     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
671                    "srcache_fetch status line done");
672 
673     pr = r->parent;
674     pr->headers_out.status = ctx->status.code;
675 
676     ctx->process_header = ngx_http_srcache_process_header;
677 
678     conf = ngx_http_get_module_loc_conf(pr, ngx_http_srcache_filter_module);
679 
680     dd("header buffer size: %d", (int) conf->header_buf_size);
681 
682     ctx->header_buf = ngx_create_temp_buf(r->pool, conf->header_buf_size);
683     if (ctx->header_buf == NULL) {
684         return NGX_ERROR;
685     }
686 
687     if (b->pos == b->last) {
688         return NGX_AGAIN;
689     }
690 
691     return ngx_http_srcache_process_header(r, b);
692 }
693 
694 
695 ngx_int_t
ngx_http_srcache_process_header(ngx_http_request_t * r,ngx_buf_t * b)696 ngx_http_srcache_process_header(ngx_http_request_t *r, ngx_buf_t *b)
697 {
698     ngx_int_t                       rc;
699     ngx_table_elt_t                 header;
700     ngx_http_srcache_ctx_t         *ctx;
701     off_t                           len, rest;
702     unsigned                        truncate;
703     u_char                         *p;
704     ngx_http_srcache_header_t      *hh;
705     ngx_http_srcache_main_conf_t   *smcf;
706 
707     smcf = ngx_http_get_module_main_conf(r, ngx_http_srcache_filter_module);
708 
709     ctx = ngx_http_get_module_ctx(r, ngx_http_srcache_filter_module);
710 
711     for ( ;; ) {
712 
713         len = b->last - b->pos;
714         rest = ctx->header_buf->end - ctx->header_buf->last;
715 
716         dd("len: %d, rest: %d", (int) len, (int) rest);
717 
718         if (len > rest) {
719             len = rest;
720             truncate = 1;
721 
722         } else {
723             truncate = 0;
724         }
725 
726         ctx->header_buf->last = ngx_copy(ctx->header_buf->last, b->pos,
727                                          (size_t) len);
728 
729         p = ctx->header_buf->pos;
730 
731         rc = ngx_http_parse_header_line(r, ctx->header_buf, 1);
732 
733         b->pos += ctx->header_buf->pos - p;
734 
735         if (rc == NGX_OK) {
736 
737             /* a header line has been parsed successfully */
738 
739             ngx_memzero(&header, sizeof(ngx_table_elt_t));
740 
741             header.hash = r->header_hash;
742 
743             header.key.len = r->header_name_end - r->header_name_start;
744             header.value.len = r->header_end - r->header_start;
745 
746             header.key.data = ngx_pnalloc(r->pool,
747                                           header.key.len + 1
748                                           + header.value.len + 1
749                                           + header.key.len);
750 
751             if (header.key.data == NULL) {
752                 return NGX_ERROR;
753             }
754 
755             header.value.data = header.key.data + header.key.len + 1;
756             header.lowcase_key = header.key.data + header.key.len + 1
757                                  + header.value.len + 1;
758 
759             ngx_cpystrn(header.key.data, r->header_name_start,
760                         header.key.len + 1);
761 
762             ngx_cpystrn(header.value.data, r->header_start,
763                         header.value.len + 1);
764 
765             if (header.key.len == r->lowcase_index) {
766                 ngx_memcpy(header.lowcase_key, r->lowcase_header,
767                            header.key.len);
768 
769             } else {
770                 ngx_strlow(header.lowcase_key, header.key.data, header.key.len);
771             }
772 
773             hh = ngx_hash_find(&smcf->headers_in_hash, header.hash,
774                                header.lowcase_key, header.key.len);
775 
776             if (hh) {
777                 if (hh->handler(r->parent, &header, hh->offset) != NGX_OK) {
778                     return NGX_ERROR;
779                 }
780 
781             } else {
782 
783                 if (ngx_http_srcache_process_header_line(r->parent, &header, 0)
784                     != NGX_OK)
785                 {
786                     return NGX_ERROR;
787                 }
788             }
789 
790             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
791                            "srcache_fetch header: \"%V: %V\"",
792                            &header.key, &header.value);
793 
794             ctx->header_buf->pos = ctx->header_buf->start;
795             ctx->header_buf->last = ctx->header_buf->start;
796 
797             continue;
798         }
799 
800         if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
801 
802             /* a whole header has been parsed successfully */
803 
804             ctx->header_buf->pos = ctx->header_buf->start;
805             ctx->header_buf->last = ctx->header_buf->start;
806             ngx_pfree(r->pool, ctx->header_buf->start);
807 
808             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
809                            "srcache_fetch header done");
810 
811             return NGX_OK;
812         }
813 
814         if (rc == NGX_AGAIN) {
815             if (truncate) {
816                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
817                               "srcache_fetch: header buffer overflown "
818                               "(maybe you should consider increasing "
819                               "srcache_header_buffer_size?)");
820 
821                 ctx->header_buf->pos = ctx->header_buf->start;
822                 ctx->header_buf->last = ctx->header_buf->start;
823                 ngx_pfree(r->pool, ctx->header_buf->start);
824 
825                 return NGX_ERROR;
826             }
827 
828             return NGX_AGAIN;
829         }
830 
831         /* there was error while a header line parsing */
832 
833         ctx->header_buf->pos = ctx->header_buf->start;
834         ctx->header_buf->last = ctx->header_buf->start;
835         ngx_pfree(r->pool, ctx->header_buf->start);
836 
837         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
838                       "srcache_fetch: cache sent invalid header");
839 
840         return NGX_ERROR;
841     }
842 }
843 
844 
845 ngx_int_t
ngx_http_srcache_store_response_header(ngx_http_request_t * r,ngx_http_srcache_ctx_t * ctx)846 ngx_http_srcache_store_response_header(ngx_http_request_t *r,
847     ngx_http_srcache_ctx_t *ctx)
848 {
849     ngx_chain_t             *cl;
850     size_t                   len;
851     ngx_buf_t               *b;
852     ngx_uint_t               status;
853     ngx_uint_t               i;
854     ngx_str_t               *status_line;
855     ngx_list_part_t         *part;
856     ngx_table_elt_t         *header;
857 
858     u_char                   buf[sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1];
859 
860     ngx_http_srcache_loc_conf_t    *conf;
861 
862     conf = ngx_http_get_module_loc_conf(r, ngx_http_srcache_filter_module);
863 
864     dd("request: %p, uri: %.*s", r, (int) r->uri.len, r->uri.data);
865 
866     len = sizeof("HTTP/1.x ") - 1 + sizeof(CRLF) - 1
867           /* the end of the header */
868           + sizeof(CRLF) - 1;
869 
870     if (r->headers_out.status_line.len) {
871         dd("status line defined");
872         len += r->headers_out.status_line.len;
873         status_line = &r->headers_out.status_line;
874         status = 0;
875 
876     } else {
877         dd("status line not defined");
878 
879         status = r->headers_out.status;
880 
881         if (status >= NGX_HTTP_OK
882             && status < NGX_HTTP_LAST_LEVEL_200)
883         {
884             /* 2XX */
885 
886             status -= NGX_HTTP_OK;
887             dd("status: %d", (int) status);
888             status_line = &ngx_http_status_lines[status];
889             len += ngx_http_status_lines[status].len;
890 
891         } else if (status >= NGX_HTTP_MOVED_PERMANENTLY
892                    && status < NGX_HTTP_LAST_LEVEL_300)
893         {
894             /* 3XX */
895 
896             if (status == NGX_HTTP_NOT_MODIFIED) {
897                 r->header_only = 1;
898             }
899 
900             status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_LEVEL_200;
901             status_line = &ngx_http_status_lines[status];
902             len += ngx_http_status_lines[status].len;
903 
904         } else if (status >= NGX_HTTP_BAD_REQUEST
905                    && status < NGX_HTTP_LAST_LEVEL_400)
906         {
907             /* 4XX */
908             status = status - NGX_HTTP_BAD_REQUEST
909                             + NGX_HTTP_LEVEL_200
910                             + NGX_HTTP_LEVEL_300;
911 
912             status_line = &ngx_http_status_lines[status];
913             len += ngx_http_status_lines[status].len;
914 
915         } else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR
916                    && status < NGX_HTTP_LAST_LEVEL_500)
917         {
918             /* 5XX */
919             status = status - NGX_HTTP_INTERNAL_SERVER_ERROR
920                             + NGX_HTTP_LEVEL_200
921                             + NGX_HTTP_LEVEL_300
922                             + NGX_HTTP_LEVEL_400;
923 
924             status_line = &ngx_http_status_lines[status];
925             len += ngx_http_status_lines[status].len;
926 
927         } else {
928             len += NGX_INT_T_LEN;
929             status_line = NULL;
930         }
931     }
932 
933     if (!conf->hide_content_type && r->headers_out.content_type.len) {
934         len += sizeof("Content-Type: ") - 1
935                + r->headers_out.content_type.len + 2;
936 
937         if (r->headers_out.content_type_len == r->headers_out.content_type.len
938             && r->headers_out.charset.len)
939         {
940             len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
941         }
942     }
943 
944     if (!conf->hide_last_modified) {
945         if (r->headers_out.last_modified_time != -1) {
946             if (r->headers_out.status != NGX_HTTP_OK
947                 && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT
948                 && r->headers_out.status != NGX_HTTP_NOT_MODIFIED
949                 && r->headers_out.status != NGX_HTTP_NO_CONTENT)
950             {
951                 r->headers_out.last_modified_time = -1;
952                 r->headers_out.last_modified = NULL;
953             }
954         }
955 
956         dd("last modified time: %d", (int) r->headers_out.last_modified_time);
957 
958         if (r->headers_out.last_modified == NULL
959             && r->headers_out.last_modified_time != -1)
960         {
961             (void) ngx_http_time(buf, r->headers_out.last_modified_time);
962 
963             len += sizeof("Last-Modified: ") - 1 + sizeof(buf) + 2;
964         }
965     }
966 
967     if (r->allow_ranges) {
968         len += sizeof("X-SRCache-Allow-Ranges: 1") - 1 + 2;
969     }
970 
971     part = &r->headers_out.headers.part;
972     header = part->elts;
973 
974     for (i = 0; /* void */; i++) {
975 
976         if (i >= part->nelts) {
977             if (part->next == NULL) {
978                 break;
979             }
980 
981             part = part->next;
982             header = part->elts;
983             i = 0;
984         }
985 
986         if (header[i].hash == 0) {
987             continue;
988         }
989 
990         if (ngx_hash_find(&conf->hide_headers_hash, header[i].hash,
991                           header[i].lowcase_key, header[i].key.len))
992         {
993             continue;
994         }
995 
996         len += header[i].key.len + sizeof(": ") - 1 + header[i].value.len
997                + sizeof(CRLF) - 1;
998     }
999 
1000     b = ngx_create_temp_buf(r->pool, len);
1001     if (b == NULL) {
1002         return NGX_ERROR;
1003     }
1004 
1005     /* "HTTP/1.x " */
1006     b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1);
1007 
1008     /* status line */
1009     if (status_line) {
1010         b->last = ngx_copy(b->last, status_line->data, status_line->len);
1011 
1012     } else {
1013         b->last = ngx_sprintf(b->last, "%ui", status);
1014     }
1015     *b->last++ = CR; *b->last++ = LF;
1016 
1017     if (!conf->hide_content_type && r->headers_out.content_type.len) {
1018         b->last = ngx_cpymem(b->last, "Content-Type: ",
1019                              sizeof("Content-Type: ") - 1);
1020         b->last = ngx_copy(b->last, r->headers_out.content_type.data,
1021                            r->headers_out.content_type.len);
1022 
1023         if (r->headers_out.content_type_len == r->headers_out.content_type.len
1024             && r->headers_out.charset.len)
1025         {
1026             b->last = ngx_cpymem(b->last, "; charset=",
1027                                  sizeof("; charset=") - 1);
1028             b->last = ngx_copy(b->last, r->headers_out.charset.data,
1029                                r->headers_out.charset.len);
1030         }
1031 
1032         *b->last++ = CR; *b->last++ = LF;
1033     }
1034 
1035     if (!conf->hide_last_modified
1036         && r->headers_out.last_modified == NULL
1037         && r->headers_out.last_modified_time != -1)
1038     {
1039         b->last = ngx_cpymem(b->last, "Last-Modified: ",
1040                              sizeof("Last-Modified: ") - 1);
1041 
1042         b->last = ngx_cpymem(b->last, buf, sizeof(buf));
1043 
1044         *b->last++ = CR; *b->last++ = LF;
1045     }
1046 
1047     if (r->allow_ranges) {
1048         b->last = ngx_cpymem(b->last, "X-SRCache-Allow-Ranges: 1\r\n",
1049                              sizeof("X-SRCache-Allow-Ranges: 1\r\n") - 1);
1050     }
1051 
1052     part = &r->headers_out.headers.part;
1053     header = part->elts;
1054 
1055     for (i = 0; /* void */; i++) {
1056 
1057         if (i >= part->nelts) {
1058             if (part->next == NULL) {
1059                 break;
1060             }
1061 
1062             part = part->next;
1063             header = part->elts;
1064             i = 0;
1065         }
1066 
1067         if (header[i].hash == 0) {
1068             continue;
1069         }
1070 
1071         dd("header hash: %lu, hash lc: %lu", (unsigned long) header[i].hash,
1072            (unsigned long) ngx_hash_key_lc(header[i].key.data,
1073                                            header[i].key.len));
1074 
1075         if (ngx_hash_find(&conf->hide_headers_hash, header[i].hash,
1076                           header[i].lowcase_key, header[i].key.len))
1077         {
1078             dd("skipped header key: %.*s", (int) header[i].key.len,
1079                header[i].key.data);
1080             continue;
1081         }
1082 
1083         dd("header not skipped");
1084 
1085         b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
1086         *b->last++ = ':'; *b->last++ = ' ';
1087 
1088         b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);
1089         *b->last++ = CR; *b->last++ = LF;
1090     }
1091 
1092     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1093                    "srcache store header %*s", (size_t) (b->last - b->pos),
1094                    b->pos);
1095 
1096     /* the end of HTTP header */
1097     *b->last++ = CR; *b->last++ = LF;
1098 
1099     if (b->last != b->end) {
1100         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1101                       "srcache_fetch: buffer error when serializing the "
1102                       "response header: %O left", (off_t) (b->last - b->end));
1103 
1104         return NGX_ERROR;
1105     }
1106 
1107     cl = ngx_alloc_chain_link(r->pool);
1108     if (cl == NULL) {
1109         return NGX_ERROR;
1110     }
1111 
1112     cl->buf = b;
1113     cl->next = NULL;
1114 
1115     ctx->body_to_cache = cl;
1116 
1117     ctx->response_length += len;
1118 
1119     return NGX_OK;
1120 }
1121 
1122 
1123 ngx_int_t
ngx_http_srcache_hide_headers_hash(ngx_conf_t * cf,ngx_http_srcache_loc_conf_t * conf,ngx_http_srcache_loc_conf_t * prev,ngx_str_t * default_hide_headers,ngx_hash_init_t * hash)1124 ngx_http_srcache_hide_headers_hash(ngx_conf_t *cf,
1125     ngx_http_srcache_loc_conf_t *conf, ngx_http_srcache_loc_conf_t *prev,
1126     ngx_str_t *default_hide_headers, ngx_hash_init_t *hash)
1127 {
1128     ngx_str_t       *h;
1129     ngx_uint_t       i, j;
1130     ngx_array_t      hide_headers;
1131     ngx_hash_key_t  *hk;
1132 
1133     if (conf->hide_headers == NGX_CONF_UNSET_PTR
1134         && conf->pass_headers == NGX_CONF_UNSET_PTR)
1135     {
1136         conf->hide_headers_hash = prev->hide_headers_hash;
1137 
1138         if (conf->hide_headers_hash.buckets) {
1139             return NGX_OK;
1140         }
1141 
1142         conf->hide_headers = prev->hide_headers;
1143         conf->pass_headers = prev->pass_headers;
1144         conf->hide_content_type = prev->hide_content_type;
1145         conf->hide_last_modified = prev->hide_last_modified;
1146 
1147     } else {
1148         if (conf->hide_headers == NGX_CONF_UNSET_PTR) {
1149             conf->hide_headers = prev->hide_headers;
1150         }
1151 
1152         if (conf->pass_headers == NGX_CONF_UNSET_PTR) {
1153             conf->pass_headers = prev->pass_headers;
1154         }
1155     }
1156 
1157     dd("init hide headers");
1158 
1159     if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
1160         != NGX_OK)
1161     {
1162         return NGX_ERROR;
1163     }
1164 
1165     for (h = default_hide_headers; h->len; h++) {
1166         hk = ngx_array_push(&hide_headers);
1167         if (hk == NULL) {
1168             return NGX_ERROR;
1169         }
1170 
1171         hk->key = *h;
1172         hk->key_hash = ngx_hash_key_lc(h->data, h->len);
1173         hk->value = (void *) 1;
1174     }
1175 
1176     if (conf->hide_headers != NGX_CONF_UNSET_PTR) {
1177         dd("hide headers not empty");
1178 
1179         h = conf->hide_headers->elts;
1180 
1181         for (i = 0; i < conf->hide_headers->nelts; i++) {
1182 
1183             hk = hide_headers.elts;
1184 
1185             for (j = 0; j < hide_headers.nelts; j++) {
1186                 if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {
1187                     goto exist;
1188                 }
1189             }
1190 
1191             hk = ngx_array_push(&hide_headers);
1192             if (hk == NULL) {
1193                 return NGX_ERROR;
1194             }
1195 
1196             hk->key = h[i];
1197             hk->key_hash = ngx_hash_key_lc(h[i].data, h[i].len);
1198             hk->value = (void *) 1;
1199 
1200             if (h[i].len == sizeof("Last-Modified") - 1
1201                 && ngx_strncasecmp(h[i].data, (u_char *) "Last-Modified",
1202                                    sizeof("Last-Modified") - 1)
1203                 == 0)
1204             {
1205                 conf->hide_last_modified = 1;
1206             }
1207 
1208             if (h[i].len == sizeof("Content-Type") - 1
1209                 && ngx_strncasecmp(h[i].data, (u_char *) "Content-Type",
1210                                    sizeof("Content-Type") - 1) == 0)
1211             {
1212                 conf->hide_content_type = 1;
1213             }
1214 
1215             dd("adding header to hide headers: %.*s", (int) h[i].len,
1216                h[i].data);
1217 
1218         exist:
1219 
1220             continue;
1221         }
1222     }
1223 
1224     if (conf->pass_headers != NGX_CONF_UNSET_PTR) {
1225 
1226         h = conf->pass_headers->elts;
1227         hk = hide_headers.elts;
1228 
1229         for (i = 0; i < conf->pass_headers->nelts; i++) {
1230             for (j = 0; j < hide_headers.nelts; j++) {
1231 
1232                 if (hk[j].key.data == NULL) {
1233                     continue;
1234                 }
1235 
1236                 if (h[i].len == sizeof("Content-Type") - 1
1237                     && ngx_strncasecmp(h[i].data, (u_char *) "Content-Type",
1238                                        sizeof("Content-Type") - 1) == 0)
1239                 {
1240                     conf->hide_content_type = 0;
1241                 }
1242 
1243                 if (h[i].len == sizeof("Last-Modified") - 1
1244                     && ngx_strncasecmp(h[i].data, (u_char *) "Last-Modified",
1245                                        sizeof("Last-Modified") - 1) == 0)
1246                 {
1247                     conf->hide_last_modified = 0;
1248                 }
1249 
1250                 if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {
1251                     hk[j].key.data = NULL;
1252                     break;
1253                 }
1254             }
1255         }
1256     }
1257 
1258     hash->hash = &conf->hide_headers_hash;
1259     hash->key = ngx_hash_key_lc;
1260     hash->pool = cf->pool;
1261     hash->temp_pool = NULL;
1262 
1263     return ngx_hash_init(hash, hide_headers.elts, hide_headers.nelts);
1264 }
1265 
1266 
1267 ngx_int_t
ngx_http_srcache_cmp_int(const void * one,const void * two)1268 ngx_http_srcache_cmp_int(const void *one, const void *two)
1269 {
1270     const ngx_int_t           *a = one;
1271     const ngx_int_t           *b = two;
1272 
1273     return (*a < *b);
1274 }
1275 
1276 /* vi:set ft=c ts=4 sw=4 et fdm=marker: */
1277