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