blob: b01e5871f2ad68773bc4d1d68b20350826d9da1c [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 Sysoeva1512b12003-11-03 17:33:31 +000082 p->header_in->pos += c->ctx.header_size;
83 if (ngx_http_proxy_process_cached_header(p) == NGX_ERROR) {
84 return NGX_HTTP_INTERNAL_SERVER_ERROR;
85 }
Igor Sysoev65977492003-11-02 22:56:18 +000086
87 } else if (rc == NGX_DECLINED) {
Igor Sysoeva1512b12003-11-03 17:33:31 +000088 p->header_in->pos += c->ctx.header_size;
Igor Sysoev65977492003-11-02 22:56:18 +000089 p->header_in->last = p->header_in->pos;
90 }
91
92 return rc;
93}
94
95
Igor Sysoeva1512b12003-11-03 17:33:31 +000096static int ngx_http_proxy_process_cached_header(ngx_http_proxy_ctx_t *p)
Igor Sysoev65977492003-11-02 22:56:18 +000097{
98 int rc, i;
99 ngx_table_elt_t *h;
100 ngx_http_request_t *r;
101 ngx_http_proxy_cache_t *c;
102
103 rc = ngx_http_proxy_parse_status_line(p);
104
105 c = p->cache;
106 r = p->request;
107
108 if (rc == NGX_AGAIN) {
109 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
110 "\"proxy_header_buffer_size\" "
111 "is too small to read header from \"%s\"",
112 c->ctx.file.name.data);
Igor Sysoeva1512b12003-11-03 17:33:31 +0000113 return NGX_ERROR;
Igor Sysoev65977492003-11-02 22:56:18 +0000114 }
115
116 if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) {
117 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
118 "no valid HTTP/1.0 header in \"%s\"",
119 c->ctx.file.name.data);
Igor Sysoeva1512b12003-11-03 17:33:31 +0000120 return NGX_ERROR;
Igor Sysoev65977492003-11-02 22:56:18 +0000121 }
122
123 /* rc == NGX_OK */
124
125 c->status = p->status;
126 c->status_line.len = p->status_end - p->status_start;
127 c->status_line.data = ngx_palloc(r->pool, c->status_line.len + 1);
128 if (c->status_line.data == NULL) {
Igor Sysoeva1512b12003-11-03 17:33:31 +0000129 return NGX_ERROR;
Igor Sysoev65977492003-11-02 22:56:18 +0000130 }
131
132 ngx_cpystrn(c->status_line.data, p->status_start, c->status_line.len + 1);
133
134 ngx_log_debug(r->connection->log, "http cache status %d '%s'" _
135 c->status _ c->status_line.data);
136
137 c->headers_in.headers = ngx_create_table(r->pool, 20);
138
139 for ( ;; ) {
140 rc = ngx_http_parse_header_line(r, p->header_in);
141
142 if (rc == NGX_OK) {
143
144 /* a header line has been parsed successfully */
145
146 h = ngx_http_add_header(&c->headers_in, ngx_http_proxy_headers_in);
147 if (h == NULL) {
Igor Sysoeva1512b12003-11-03 17:33:31 +0000148 return NGX_ERROR;
Igor Sysoev65977492003-11-02 22:56:18 +0000149 }
150
151 h->key.len = r->header_name_end - r->header_name_start;
152 h->value.len = r->header_end - r->header_start;
153
154 h->key.data = ngx_palloc(r->pool,
155 h->key.len + 1 + h->value.len + 1);
156 if (h->key.data == NULL) {
Igor Sysoeva1512b12003-11-03 17:33:31 +0000157 return NGX_ERROR;
Igor Sysoev65977492003-11-02 22:56:18 +0000158 }
159
160 h->value.data = h->key.data + h->key.len + 1;
161 ngx_cpystrn(h->key.data, r->header_name_start, h->key.len + 1);
162 ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1);
163
164 for (i = 0; ngx_http_proxy_headers_in[i].name.len != 0; i++) {
165 if (ngx_http_proxy_headers_in[i].name.len != h->key.len) {
166 continue;
167 }
168
169 if (ngx_strcasecmp(ngx_http_proxy_headers_in[i].name.data,
170 h->key.data) == 0)
171 {
172 *((ngx_table_elt_t **) ((char *) &c->headers_in
173 + ngx_http_proxy_headers_in[i].offset)) = h;
174 break;
175 }
176 }
177
178 ngx_log_debug(r->connection->log, "HTTP cache header: '%s: %s'" _
179 h->key.data _ h->value.data);
180
181 continue;
182
183 } else if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
184
185 /* a whole header has been parsed successfully */
186
187 ngx_log_debug(r->connection->log, "HTTP header done");
188
Igor Sysoeva1512b12003-11-03 17:33:31 +0000189 return NGX_OK;
Igor Sysoev65977492003-11-02 22:56:18 +0000190
191 } else if (rc == NGX_HTTP_PARSE_INVALID_HEADER) {
192
193 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
194 "invalid header in \"%s\"",
195 c->ctx.file.name.data);
Igor Sysoeva1512b12003-11-03 17:33:31 +0000196 return NGX_ERROR;
Igor Sysoev65977492003-11-02 22:56:18 +0000197 }
198
199 /* rc == NGX_AGAIN || rc == NGX_HTTP_PARSE_TOO_LONG_HEADER */
200
201 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
202 "\"proxy_header_buffer_size\" "
203 "is too small to read header from \"%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
209
210int ngx_http_proxy_send_cached_response(ngx_http_proxy_ctx_t *p)
211{
212 int rc;
213 ngx_hunk_t *h;
214 ngx_chain_t out;
215 ngx_http_request_t *r;
216
217 r = p->request;
218
Igor Sysoeva1512b12003-11-03 17:33:31 +0000219 r->headers_out.status = p->cache->status;
Igor Sysoev65977492003-11-02 22:56:18 +0000220
221#if 0
222 r->headers_out.content_length_n = -1;
223 r->headers_out.content_length = NULL;
224#endif
225
226 /* copy an cached header to r->headers_out */
227
228 if (ngx_http_proxy_copy_header(p, &p->cache->headers_in) == NGX_ERROR) {
229 return NGX_HTTP_INTERNAL_SERVER_ERROR;
230 }
231
232 /* we need to allocate all before the header would be sent */
233
234 if (!((h = ngx_calloc_hunk(r->pool)))) {
235 return NGX_HTTP_INTERNAL_SERVER_ERROR;
236 }
237
238 if (!((h->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t))))) {
239 return NGX_HTTP_INTERNAL_SERVER_ERROR;
240 }
241
242 rc = ngx_http_send_header(r);
243
244 /* NEEDED ??? */ p->header_sent = 1;
245
246 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
247 return rc;
248 }
249
250 /* TODO: part in p->header_in */
251
252 h->type = r->main ? NGX_HUNK_FILE : NGX_HUNK_FILE|NGX_HUNK_LAST;
253
254 h->file_pos = p->header_in->pos - p->header_in->start;
255 h->file_last = h->file_pos + p->cache->ctx.header.length;
256
257 h->file->fd = p->cache->ctx.file.fd;
258 h->file->log = r->connection->log;
259
260 out.hunk = h;
261 out.next = NULL;
262
263 return ngx_http_output_filter(r, &out);
264}
Igor Sysoeva1512b12003-11-03 17:33:31 +0000265
266
Igor Sysoevcf80a702003-11-03 22:20:44 +0000267int ngx_http_proxy_is_cachable(ngx_http_proxy_ctx_t *p)
268{
269 time_t date, last_modified, expires;
270 ngx_http_proxy_headers_in_t *h;
271
272 switch (p->upstream->status) {
273 case NGX_HTTP_OK:
274 case NGX_HTTP_MOVED_PERMANENTLY:
275 case NGX_HTTP_MOVED_TEMPORARILY:
276 break;
277
278#if 0
279 case NGX_HTTP_NOT_MODIFIED:
280 return 1;
281#endif
282
283 default:
284 return 0;
285 }
286
287 h = &p->upstream->headers_in;
288
289 date = NGX_ERROR;
290 if (h->date) {
291 date = ngx_http_parse_time(h->date->value.data, h->date->value.len);
292 }
293 if (date == NGX_ERROR) {
294 date = ngx_time();
295 }
296 p->cache->ctx.header.date = date;
297
298 last_modified = NGX_ERROR;
299 if (h->last_modified) {
300 last_modified = ngx_http_parse_time(h->last_modified->value.data,
301 h->last_modified->value.len);
302 p->cache->ctx.header.last_modified = last_modified;
303 }
304
305 if (h->x_accel_expires) {
306 expires = ngx_atoi(h->x_accel_expires->value.data,
307 h->x_accel_expires->value.len);
308 if (expires != NGX_ERROR) {
309 p->state->reason = NGX_HTTP_PROXY_CACHE_XAE;
310 p->cache->ctx.header.expires = date + expires;
311 return (expires > 0);
312 }
313 }
314
315 if (!p->lcf->ignore_expires) {
316
317 /* TODO: Cache-Control: no-cache, max-age= */
318
319 if (h->expires) {
320 expires = ngx_http_parse_time(h->expires->value.data,
321 h->expires->value.len);
322 if (expires != NGX_ERROR) {
323 p->state->reason = NGX_HTTP_PROXY_CACHE_EXP;
324 p->cache->ctx.header.expires = expires;
325 return (date < expires);
326 }
327 }
328 }
329
330 if (p->upstream->status == NGX_HTTP_MOVED_PERMANENTLY) {
331 p->state->reason = NGX_HTTP_PROXY_CACHE_MVD;
332 p->cache->ctx.header.expires = /* STUB: 1 hour */ 60 * 60;
333 return 1;
334 }
335
336 if (p->upstream->status == NGX_HTTP_MOVED_TEMPORARILY) {
337 return 1;
338 }
339
340 if (last_modified != NGX_ERROR && p->lcf->lm_factor > 0) {
341
342 /* FIXME: time_t == int_64_t */
343
344 p->state->reason = NGX_HTTP_PROXY_CACHE_LMF;
345 p->cache->ctx.header.expires = ngx_time()
346 + (((int64_t) (date - last_modified)) * p->lcf->lm_factor) / 100;
347 return 1;
348 }
349
350 if (p->lcf->default_expires > 0) {
351 p->state->reason = NGX_HTTP_PROXY_CACHE_PDE;
352 p->cache->ctx.header.expires = p->lcf->default_expires;
353 return 1;
354 }
355
356 return 0;
357}
358
359
Igor Sysoeva1512b12003-11-03 17:33:31 +0000360int ngx_http_proxy_update_cache(ngx_http_proxy_ctx_t *p)
361{
362 if (p->cache == NULL) {
363 return NGX_OK;
364 }
365
366 return ngx_http_cache_update_file(p->request, &p->cache->ctx,
367 &p->upstream->event_pipe->temp_file->file.name);
368}