blob: 0bc7c6159ee419cb1af8fe59f69fab9cd8d6a3ba [file] [log] [blame]
Igor Sysoev65977492003-11-02 22:56:18 +00001
2#include <ngx_config.h>
3#include <ngx_core.h>
4#include <ngx_http.h>
5#include <ngx_http_proxy_handler.h>
6
7
Igor Sysoeva1512b12003-11-03 17:33:31 +00008static int ngx_http_proxy_process_cached_header(ngx_http_proxy_ctx_t *p);
9
10
Igor Sysoev65977492003-11-02 22:56:18 +000011int ngx_http_proxy_get_cached_response(ngx_http_proxy_ctx_t *p)
12{
Igor Sysoeva1512b12003-11-03 17:33:31 +000013 int rc;
14 char *last;
15 ngx_http_request_t *r;
16 ngx_http_proxy_cache_t *c;
17 ngx_http_proxy_upstream_conf_t *u;
Igor Sysoev65977492003-11-02 22:56:18 +000018
19 r = p->request;
20
21 if (!(c = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_cache_t)))) {
22 return NGX_HTTP_INTERNAL_SERVER_ERROR;
23 }
24
Igor Sysoeva1512b12003-11-03 17:33:31 +000025 p->cache = c;
26
Igor Sysoev65977492003-11-02 22:56:18 +000027 c->ctx.file.fd = NGX_INVALID_FILE;
28 c->ctx.file.log = r->connection->log;
29 c->ctx.path = p->lcf->cache_path;
30
31 u = p->lcf->upstream;
32
33 c->ctx.key.len = u->url.len + r->uri.len - u->location->len + r->args.len;
34 if (!(c->ctx.key.data = ngx_palloc(r->pool, c->ctx.key.len + 1))) {
35 return NGX_HTTP_INTERNAL_SERVER_ERROR;
36 }
37
38 last = ngx_cpymem(c->ctx.key.data, u->url.data, u->url.len);
39
40 last = ngx_cpymem(last, r->uri.data + u->location->len,
41 r->uri.len - u->location->len);
42
43 if (r->args.len > 0) {
44 *(last++) = '?';
45 last = ngx_cpymem(last, r->args.data, r->args.len);
46 }
47 *last = '\0';
48
49 p->header_in = ngx_create_temp_hunk(r->pool, p->lcf->header_buffer_size);
50 if (p->header_in == NULL) {
51 return NGX_HTTP_INTERNAL_SERVER_ERROR;
52 }
53 p->header_in->tag = (ngx_hunk_tag_t) &ngx_http_proxy_module;
54
55 c->ctx.buf = p->header_in;
Igor Sysoev65977492003-11-02 22:56:18 +000056
57 rc = ngx_http_cache_get_file(r, &c->ctx);
58
Igor Sysoevcf80a702003-11-03 22:20:44 +000059 switch (rc) {
60 case NGX_HTTP_CACHE_STALE:
Igor Sysoeva1512b12003-11-03 17:33:31 +000061 p->stale = 1;
Igor Sysoevcf80a702003-11-03 22:20:44 +000062 p->state->cache = NGX_HTTP_PROXY_CACHE_EXPR;
63 break;
64
65 case NGX_HTTP_CACHE_AGED:
66 p->stale = 1;
67 p->state->cache = NGX_HTTP_PROXY_CACHE_AGED;
68 break;
69
70 case NGX_OK:
71 p->state->cache = NGX_HTTP_PROXY_CACHE_HIT;
72 break;
73
74 default:
75 p->state->cache = NGX_HTTP_PROXY_CACHE_MISS;
Igor Sysoeva1512b12003-11-03 17:33:31 +000076 }
77
Igor Sysoevcf80a702003-11-03 22:20:44 +000078 if (rc == NGX_OK
79 || rc == NGX_HTTP_CACHE_STALE
80 || rc == NGX_HTTP_CACHE_AGED)
81 {
Igor Sysoev9cc1ace2003-11-04 22:12:39 +000082 p->header_in->pos = p->header_in->start + c->ctx.header_size;
Igor Sysoeva1512b12003-11-03 17:33:31 +000083 if (ngx_http_proxy_process_cached_header(p) == NGX_ERROR) {
84 return NGX_HTTP_INTERNAL_SERVER_ERROR;
85 }
Igor Sysoev9cc1ace2003-11-04 22:12:39 +000086 p->header_in->pos = p->header_in->start + c->ctx.header_size;
87 p->header_in->last = p->header_in->pos;
Igor Sysoev65977492003-11-02 22:56:18 +000088
89 } else if (rc == NGX_DECLINED) {
Igor Sysoev9cc1ace2003-11-04 22:12:39 +000090 p->header_in->pos = p->header_in->start + c->ctx.header_size;
Igor Sysoev65977492003-11-02 22:56:18 +000091 p->header_in->last = p->header_in->pos;
92 }
93
94 return rc;
95}
96
97
Igor Sysoeva1512b12003-11-03 17:33:31 +000098static int ngx_http_proxy_process_cached_header(ngx_http_proxy_ctx_t *p)
Igor Sysoev65977492003-11-02 22:56:18 +000099{
100 int rc, i;
101 ngx_table_elt_t *h;
102 ngx_http_request_t *r;
103 ngx_http_proxy_cache_t *c;
104
105 rc = ngx_http_proxy_parse_status_line(p);
106
107 c = p->cache;
108 r = p->request;
109
110 if (rc == NGX_AGAIN) {
111 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
112 "\"proxy_header_buffer_size\" "
113 "is too small to read header from \"%s\"",
114 c->ctx.file.name.data);
Igor Sysoeva1512b12003-11-03 17:33:31 +0000115 return NGX_ERROR;
Igor Sysoev65977492003-11-02 22:56:18 +0000116 }
117
118 if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) {
119 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
120 "no valid HTTP/1.0 header in \"%s\"",
121 c->ctx.file.name.data);
Igor Sysoeva1512b12003-11-03 17:33:31 +0000122 return NGX_ERROR;
Igor Sysoev65977492003-11-02 22:56:18 +0000123 }
124
125 /* rc == NGX_OK */
126
127 c->status = p->status;
128 c->status_line.len = p->status_end - p->status_start;
129 c->status_line.data = ngx_palloc(r->pool, c->status_line.len + 1);
130 if (c->status_line.data == NULL) {
Igor Sysoeva1512b12003-11-03 17:33:31 +0000131 return NGX_ERROR;
Igor Sysoev65977492003-11-02 22:56:18 +0000132 }
133
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000134 /* reset for the possible parsing the upstream header */
135
136 p->status = 0;
137 p->status_count = 0;
138
Igor Sysoev65977492003-11-02 22:56:18 +0000139 ngx_cpystrn(c->status_line.data, p->status_start, c->status_line.len + 1);
140
141 ngx_log_debug(r->connection->log, "http cache status %d '%s'" _
142 c->status _ c->status_line.data);
143
144 c->headers_in.headers = ngx_create_table(r->pool, 20);
145
146 for ( ;; ) {
147 rc = ngx_http_parse_header_line(r, p->header_in);
148
149 if (rc == NGX_OK) {
150
151 /* a header line has been parsed successfully */
152
153 h = ngx_http_add_header(&c->headers_in, ngx_http_proxy_headers_in);
154 if (h == NULL) {
Igor Sysoeva1512b12003-11-03 17:33:31 +0000155 return NGX_ERROR;
Igor Sysoev65977492003-11-02 22:56:18 +0000156 }
157
158 h->key.len = r->header_name_end - r->header_name_start;
159 h->value.len = r->header_end - r->header_start;
160
161 h->key.data = ngx_palloc(r->pool,
162 h->key.len + 1 + h->value.len + 1);
163 if (h->key.data == NULL) {
Igor Sysoeva1512b12003-11-03 17:33:31 +0000164 return NGX_ERROR;
Igor Sysoev65977492003-11-02 22:56:18 +0000165 }
166
167 h->value.data = h->key.data + h->key.len + 1;
168 ngx_cpystrn(h->key.data, r->header_name_start, h->key.len + 1);
169 ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1);
170
171 for (i = 0; ngx_http_proxy_headers_in[i].name.len != 0; i++) {
172 if (ngx_http_proxy_headers_in[i].name.len != h->key.len) {
173 continue;
174 }
175
176 if (ngx_strcasecmp(ngx_http_proxy_headers_in[i].name.data,
177 h->key.data) == 0)
178 {
179 *((ngx_table_elt_t **) ((char *) &c->headers_in
180 + ngx_http_proxy_headers_in[i].offset)) = h;
181 break;
182 }
183 }
184
185 ngx_log_debug(r->connection->log, "HTTP cache header: '%s: %s'" _
186 h->key.data _ h->value.data);
187
188 continue;
189
190 } else if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
191
192 /* a whole header has been parsed successfully */
193
194 ngx_log_debug(r->connection->log, "HTTP header done");
195
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000196 c->ctx.file_start = p->header_in->pos - p->header_in->start;
197
Igor Sysoeva1512b12003-11-03 17:33:31 +0000198 return NGX_OK;
Igor Sysoev65977492003-11-02 22:56:18 +0000199
200 } else if (rc == NGX_HTTP_PARSE_INVALID_HEADER) {
201
202 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
203 "invalid header in \"%s\"",
204 c->ctx.file.name.data);
Igor Sysoeva1512b12003-11-03 17:33:31 +0000205 return NGX_ERROR;
Igor Sysoev65977492003-11-02 22:56:18 +0000206 }
207
208 /* rc == NGX_AGAIN || rc == NGX_HTTP_PARSE_TOO_LONG_HEADER */
209
210 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
211 "\"proxy_header_buffer_size\" "
212 "is too small to read header from \"%s\"",
213 c->ctx.file.name.data);
Igor Sysoeva1512b12003-11-03 17:33:31 +0000214 return NGX_ERROR;
Igor Sysoev65977492003-11-02 22:56:18 +0000215 }
216}
217
218
Igor Sysoeve8732b02003-11-05 17:03:41 +0000219#if 0
220
221static void ngx_http_proxy_cache_busy_lock(ngx_http_proxy_ctx_t *p)
222{
223 rc = ngx_http_busy_lock(p->lcf->busy_lock, p->cache->ctx.md5);
224
225 if (rc == NGX_OK) {
226 ngx_http_proxy_request_upstream(p);
227 }
228
229 if (rc == NGX_AGAIN) {
230 if (p->busy_lock_time) {
231 ngx_add_timer(p->request->connection->read, 1000);
232 return;
233 }
234 }
235
236 rc == NGX_ERROR
237 check waitn
238}
239
240#endif
241
242
Igor Sysoev65977492003-11-02 22:56:18 +0000243int ngx_http_proxy_send_cached_response(ngx_http_proxy_ctx_t *p)
244{
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000245 int rc, len, i;
246 off_t rest;
247 ngx_hunk_t *h0, *h1;
248 ngx_chain_t out[2];
Igor Sysoev65977492003-11-02 22:56:18 +0000249 ngx_http_request_t *r;
250
251 r = p->request;
252
Igor Sysoeva1512b12003-11-03 17:33:31 +0000253 r->headers_out.status = p->cache->status;
Igor Sysoev65977492003-11-02 22:56:18 +0000254
255#if 0
256 r->headers_out.content_length_n = -1;
257 r->headers_out.content_length = NULL;
258#endif
259
260 /* copy an cached header to r->headers_out */
261
262 if (ngx_http_proxy_copy_header(p, &p->cache->headers_in) == NGX_ERROR) {
263 return NGX_HTTP_INTERNAL_SERVER_ERROR;
264 }
265
266 /* we need to allocate all before the header would be sent */
267
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000268 len = p->header_in->end - (p->header_in->start + p->cache->ctx.file_start);
269
270 h0 = NULL;
271 h1 = NULL;
272
273 if (len) {
274 if (!((h0 = ngx_calloc_hunk(r->pool)))) {
275 return NGX_HTTP_INTERNAL_SERVER_ERROR;
276 }
277
278 if (!((h0->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t))))) {
279 return NGX_HTTP_INTERNAL_SERVER_ERROR;
280 }
Igor Sysoev65977492003-11-02 22:56:18 +0000281 }
282
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000283 if (len < p->cache->ctx.length) {
284 if (!((h1 = ngx_calloc_hunk(r->pool)))) {
285 return NGX_HTTP_INTERNAL_SERVER_ERROR;
286 }
287
288 if (!((h1->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t))))) {
289 return NGX_HTTP_INTERNAL_SERVER_ERROR;
290 }
Igor Sysoev65977492003-11-02 22:56:18 +0000291 }
292
293 rc = ngx_http_send_header(r);
294
295 /* NEEDED ??? */ p->header_sent = 1;
296
297 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
298 return rc;
299 }
300
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000301 rest = p->cache->ctx.length;
Igor Sysoev65977492003-11-02 22:56:18 +0000302
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000303 if (len) {
304 if (p->valid_header_in) {
305 h0->pos = p->header_in->start + p->cache->ctx.file_start;
Igor Sysoev65977492003-11-02 22:56:18 +0000306
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000307 if (len > p->cache->ctx.length) {
308 h0->last = h0->pos + p->cache->ctx.length;
Igor Sysoev65977492003-11-02 22:56:18 +0000309
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000310 } else {
311 h0->last = p->header_in->end;
312 }
Igor Sysoev65977492003-11-02 22:56:18 +0000313
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000314 h0->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP;
315 }
316
317 h0->type |= NGX_HUNK_FILE;
318 h0->file_pos = p->cache->ctx.file_start;
319
320 h0->file->fd = p->cache->ctx.file.fd;
321 h0->file->log = r->connection->log;
322
323 if (len > p->cache->ctx.length) {
324 h0->file_last = h0->file_pos + p->cache->ctx.length;
325 rest = 0;
326
327 } else {
328 h0->file_last = h0->file_pos + len;
329 rest -= len;
330 }
331
332 out[0].hunk = h0;
333 out[0].next = &out[1];
334 i = 0;
335
336 } else {
337 i = -1;
338 }
339
340 if (rest) {
341 h1->file_pos = p->cache->ctx.file_start + len;
342 h1->file_last = h1->file_pos + rest;
343 h1->type = NGX_HUNK_FILE;
344
345 h1->file->fd = p->cache->ctx.file.fd;
346 h1->file->log = r->connection->log;
347
348 out[++i].hunk = h1;
349 }
350
351 out[i].next = NULL;
352 if (!r->main) {
353 out[i].hunk->type |= NGX_HUNK_LAST;
354 }
355
356 return ngx_http_output_filter(r, out);
Igor Sysoev65977492003-11-02 22:56:18 +0000357}
Igor Sysoeva1512b12003-11-03 17:33:31 +0000358
359
Igor Sysoevcf80a702003-11-03 22:20:44 +0000360int ngx_http_proxy_is_cachable(ngx_http_proxy_ctx_t *p)
361{
362 time_t date, last_modified, expires;
363 ngx_http_proxy_headers_in_t *h;
364
365 switch (p->upstream->status) {
366 case NGX_HTTP_OK:
367 case NGX_HTTP_MOVED_PERMANENTLY:
368 case NGX_HTTP_MOVED_TEMPORARILY:
369 break;
370
371#if 0
372 case NGX_HTTP_NOT_MODIFIED:
373 return 1;
374#endif
375
376 default:
377 return 0;
378 }
379
380 h = &p->upstream->headers_in;
381
382 date = NGX_ERROR;
383 if (h->date) {
384 date = ngx_http_parse_time(h->date->value.data, h->date->value.len);
385 }
386 if (date == NGX_ERROR) {
387 date = ngx_time();
388 }
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000389 p->cache->ctx.date = date;
Igor Sysoevcf80a702003-11-03 22:20:44 +0000390
391 last_modified = NGX_ERROR;
392 if (h->last_modified) {
393 last_modified = ngx_http_parse_time(h->last_modified->value.data,
394 h->last_modified->value.len);
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000395 p->cache->ctx.last_modified = last_modified;
Igor Sysoevcf80a702003-11-03 22:20:44 +0000396 }
397
398 if (h->x_accel_expires) {
399 expires = ngx_atoi(h->x_accel_expires->value.data,
400 h->x_accel_expires->value.len);
401 if (expires != NGX_ERROR) {
402 p->state->reason = NGX_HTTP_PROXY_CACHE_XAE;
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000403 p->cache->ctx.expires = date + expires;
Igor Sysoevcf80a702003-11-03 22:20:44 +0000404 return (expires > 0);
405 }
406 }
407
408 if (!p->lcf->ignore_expires) {
409
410 /* TODO: Cache-Control: no-cache, max-age= */
411
412 if (h->expires) {
413 expires = ngx_http_parse_time(h->expires->value.data,
414 h->expires->value.len);
415 if (expires != NGX_ERROR) {
416 p->state->reason = NGX_HTTP_PROXY_CACHE_EXP;
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000417 p->cache->ctx.expires = expires;
Igor Sysoevcf80a702003-11-03 22:20:44 +0000418 return (date < expires);
419 }
420 }
421 }
422
423 if (p->upstream->status == NGX_HTTP_MOVED_PERMANENTLY) {
424 p->state->reason = NGX_HTTP_PROXY_CACHE_MVD;
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000425 p->cache->ctx.expires = /* STUB: 1 hour */ 60 * 60;
Igor Sysoevcf80a702003-11-03 22:20:44 +0000426 return 1;
427 }
428
429 if (p->upstream->status == NGX_HTTP_MOVED_TEMPORARILY) {
430 return 1;
431 }
432
433 if (last_modified != NGX_ERROR && p->lcf->lm_factor > 0) {
434
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000435 /* FIXME: time_t == int_64_t, we can use fpu */
Igor Sysoevcf80a702003-11-03 22:20:44 +0000436
437 p->state->reason = NGX_HTTP_PROXY_CACHE_LMF;
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000438 p->cache->ctx.expires = ngx_time()
Igor Sysoevcf80a702003-11-03 22:20:44 +0000439 + (((int64_t) (date - last_modified)) * p->lcf->lm_factor) / 100;
440 return 1;
441 }
442
443 if (p->lcf->default_expires > 0) {
444 p->state->reason = NGX_HTTP_PROXY_CACHE_PDE;
Igor Sysoev9cc1ace2003-11-04 22:12:39 +0000445 p->cache->ctx.expires = p->lcf->default_expires;
Igor Sysoevcf80a702003-11-03 22:20:44 +0000446 return 1;
447 }
448
449 return 0;
450}
451
452
Igor Sysoeva1512b12003-11-03 17:33:31 +0000453int ngx_http_proxy_update_cache(ngx_http_proxy_ctx_t *p)
454{
Igor Sysoeve8732b02003-11-05 17:03:41 +0000455 ngx_event_pipe_t *ep;
456
Igor Sysoeva1512b12003-11-03 17:33:31 +0000457 if (p->cache == NULL) {
458 return NGX_OK;
459 }
460
Igor Sysoeve8732b02003-11-05 17:03:41 +0000461 ep = p->upstream->event_pipe;
462
463 if (p->cache->ctx.length == -1) {
464 /* TODO: test rc */
465 ngx_write_file(&ep->temp_file->file,
466 (char *) &ep->read_length, sizeof(off_t),
467 offsetof(ngx_http_cache_header_t, length));
468 }
469
Igor Sysoeva1512b12003-11-03 17:33:31 +0000470 return ngx_http_cache_update_file(p->request, &p->cache->ctx,
Igor Sysoeve8732b02003-11-05 17:03:41 +0000471 &ep->temp_file->file.name);
Igor Sysoeva1512b12003-11-03 17:33:31 +0000472}