a prelimiary proxy cache support
Igor Sysoev
13 years ago
61 | 61 | if [ $HTTP != YES ]; then |
62 | 62 | have=NGX_CRYPT . auto/nohave |
63 | 63 | CRYPT_LIB= |
64 | fi | |
65 | ||
66 | ||
67 | if [ $HTTP_CACHE = YES ]; then | |
68 | USE_MD5=YES | |
69 | have=NGX_HTTP_CACHE . auto/have | |
70 | HTTP_SRCS="$HTTP_SRCS $HTTP_FILE_CACHE_SRCS" | |
64 | 71 | fi |
65 | 72 | |
66 | 73 | |
304 | 311 | HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_IP_HASH_SRCS" |
305 | 312 | fi |
306 | 313 | |
307 | # STUB | |
308 | #USE_MD5=YES | |
309 | #HTTP_SRCS="$HTTP_SRCS $HTTP_CACHE_SRCS" | |
310 | #HTTP_SRCS="$HTTP_SRCS $HTTP_FILE_CACHE_SRCS" | |
311 | ||
312 | 314 | if [ $HTTP_STUB_STATUS = YES ]; then |
313 | 315 | have=NGX_STAT_STUB . auto/have |
314 | 316 | HTTP_MODULES="$HTTP_MODULES ngx_http_stub_status_module" |
51 | 51 | NGX_HTTP_PROXY_TEMP_PATH= |
52 | 52 | NGX_HTTP_FASTCGI_TEMP_PATH= |
53 | 53 | |
54 | HTTP_CACHE=YES | |
54 | 55 | HTTP_CHARSET=YES |
55 | 56 | HTTP_GZIP=YES |
56 | 57 | HTTP_SSL=NO |
164 | 165 | --with-ipv6) NGX_IPV6=YES ;; |
165 | 166 | |
166 | 167 | --without-http) HTTP=NO ;; |
168 | --without-http-cache) HTTP_CACHE=NO ;; | |
169 | ||
167 | 170 | --http-log-path=*) NGX_HTTP_LOG_PATH="$value" ;; |
168 | 171 | --http-client-body-temp-path=*) NGX_HTTP_CLIENT_TEMP_PATH="$value" ;; |
169 | 172 | --http-proxy-temp-path=*) NGX_HTTP_PROXY_TEMP_PATH="$value" ;; |
336 | 339 | files |
337 | 340 | |
338 | 341 | --without-http disable HTTP server |
342 | --without-http-cache disable HTTP cache | |
339 | 343 | |
340 | 344 | --with-mail enable POP3/IMAP4/SMTP proxy module |
341 | 345 | --with-mail_ssl_module enable ngx_mail_ssl_module |
32 | 32 | src/core/ngx_cycle.h \ |
33 | 33 | src/core/ngx_conf_file.h \ |
34 | 34 | src/core/ngx_resolver.h \ |
35 | src/core/ngx_open_file_cache.h \ | |
36 | src/core/ngx_garbage_collector.h" | |
35 | src/core/ngx_open_file_cache.h" | |
37 | 36 | |
38 | 37 | |
39 | 38 | CORE_SRCS="src/core/nginx.c \ |
61 | 60 | src/core/ngx_cpuinfo.c \ |
62 | 61 | src/core/ngx_conf_file.c \ |
63 | 62 | src/core/ngx_resolver.c \ |
64 | src/core/ngx_open_file_cache.c \ | |
65 | src/core/ngx_garbage_collector.c" | |
63 | src/core/ngx_open_file_cache.c" | |
66 | 64 | |
67 | 65 | |
68 | 66 | REGEX_DEPS=src/core/ngx_regex.h |
252 | 250 | ngx_http_core_module \ |
253 | 251 | ngx_http_log_module \ |
254 | 252 | ngx_http_upstream_module" |
255 | ||
256 | HTTP_CACHE_MODULE=ngx_http_cache_module | |
257 | 253 | |
258 | 254 | HTTP_WRITE_FILTER_MODULE="ngx_http_write_filter_module" |
259 | 255 | HTTP_HEADER_FILTER_MODULE="ngx_http_header_filter_module" |
312 | 308 | |
313 | 309 | HTTP_POSTPONE_FILTER_SRCS=src/http/ngx_http_postpone_filter_module.c |
314 | 310 | |
315 | HTTP_CACHE_SRCS=src/http/ngx_http_cache.c | |
316 | 311 | HTTP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c |
317 | 312 | |
318 | 313 |
263 | 263 | } |
264 | 264 | |
265 | 265 | path->len = 0; |
266 | path->cleaner = (ngx_gc_handler_pt) cmd->post; | |
266 | path->cleaner = (ngx_path_cleaner_pt) cmd->post; | |
267 | 267 | path->conf_file = cf->conf_file->file.name.data; |
268 | 268 | path->line = cf->conf_file->line; |
269 | 269 | |
284 | 284 | *slot = path; |
285 | 285 | |
286 | 286 | if (ngx_add_path(cf, slot) == NGX_ERROR) { |
287 | return NGX_CONF_ERROR; | |
288 | } | |
289 | ||
290 | return NGX_CONF_OK; | |
291 | } | |
292 | ||
293 | ||
294 | char * | |
295 | ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, ngx_path_t *prev, | |
296 | ngx_path_init_t *init) | |
297 | { | |
298 | if (*path) { | |
299 | return NGX_CONF_OK; | |
300 | } | |
301 | ||
302 | if (prev) { | |
303 | *path = prev; | |
304 | return NGX_CONF_OK; | |
305 | } | |
306 | ||
307 | *path = ngx_palloc(cf->pool, sizeof(ngx_path_t)); | |
308 | if (*path == NULL) { | |
309 | return NGX_CONF_ERROR; | |
310 | } | |
311 | ||
312 | (*path)->name = init->name; | |
313 | ||
314 | if (ngx_conf_full_name(cf->cycle, &(*path)->name, 0) != NGX_OK) { | |
315 | return NGX_CONF_ERROR; | |
316 | } | |
317 | ||
318 | (*path)->level[0] = init->level[0]; | |
319 | (*path)->level[1] = init->level[1]; | |
320 | (*path)->level[2] = init->level[2]; | |
321 | ||
322 | (*path)->len = init->level[0] + (init->level[0] ? 1 : 0) | |
323 | + init->level[1] + (init->level[1] ? 1 : 0) | |
324 | + init->level[2] + (init->level[2] ? 1 : 0); | |
325 | ||
326 | (*path)->cleaner = NULL; | |
327 | (*path)->conf_file = NULL; | |
328 | ||
329 | if (ngx_add_path(cf, path) != NGX_OK) { | |
287 | 330 | return NGX_CONF_ERROR; |
288 | 331 | } |
289 | 332 |
10 | 10 | #include <ngx_config.h> |
11 | 11 | #include <ngx_core.h> |
12 | 12 | |
13 | typedef struct ngx_path_s ngx_path_t; | |
14 | ||
15 | #include <ngx_garbage_collector.h> | |
16 | ||
17 | 13 | |
18 | 14 | struct ngx_file_s { |
19 | ngx_fd_t fd; | |
20 | ngx_str_t name; | |
21 | ngx_file_info_t info; | |
15 | ngx_fd_t fd; | |
16 | ngx_str_t name; | |
17 | ngx_file_info_t info; | |
22 | 18 | |
23 | off_t offset; | |
24 | off_t sys_offset; | |
19 | off_t offset; | |
20 | off_t sys_offset; | |
25 | 21 | |
26 | ngx_log_t *log; | |
22 | ngx_log_t *log; | |
27 | 23 | |
28 | unsigned valid_info:1; | |
29 | unsigned directio:1; | |
24 | unsigned valid_info:1; | |
25 | unsigned directio:1; | |
30 | 26 | }; |
31 | 27 | |
32 | 28 | #define NGX_MAX_PATH_LEVEL 3 |
33 | 29 | |
34 | struct ngx_path_s { | |
35 | ngx_str_t name; | |
36 | size_t len; | |
37 | size_t level[3]; | |
38 | ngx_gc_handler_pt cleaner; | |
39 | 30 | |
40 | u_char *conf_file; | |
41 | ngx_uint_t line; | |
42 | }; | |
31 | typedef time_t (*ngx_path_cleaner_pt) (void *data); | |
43 | 32 | |
44 | 33 | |
45 | 34 | typedef struct { |
46 | ngx_file_t file; | |
47 | off_t offset; | |
48 | ngx_path_t *path; | |
49 | ngx_pool_t *pool; | |
50 | char *warn; | |
35 | ngx_str_t name; | |
36 | size_t len; | |
37 | size_t level[3]; | |
51 | 38 | |
52 | ngx_uint_t access; | |
39 | ngx_path_cleaner_pt cleaner; | |
40 | void *data; | |
53 | 41 | |
54 | unsigned log_level:8; | |
55 | unsigned persistent:1; | |
56 | unsigned clean:1; | |
42 | u_char *conf_file; | |
43 | ngx_uint_t line; | |
44 | } ngx_path_t; | |
45 | ||
46 | ||
47 | typedef struct { | |
48 | ngx_str_t name; | |
49 | size_t level[3]; | |
50 | } ngx_path_init_t; | |
51 | ||
52 | ||
53 | typedef struct { | |
54 | ngx_file_t file; | |
55 | off_t offset; | |
56 | ngx_path_t *path; | |
57 | ngx_pool_t *pool; | |
58 | char *warn; | |
59 | ||
60 | ngx_uint_t access; | |
61 | ||
62 | unsigned log_level:8; | |
63 | unsigned persistent:1; | |
64 | unsigned clean:1; | |
57 | 65 | } ngx_temp_file_t; |
58 | 66 | |
59 | 67 | |
60 | 68 | typedef struct { |
61 | ngx_uint_t access; | |
62 | ngx_uint_t path_access; | |
63 | time_t time; | |
64 | ngx_fd_t fd; | |
65 | ngx_err_t rename_error; | |
69 | ngx_uint_t access; | |
70 | ngx_uint_t path_access; | |
71 | time_t time; | |
72 | ngx_fd_t fd; | |
73 | ngx_err_t rename_error; | |
66 | 74 | |
67 | unsigned create_path:1; | |
68 | unsigned delete_file:1; | |
69 | unsigned log_rename_error:1; | |
75 | unsigned create_path:1; | |
76 | unsigned delete_file:1; | |
77 | unsigned log_rename_error:1; | |
70 | 78 | |
71 | ngx_log_t *log; | |
79 | ngx_log_t *log; | |
72 | 80 | } ngx_ext_rename_file_t; |
73 | 81 | |
74 | 82 | |
112 | 120 | ngx_atomic_uint_t ngx_next_temp_number(ngx_uint_t collision); |
113 | 121 | |
114 | 122 | char *ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); |
123 | char *ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, | |
124 | ngx_path_t *prev, ngx_path_init_t *init); | |
115 | 125 | char *ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); |
116 | 126 | |
117 | 127 | |
118 | #define ngx_conf_merge_path_value(curr, prev, path, l1, l2, l3, clean, cf) \ | |
119 | if (curr == NULL) { \ | |
120 | if (prev == NULL) { \ | |
121 | curr = ngx_palloc(cf->pool, sizeof(ngx_path_t)); \ | |
122 | if (curr == NULL) { \ | |
123 | return NGX_CONF_ERROR; \ | |
124 | } \ | |
125 | \ | |
126 | curr->name.len = sizeof(path) - 1; \ | |
127 | curr->name.data = (u_char *) path; \ | |
128 | \ | |
129 | if (ngx_conf_full_name(cf->cycle, &curr->name, 0) == NGX_ERROR) { \ | |
130 | return NGX_CONF_ERROR; \ | |
131 | } \ | |
132 | \ | |
133 | curr->level[0] = l1; \ | |
134 | curr->level[1] = l2; \ | |
135 | curr->level[2] = l3; \ | |
136 | curr->len = l1 + l2 + l3 + (l1 ? 1:0) + (l2 ? 1:0) + (l3 ? 1:0); \ | |
137 | curr->cleaner = clean; \ | |
138 | curr->conf_file = NULL; \ | |
139 | \ | |
140 | if (ngx_add_path(cf, &curr) == NGX_ERROR) { \ | |
141 | return NGX_CONF_ERROR; \ | |
142 | } \ | |
143 | \ | |
144 | } else { \ | |
145 | curr = prev; \ | |
146 | } \ | |
147 | } | |
148 | ||
149 | ||
150 | ||
151 | 128 | #endif /* _NGX_FILE_H_INCLUDED_ */ |
0 | ||
1 | /* | |
2 | * Copyright (C) Igor Sysoev | |
3 | */ | |
4 | ||
5 | ||
6 | #include <ngx_config.h> | |
7 | #include <ngx_core.h> | |
8 | ||
9 | ||
10 | ||
11 | ngx_int_t ngx_collect_garbage(ngx_gc_t *ctx, ngx_str_t *dname, ngx_int_t level) | |
12 | { | |
13 | int rc; | |
14 | u_char *last; | |
15 | size_t len; | |
16 | ngx_err_t err; | |
17 | ngx_str_t fname, buf; | |
18 | ngx_dir_t dir; | |
19 | ||
20 | buf.len = 0; | |
21 | #if (NGX_SUPPRESS_WARN) | |
22 | buf.data = NULL; | |
23 | fname.data = NULL; | |
24 | #endif | |
25 | ||
26 | ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0, | |
27 | "gc dir \"%s\":%d", dname->data, dname->len); | |
28 | ||
29 | if (ngx_open_dir(dname, &dir) == NGX_ERROR) { | |
30 | ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, | |
31 | ngx_open_dir_n " \"%s\" failed", dname->data); | |
32 | return NGX_ERROR; | |
33 | } | |
34 | ||
35 | for ( ;; ) { | |
36 | ngx_set_errno(0); | |
37 | if (ngx_read_dir(&dir) == NGX_ERROR) { | |
38 | err = ngx_errno; | |
39 | ||
40 | if (err != NGX_ENOMOREFILES) { | |
41 | ngx_log_error(NGX_LOG_CRIT, ctx->log, err, | |
42 | ngx_read_dir_n " \"%s\" failed", dname->data); | |
43 | rc = NGX_ERROR; | |
44 | ||
45 | } else { | |
46 | rc = NGX_OK; | |
47 | } | |
48 | ||
49 | break; | |
50 | } | |
51 | ||
52 | len = ngx_de_namelen(&dir); | |
53 | ||
54 | ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0, | |
55 | "gc name \"%s\":%d", ngx_de_name(&dir), len); | |
56 | ||
57 | if (len == 1 && ngx_de_name(&dir)[0] == '.') { | |
58 | continue; | |
59 | } | |
60 | ||
61 | if (len == 2 | |
62 | && ngx_de_name(&dir)[0] == '.' | |
63 | && ngx_de_name(&dir)[1] == '.') | |
64 | { | |
65 | continue; | |
66 | } | |
67 | ||
68 | fname.len = dname->len + 1+ len; | |
69 | ||
70 | if (fname.len + NGX_DIR_MASK_LEN > buf.len) { | |
71 | ||
72 | if (buf.len) { | |
73 | ngx_free(buf.data); | |
74 | } | |
75 | ||
76 | buf.len = dname->len + 1 + len + NGX_DIR_MASK_LEN; | |
77 | ||
78 | buf.data = ngx_alloc(buf.len + 1, ctx->log); | |
79 | if (buf.data == NULL) { | |
80 | return NGX_ABORT; | |
81 | } | |
82 | } | |
83 | ||
84 | last = ngx_cpymem(buf.data, dname->data, dname->len); | |
85 | *last++ = '/'; | |
86 | ngx_memcpy(last, ngx_de_name(&dir), len + 1); | |
87 | fname.data = buf.data; | |
88 | ||
89 | ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, | |
90 | "gc path: \"%s\"", fname.data); | |
91 | ||
92 | if (!dir.valid_info) { | |
93 | if (ngx_de_info(fname.data, &dir) == NGX_FILE_ERROR) { | |
94 | ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, | |
95 | ngx_de_info_n " \"%s\" failed", fname.data); | |
96 | continue; | |
97 | } | |
98 | } | |
99 | ||
100 | if (ngx_de_is_dir(&dir)) { | |
101 | ||
102 | ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, | |
103 | "gc enter dir \"%s\"", fname.data); | |
104 | ||
105 | if (level == -1 | |
106 | /* there can not be directory on the last level */ | |
107 | || level == NGX_MAX_PATH_LEVEL | |
108 | /* an directory from the old path hierarchy */ | |
109 | || len != ctx->path->level[level]) | |
110 | { | |
111 | if (ngx_collect_garbage(ctx, &fname, -1) == NGX_ABORT) { | |
112 | return NGX_ABORT; | |
113 | } | |
114 | ||
115 | fname.data[fname.len] = '\0'; | |
116 | ||
117 | ngx_log_error(NGX_LOG_NOTICE, ctx->log, 0, | |
118 | "delete old hierachy directory \"%s\"", | |
119 | fname.data); | |
120 | ||
121 | if (ngx_delete_dir(fname.data) == NGX_FILE_ERROR) { | |
122 | ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, | |
123 | ngx_delete_dir_n " \"%s\" failed", | |
124 | fname.data); | |
125 | } else { | |
126 | ctx->deleted++; | |
127 | ctx->freed += ngx_de_size(&dir); | |
128 | } | |
129 | ||
130 | continue; | |
131 | } | |
132 | ||
133 | if (ngx_collect_garbage(ctx, &fname, level + 1) == NGX_ABORT) { | |
134 | return NGX_ABORT; | |
135 | } | |
136 | ||
137 | } else if (ngx_de_is_file(&dir)) { | |
138 | ||
139 | ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, | |
140 | "gc file \"%s\"", fname.data); | |
141 | ||
142 | if (level == -1 | |
143 | || (level < NGX_MAX_PATH_LEVEL && ctx->path->level[level] != 0)) | |
144 | { | |
145 | if (ngx_delete_file(fname.data) == NGX_FILE_ERROR) { | |
146 | ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, | |
147 | ngx_delete_file_n " \"%s\" failed", | |
148 | fname.data); | |
149 | } else { | |
150 | ctx->deleted++; | |
151 | ctx->freed += ngx_de_size(&dir); | |
152 | } | |
153 | ||
154 | continue; | |
155 | } | |
156 | ||
157 | if (ctx->handler(ctx, &fname, &dir) == NGX_ABORT) { | |
158 | return NGX_ABORT; | |
159 | } | |
160 | ||
161 | } else { | |
162 | ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, | |
163 | "the file \"%s\" has unknown type, deleting", | |
164 | fname.data); | |
165 | ||
166 | if (ngx_delete_file(fname.data) == NGX_FILE_ERROR) { | |
167 | ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, | |
168 | ngx_delete_file_n " \"%s\" failed", fname.data); | |
169 | } else { | |
170 | ctx->deleted++; | |
171 | ctx->freed += ngx_de_size(&dir); | |
172 | } | |
173 | } | |
174 | } | |
175 | ||
176 | if (buf.len) { | |
177 | ngx_free(buf.data); | |
178 | } | |
179 | ||
180 | if (ngx_close_dir(&dir) == NGX_ERROR) { | |
181 | ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, | |
182 | ngx_close_dir_n " \"%s\" failed", fname.data); | |
183 | } | |
184 | ||
185 | return rc; | |
186 | } | |
187 | ||
188 | ||
189 | ngx_int_t ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name, | |
190 | ngx_dir_t *dir) | |
191 | { | |
192 | /* | |
193 | * We use mtime only and do not use atime because: | |
194 | * on NTFS access time has a resolution of 1 hour, | |
195 | * on NT FAT access time has a resolution of 1 day, | |
196 | * Unices have the mount option "noatime". | |
197 | */ | |
198 | ||
199 | if (ngx_time() - ngx_de_mtime(dir) < 3600) { | |
200 | return NGX_OK; | |
201 | } | |
202 | ||
203 | ngx_log_error(NGX_LOG_NOTICE, ctx->log, 0, | |
204 | "delete the stale temporary file \"%s\"", name->data); | |
205 | ||
206 | if (ngx_delete_file(name->data) == NGX_FILE_ERROR) { | |
207 | ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, | |
208 | ngx_delete_file_n " \"%s\" failed", name->data); | |
209 | return NGX_ERROR; | |
210 | } | |
211 | ||
212 | ctx->deleted++; | |
213 | ctx->freed += ngx_de_size(dir); | |
214 | ||
215 | return NGX_OK; | |
216 | } |
0 | ||
1 | /* | |
2 | * Copyright (C) Igor Sysoev | |
3 | */ | |
4 | ||
5 | ||
6 | #ifndef _NGX_GARBAGE_COLLECTOR_H_INCLUDED_ | |
7 | #define _NGX_GARBAGE_COLLECTOR_H_INCLUDED_ | |
8 | ||
9 | ||
10 | typedef struct ngx_gc_s ngx_gc_t; | |
11 | ||
12 | typedef ngx_int_t (*ngx_gc_handler_pt) (ngx_gc_t *ctx, ngx_str_t *name, | |
13 | ngx_dir_t *dir); | |
14 | ||
15 | ||
16 | struct ngx_gc_s { | |
17 | ngx_path_t *path; | |
18 | u_int deleted; | |
19 | off_t freed; | |
20 | ngx_gc_handler_pt handler; | |
21 | ngx_log_t *log; | |
22 | }; | |
23 | ||
24 | ||
25 | ngx_int_t ngx_collect_garbage(ngx_gc_t *ctx, ngx_str_t *dname, ngx_int_t level); | |
26 | ngx_int_t ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name, | |
27 | ngx_dir_t *dir); | |
28 | ||
29 | ||
30 | #endif /* _NGX_GARBAGE_COLLECTOR_H_INCLUDED_ */ |
325 | 325 | |
326 | 326 | |
327 | 327 | void |
328 | ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd) | |
329 | { | |
330 | ngx_pool_cleanup_t *c; | |
331 | ngx_pool_cleanup_file_t *cf; | |
332 | ||
333 | for (c = p->cleanup; c; c = c->next) { | |
334 | if (c->handler == ngx_pool_cleanup_file) { | |
335 | ||
336 | cf = c->data; | |
337 | ||
338 | if (cf->fd == fd) { | |
339 | c->handler(cf); | |
340 | c->handler = NULL; | |
341 | return; | |
342 | } | |
343 | } | |
344 | } | |
345 | } | |
346 | ||
347 | ||
348 | void | |
328 | 349 | ngx_pool_cleanup_file(void *data) |
329 | 350 | { |
330 | 351 | ngx_pool_cleanup_file_t *c = data; |
81 | 81 | |
82 | 82 | |
83 | 83 | ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size); |
84 | void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd); | |
84 | 85 | void ngx_pool_cleanup_file(void *data); |
85 | 86 | void ngx_pool_delete_file(void *data); |
86 | 87 |
422 | 422 | size_t bsize; |
423 | 423 | ngx_int_t rc; |
424 | 424 | ngx_uint_t flush, prev_last_shadow; |
425 | ngx_chain_t *out, **ll, *cl; | |
425 | ngx_chain_t *out, **ll, *cl, file; | |
426 | 426 | ngx_connection_t *downstream; |
427 | 427 | |
428 | 428 | downstream = p->downstream; |
485 | 485 | } |
486 | 486 | |
487 | 487 | p->in = NULL; |
488 | } | |
489 | ||
490 | if (p->cacheable && p->buf_to_file) { | |
491 | ||
492 | file.buf = p->buf_to_file; | |
493 | file.next = NULL; | |
494 | ||
495 | if (ngx_write_chain_to_temp_file(p->temp_file, &file) | |
496 | == NGX_ERROR) | |
497 | { | |
498 | return NGX_ABORT; | |
499 | } | |
488 | 500 | } |
489 | 501 | |
490 | 502 | ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, |
287 | 287 | ngx_conf_set_path_slot, |
288 | 288 | NGX_HTTP_LOC_CONF_OFFSET, |
289 | 289 | offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_path), |
290 | (void *) ngx_garbage_collector_temp_handler }, | |
290 | NULL }, | |
291 | 291 | |
292 | 292 | { ngx_string("fastcgi_max_temp_file_size"), |
293 | 293 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, |
432 | 432 | ngx_string("X-Accel-Buffering"), |
433 | 433 | ngx_string("X-Accel-Charset"), |
434 | 434 | ngx_null_string |
435 | }; | |
436 | ||
437 | ||
438 | static ngx_path_init_t ngx_http_fastcgi_temp_path = { | |
439 | ngx_string(NGX_HTTP_FASTCGI_TEMP_PATH), { 1, 2, 0 } | |
435 | 440 | }; |
436 | 441 | |
437 | 442 | |
1922 | 1927 | |NGX_HTTP_UPSTREAM_FT_OFF; |
1923 | 1928 | } |
1924 | 1929 | |
1925 | ngx_conf_merge_path_value(conf->upstream.temp_path, | |
1930 | if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path, | |
1926 | 1931 | prev->upstream.temp_path, |
1927 | NGX_HTTP_FASTCGI_TEMP_PATH, 1, 2, 0, | |
1928 | ngx_garbage_collector_temp_handler, cf); | |
1932 | &ngx_http_fastcgi_temp_path) | |
1933 | != NGX_OK) | |
1934 | { | |
1935 | return NGX_CONF_ERROR; | |
1936 | } | |
1929 | 1937 | |
1930 | 1938 | ngx_conf_merge_value(conf->upstream.pass_request_headers, |
1931 | 1939 | prev->upstream.pass_request_headers, 1); |
82 | 82 | } |
83 | 83 | |
84 | 84 | r->headers_out.status = NGX_HTTP_NOT_MODIFIED; |
85 | r->headers_out.status_line.len = 0; | |
85 | 86 | r->headers_out.content_type.len = 0; |
86 | 87 | ngx_http_clear_content_length(r); |
87 | 88 | ngx_http_clear_accept_ranges(r); |
31 | 31 | |
32 | 32 | |
33 | 33 | typedef struct { |
34 | ngx_str_t key_start; | |
34 | 35 | ngx_str_t schema; |
35 | 36 | ngx_str_t host_header; |
36 | 37 | ngx_str_t port; |
88 | 89 | |
89 | 90 | static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r, |
90 | 91 | ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf); |
92 | #if (NGX_HTTP_CACHE) | |
93 | static ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r); | |
94 | #endif | |
91 | 95 | static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r); |
92 | 96 | static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r); |
93 | 97 | static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r); |
115 | 119 | static void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf); |
116 | 120 | static char *ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, |
117 | 121 | void *parent, void *child); |
122 | static ngx_int_t ngx_http_proxy_merge_headers(ngx_conf_t *cf, | |
123 | ngx_http_proxy_loc_conf_t *conf, ngx_http_proxy_loc_conf_t *prev); | |
118 | 124 | |
119 | 125 | static char *ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, |
120 | 126 | void *conf); |
122 | 128 | void *conf); |
123 | 129 | static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, |
124 | 130 | void *conf); |
131 | #if (NGX_HTTP_CACHE) | |
132 | static char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd, | |
133 | void *conf); | |
134 | #endif | |
125 | 135 | |
126 | 136 | static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data); |
127 | 137 | |
134 | 144 | static ngx_int_t ngx_http_proxy_set_ssl(ngx_conf_t *cf, |
135 | 145 | ngx_http_proxy_loc_conf_t *plcf); |
136 | 146 | #endif |
137 | static ngx_int_t ngx_http_proxy_set_vars(ngx_pool_t *pool, ngx_url_t *u, | |
138 | ngx_http_proxy_vars_t *v); | |
147 | static void ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v); | |
139 | 148 | |
140 | 149 | |
141 | 150 | static ngx_conf_post_t ngx_http_proxy_lowat_post = |
156 | 165 | }; |
157 | 166 | |
158 | 167 | |
168 | ngx_module_t ngx_http_proxy_module; | |
169 | ||
170 | ||
159 | 171 | static ngx_command_t ngx_http_proxy_commands[] = { |
160 | 172 | |
161 | 173 | { ngx_string("proxy_pass"), |
305 | 317 | offsetof(ngx_http_proxy_loc_conf_t, upstream.busy_buffers_size_conf), |
306 | 318 | NULL }, |
307 | 319 | |
320 | #if (NGX_HTTP_CACHE) | |
321 | ||
322 | { ngx_string("proxy_cache"), | |
323 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
324 | ngx_http_proxy_cache, | |
325 | NGX_HTTP_LOC_CONF_OFFSET, | |
326 | offsetof(ngx_http_proxy_loc_conf_t, upstream.cache), | |
327 | NULL }, | |
328 | ||
329 | { ngx_string("proxy_cache_path"), | |
330 | NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE, | |
331 | ngx_http_file_cache_set_slot, | |
332 | 0, | |
333 | 0, | |
334 | &ngx_http_proxy_module }, | |
335 | ||
336 | { ngx_string("proxy_cache_valid"), | |
337 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, | |
338 | ngx_http_file_cache_valid_set_slot, | |
339 | NGX_HTTP_LOC_CONF_OFFSET, | |
340 | offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_valid), | |
341 | NULL }, | |
342 | ||
343 | { ngx_string("proxy_cache_min_uses"), | |
344 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
345 | ngx_conf_set_num_slot, | |
346 | NGX_HTTP_LOC_CONF_OFFSET, | |
347 | offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_min_uses), | |
348 | NULL }, | |
349 | ||
350 | { ngx_string("proxy_cache_use_stale"), | |
351 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, | |
352 | ngx_conf_set_bitmask_slot, | |
353 | NGX_HTTP_LOC_CONF_OFFSET, | |
354 | offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_use_stale), | |
355 | &ngx_http_proxy_next_upstream_masks }, | |
356 | ||
357 | #endif | |
358 | ||
308 | 359 | { ngx_string("proxy_temp_path"), |
309 | 360 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234, |
310 | 361 | ngx_conf_set_path_slot, |
311 | 362 | NGX_HTTP_LOC_CONF_OFFSET, |
312 | 363 | offsetof(ngx_http_proxy_loc_conf_t, upstream.temp_path), |
313 | (void *) ngx_garbage_collector_temp_handler }, | |
364 | NULL }, | |
314 | 365 | |
315 | 366 | { ngx_string("proxy_max_temp_file_size"), |
316 | 367 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, |
454 | 505 | }; |
455 | 506 | |
456 | 507 | |
508 | static ngx_path_init_t ngx_http_proxy_temp_path = { | |
509 | ngx_string(NGX_HTTP_PROXY_TEMP_PATH), { 1, 2, 0 } | |
510 | }; | |
511 | ||
512 | ||
457 | 513 | static ngx_int_t |
458 | 514 | ngx_http_proxy_handler(ngx_http_request_t *r) |
459 | 515 | { |
501 | 557 | |
502 | 558 | u->conf = &plcf->upstream; |
503 | 559 | |
560 | #if (NGX_HTTP_CACHE) | |
561 | u->create_key = ngx_http_proxy_create_key; | |
562 | #endif | |
504 | 563 | u->create_request = ngx_http_proxy_create_request; |
505 | 564 | u->reinit_request = ngx_http_proxy_reinit_request; |
506 | 565 | u->process_header = ngx_http_proxy_process_status_line; |
536 | 595 | ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx, |
537 | 596 | ngx_http_proxy_loc_conf_t *plcf) |
538 | 597 | { |
539 | u_char *p; | |
540 | size_t add; | |
541 | u_short port; | |
542 | ngx_str_t proxy; | |
543 | ngx_url_t u; | |
598 | u_char *p; | |
599 | size_t add; | |
600 | u_short port; | |
601 | ngx_str_t proxy; | |
602 | ngx_url_t url; | |
603 | ngx_http_upstream_t *u; | |
544 | 604 | |
545 | 605 | if (ngx_http_script_run(r, &proxy, plcf->proxy_lengths->elts, 0, |
546 | 606 | plcf->proxy_values->elts) |
570 | 630 | return NGX_ERROR; |
571 | 631 | } |
572 | 632 | |
573 | r->upstream->schema.len = add; | |
574 | r->upstream->schema.data = proxy.data; | |
575 | ||
576 | ngx_memzero(&u, sizeof(ngx_url_t)); | |
577 | ||
578 | u.url.len = proxy.len - add; | |
579 | u.url.data = proxy.data + add; | |
580 | u.default_port = port; | |
581 | u.uri_part = 1; | |
582 | u.no_resolve = 1; | |
583 | ||
584 | if (ngx_parse_url(r->pool, &u) != NGX_OK) { | |
585 | if (u.err) { | |
633 | u = r->upstream; | |
634 | ||
635 | u->schema.len = add; | |
636 | u->schema.data = proxy.data; | |
637 | ||
638 | ngx_memzero(&url, sizeof(ngx_url_t)); | |
639 | ||
640 | url.url.len = proxy.len - add; | |
641 | url.url.data = proxy.data + add; | |
642 | url.default_port = port; | |
643 | url.uri_part = 1; | |
644 | url.no_resolve = 1; | |
645 | ||
646 | if (ngx_parse_url(r->pool, &url) != NGX_OK) { | |
647 | if (url.err) { | |
586 | 648 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
587 | "%s in upstream \"%V\"", u.err, &u.url); | |
649 | "%s in upstream \"%V\"", url.err, &url.url); | |
588 | 650 | } |
589 | 651 | |
590 | 652 | return NGX_ERROR; |
591 | 653 | } |
592 | 654 | |
593 | if (u.uri.len && u.uri.data[0] == '?') { | |
594 | p = ngx_pnalloc(r->pool, u.uri.len + 1); | |
655 | if (url.uri.len && url.uri.data[0] == '?') { | |
656 | p = ngx_pnalloc(r->pool, url.uri.len + 1); | |
595 | 657 | if (p == NULL) { |
596 | 658 | return NGX_ERROR; |
597 | 659 | } |
598 | 660 | |
599 | 661 | *p++ = '/'; |
600 | ngx_memcpy(p, u.uri.data, u.uri.len); | |
601 | ||
602 | u.uri.len++; | |
603 | u.uri.data = p - 1; | |
604 | } | |
605 | ||
606 | if (ngx_http_proxy_set_vars(r->pool, &u, &ctx->vars) != NGX_OK) { | |
662 | ngx_memcpy(p, url.uri.data, url.uri.len); | |
663 | ||
664 | url.uri.len++; | |
665 | url.uri.data = p - 1; | |
666 | } | |
667 | ||
668 | ctx->vars.key_start = u->schema; | |
669 | ||
670 | ngx_http_proxy_set_vars(&url, &ctx->vars); | |
671 | ||
672 | u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t)); | |
673 | if (u->resolved == NULL) { | |
607 | 674 | return NGX_ERROR; |
608 | 675 | } |
609 | 676 | |
610 | r->upstream->resolved = ngx_pcalloc(r->pool, | |
611 | sizeof(ngx_http_upstream_resolved_t)); | |
612 | if (r->upstream->resolved == NULL) { | |
677 | if (url.addrs && url.addrs[0].sockaddr) { | |
678 | u->resolved->sockaddr = url.addrs[0].sockaddr; | |
679 | u->resolved->socklen = url.addrs[0].socklen; | |
680 | u->resolved->naddrs = 1; | |
681 | u->resolved->host = url.addrs[0].name; | |
682 | ||
683 | } else { | |
684 | u->resolved->host = url.host; | |
685 | u->resolved->port = (in_port_t) (url.no_port ? port : url.port); | |
686 | u->resolved->no_port = url.no_port; | |
687 | } | |
688 | ||
689 | return NGX_OK; | |
690 | } | |
691 | ||
692 | ||
693 | #if (NGX_HTTP_CACHE) | |
694 | ||
695 | static ngx_int_t | |
696 | ngx_http_proxy_create_key(ngx_http_request_t *r) | |
697 | { | |
698 | size_t len, loc_len; | |
699 | u_char *p; | |
700 | uintptr_t escape; | |
701 | ngx_str_t *key; | |
702 | ngx_http_upstream_t *u; | |
703 | ngx_http_proxy_ctx_t *ctx; | |
704 | ngx_http_proxy_loc_conf_t *plcf; | |
705 | ||
706 | u = r->upstream; | |
707 | ||
708 | plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); | |
709 | ||
710 | ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); | |
711 | ||
712 | key = ngx_array_push(&r->cache->keys); | |
713 | if (key == NULL) { | |
613 | 714 | return NGX_ERROR; |
614 | 715 | } |
615 | 716 | |
616 | if (u.addrs && u.addrs[0].sockaddr) { | |
617 | r->upstream->resolved->sockaddr = u.addrs[0].sockaddr; | |
618 | r->upstream->resolved->socklen = u.addrs[0].socklen; | |
619 | r->upstream->resolved->naddrs = 1; | |
620 | r->upstream->resolved->host = u.addrs[0].name; | |
621 | ||
717 | *key = ctx->vars.key_start; | |
718 | ||
719 | key = ngx_array_push(&r->cache->keys); | |
720 | if (key == NULL) { | |
721 | return NGX_ERROR; | |
722 | } | |
723 | ||
724 | if (plcf->proxy_lengths) { | |
725 | ||
726 | *key = ctx->vars.uri; | |
727 | u->uri = ctx->vars.uri; | |
728 | ||
729 | return NGX_OK; | |
730 | ||
731 | } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main) | |
732 | { | |
733 | *key = r->unparsed_uri; | |
734 | u->uri = r->unparsed_uri; | |
735 | ||
736 | return NGX_OK; | |
737 | } | |
738 | ||
739 | loc_len = (r->valid_location && ctx->vars.uri.len) ? plcf->location.len : 0; | |
740 | ||
741 | if (r->quoted_uri || r->internal) { | |
742 | escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len, | |
743 | r->uri.len - loc_len, NGX_ESCAPE_URI); | |
622 | 744 | } else { |
623 | r->upstream->resolved->host = u.host; | |
624 | r->upstream->resolved->port = (in_port_t) (u.no_port ? u.default_port: | |
625 | u.port); | |
626 | r->upstream->resolved->no_port = u.no_port; | |
627 | } | |
745 | escape = 0; | |
746 | } | |
747 | ||
748 | len = ctx->vars.uri.len + r->uri.len - loc_len + escape | |
749 | + sizeof("?") - 1 + r->args.len; | |
750 | ||
751 | p = ngx_pnalloc(r->pool, len); | |
752 | if (p == NULL) { | |
753 | return NGX_ERROR; | |
754 | } | |
755 | ||
756 | key->data = p; | |
757 | ||
758 | if (r->valid_location) { | |
759 | p = ngx_copy(p, ctx->vars.uri.data, ctx->vars.uri.len); | |
760 | } | |
761 | ||
762 | if (escape) { | |
763 | ngx_escape_uri(p, r->uri.data + loc_len, | |
764 | r->uri.len - loc_len, NGX_ESCAPE_URI); | |
765 | p += r->uri.len - loc_len + escape; | |
766 | ||
767 | } else { | |
768 | p = ngx_copy(p, r->uri.data + loc_len, r->uri.len - loc_len); | |
769 | } | |
770 | ||
771 | if (r->args.len > 0) { | |
772 | *p++ = '?'; | |
773 | p = ngx_copy(p, r->args.data, r->args.len); | |
774 | } | |
775 | ||
776 | key->len = p - key->data; | |
777 | u->uri = *key; | |
628 | 778 | |
629 | 779 | return NGX_OK; |
630 | 780 | } |
781 | ||
782 | #endif | |
631 | 783 | |
632 | 784 | |
633 | 785 | static ngx_int_t |
634 | 786 | ngx_http_proxy_create_request(ngx_http_request_t *r) |
635 | 787 | { |
636 | size_t len, loc_len, body_len; | |
788 | size_t len, uri_len, loc_len, body_len; | |
637 | 789 | uintptr_t escape; |
638 | 790 | ngx_buf_t *b; |
639 | 791 | ngx_str_t method; |
674 | 826 | ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); |
675 | 827 | |
676 | 828 | if (plcf->proxy_lengths) { |
677 | len += ctx->vars.uri.len; | |
829 | uri_len = ctx->vars.uri.len; | |
678 | 830 | |
679 | 831 | } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main) |
680 | 832 | { |
681 | 833 | unparsed_uri = 1; |
682 | len += r->unparsed_uri.len; | |
834 | uri_len = r->unparsed_uri.len; | |
683 | 835 | |
684 | 836 | } else { |
685 | 837 | loc_len = (r->valid_location && ctx->vars.uri.len) ? |
690 | 842 | r->uri.len - loc_len, NGX_ESCAPE_URI); |
691 | 843 | } |
692 | 844 | |
693 | len += ctx->vars.uri.len + r->uri.len - loc_len + escape | |
694 | + sizeof("?") - 1 + r->args.len; | |
695 | } | |
845 | uri_len = ctx->vars.uri.len + r->uri.len - loc_len + escape | |
846 | + sizeof("?") - 1 + r->args.len; | |
847 | } | |
848 | ||
849 | if (uri_len == 0) { | |
850 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
851 | "zero length URI to proxy"); | |
852 | return NGX_ERROR; | |
853 | } | |
854 | ||
855 | len += uri_len; | |
696 | 856 | |
697 | 857 | ngx_http_script_flush_no_cacheable_variables(r, plcf->flushes); |
698 | 858 | |
979 | 1139 | u = r->upstream; |
980 | 1140 | |
981 | 1141 | if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) { |
1142 | ||
1143 | #if (NGX_HTTP_CACHE) | |
1144 | ||
1145 | if (r->cache) { | |
1146 | r->http_version = NGX_HTTP_VERSION_9; | |
1147 | u->headers_in.status_n = NGX_HTTP_OK; | |
1148 | return NGX_OK; | |
1149 | } | |
1150 | ||
1151 | #endif | |
1152 | ||
982 | 1153 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
983 | 1154 | "upstream sent no valid HTTP/1.0 header"); |
984 | 1155 | |
995 | 1166 | return NGX_OK; |
996 | 1167 | } |
997 | 1168 | |
1169 | if (u->state) { | |
1170 | u->state->status = ctx->status; | |
1171 | } | |
1172 | ||
998 | 1173 | u->headers_in.status_n = ctx->status; |
999 | u->state->status = ctx->status; | |
1000 | 1174 | |
1001 | 1175 | u->headers_in.status_line.len = ctx->status_end - ctx->status_start; |
1002 | 1176 | u->headers_in.status_line.data = ngx_pnalloc(r->pool, |
1637 | 1811 | * |
1638 | 1812 | * conf->upstream.bufs.num = 0; |
1639 | 1813 | * conf->upstream.next_upstream = 0; |
1814 | * conf->upstream.use_stale_cache = 0; | |
1640 | 1815 | * conf->upstream.temp_path = NULL; |
1641 | 1816 | * conf->upstream.hide_headers_hash = { NULL, 0 }; |
1642 | 1817 | * conf->upstream.uri = { 0, NULL }; |
1674 | 1849 | conf->upstream.pass_request_headers = NGX_CONF_UNSET; |
1675 | 1850 | conf->upstream.pass_request_body = NGX_CONF_UNSET; |
1676 | 1851 | |
1852 | #if (NGX_HTTP_CACHE) | |
1853 | conf->upstream.cache = NGX_CONF_UNSET_PTR; | |
1854 | conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT; | |
1855 | conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; | |
1856 | #endif | |
1857 | ||
1677 | 1858 | conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; |
1678 | 1859 | conf->upstream.pass_headers = NGX_CONF_UNSET_PTR; |
1679 | 1860 | |
1701 | 1882 | ngx_http_proxy_loc_conf_t *prev = parent; |
1702 | 1883 | ngx_http_proxy_loc_conf_t *conf = child; |
1703 | 1884 | |
1704 | u_char *p; | |
1705 | size_t size; | |
1706 | uintptr_t *code; | |
1707 | ngx_uint_t i; | |
1708 | ngx_keyval_t *src, *s, *h; | |
1709 | ngx_hash_key_t *hk; | |
1710 | ngx_hash_init_t hash; | |
1711 | ngx_http_proxy_redirect_t *pr; | |
1712 | ngx_http_script_compile_t sc; | |
1713 | ngx_http_script_copy_code_t *copy; | |
1885 | size_t size; | |
1886 | ngx_keyval_t *s; | |
1887 | ngx_hash_init_t hash; | |
1888 | ngx_http_proxy_redirect_t *pr; | |
1889 | ngx_http_script_compile_t sc; | |
1714 | 1890 | |
1715 | 1891 | if (conf->upstream.store != 0) { |
1716 | 1892 | ngx_conf_merge_value(conf->upstream.store, |
1849 | 2025 | |NGX_HTTP_UPSTREAM_FT_OFF; |
1850 | 2026 | } |
1851 | 2027 | |
1852 | ngx_conf_merge_path_value(conf->upstream.temp_path, | |
2028 | if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path, | |
1853 | 2029 | prev->upstream.temp_path, |
1854 | NGX_HTTP_PROXY_TEMP_PATH, 1, 2, 0, | |
1855 | ngx_garbage_collector_temp_handler, cf); | |
2030 | &ngx_http_proxy_temp_path) | |
2031 | != NGX_OK) | |
2032 | { | |
2033 | return NGX_CONF_ERROR; | |
2034 | } | |
2035 | ||
2036 | ||
2037 | #if (NGX_HTTP_CACHE) | |
2038 | ||
2039 | ngx_conf_merge_ptr_value(conf->upstream.cache, | |
2040 | prev->upstream.cache, NULL); | |
2041 | ||
2042 | if (conf->upstream.cache && conf->upstream.cache->data == NULL) { | |
2043 | ngx_shm_zone_t *shm_zone; | |
2044 | ||
2045 | shm_zone = conf->upstream.cache; | |
2046 | ||
2047 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
2048 | "\"proxy_cache\" zone \"%V\" is unknown", | |
2049 | &shm_zone->name); | |
2050 | ||
2051 | return NGX_CONF_ERROR; | |
2052 | } | |
2053 | ||
2054 | ngx_conf_merge_uint_value(conf->upstream.cache_min_uses, | |
2055 | prev->upstream.cache_min_uses, 1); | |
2056 | ||
2057 | ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale, | |
2058 | prev->upstream.cache_use_stale, | |
2059 | (NGX_CONF_BITMASK_SET | |
2060 | |NGX_HTTP_UPSTREAM_FT_OFF)); | |
2061 | ||
2062 | if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) { | |
2063 | conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET | |
2064 | |NGX_HTTP_UPSTREAM_FT_OFF; | |
2065 | } | |
2066 | ||
2067 | ngx_conf_merge_ptr_value(conf->upstream.cache_valid, | |
2068 | prev->upstream.cache_valid, NULL); | |
2069 | ||
2070 | if (conf->upstream.cache_valid == NULL) { | |
2071 | conf->upstream.cache = NULL; | |
2072 | } | |
2073 | ||
2074 | #endif | |
1856 | 2075 | |
1857 | 2076 | if (conf->method.len == 0) { |
1858 | 2077 | conf->method = prev->method; |
1983 | 2202 | s->value.data = (u_char *) "$proxy_internal_body_length"; |
1984 | 2203 | } |
1985 | 2204 | |
2205 | if (ngx_http_proxy_merge_headers(cf, conf, prev) != NGX_OK) { | |
2206 | return NGX_CONF_ERROR; | |
2207 | } | |
2208 | ||
2209 | return NGX_CONF_OK; | |
2210 | } | |
2211 | ||
2212 | ||
2213 | static ngx_int_t | |
2214 | ngx_http_proxy_merge_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf, | |
2215 | ngx_http_proxy_loc_conf_t *prev) | |
2216 | { | |
2217 | u_char *p; | |
2218 | size_t size; | |
2219 | uintptr_t *code; | |
2220 | ngx_uint_t i; | |
2221 | ngx_keyval_t *src, *s, *h; | |
2222 | ngx_hash_key_t *hk; | |
2223 | ngx_hash_init_t hash; | |
2224 | ngx_http_script_compile_t sc; | |
2225 | ngx_http_script_copy_code_t *copy; | |
1986 | 2226 | |
1987 | 2227 | if (conf->headers_source == NULL) { |
1988 | 2228 | conf->flushes = prev->flushes; |
1993 | 2233 | } |
1994 | 2234 | |
1995 | 2235 | if (conf->headers_set_hash.buckets) { |
1996 | return NGX_CONF_OK; | |
2236 | return NGX_OK; | |
1997 | 2237 | } |
1998 | 2238 | |
1999 | 2239 | |
2000 | 2240 | conf->headers_names = ngx_array_create(cf->pool, 4, sizeof(ngx_hash_key_t)); |
2001 | 2241 | if (conf->headers_names == NULL) { |
2002 | return NGX_CONF_ERROR; | |
2242 | return NGX_ERROR; | |
2003 | 2243 | } |
2004 | 2244 | |
2005 | 2245 | if (conf->headers_source == NULL) { |
2006 | 2246 | conf->headers_source = ngx_array_create(cf->pool, 4, |
2007 | 2247 | sizeof(ngx_keyval_t)); |
2008 | 2248 | if (conf->headers_source == NULL) { |
2009 | return NGX_CONF_ERROR; | |
2249 | return NGX_ERROR; | |
2010 | 2250 | } |
2011 | 2251 | } |
2012 | 2252 | |
2013 | 2253 | conf->headers_set_len = ngx_array_create(cf->pool, 64, 1); |
2014 | 2254 | if (conf->headers_set_len == NULL) { |
2015 | return NGX_CONF_ERROR; | |
2255 | return NGX_ERROR; | |
2016 | 2256 | } |
2017 | 2257 | |
2018 | 2258 | conf->headers_set = ngx_array_create(cf->pool, 512, 1); |
2019 | 2259 | if (conf->headers_set == NULL) { |
2020 | return NGX_CONF_ERROR; | |
2260 | return NGX_ERROR; | |
2021 | 2261 | } |
2022 | 2262 | |
2023 | 2263 | |
2033 | 2273 | |
2034 | 2274 | s = ngx_array_push(conf->headers_source); |
2035 | 2275 | if (s == NULL) { |
2036 | return NGX_CONF_ERROR; | |
2276 | return NGX_ERROR; | |
2037 | 2277 | } |
2038 | 2278 | |
2039 | 2279 | *s = *h; |
2051 | 2291 | |
2052 | 2292 | hk = ngx_array_push(conf->headers_names); |
2053 | 2293 | if (hk == NULL) { |
2054 | return NGX_CONF_ERROR; | |
2294 | return NGX_ERROR; | |
2055 | 2295 | } |
2056 | 2296 | |
2057 | 2297 | hk->key = src[i].key; |
2066 | 2306 | copy = ngx_array_push_n(conf->headers_set_len, |
2067 | 2307 | sizeof(ngx_http_script_copy_code_t)); |
2068 | 2308 | if (copy == NULL) { |
2069 | return NGX_CONF_ERROR; | |
2309 | return NGX_ERROR; | |
2070 | 2310 | } |
2071 | 2311 | |
2072 | 2312 | copy->code = (ngx_http_script_code_pt) |
2083 | 2323 | |
2084 | 2324 | copy = ngx_array_push_n(conf->headers_set, size); |
2085 | 2325 | if (copy == NULL) { |
2086 | return NGX_CONF_ERROR; | |
2326 | return NGX_ERROR; | |
2087 | 2327 | } |
2088 | 2328 | |
2089 | 2329 | copy->code = ngx_http_script_copy_code; |
2101 | 2341 | copy = ngx_array_push_n(conf->headers_set_len, |
2102 | 2342 | sizeof(ngx_http_script_copy_code_t)); |
2103 | 2343 | if (copy == NULL) { |
2104 | return NGX_CONF_ERROR; | |
2344 | return NGX_ERROR; | |
2105 | 2345 | } |
2106 | 2346 | |
2107 | 2347 | copy->code = (ngx_http_script_code_pt) |
2115 | 2355 | |
2116 | 2356 | copy = ngx_array_push_n(conf->headers_set, size); |
2117 | 2357 | if (copy == NULL) { |
2118 | return NGX_CONF_ERROR; | |
2358 | return NGX_ERROR; | |
2119 | 2359 | } |
2120 | 2360 | |
2121 | 2361 | copy->code = ngx_http_script_copy_code; |
2135 | 2375 | sc.values = &conf->headers_set; |
2136 | 2376 | |
2137 | 2377 | if (ngx_http_script_compile(&sc) != NGX_OK) { |
2138 | return NGX_CONF_ERROR; | |
2378 | return NGX_ERROR; | |
2139 | 2379 | } |
2140 | 2380 | |
2141 | 2381 | |
2142 | 2382 | copy = ngx_array_push_n(conf->headers_set_len, |
2143 | 2383 | sizeof(ngx_http_script_copy_code_t)); |
2144 | 2384 | if (copy == NULL) { |
2145 | return NGX_CONF_ERROR; | |
2385 | return NGX_ERROR; | |
2146 | 2386 | } |
2147 | 2387 | |
2148 | 2388 | copy->code = (ngx_http_script_code_pt) |
2156 | 2396 | |
2157 | 2397 | copy = ngx_array_push_n(conf->headers_set, size); |
2158 | 2398 | if (copy == NULL) { |
2159 | return NGX_CONF_ERROR; | |
2399 | return NGX_ERROR; | |
2160 | 2400 | } |
2161 | 2401 | |
2162 | 2402 | copy->code = ngx_http_script_copy_code; |
2168 | 2408 | |
2169 | 2409 | code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t)); |
2170 | 2410 | if (code == NULL) { |
2171 | return NGX_CONF_ERROR; | |
2411 | return NGX_ERROR; | |
2172 | 2412 | } |
2173 | 2413 | |
2174 | 2414 | *code = (uintptr_t) NULL; |
2175 | 2415 | |
2176 | 2416 | code = ngx_array_push_n(conf->headers_set, sizeof(uintptr_t)); |
2177 | 2417 | if (code == NULL) { |
2178 | return NGX_CONF_ERROR; | |
2418 | return NGX_ERROR; | |
2179 | 2419 | } |
2180 | 2420 | |
2181 | 2421 | *code = (uintptr_t) NULL; |
2183 | 2423 | |
2184 | 2424 | code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t)); |
2185 | 2425 | if (code == NULL) { |
2186 | return NGX_CONF_ERROR; | |
2426 | return NGX_ERROR; | |
2187 | 2427 | } |
2188 | 2428 | |
2189 | 2429 | *code = (uintptr_t) NULL; |
2201 | 2441 | conf->headers_names->nelts) |
2202 | 2442 | != NGX_OK) |
2203 | 2443 | { |
2204 | return NGX_CONF_ERROR; | |
2205 | } | |
2206 | ||
2207 | return NGX_CONF_OK; | |
2444 | return NGX_ERROR; | |
2445 | } | |
2446 | ||
2447 | return NGX_OK; | |
2208 | 2448 | } |
2209 | 2449 | |
2210 | 2450 | |
2297 | 2537 | return NGX_CONF_ERROR; |
2298 | 2538 | } |
2299 | 2539 | |
2300 | if (ngx_http_proxy_set_vars(cf->pool, &u, &plcf->vars) != NGX_OK) { | |
2301 | return NGX_CONF_ERROR; | |
2302 | } | |
2303 | ||
2304 | 2540 | plcf->vars.schema.len = add; |
2305 | 2541 | plcf->vars.schema.data = url->data; |
2542 | plcf->vars.key_start = plcf->vars.schema; | |
2543 | ||
2544 | ngx_http_proxy_set_vars(&u, &plcf->vars); | |
2545 | ||
2546 | clcf->handler = ngx_http_proxy_handler; | |
2547 | ||
2306 | 2548 | plcf->location = clcf->name; |
2307 | ||
2308 | clcf->handler = ngx_http_proxy_handler; | |
2309 | 2549 | |
2310 | 2550 | if (clcf->named |
2311 | 2551 | #if (NGX_PCRE) |
2472 | 2712 | } |
2473 | 2713 | |
2474 | 2714 | |
2715 | #if (NGX_HTTP_CACHE) | |
2716 | ||
2717 | static char * | |
2718 | ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
2719 | { | |
2720 | ngx_http_proxy_loc_conf_t *plcf = conf; | |
2721 | ||
2722 | ngx_str_t *value; | |
2723 | ||
2724 | value = cf->args->elts; | |
2725 | ||
2726 | if (plcf->upstream.cache != NGX_CONF_UNSET_PTR) { | |
2727 | return "is duplicate"; | |
2728 | } | |
2729 | ||
2730 | if (ngx_strcmp(value[1].data, "off") == 0) { | |
2731 | plcf->upstream.cache = NULL; | |
2732 | return NGX_CONF_OK; | |
2733 | } | |
2734 | ||
2735 | plcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, | |
2736 | &ngx_http_proxy_module); | |
2737 | if (plcf->upstream.cache == NULL) { | |
2738 | return NGX_CONF_ERROR; | |
2739 | } | |
2740 | ||
2741 | return NGX_CONF_OK; | |
2742 | } | |
2743 | ||
2744 | #endif | |
2745 | ||
2746 | ||
2475 | 2747 | static char * |
2476 | 2748 | ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data) |
2477 | 2749 | { |
2562 | 2834 | #endif |
2563 | 2835 | |
2564 | 2836 | |
2565 | static ngx_int_t | |
2566 | ngx_http_proxy_set_vars(ngx_pool_t *pool, ngx_url_t *u, | |
2567 | ngx_http_proxy_vars_t *v) | |
2837 | static void | |
2838 | ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v) | |
2568 | 2839 | { |
2569 | 2840 | if (u->family != AF_UNIX) { |
2841 | ||
2570 | 2842 | if (u->no_port || u->port == u->default_port) { |
2843 | ||
2571 | 2844 | v->host_header = u->host; |
2572 | 2845 | |
2573 | 2846 | if (u->default_port == 80) { |
2584 | 2857 | v->host_header.data = u->host.data; |
2585 | 2858 | v->port = u->port_text; |
2586 | 2859 | } |
2860 | ||
2861 | v->key_start.len += v->host_header.len; | |
2587 | 2862 | |
2588 | 2863 | } else { |
2589 | 2864 | v->host_header.len = sizeof("localhost") - 1; |
2590 | 2865 | v->host_header.data = (u_char *) "localhost"; |
2591 | 2866 | v->port.len = 0; |
2592 | 2867 | v->port.data = (u_char *) ""; |
2868 | v->key_start.len += sizeof("unix:") - 1 + u->host.len + 1; | |
2593 | 2869 | } |
2594 | 2870 | |
2595 | 2871 | v->uri = u->uri; |
2596 | ||
2597 | return NGX_OK; | |
2598 | } | |
2872 | } |
198 | 198 | ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); |
199 | 199 | |
200 | 200 | r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; |
201 | r->headers_out.status_line.len = 0; | |
201 | 202 | |
202 | 203 | if (ctx->ranges.nelts == 1) { |
203 | 204 | return ngx_http_range_singlepart_header(r, ctx); |
707 | 708 | ngx_http_range_multipart_body(ngx_http_request_t *r, |
708 | 709 | ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in) |
709 | 710 | { |
711 | off_t body_start; | |
710 | 712 | ngx_buf_t *b, *buf; |
711 | 713 | ngx_uint_t i; |
712 | 714 | ngx_chain_t *out, *hcl, *rcl, *dcl, **ll; |
715 | 717 | ll = &out; |
716 | 718 | buf = in->buf; |
717 | 719 | range = ctx->ranges.elts; |
720 | ||
721 | #if (NGX_HTTP_CACHE) | |
722 | body_start = r->cached ? r->cache->body_start : 0; | |
723 | #else | |
724 | body_start = 0; | |
725 | #endif | |
718 | 726 | |
719 | 727 | for (i = 0; i < ctx->ranges.nelts; i++) { |
720 | 728 | |
776 | 784 | b->file = buf->file; |
777 | 785 | |
778 | 786 | if (buf->in_file) { |
779 | b->file_pos = range[i].start; | |
780 | b->file_last = range[i].end; | |
787 | b->file_pos = body_start + range[i].start; | |
788 | b->file_last = body_start + range[i].end; | |
781 | 789 | } |
782 | 790 | |
783 | 791 | if (ngx_buf_in_memory(buf)) { |
9 | 9 | |
10 | 10 | #include <ngx_config.h> |
11 | 11 | #include <ngx_core.h> |
12 | #include <ngx_garbage_collector.h> | |
13 | 12 | |
14 | 13 | |
15 | typedef struct ngx_http_request_s ngx_http_request_t; | |
16 | typedef struct ngx_http_upstream_s ngx_http_upstream_t; | |
17 | typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t; | |
14 | typedef struct ngx_http_request_s ngx_http_request_t; | |
15 | typedef struct ngx_http_upstream_s ngx_http_upstream_t; | |
16 | typedef struct ngx_http_cache_s ngx_http_cache_t; | |
17 | typedef struct ngx_http_file_cache_s ngx_http_file_cache_t; | |
18 | typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t; | |
18 | 19 | |
19 | 20 | typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r, |
20 | 21 | ngx_table_elt_t *h, ngx_uint_t offset); |
21 | 22 | typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r, |
22 | 23 | ngx_http_request_t *sr, u_char *buf, size_t len); |
23 | 24 | |
24 | ||
25 | #if (NGX_HTTP_CACHE) | |
26 | #include <ngx_http_cache.h> | |
27 | #endif | |
28 | /* STUB */ | |
29 | #include <ngx_http_cache.h> | |
30 | 25 | |
31 | 26 | #include <ngx_http_variables.h> |
32 | 27 | #include <ngx_http_request.h> |
37 | 32 | #include <ngx_http_script.h> |
38 | 33 | #include <ngx_http_core_module.h> |
39 | 34 | |
35 | #if (NGX_HTTP_CACHE) | |
36 | #include <ngx_http_cache.h> | |
37 | #endif | |
40 | 38 | #if (NGX_HTTP_SSI) |
41 | 39 | #include <ngx_http_ssi_filter_module.h> |
42 | 40 | #endif |
0 | ||
1 | /* | |
2 | * Copyright (C) Igor Sysoev | |
3 | */ | |
4 | ||
5 | ||
6 | #include <ngx_config.h> | |
7 | #include <ngx_core.h> | |
8 | #include <ngx_http.h> | |
9 | ||
10 | ||
11 | #if 0 | |
12 | ||
13 | static ngx_http_module_t ngx_http_cache_module_ctx = { | |
14 | NULL, /* pre conf */ | |
15 | ||
16 | NULL, /* create main configuration */ | |
17 | NULL, /* init main configuration */ | |
18 | ||
19 | NULL, /* create server configuration */ | |
20 | NULL, /* merge server configuration */ | |
21 | ||
22 | NULL, /* create location configuration */ | |
23 | NULL /* merge location configuration */ | |
24 | }; | |
25 | ||
26 | ||
27 | ngx_module_t ngx_http_cache_module = { | |
28 | NGX_MODULE, | |
29 | &ngx_http_cache_module_ctx, /* module context */ | |
30 | NULL, /* module directives */ | |
31 | NGX_HTTP_MODULE, /* module type */ | |
32 | NULL, /* init module */ | |
33 | NULL /* init process */ | |
34 | }; | |
35 | ||
36 | #endif | |
37 | ||
38 | ||
39 | static ngx_int_t ngx_http_cache_create(ngx_http_request_t *r) | |
40 | { | |
41 | ngx_str_t *key; | |
42 | ||
43 | if (!(r->cache = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t)))) { | |
44 | return NGX_ERROR; | |
45 | } | |
46 | ||
47 | if (ngx_array_init(&r->cache->key, r->pool, 5, sizeof(ngx_str_t)) | |
48 | == NGX_ERROR) | |
49 | { | |
50 | return NGX_ERROR; | |
51 | } | |
52 | ||
53 | /* preallocate the primary key */ | |
54 | ||
55 | if (!(key = ngx_array_push(&r->cache->key))) { | |
56 | return NGX_ERROR; | |
57 | } | |
58 | ||
59 | key->len = 0; | |
60 | key->data = NULL; | |
61 | ||
62 | /* | |
63 | * we use offsetof() because sizeof() pads the struct size to the int size | |
64 | */ | |
65 | ||
66 | r->cache->header_size = offsetof(ngx_http_cache_header_t, key); | |
67 | ||
68 | r->cache->log = r->connection->log; | |
69 | r->cache->file.log = r->connection->log; | |
70 | ||
71 | return NGX_OK; | |
72 | } | |
73 | ||
74 | ||
75 | ngx_int_t ngx_http_cache_get(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx) | |
76 | { | |
77 | ngx_str_t *key; | |
78 | ngx_http_cache_t *c; | |
79 | ||
80 | if (r->cache == NULL) { | |
81 | if (ngx_http_cache_create(r) == NGX_ERROR) { | |
82 | return NGX_ABORT; | |
83 | } | |
84 | } | |
85 | ||
86 | c = r->cache; | |
87 | key = c->key.elts; | |
88 | ||
89 | if (ctx->primary) { | |
90 | key[0] = ctx->key; | |
91 | c->header_size += ctx->key.len; | |
92 | c->key_len += ctx->key.len; | |
93 | c->buf = ctx->buf; | |
94 | ||
95 | } else { | |
96 | if (key[0].len == 0) { | |
97 | key[0] = r->uri; | |
98 | c->header_size += r->uri.len; | |
99 | c->key_len += ctx->key.len; | |
100 | } | |
101 | ||
102 | if (!(key = ngx_array_push(&r->cache->key))) { | |
103 | return NGX_ABORT; | |
104 | } | |
105 | ||
106 | c->header_size += ctx->key.len; | |
107 | c->key_len += ctx->key.len; | |
108 | } | |
109 | ||
110 | #if 0 | |
111 | ||
112 | if (ctx->memory) { | |
113 | ngx_http_memory_cache_get(r, ctx); | |
114 | } | |
115 | ||
116 | #endif | |
117 | ||
118 | if (ctx->file) { | |
119 | return ngx_http_file_cache_get(r, ctx); | |
120 | } | |
121 | ||
122 | return NGX_DECLINED; | |
123 | } | |
124 | ||
125 | ||
126 | #if 0 | |
127 | ||
128 | ||
129 | ngx_http_cache_t *ngx_http_cache_get(ngx_http_cache_hash_t *hash, | |
130 | ngx_http_cleanup_t *cleanup, | |
131 | ngx_str_t *key, uint32_t *crc) | |
132 | { | |
133 | ngx_uint_t i; | |
134 | ngx_http_cache_t *c; | |
135 | ||
136 | *crc = ngx_crc(key->data, key->len); | |
137 | ||
138 | c = hash->elts + *crc % hash->hash * hash->nelts; | |
139 | ||
140 | if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { | |
141 | return (void *) NGX_ERROR; | |
142 | } | |
143 | ||
144 | for (i = 0; i < hash->nelts; i++) { | |
145 | if (c[i].crc == *crc | |
146 | && c[i].key.len == key->len | |
147 | && ngx_rstrncmp(c[i].key.data, key->data, key->len) == 0) | |
148 | { | |
149 | #if 0 | |
150 | if (c[i].expired) { | |
151 | ngx_mutex_unlock(&hash->mutex); | |
152 | return (void *) NGX_AGAIN; | |
153 | } | |
154 | #endif | |
155 | ||
156 | c[i].refs++; | |
157 | ||
158 | if ((!(c[i].notify && (ngx_event_flags & NGX_USE_KQUEUE_EVENT))) | |
159 | && (ngx_cached_time - c[i].updated >= hash->update)) | |
160 | { | |
161 | c[i].expired = 1; | |
162 | } | |
163 | ||
164 | ngx_mutex_unlock(&hash->mutex); | |
165 | ||
166 | if (cleanup) { | |
167 | cleanup->data.cache.hash = hash; | |
168 | cleanup->data.cache.cache = &c[i]; | |
169 | cleanup->valid = 1; | |
170 | cleanup->cache = 1; | |
171 | } | |
172 | ||
173 | return &c[i]; | |
174 | } | |
175 | } | |
176 | ||
177 | ngx_mutex_unlock(&hash->mutex); | |
178 | ||
179 | return NULL; | |
180 | } | |
181 | ||
182 | ||
183 | ngx_http_cache_t *ngx_http_cache_alloc(ngx_http_cache_hash_t *hash, | |
184 | ngx_http_cache_t *cache, | |
185 | ngx_http_cleanup_t *cleanup, | |
186 | ngx_str_t *key, uint32_t crc, | |
187 | ngx_str_t *value, ngx_log_t *log) | |
188 | { | |
189 | time_t old; | |
190 | ngx_uint_t i; | |
191 | ngx_http_cache_t *c; | |
192 | ||
193 | old = ngx_cached_time + 1; | |
194 | ||
195 | c = hash->elts + crc % hash->hash * hash->nelts; | |
196 | ||
197 | if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { | |
198 | return (void *) NGX_ERROR; | |
199 | } | |
200 | ||
201 | if (cache == NULL) { | |
202 | ||
203 | /* allocate a new entry */ | |
204 | ||
205 | for (i = 0; i < hash->nelts; i++) { | |
206 | if (c[i].refs > 0) { | |
207 | /* a busy entry */ | |
208 | continue; | |
209 | } | |
210 | ||
211 | if (c[i].key.len == 0) { | |
212 | /* a free entry is found */ | |
213 | cache = &c[i]; | |
214 | break; | |
215 | } | |
216 | ||
217 | /* looking for the oldest cache entry */ | |
218 | ||
219 | if (old > c[i].accessed) { | |
220 | ||
221 | old = c[i].accessed; | |
222 | cache = &c[i]; | |
223 | } | |
224 | } | |
225 | ||
226 | if (cache == NULL) { | |
227 | ngx_mutex_unlock(&hash->mutex); | |
228 | return NULL; | |
229 | } | |
230 | ||
231 | ngx_http_cache_free(cache, key, value, log); | |
232 | ||
233 | if (cache->key.data == NULL) { | |
234 | cache->key.data = ngx_alloc(key->len, log); | |
235 | if (cache->key.data == NULL) { | |
236 | ngx_http_cache_free(cache, NULL, NULL, log); | |
237 | ngx_mutex_unlock(&hash->mutex); | |
238 | return NULL; | |
239 | } | |
240 | } | |
241 | ||
242 | cache->key.len = key->len; | |
243 | ngx_memcpy(cache->key.data, key->data, key->len); | |
244 | ||
245 | } else if (value) { | |
246 | ngx_http_cache_free(cache, key, value, log); | |
247 | } | |
248 | ||
249 | if (value) { | |
250 | if (cache->data.value.data == NULL) { | |
251 | cache->data.value.data = ngx_alloc(value->len, log); | |
252 | if (cache->data.value.data == NULL) { | |
253 | ngx_http_cache_free(cache, NULL, NULL, log); | |
254 | ngx_mutex_unlock(&hash->mutex); | |
255 | return NULL; | |
256 | } | |
257 | } | |
258 | ||
259 | cache->data.value.len = value->len; | |
260 | ngx_memcpy(cache->data.value.data, value->data, value->len); | |
261 | } | |
262 | ||
263 | cache->crc = crc; | |
264 | cache->key.len = key->len; | |
265 | ||
266 | cache->refs = 1; | |
267 | cache->count = 0; | |
268 | ||
269 | cache->deleted = 0; | |
270 | cache->expired = 0; | |
271 | cache->memory = 0; | |
272 | cache->mmap = 0; | |
273 | cache->notify = 0; | |
274 | ||
275 | if (cleanup) { | |
276 | cleanup->data.cache.hash = hash; | |
277 | cleanup->data.cache.cache = cache; | |
278 | cleanup->valid = 1; | |
279 | cleanup->cache = 1; | |
280 | } | |
281 | ||
282 | ngx_mutex_unlock(&hash->mutex); | |
283 | ||
284 | return cache; | |
285 | } | |
286 | ||
287 | ||
288 | void ngx_http_cache_free(ngx_http_cache_t *cache, | |
289 | ngx_str_t *key, ngx_str_t *value, ngx_log_t *log) | |
290 | { | |
291 | if (cache->memory) { | |
292 | if (cache->data.value.data | |
293 | && (value == NULL || value->len > cache->data.value.len)) | |
294 | { | |
295 | ngx_free(cache->data.value.data); | |
296 | cache->data.value.data = NULL; | |
297 | } | |
298 | } | |
299 | ||
300 | /* TODO: mmap */ | |
301 | ||
302 | cache->data.value.len = 0; | |
303 | ||
304 | if (cache->fd != NGX_INVALID_FILE) { | |
305 | ||
306 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, | |
307 | "http cache close fd: %d", cache->fd); | |
308 | ||
309 | if (ngx_close_file(cache->fd) == NGX_FILE_ERROR) { | |
310 | ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, | |
311 | ngx_close_file_n " \"%s\" failed", | |
312 | cache->key.data); | |
313 | } | |
314 | ||
315 | cache->fd = NGX_INVALID_FILE; | |
316 | } | |
317 | ||
318 | if (cache->key.data && (key == NULL || key->len > cache->key.len)) { | |
319 | ngx_free(cache->key.data); | |
320 | cache->key.data = NULL; | |
321 | } | |
322 | ||
323 | cache->key.len = 0; | |
324 | ||
325 | cache->refs = 0; | |
326 | } | |
327 | ||
328 | ||
329 | void ngx_http_cache_lock(ngx_http_cache_hash_t *hash, ngx_http_cache_t *cache) | |
330 | { | |
331 | if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { | |
332 | return; | |
333 | } | |
334 | } | |
335 | ||
336 | ||
337 | void ngx_http_cache_unlock(ngx_http_cache_hash_t *hash, | |
338 | ngx_http_cache_t *cache, ngx_log_t *log) | |
339 | { | |
340 | if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { | |
341 | return; | |
342 | } | |
343 | ||
344 | cache->refs--; | |
345 | ||
346 | if (cache->refs == 0 && cache->deleted) { | |
347 | ngx_http_cache_free(cache, NULL, NULL, log); | |
348 | } | |
349 | ||
350 | ngx_mutex_unlock(&hash->mutex); | |
351 | } | |
352 | ||
353 | ||
354 | #if 0 | |
355 | ||
356 | ngx_http_cache_add_file_event(ngx_http_cache_hash_t *hash, | |
357 | ngx_http_cache_t *cache) | |
358 | { | |
359 | ngx_event_t *ev; | |
360 | ngx_http_cache_event_ctx_t *ctx; | |
361 | ||
362 | ev = &ngx_cycle->read_events[fd]; | |
363 | ngx_memzero(ev, sizeof(ngx_event_t); | |
364 | ||
365 | ev->data = data; | |
366 | ev->event_handler = ngx_http_cache_invalidate; | |
367 | ||
368 | return ngx_add_event(ev, NGX_VNODE_EVENT, 0); | |
369 | } | |
370 | ||
371 | ||
372 | void ngx_http_cache_invalidate(ngx_event_t *ev) | |
373 | { | |
374 | ngx_http_cache_event_ctx_t *ctx; | |
375 | ||
376 | ctx = ev->data; | |
377 | ||
378 | ngx_http_cache_lock(&ctx->hash->mutex); | |
379 | ||
380 | if (ctx->cache->refs == 0) | |
381 | ngx_http_cache_free(ctx->cache, NULL, NULL, ctx->log); | |
382 | ||
383 | } else { | |
384 | ctx->cache->deleted = 1; | |
385 | } | |
386 | ||
387 | ngx_http_cache_unlock(&ctx->hash->mutex); | |
388 | } | |
389 | ||
390 | #endif | |
391 | ||
392 | ||
393 | /* TODO: currently fd only */ | |
394 | ||
395 | ngx_int_t ngx_http_send_cached(ngx_http_request_t *r) | |
396 | { | |
397 | ngx_int_t rc; | |
398 | ngx_hunk_t *h; | |
399 | ngx_chain_t out; | |
400 | ngx_http_log_ctx_t *ctx; | |
401 | ||
402 | ctx = r->connection->log->data; | |
403 | ctx->action = "sending response to client"; | |
404 | ||
405 | r->headers_out.status = NGX_HTTP_OK; | |
406 | r->headers_out.content_length_n = r->cache->data.size; | |
407 | r->headers_out.last_modified_time = r->cache->last_modified; | |
408 | ||
409 | if (ngx_http_set_content_type(r) != NGX_OK) { | |
410 | return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
411 | } | |
412 | ||
413 | /* we need to allocate all before the header would be sent */ | |
414 | ||
415 | if (!(h = ngx_pcalloc(r->pool, sizeof(ngx_hunk_t)))) { | |
416 | return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
417 | } | |
418 | ||
419 | if (!(h->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)))) { | |
420 | return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
421 | } | |
422 | ||
423 | rc = ngx_http_send_header(r); | |
424 | ||
425 | if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { | |
426 | return rc; | |
427 | } | |
428 | ||
429 | h->type = r->main ? NGX_HUNK_FILE : NGX_HUNK_FILE|NGX_HUNK_LAST; | |
430 | ||
431 | h->file_pos = 0; | |
432 | h->file_last = r->cache->data.size; | |
433 | ||
434 | h->file->fd = r->cache->fd; | |
435 | h->file->log = r->connection->log; | |
436 | ||
437 | out.hunk = h; | |
438 | out.next = NULL; | |
439 | ||
440 | return ngx_http_output_filter(r, &out); | |
441 | } | |
442 | ||
443 | ||
444 | char *ngx_http_set_cache_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
445 | { | |
446 | char *p = conf; | |
447 | ||
448 | ngx_int_t i, j, dup, invalid; | |
449 | ngx_str_t *value, line; | |
450 | ngx_http_cache_t *c; | |
451 | ngx_http_cache_hash_t *ch, **chp; | |
452 | ||
453 | chp = (ngx_http_cache_hash_t **) (p + cmd->offset); | |
454 | if (*chp) { | |
455 | return "is duplicate"; | |
456 | } | |
457 | ||
458 | if (!(ch = ngx_pcalloc(cf->pool, sizeof(ngx_http_cache_hash_t)))) { | |
459 | return NGX_CONF_ERROR; | |
460 | } | |
461 | *chp = ch; | |
462 | ||
463 | dup = 0; | |
464 | invalid = 0; | |
465 | ||
466 | value = cf->args->elts; | |
467 | ||
468 | for (i = 1; i < cf->args->nelts; i++) { | |
469 | ||
470 | if (value[i].data[1] != '=') { | |
471 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
472 | "invalid value \"%s\"", value[i].data); | |
473 | return NGX_CONF_ERROR; | |
474 | } | |
475 | ||
476 | switch (value[i].data[0]) { | |
477 | ||
478 | case 'h': | |
479 | if (ch->hash) { | |
480 | dup = 1; | |
481 | break; | |
482 | } | |
483 | ||
484 | ch->hash = ngx_atoi(value[i].data + 2, value[i].len - 2); | |
485 | if (ch->hash == (size_t) NGX_ERROR || ch->hash == 0) { | |
486 | invalid = 1; | |
487 | break; | |
488 | } | |
489 | ||
490 | continue; | |
491 | ||
492 | case 'n': | |
493 | if (ch->nelts) { | |
494 | dup = 1; | |
495 | break; | |
496 | } | |
497 | ||
498 | ch->nelts = ngx_atoi(value[i].data + 2, value[i].len - 2); | |
499 | if (ch->nelts == (size_t) NGX_ERROR || ch->nelts == 0) { | |
500 | invalid = 1; | |
501 | break; | |
502 | } | |
503 | ||
504 | continue; | |
505 | ||
506 | case 'l': | |
507 | if (ch->life) { | |
508 | dup = 1; | |
509 | break; | |
510 | } | |
511 | ||
512 | line.len = value[i].len - 2; | |
513 | line.data = value[i].data + 2; | |
514 | ||
515 | ch->life = ngx_parse_time(&line, 1); | |
516 | if (ch->life == NGX_ERROR || ch->life == 0) { | |
517 | invalid = 1; | |
518 | break; | |
519 | } | |
520 | ||
521 | continue; | |
522 | ||
523 | case 'u': | |
524 | if (ch->update) { | |
525 | dup = 1; | |
526 | break; | |
527 | } | |
528 | ||
529 | line.len = value[i].len - 2; | |
530 | line.data = value[i].data + 2; | |
531 | ||
532 | ch->update = ngx_parse_time(&line, 1); | |
533 | if (ch->update == NGX_ERROR || ch->update == 0) { | |
534 | invalid = 1; | |
535 | break; | |
536 | } | |
537 | ||
538 | continue; | |
539 | ||
540 | default: | |
541 | invalid = 1; | |
542 | } | |
543 | ||
544 | if (dup) { | |
545 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
546 | "duplicate value \"%s\"", value[i].data); | |
547 | return NGX_CONF_ERROR; | |
548 | } | |
549 | ||
550 | if (invalid) { | |
551 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
552 | "invalid value \"%s\"", value[i].data); | |
553 | return NGX_CONF_ERROR; | |
554 | } | |
555 | } | |
556 | ||
557 | ch->elts = ngx_pcalloc(cf->pool, | |
558 | ch->hash * ch->nelts * sizeof(ngx_http_cache_t)); | |
559 | if (ch->elts == NULL) { | |
560 | return NGX_CONF_ERROR; | |
561 | } | |
562 | ||
563 | for (i = 0; i < (ngx_int_t) ch->hash; i++) { | |
564 | c = ch->elts + i * ch->nelts; | |
565 | ||
566 | for (j = 0; j < (ngx_int_t) ch->nelts; j++) { | |
567 | c[j].fd = NGX_INVALID_FILE; | |
568 | } | |
569 | } | |
570 | ||
571 | return NGX_CONF_OK; | |
572 | } | |
573 | ||
574 | ||
575 | #endif |
12 | 12 | #include <ngx_http.h> |
13 | 13 | |
14 | 14 | |
15 | /* | |
16 | * The 3 bits allows the 7 uses before the cache entry allocation. | |
17 | * We can use maximum 7 bits, i.e up to the 127 uses. | |
18 | */ | |
19 | #define NGX_HTTP_CACHE_LAZY_ALLOCATION_BITS 3 | |
15 | /**/ | |
16 | #define NGX_HTTP_CACHE_STALE 1 | |
17 | #define NGX_HTTP_CACHE_AGED 2 | |
18 | #define NGX_HTTP_CACHE_THE_SAME 3 | |
19 | /**/ | |
20 | ||
21 | #define NGX_HTTP_CACHE_KEY_LEN 16 | |
20 | 22 | |
21 | 23 | |
22 | 24 | typedef struct { |
23 | uint32_t crc; | |
24 | ngx_str_t key; | |
25 | time_t accessed; | |
25 | ngx_uint_t status; | |
26 | time_t valid; | |
27 | } ngx_http_cache_valid_t; | |
26 | 28 | |
27 | unsigned refs:20; /* 1048576 references */ | |
28 | 29 | |
29 | unsigned count:NGX_HTTP_CACHE_LAZY_ALLOCATION_BITS; | |
30 | /* ngx_http_file_cache_node_t takes exactly 64 bytes on FreeBSD/i386 */ | |
30 | 31 | |
31 | unsigned deleted:1; | |
32 | unsigned expired:1; | |
33 | unsigned memory:1; | |
34 | unsigned mmap:1; | |
35 | unsigned notify:1; | |
32 | typedef struct { | |
33 | ngx_rbtree_node_t node; | |
34 | ngx_queue_t queue; | |
36 | 35 | |
37 | ngx_fd_t fd; | |
38 | #if (NGX_USE_HTTP_FILE_CACHE_UNIQ) | |
39 | ngx_file_uniq_t uniq; /* no needed with kqueue */ | |
40 | #endif | |
41 | time_t last_modified; | |
42 | time_t updated; | |
36 | u_char key[NGX_HTTP_CACHE_KEY_LEN | |
37 | - sizeof(ngx_rbtree_key_t)]; | |
43 | 38 | |
44 | union { | |
45 | off_t size; | |
46 | ngx_str_t value; | |
47 | } data; | |
48 | } ngx_http_cache_entry_t; | |
39 | unsigned count:20; | |
40 | unsigned uses:10; | |
41 | unsigned valid_msec:10; | |
42 | unsigned error:10; | |
43 | /* 7 unused bits */ | |
44 | unsigned exists:1; | |
45 | ||
46 | ngx_file_uniq_t uniq; | |
47 | time_t expire; | |
48 | time_t valid_sec; | |
49 | size_t body_start; | |
50 | } ngx_http_file_cache_node_t; | |
51 | ||
52 | ||
53 | struct ngx_http_cache_s { | |
54 | ngx_file_t file; | |
55 | ngx_array_t keys; | |
56 | uint32_t crc32; | |
57 | u_char key[NGX_HTTP_CACHE_KEY_LEN]; | |
58 | ||
59 | ngx_file_uniq_t uniq; | |
60 | time_t valid_sec; | |
61 | time_t last_modified; | |
62 | time_t date; | |
63 | ||
64 | size_t header_start; | |
65 | size_t body_start; | |
66 | off_t length; | |
67 | ||
68 | ngx_uint_t min_uses; | |
69 | ngx_uint_t uses; | |
70 | ngx_uint_t error; | |
71 | ngx_uint_t valid_msec; | |
72 | ||
73 | ngx_buf_t *buf; | |
74 | ||
75 | ngx_http_file_cache_t *file_cache; | |
76 | ngx_http_file_cache_node_t *node; | |
77 | ||
78 | unsigned updated:1; | |
79 | unsigned exists:1; | |
80 | unsigned temp_file:1; | |
81 | }; | |
49 | 82 | |
50 | 83 | |
51 | 84 | typedef struct { |
52 | time_t expires; | |
53 | time_t last_modified; | |
54 | time_t date; | |
55 | off_t length; | |
56 | size_t key_len; | |
57 | char key[1]; | |
58 | } ngx_http_cache_header_t; | |
85 | time_t valid_sec; | |
86 | time_t last_modified; | |
87 | time_t date; | |
88 | uint32_t crc32; | |
89 | u_short valid_msec; | |
90 | u_short header_start; | |
91 | u_short body_start; | |
92 | } ngx_http_file_cache_header_t; | |
59 | 93 | |
60 | 94 | |
61 | #define NGX_HTTP_CACHE_HASH 7 | |
62 | #define NGX_HTTP_CACHE_NELTS 4 | |
95 | struct ngx_http_file_cache_s { | |
96 | ngx_rbtree_t *rbtree; | |
97 | ngx_queue_t *queue; | |
98 | ngx_slab_pool_t *shpool; | |
63 | 99 | |
64 | typedef struct { | |
65 | ngx_http_cache_entry_t *elts; | |
66 | size_t hash; | |
67 | size_t nelts; | |
68 | time_t life; | |
69 | time_t update; | |
70 | #if (NGX_THREADS) | |
71 | ngx_mutex_t mutex; | |
72 | #endif | |
73 | ngx_pool_t *pool; | |
74 | } ngx_http_cache_hash_t; | |
100 | ngx_path_t *path; | |
101 | ||
102 | time_t inactive; | |
103 | time_t created; | |
104 | time_t clean_time; | |
105 | time_t next_clean_time; | |
106 | ||
107 | ngx_shm_zone_t *shm_zone; | |
108 | }; | |
75 | 109 | |
76 | 110 | |
77 | typedef struct { | |
78 | ngx_http_cache_hash_t *hash; | |
79 | ngx_http_cache_entry_t *cache; | |
80 | ngx_file_t file; | |
81 | ngx_array_t key; | |
82 | uint32_t crc; | |
83 | u_char md5[16]; | |
84 | ngx_path_t *path; | |
85 | ngx_buf_t *buf; | |
86 | time_t expires; | |
87 | time_t last_modified; | |
88 | time_t date; | |
89 | off_t length; | |
90 | size_t key_len; | |
91 | size_t file_start; | |
92 | ngx_file_uniq_t uniq; | |
93 | ngx_log_t *log; | |
94 | ||
95 | /* STUB */ | |
96 | ssize_t header_size; | |
97 | ngx_str_t key0; | |
98 | } ngx_http_cache_t; | |
111 | void ngx_http_file_cache_create_key(ngx_http_request_t *r); | |
112 | ngx_int_t ngx_http_file_cache_open(ngx_http_request_t *r); | |
113 | void ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf); | |
114 | void ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf); | |
115 | ngx_int_t ngx_http_cache_send(ngx_http_request_t *); | |
116 | void ngx_http_file_cache_free(ngx_http_request_t *r, ngx_temp_file_t *tf); | |
117 | time_t ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status); | |
99 | 118 | |
100 | 119 | |
101 | typedef struct { | |
102 | ngx_path_t *path; | |
103 | ngx_str_t key; | |
104 | ngx_buf_t *buf; | |
105 | ||
106 | unsigned file:1; | |
107 | unsigned memory:1; | |
108 | unsigned primary:1; | |
109 | } ngx_http_cache_ctx_t; | |
110 | ||
111 | ||
112 | #define NGX_HTTP_CACHE_STALE 1 | |
113 | #define NGX_HTTP_CACHE_AGED 2 | |
114 | #define NGX_HTTP_CACHE_THE_SAME 3 | |
115 | ||
116 | ||
117 | ngx_int_t ngx_http_cache_get(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx); | |
118 | ||
119 | ngx_int_t ngx_http_file_cache_get(ngx_http_request_t *r, | |
120 | ngx_http_cache_ctx_t *ctx); | |
121 | ||
122 | ngx_int_t ngx_http_file_cache_open(ngx_http_cache_t *c); | |
123 | ||
124 | ngx_int_t ngx_http_cache_cleaner_handler(ngx_gc_t *gc, ngx_str_t *name, | |
125 | ngx_dir_t *dir); | |
126 | ||
127 | ||
128 | #if 0 | |
129 | ||
130 | ngx_http_cache_t *ngx_http_cache_get(ngx_http_cache_hash_t *cache, | |
131 | ngx_http_cleanup_t *cleanup, | |
132 | ngx_str_t *key, uint32_t *crc); | |
133 | ||
134 | ngx_http_cache_t *ngx_http_cache_alloc(ngx_http_cache_hash_t *hash, | |
135 | ngx_http_cache_t *cache, | |
136 | ngx_http_cleanup_t *cleanup, | |
137 | ngx_str_t *key, uint32_t crc, | |
138 | ngx_str_t *value, ngx_log_t *log); | |
139 | void ngx_http_cache_free(ngx_http_cache_t *cache, | |
140 | ngx_str_t *key, ngx_str_t *value, ngx_log_t *log); | |
141 | void ngx_http_cache_lock(ngx_http_cache_hash_t *hash, ngx_http_cache_t *cache); | |
142 | void ngx_http_cache_unlock(ngx_http_cache_hash_t *hash, | |
143 | ngx_http_cache_t *cache, ngx_log_t *log); | |
144 | ||
145 | int ngx_http_cache_update_file(ngx_http_request_t *r,ngx_http_cache_ctx_t *ctx, | |
146 | ngx_str_t *temp_file); | |
147 | ||
148 | int ngx_http_send_cached(ngx_http_request_t *r); | |
149 | ||
150 | ||
151 | char *ngx_http_set_cache_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); | |
152 | ||
153 | #endif | |
120 | char *ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, | |
121 | void *conf); | |
122 | char *ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, | |
123 | void *conf); | |
154 | 124 | |
155 | 125 | |
156 | 126 | #endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */ |
118 | 118 | }; |
119 | 119 | |
120 | 120 | |
121 | static ngx_path_init_t ngx_http_client_temp_path = { | |
122 | ngx_string(NGX_HTTP_CLIENT_TEMP_PATH), { 0, 0, 0 } | |
123 | }; | |
124 | ||
125 | ||
121 | 126 | #if (NGX_HTTP_GZIP) |
122 | 127 | |
123 | 128 | static ngx_conf_enum_t ngx_http_gzip_http_version[] = { |
346 | 351 | ngx_conf_set_path_slot, |
347 | 352 | NGX_HTTP_LOC_CONF_OFFSET, |
348 | 353 | offsetof(ngx_http_core_loc_conf_t, client_body_temp_path), |
349 | (void *) ngx_garbage_collector_temp_handler }, | |
354 | NULL }, | |
350 | 355 | |
351 | 356 | { ngx_string("client_body_in_file_only"), |
352 | 357 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, |
2183 | 2188 | r->loc_conf = cscf->ctx->loc_conf; |
2184 | 2189 | |
2185 | 2190 | ngx_http_update_location_config(r); |
2191 | ||
2192 | #if (NGX_HTTP_CACHE) | |
2193 | r->cache = NULL; | |
2194 | #endif | |
2186 | 2195 | |
2187 | 2196 | r->internal = 1; |
2188 | 2197 | |
3155 | 3164 | conf->resolver = prev->resolver; |
3156 | 3165 | } |
3157 | 3166 | |
3158 | ngx_conf_merge_path_value(conf->client_body_temp_path, | |
3167 | if (ngx_conf_merge_path_value(cf, &conf->client_body_temp_path, | |
3159 | 3168 | prev->client_body_temp_path, |
3160 | NGX_HTTP_CLIENT_TEMP_PATH, 0, 0, 0, | |
3161 | ngx_garbage_collector_temp_handler, cf); | |
3169 | &ngx_http_client_temp_path) | |
3170 | != NGX_OK) | |
3171 | { | |
3172 | return NGX_CONF_ERROR; | |
3173 | } | |
3162 | 3174 | |
3163 | 3175 | ngx_conf_merge_value(conf->reset_timedout_connection, |
3164 | 3176 | prev->reset_timedout_connection, 0); |
6 | 6 | #include <ngx_config.h> |
7 | 7 | #include <ngx_core.h> |
8 | 8 | #include <ngx_http.h> |
9 | ||
10 | ||
11 | #if (NGX_HAVE_OPENSSL_MD5_H) | |
12 | #include <openssl/md5.h> | |
13 | #else | |
14 | #include <md5.h> | |
15 | #endif | |
16 | ||
17 | #if (NGX_OPENSSL_MD5) | |
18 | #define MD5Init MD5_Init | |
19 | #define MD5Update MD5_Update | |
20 | #define MD5Final MD5_Final | |
21 | #endif | |
22 | ||
23 | ||
24 | ngx_int_t ngx_http_file_cache_get(ngx_http_request_t *r, | |
25 | ngx_http_cache_ctx_t *ctx) | |
26 | { | |
9 | #include <ngx_md5.h> | |
10 | ||
11 | ||
12 | static ngx_int_t ngx_http_file_cache_exists(ngx_http_request_t *r, | |
13 | ngx_http_file_cache_t *cache); | |
14 | static ngx_http_file_cache_node_t * | |
15 | ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key); | |
16 | static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, | |
17 | ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); | |
18 | static void ngx_http_file_cache_cleanup(void *data); | |
19 | static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache, | |
20 | ngx_uint_t force); | |
21 | static ngx_int_t ngx_http_file_cache_clean_noop(ngx_tree_ctx_t *ctx, | |
22 | ngx_str_t *path); | |
23 | static ngx_int_t ngx_http_file_cache_clean_file(ngx_tree_ctx_t *ctx, | |
24 | ngx_str_t *path); | |
25 | ||
26 | ||
27 | static u_char ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' }; | |
28 | ||
29 | ||
30 | static ngx_int_t | |
31 | ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data) | |
32 | { | |
33 | ngx_http_file_cache_t *ocache = data; | |
34 | ||
35 | ngx_rbtree_node_t *sentinel; | |
36 | ngx_http_file_cache_t *cache; | |
37 | ||
38 | cache = shm_zone->data; | |
39 | ||
40 | if (ocache) { | |
41 | if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) { | |
42 | ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, | |
43 | "cache \"%V\" uses the \"%V\" cache path " | |
44 | "while previously it used the \"%V\" cache path", | |
45 | &shm_zone->name, &cache->path->name, | |
46 | &ocache->path->name); | |
47 | ||
48 | return NGX_ERROR; | |
49 | } | |
50 | ||
51 | cache->rbtree = ocache->rbtree; | |
52 | cache->queue = ocache->queue; | |
53 | cache->shpool = ocache->shpool; | |
54 | cache->created = ocache->created; | |
55 | ||
56 | return NGX_OK; | |
57 | } | |
58 | ||
59 | cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; | |
60 | ||
61 | cache->rbtree = ngx_slab_alloc(cache->shpool, sizeof(ngx_rbtree_t)); | |
62 | if (cache->rbtree == NULL) { | |
63 | return NGX_ERROR; | |
64 | } | |
65 | ||
66 | sentinel = ngx_slab_alloc(cache->shpool, sizeof(ngx_rbtree_node_t)); | |
67 | if (sentinel == NULL) { | |
68 | return NGX_ERROR; | |
69 | } | |
70 | ||
71 | ngx_rbtree_init(cache->rbtree, sentinel, | |
72 | ngx_http_file_cache_rbtree_insert_value); | |
73 | ||
74 | cache->queue = ngx_slab_alloc(cache->shpool, sizeof(ngx_queue_t)); | |
75 | if (cache->queue == NULL) { | |
76 | return NGX_ERROR; | |
77 | } | |
78 | ||
79 | ngx_queue_init(cache->queue); | |
80 | ||
81 | cache->created = ngx_time(); | |
82 | ||
83 | return NGX_OK; | |
84 | } | |
85 | ||
86 | ||
87 | void | |
88 | ngx_http_file_cache_create_key(ngx_http_request_t *r) | |
89 | { | |
90 | size_t len; | |
91 | ngx_str_t *key; | |
27 | 92 | ngx_uint_t i; |
28 | ngx_str_t *key; | |
93 | ngx_md5_t md5; | |
29 | 94 | ngx_http_cache_t *c; |
30 | MD5_CTX md5; | |
31 | 95 | |
32 | 96 | c = r->cache; |
33 | 97 | |
34 | c->file.name.len = ctx->path->name.len + 1 + ctx->path->len + 32; | |
35 | if (!(c->file.name.data = ngx_palloc(r->pool, c->file.name.len + 1))) { | |
36 | return NGX_ABORT; | |
37 | } | |
38 | ||
39 | MD5Init(&md5); | |
40 | ||
41 | key = c->key.elts; | |
42 | for (i = 0; i < c->key.nelts; i++) { | |
43 | MD5Update(&md5, key[i].data, key[i].len); | |
44 | } | |
45 | ||
46 | MD5Update(&md5, ctx->key.data, ctx->key.len); | |
47 | ||
48 | MD5Final(c->md5, &md5); | |
49 | ||
50 | ngx_memcpy(c->file.name.data, ctx->path->name.data, ctx->path->name.len); | |
51 | ||
52 | ngx_md5_text(c->file.name.data + ctx->path->name.len + 1 + ctx->path->len, | |
53 | c->md5); | |
54 | ||
55 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
56 | "file cache key: %V, md5: %s", &ctx->key, | |
57 | c->file.name.data + ctx->path->name.len + 1 + ctx->path->len); | |
58 | ||
59 | ngx_create_hashed_filename(&c->file, ctx->path); | |
98 | len = 0; | |
99 | ||
100 | ngx_crc32_init(c->crc32); | |
101 | ngx_md5_init(&md5); | |
102 | ||
103 | key = c->keys.elts; | |
104 | for (i = 0; i < c->keys.nelts; i++) { | |
105 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
106 | "http cache key: \"%V\"", &key[i]); | |
107 | ||
108 | len += key[i].len; | |
109 | ||
110 | ngx_crc32_update(&c->crc32, key[i].data, key[i].len); | |
111 | ngx_md5_update(&md5, key[i].data, key[i].len); | |
112 | } | |
113 | ||
114 | c->header_start = sizeof(ngx_http_file_cache_header_t) | |
115 | + sizeof(ngx_http_file_cache_key) + len + 1; | |
116 | ||
117 | ngx_crc32_final(c->crc32); | |
118 | ngx_md5_final(c->key, &md5); | |
119 | } | |
120 | ||
121 | ||
122 | ngx_int_t | |
123 | ngx_http_file_cache_open(ngx_http_request_t *r) | |
124 | { | |
125 | u_char *p; | |
126 | time_t now; | |
127 | ssize_t n; | |
128 | ngx_int_t rc, rv; | |
129 | ngx_uint_t cold, test; | |
130 | ngx_path_t *path; | |
131 | ngx_http_cache_t *c; | |
132 | ngx_pool_cleanup_t *cln; | |
133 | ngx_open_file_info_t of; | |
134 | ngx_http_file_cache_t *cache; | |
135 | ngx_http_core_loc_conf_t *clcf; | |
136 | ngx_http_file_cache_header_t *h; | |
137 | ||
138 | c = r->cache; | |
139 | cache = c->file_cache; | |
140 | ||
141 | cln = ngx_pool_cleanup_add(r->pool, 0); | |
142 | if (cln == NULL) { | |
143 | return NGX_ERROR; | |
144 | } | |
145 | ||
146 | rc = ngx_http_file_cache_exists(r, cache); | |
147 | ||
148 | ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
149 | "http file cache exists: %i u:%ui e:%d", | |
150 | rc, c->uses, c->exists); | |
151 | ||
152 | if (rc == NGX_ERROR) { | |
153 | return rc; | |
154 | } | |
155 | ||
156 | cln->handler = ngx_http_file_cache_cleanup; | |
157 | cln->data = c; | |
158 | ||
159 | if (rc == NGX_AGAIN) { | |
160 | return rc; | |
161 | } | |
162 | ||
163 | now = ngx_time(); | |
164 | ||
165 | cold = (now - cache->created < cache->inactive) ? 1 : 0; | |
166 | ||
167 | if (rc == NGX_OK) { | |
168 | ||
169 | if (c->error) { | |
170 | return c->error; | |
171 | } | |
172 | ||
173 | c->temp_file = 1; | |
174 | test = c->exists ? 1 : 0; | |
175 | rv = NGX_DECLINED; | |
176 | ||
177 | } else { /* rc == NGX_DECLINED */ | |
178 | ||
179 | if (c->min_uses > 1) { | |
180 | ||
181 | if (!cold) { | |
182 | return NGX_AGAIN; | |
183 | } | |
184 | ||
185 | test = 1; | |
186 | rv = NGX_AGAIN; | |
187 | ||
188 | } else { | |
189 | c->temp_file = 1; | |
190 | test = cold ? 1 : 0; | |
191 | rv = NGX_DECLINED; | |
192 | } | |
193 | } | |
194 | ||
195 | path = cache->path; | |
196 | ||
197 | c->file.name.len = path->name.len + 1 + path->len | |
198 | + 2 * NGX_HTTP_CACHE_KEY_LEN; | |
199 | ||
200 | c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1); | |
201 | if (c->file.name.data == NULL) { | |
202 | return NGX_ERROR; | |
203 | } | |
204 | ||
205 | ngx_memcpy(c->file.name.data, path->name.data, path->name.len); | |
206 | ||
207 | p = c->file.name.data + path->name.len + 1 + path->len; | |
208 | p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN); | |
209 | *p = '\0'; | |
210 | ||
211 | ngx_create_hashed_filename(path, c->file.name.data, c->file.name.len); | |
212 | ||
213 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, | |
214 | "cache file: \"%s\"", c->file.name.data); | |
215 | ||
216 | if (!test) { | |
217 | return NGX_DECLINED; | |
218 | } | |
219 | ||
220 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
221 | ||
222 | ngx_memzero(&of, sizeof(ngx_open_file_info_t)); | |
223 | ||
224 | of.uniq = c->uniq; | |
225 | of.valid = clcf->open_file_cache_valid; | |
226 | of.min_uses = clcf->open_file_cache_min_uses; | |
227 | of.events = clcf->open_file_cache_events; | |
228 | of.directio = NGX_OPEN_FILE_DIRECTIO_OFF; | |
229 | ||
230 | if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool) | |
231 | != NGX_OK) | |
232 | { | |
233 | switch (of.err) { | |
234 | ||
235 | case 0: | |
236 | return NGX_ERROR; | |
237 | ||
238 | case NGX_ENOENT: | |
239 | case NGX_ENOTDIR: | |
240 | return rv; | |
241 | ||
242 | default: | |
243 | ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, | |
244 | ngx_open_file_n " \"%s\" failed", c->file.name.data); | |
245 | return NGX_ERROR; | |
246 | } | |
247 | } | |
60 | 248 | |
61 | 249 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
62 | "file cache name: %s", c->file.name.data); | |
63 | ||
64 | return ngx_http_file_cache_open(r->cache); | |
65 | } | |
66 | ||
67 | ||
68 | ngx_int_t ngx_http_file_cache_open(ngx_http_cache_t *c) | |
69 | { | |
70 | ssize_t n; | |
71 | ngx_err_t err; | |
72 | ngx_http_cache_header_t *h; | |
73 | ||
74 | c->file.fd = ngx_open_file(c->file.name.data, | |
75 | NGX_FILE_RDONLY, NGX_FILE_OPEN); | |
76 | ||
77 | if (c->file.fd == NGX_INVALID_FILE) { | |
78 | err = ngx_errno; | |
79 | ||
80 | if (err == NGX_ENOENT || err == NGX_ENOTDIR) { | |
81 | return NGX_DECLINED; | |
82 | } | |
83 | ||
84 | ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno, | |
85 | ngx_open_file_n " \"%s\" failed", c->file.name.data); | |
250 | "http file cache fd: %d", of.fd); | |
251 | ||
252 | c->file.fd = of.fd; | |
253 | ||
254 | c->buf = ngx_create_temp_buf(r->pool, c->body_start); | |
255 | if (c->buf == NULL) { | |
86 | 256 | return NGX_ERROR; |
87 | 257 | } |
88 | 258 | |
89 | if (c->uniq) { | |
90 | if (ngx_fd_info(c->file.fd, &c->file.info) == NGX_FILE_ERROR) { | |
91 | ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno, | |
92 | ngx_fd_info_n " \"%s\" failed", c->file.name.data); | |
93 | ||
94 | return NGX_ERROR; | |
95 | } | |
96 | ||
97 | if (ngx_file_uniq(&c->file.info) == c->uniq) { | |
98 | if (ngx_close_file(c->file.fd) == NGX_FILE_ERROR) { | |
99 | ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, | |
100 | ngx_close_file_n " \"%s\" failed", | |
101 | c->file.name.data); | |
102 | } | |
103 | ||
104 | return NGX_HTTP_CACHE_THE_SAME; | |
105 | } | |
106 | } | |
107 | ||
108 | n = ngx_read_file(&c->file, c->buf->pos, c->buf->end - c->buf->last, 0); | |
109 | ||
110 | if (n == NGX_ERROR || n == NGX_AGAIN) { | |
259 | n = ngx_read_file(&c->file, c->buf->pos, c->body_start, 0); | |
260 | ||
261 | if (n == NGX_ERROR) { | |
111 | 262 | return n; |
112 | 263 | } |
113 | 264 | |
114 | if (n <= c->header_size) { | |
115 | ngx_log_error(NGX_LOG_CRIT, c->log, 0, | |
265 | if ((size_t) n <= c->header_start) { | |
266 | ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, | |
116 | 267 | "cache file \"%s\" is too small", c->file.name.data); |
117 | 268 | return NGX_ERROR; |
118 | 269 | } |
119 | 270 | |
120 | h = (ngx_http_cache_header_t *) c->buf->pos; | |
121 | c->expires = h->expires; | |
122 | c->last_modified= h->last_modified; | |
271 | h = (ngx_http_file_cache_header_t *) c->buf->pos; | |
272 | ||
273 | if (h->crc32 != c->crc32 || (size_t) h->header_start != c->header_start) { | |
274 | ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, | |
275 | "cache file \"%s\" has md5 collision", c->file.name.data); | |
276 | return NGX_DECLINED; | |
277 | } | |
278 | ||
279 | c->buf->last += n; | |
280 | ||
281 | c->valid_sec = h->valid_sec; | |
282 | c->last_modified = h->last_modified; | |
123 | 283 | c->date = h->date; |
124 | c->length = h->length; | |
125 | ||
126 | if (h->key_len > (size_t) (c->buf->end - c->buf->pos)) { | |
127 | ngx_log_error(NGX_LOG_ALERT, c->log, 0, | |
128 | "cache file \"%s\" is probably invalid", | |
129 | c->file.name.data); | |
130 | return NGX_DECLINED; | |
131 | } | |
132 | ||
133 | #if 0 | |
134 | ||
135 | /* TODO */ | |
136 | ||
137 | if (c->key_len && h->key_len != c->key_len) { | |
138 | ||
139 | ngx_strncmp(h->key, c->key_data, h->key_len) != 0)) | |
140 | ||
141 | h->key[h->key_len] = '\0'; | |
142 | ngx_log_error(NGX_LOG_ALERT, c->log, 0, | |
143 | "md5 collision: \"%s\" and \"%s\"", | |
144 | h->key, c->key.data); | |
145 | return NGX_DECLINED; | |
146 | } | |
147 | ||
148 | #endif | |
149 | ||
150 | c->buf->last += n; | |
151 | ||
152 | if (c->expires < ngx_time()) { | |
153 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
154 | "http file cache expired"); | |
284 | c->valid_msec = h->valid_msec; | |
285 | c->length = of.size; | |
286 | c->body_start = h->body_start; | |
287 | ||
288 | r->cached = 1; | |
289 | ||
290 | if (cold) { | |
291 | ||
292 | ngx_shmtx_lock(&cache->shpool->mutex); | |
293 | ||
294 | c->node->uses = c->min_uses; | |
295 | c->node->body_start = c->body_start; | |
296 | c->node->exists = 1; | |
297 | ||
298 | ngx_shmtx_unlock(&cache->shpool->mutex); | |
299 | } | |
300 | ||
301 | if (c->valid_sec < now) { | |
302 | ||
303 | c->uses = c->min_uses; | |
304 | ||
305 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
306 | "http file cache expired: %T %T", c->valid_sec, now); | |
307 | ||
155 | 308 | return NGX_HTTP_CACHE_STALE; |
156 | 309 | } |
157 | 310 | |
158 | 311 | /* TODO: NGX_HTTP_CACHE_AGED */ |
159 | 312 | |
160 | /* STUB */ return NGX_DECLINED; | |
161 | ||
162 | 313 | return NGX_OK; |
163 | 314 | } |
164 | 315 | |
165 | 316 | |
166 | #if 0 | |
167 | ||
168 | ||
169 | int ngx_http_cache_update_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx, | |
170 | ngx_str_t *temp_file) | |
171 | { | |
172 | int retry; | |
173 | ngx_err_t err; | |
174 | ||
175 | retry = 0; | |
317 | static ngx_int_t | |
318 | ngx_http_file_cache_exists(ngx_http_request_t *r, ngx_http_file_cache_t *cache) | |
319 | { | |
320 | ngx_int_t rc; | |
321 | ngx_http_file_cache_node_t *fcn; | |
322 | ||
323 | ngx_shmtx_lock(&cache->shpool->mutex); | |
324 | ||
325 | fcn = ngx_http_file_cache_lookup(cache, r->cache->key); | |
326 | ||
327 | if (fcn) { | |
328 | ngx_queue_remove(&fcn->queue); | |
329 | ||
330 | if (fcn->error) { | |
331 | ||
332 | if (fcn->valid_sec < ngx_time()) { | |
333 | goto renew; | |
334 | } | |
335 | ||
336 | rc = NGX_OK; | |
337 | ||
338 | goto done; | |
339 | } | |
340 | ||
341 | fcn->uses++; | |
342 | fcn->count++; | |
343 | ||
344 | if (fcn->exists) { | |
345 | ||
346 | r->cache->exists = fcn->exists; | |
347 | r->cache->body_start = fcn->body_start; | |
348 | ||
349 | rc = NGX_OK; | |
350 | ||
351 | goto done; | |
352 | } | |
353 | ||
354 | if (fcn->uses >= r->cache->min_uses) { | |
355 | ||
356 | r->cache->exists = fcn->exists; | |
357 | ||
358 | if (fcn->body_start) { | |
359 | r->cache->body_start = fcn->body_start; | |
360 | } | |
361 | ||
362 | rc = NGX_OK; | |
363 | ||
364 | } else { | |
365 | rc = NGX_AGAIN; | |
366 | } | |
367 | ||
368 | goto done; | |
369 | } | |
370 | ||
371 | fcn = ngx_slab_alloc_locked(cache->shpool, | |
372 | sizeof(ngx_http_file_cache_node_t)); | |
373 | if (fcn == NULL) { | |
374 | ngx_shmtx_unlock(&cache->shpool->mutex); | |
375 | ||
376 | (void) ngx_http_file_cache_expire(cache, 1); | |
377 | ||
378 | ngx_shmtx_lock(&cache->shpool->mutex); | |
379 | ||
380 | fcn = ngx_slab_alloc_locked(cache->shpool, | |
381 | sizeof(ngx_http_file_cache_node_t)); | |
382 | if (fcn == NULL) { | |
383 | rc = NGX_ERROR; | |
384 | goto failed; | |
385 | } | |
386 | } | |
387 | ||
388 | ngx_memcpy((u_char *) &fcn->node.key, r->cache->key, | |
389 | sizeof(ngx_rbtree_key_t)); | |
390 | ||
391 | ngx_memcpy(fcn->key, &r->cache->key[sizeof(ngx_rbtree_key_t)], | |
392 | NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); | |
393 | ||
394 | ngx_rbtree_insert(cache->rbtree, &fcn->node); | |
395 | ||
396 | renew: | |
397 | ||
398 | rc = NGX_DECLINED; | |
399 | ||
400 | fcn->uses = 1; | |
401 | fcn->count = 1; | |
402 | fcn->valid_msec = 0; | |
403 | fcn->error = 0; | |
404 | fcn->exists = 0; | |
405 | fcn->valid_sec = 0; | |
406 | fcn->uniq = 0; | |
407 | fcn->body_start = 0; | |
408 | ||
409 | done: | |
410 | ||
411 | fcn->expire = ngx_time() + cache->inactive; | |
412 | ||
413 | ngx_queue_insert_head(cache->queue, &fcn->queue); | |
414 | ||
415 | r->cache->uniq = fcn->uniq; | |
416 | r->cache->uses = fcn->uses; | |
417 | r->cache->error = fcn->error; | |
418 | r->cache->node = fcn; | |
419 | ||
420 | failed: | |
421 | ||
422 | ngx_shmtx_unlock(&cache->shpool->mutex); | |
423 | ||
424 | return rc; | |
425 | } | |
426 | ||
427 | ||
428 | static ngx_http_file_cache_node_t * | |
429 | ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key) | |
430 | { | |
431 | ngx_int_t rc; | |
432 | ngx_rbtree_key_t node_key; | |
433 | ngx_rbtree_node_t *node, *sentinel; | |
434 | ngx_http_file_cache_node_t *fcn; | |
435 | ||
436 | ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t)); | |
437 | ||
438 | node = cache->rbtree->root; | |
439 | sentinel = cache->rbtree->sentinel; | |
440 | ||
441 | while (node != sentinel) { | |
442 | ||
443 | if (node_key < node->key) { | |
444 | node = node->left; | |
445 | continue; | |
446 | } | |
447 | ||
448 | if (node_key > node->key) { | |
449 | node = node->right; | |
450 | continue; | |
451 | } | |
452 | ||
453 | /* node_key == node->key */ | |
454 | ||
455 | do { | |
456 | fcn = (ngx_http_file_cache_node_t *) node; | |
457 | ||
458 | rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key, | |
459 | NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); | |
460 | ||
461 | if (rc == 0) { | |
462 | return fcn; | |
463 | } | |
464 | ||
465 | node = (rc < 0) ? node->left : node->right; | |
466 | ||
467 | } while (node != sentinel && node_key == node->key); | |
468 | ||
469 | break; | |
470 | } | |
471 | ||
472 | /* not found */ | |
473 | ||
474 | return NULL; | |
475 | } | |
476 | ||
477 | ||
478 | static void | |
479 | ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, | |
480 | ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) | |
481 | { | |
482 | ngx_rbtree_node_t **p; | |
483 | ngx_http_file_cache_node_t *cn, *cnt; | |
176 | 484 | |
177 | 485 | for ( ;; ) { |
178 | if (ngx_rename_file(temp_file->data, ctx->file.name.data) == NGX_OK) { | |
179 | return NGX_OK; | |
180 | } | |
181 | ||
182 | err = ngx_errno; | |
183 | ||
184 | #if (NGX_WIN32) | |
185 | if (err == NGX_EEXIST) { | |
186 | if (ngx_win32_rename_file(temp_file, &ctx->file.name, r->pool) | |
187 | == NGX_ERROR) | |
188 | { | |
189 | return NGX_ERROR; | |
190 | } | |
191 | } | |
192 | #endif | |
193 | ||
194 | if (retry || (err != NGX_ENOENT && err != NGX_ENOTDIR)) { | |
195 | ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, | |
196 | ngx_rename_file_n "(\"%s\", \"%s\") failed", | |
197 | temp_file->data, ctx->file.name.data); | |
198 | ||
199 | return NGX_ERROR; | |
200 | } | |
201 | ||
202 | if (ngx_create_path(&ctx->file, ctx->path) == NGX_ERROR) { | |
203 | return NGX_ERROR; | |
204 | } | |
205 | ||
206 | retry = 1; | |
207 | } | |
208 | } | |
209 | ||
210 | ||
211 | #endif | |
212 | ||
213 | ||
214 | ngx_int_t ngx_http_cache_cleaner_handler(ngx_gc_t *gc, ngx_str_t *name, | |
215 | ngx_dir_t *dir) | |
216 | { | |
217 | int rc; | |
218 | ngx_buf_t buf; | |
219 | ngx_http_cache_t c; | |
220 | u_char data[sizeof(ngx_http_cache_header_t)]; | |
221 | ||
222 | ngx_memzero(&c, sizeof(ngx_http_cache_t)); | |
223 | ||
224 | c.file.fd = NGX_INVALID_FILE; | |
225 | c.file.name = *name; | |
226 | c.file.log = gc->log; | |
227 | ||
228 | c.header_size = sizeof(ngx_http_cache_header_t); | |
229 | c.buf = &buf; | |
230 | c.log = gc->log; | |
231 | c.key_len = 0; | |
232 | ||
233 | buf.memory = 1; | |
234 | buf.temporary = 1; | |
235 | buf.pos = data; | |
236 | buf.last = data; | |
237 | buf.start = data; | |
238 | buf.end = data + sizeof(ngx_http_cache_header_t); | |
239 | ||
240 | rc = ngx_http_file_cache_open(&c); | |
241 | ||
242 | /* TODO: NGX_AGAIN */ | |
243 | ||
244 | if (rc != NGX_ERROR&& rc != NGX_DECLINED && rc != NGX_HTTP_CACHE_STALE) { | |
486 | ||
487 | if (node->key < temp->key) { | |
488 | ||
489 | p = &temp->left; | |
490 | ||
491 | } else if (node->key > temp->key) { | |
492 | ||
493 | p = &temp->right; | |
494 | ||
495 | } else { /* node->key == temp->key */ | |
496 | ||
497 | cn = (ngx_http_file_cache_node_t *) node; | |
498 | cnt = (ngx_http_file_cache_node_t *) temp; | |
499 | ||
500 | p = (ngx_memcmp(cn->key, cnt->key, | |
501 | NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)) | |
502 | < 0) | |
503 | ? &temp->left : &temp->right; | |
504 | } | |
505 | ||
506 | if (*p == sentinel) { | |
507 | break; | |
508 | } | |
509 | ||
510 | temp = *p; | |
511 | } | |
512 | ||
513 | *p = node; | |
514 | node->parent = temp; | |
515 | node->left = sentinel; | |
516 | node->right = sentinel; | |
517 | ngx_rbt_red(node); | |
518 | } | |
519 | ||
520 | ||
521 | void | |
522 | ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf) | |
523 | { | |
524 | ngx_http_file_cache_header_t *h = (ngx_http_file_cache_header_t *) buf; | |
525 | ||
526 | u_char *p; | |
527 | ngx_str_t *key; | |
528 | ngx_uint_t i; | |
529 | ngx_http_cache_t *c; | |
530 | ||
531 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
532 | "http file cache set header"); | |
533 | ||
534 | c = r->cache; | |
535 | ||
536 | h->valid_sec = c->valid_sec; | |
537 | h->last_modified = c->last_modified; | |
538 | h->date = c->date; | |
539 | h->crc32 = c->crc32; | |
540 | h->valid_msec = (u_short) c->valid_msec; | |
541 | h->header_start = (u_short) c->header_start; | |
542 | h->body_start = (u_short) c->body_start; | |
543 | ||
544 | p = buf + sizeof(ngx_http_file_cache_header_t); | |
545 | ||
546 | p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key)); | |
547 | ||
548 | key = c->keys.elts; | |
549 | for (i = 0; i < c->keys.nelts; i++) { | |
550 | p = ngx_copy(p, key[i].data, key[i].len); | |
551 | } | |
552 | ||
553 | *p = LF; | |
554 | } | |
555 | ||
556 | ||
557 | void | |
558 | ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf) | |
559 | { | |
560 | ngx_int_t rc; | |
561 | ngx_file_uniq_t uniq; | |
562 | ngx_file_info_t fi; | |
563 | ngx_http_cache_t *c; | |
564 | ngx_ext_rename_file_t ext; | |
565 | ngx_http_file_cache_t *cache; | |
566 | ||
567 | c = r->cache; | |
568 | ||
569 | if (c->updated) { | |
570 | return; | |
571 | } | |
572 | ||
573 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
574 | "http file cache update"); | |
575 | ||
576 | c->updated = 1; | |
577 | ||
578 | cache = c->file_cache; | |
579 | ||
580 | uniq = 0; | |
581 | ||
582 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
583 | "http file cache rename: \"%s\" to \"%s\"", | |
584 | tf->file.name.data, c->file.name.data); | |
585 | ||
586 | ext.access = NGX_FILE_OWNER_ACCESS; | |
587 | ext.path_access = NGX_FILE_OWNER_ACCESS; | |
588 | ext.time = -1; | |
589 | ext.create_path = 1; | |
590 | ext.delete_file = 1; | |
591 | ext.log_rename_error = 1; | |
592 | ext.log = r->connection->log; | |
593 | ||
594 | rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext); | |
595 | ||
596 | if (rc == NGX_OK) { | |
597 | ||
598 | if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) { | |
599 | ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, | |
600 | ngx_fd_info_n " \"%s\" failed", tf->file.name.data); | |
601 | ||
602 | rc = NGX_ERROR; | |
603 | ||
604 | } else { | |
605 | uniq = ngx_file_uniq(&fi); | |
606 | } | |
607 | } | |
608 | ||
609 | ngx_shmtx_lock(&cache->shpool->mutex); | |
610 | ||
611 | c->node->count--; | |
612 | c->node->uniq = uniq; | |
613 | c->node->body_start = c->body_start; | |
614 | ||
615 | if (rc == NGX_OK) { | |
616 | c->node->exists = 1; | |
617 | } | |
618 | ||
619 | ngx_shmtx_unlock(&cache->shpool->mutex); | |
620 | } | |
621 | ||
622 | ||
623 | ngx_int_t | |
624 | ngx_http_cache_send(ngx_http_request_t *r) | |
625 | { | |
626 | ngx_int_t rc; | |
627 | ngx_buf_t *b; | |
628 | ngx_chain_t out; | |
629 | ngx_http_cache_t *c; | |
630 | ||
631 | c = r->cache; | |
632 | ||
633 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
634 | "http file cache send: %s", c->file.name.data); | |
635 | ||
636 | /* we need to allocate all before the header would be sent */ | |
637 | ||
638 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); | |
639 | if (b == NULL) { | |
640 | return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
641 | } | |
642 | ||
643 | b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)); | |
644 | if (b->file == NULL) { | |
645 | return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
646 | } | |
647 | ||
648 | rc = ngx_http_send_header(r); | |
649 | ||
650 | if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { | |
651 | return rc; | |
652 | } | |
653 | ||
654 | b->file_pos = c->body_start; | |
655 | b->file_last = c->length; | |
656 | ||
657 | b->in_file = (c->length - c->body_start) ? 1: 0; | |
658 | b->last_buf = (r == r->main) ? 1: 0; | |
659 | b->last_in_chain = 1; | |
660 | ||
661 | b->file->fd = c->file.fd; | |
662 | b->file->name = c->file.name; | |
663 | b->file->log = r->connection->log; | |
664 | ||
665 | out.buf = b; | |
666 | out.next = NULL; | |
667 | ||
668 | return ngx_http_output_filter(r, &out); | |
669 | } | |
670 | ||
671 | ||
672 | void | |
673 | ngx_http_file_cache_free(ngx_http_request_t *r, ngx_temp_file_t *tf) | |
674 | { | |
675 | ngx_http_cache_t *c; | |
676 | ngx_http_file_cache_t *cache; | |
677 | ||
678 | c = r->cache; | |
679 | ||
680 | if (c->updated) { | |
681 | return; | |
682 | } | |
683 | ||
684 | c->updated = 1; | |
685 | ||
686 | cache = c->file_cache; | |
687 | ||
688 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
689 | "http file cache free"); | |
690 | ||
691 | ngx_shmtx_lock(&cache->shpool->mutex); | |
692 | ||
693 | c->node->count--; | |
694 | ||
695 | if (c->error) { | |
696 | c->node->valid_sec = c->valid_sec; | |
697 | c->node->valid_msec = c->valid_msec; | |
698 | c->node->error = c->error; | |
699 | } | |
700 | ||
701 | ngx_shmtx_unlock(&cache->shpool->mutex); | |
702 | ||
703 | if (c->temp_file) { | |
704 | if (tf && tf->file.fd != NGX_INVALID_FILE) { | |
705 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
706 | "http file cache incomplete: \"%s\"", | |
707 | tf->file.name.data); | |
708 | ||
709 | if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) { | |
710 | ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, | |
711 | ngx_delete_file_n " \"%s\" failed", | |
712 | tf->file.name.data); | |
713 | } | |
714 | } | |
715 | } | |
716 | } | |
717 | ||
718 | ||
719 | static void | |
720 | ngx_http_file_cache_cleanup(void *data) | |
721 | { | |
722 | ngx_http_cache_t *c = data; | |
723 | ||
724 | ngx_http_file_cache_t *cache; | |
725 | ||
726 | if (c->updated) { | |
727 | return; | |
728 | } | |
729 | ||
730 | c->updated = 1; | |
731 | ||
732 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0, | |
733 | "http file cache cleanup"); | |
734 | ||
735 | if (c->error) { | |
736 | return; | |
737 | } | |
738 | ||
739 | cache = c->file_cache; | |
740 | ||
741 | ngx_shmtx_lock(&cache->shpool->mutex); | |
742 | ||
743 | c->node->count--; | |
744 | ||
745 | ngx_shmtx_unlock(&cache->shpool->mutex); | |
746 | } | |
747 | ||
748 | ||
749 | static time_t | |
750 | ngx_http_file_cache_expire(ngx_http_file_cache_t *cache, ngx_uint_t forced) | |
751 | { | |
752 | u_char *name, *p; | |
753 | size_t len; | |
754 | time_t now, wait; | |
755 | ngx_uint_t tries; | |
756 | ngx_path_t *path; | |
757 | ngx_queue_t *q; | |
758 | ngx_http_file_cache_node_t *fcn; | |
759 | u_char key[2 * NGX_HTTP_CACHE_KEY_LEN]; | |
760 | ||
761 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, | |
762 | "http file cache expire"); | |
763 | ||
764 | path = cache->path; | |
765 | len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; | |
766 | ||
767 | now = ngx_time(); | |
768 | ||
769 | ngx_shmtx_lock(&cache->shpool->mutex); | |
770 | ||
771 | tries = 0; | |
772 | ||
773 | for ( ;; ) { | |
774 | ||
775 | if (ngx_queue_empty(cache->queue)) { | |
776 | wait = cache->inactive; | |
777 | break; | |
778 | } | |
779 | ||
780 | q = ngx_queue_last(cache->queue); | |
781 | ||
782 | fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); | |
783 | ||
784 | if (!forced) { | |
785 | wait = fcn->expire - now; | |
786 | ||
787 | if (wait > 0) { | |
788 | break; | |
789 | } | |
790 | } | |
791 | ||
792 | ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, | |
793 | "http file cache expire: #%d %d %02xd%02xd%02xd%02xd", | |
794 | fcn->count, fcn->exists, | |
795 | fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); | |
796 | ||
797 | if (fcn->count) { | |
798 | ||
799 | if (!forced) { | |
800 | ||
801 | p = ngx_hex_dump(key, (u_char *) &fcn->node.key, | |
802 | sizeof(ngx_rbtree_key_t)); | |
803 | (void) ngx_hex_dump(p, fcn->key, | |
804 | NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); | |
805 | ||
806 | /* | |
807 | * abnormally exited workers may leave locked cache entries, | |
808 | * and although it may be safe to remove them completely, | |
809 | * we prefer to remove them from inactive queue and rbtree | |
810 | * only, and to allow other leaks | |
811 | */ | |
812 | ||
813 | ngx_queue_remove(q); | |
814 | ||
815 | ngx_rbtree_delete(cache->rbtree, &fcn->node); | |
816 | ||
817 | ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, | |
818 | "ignore long locked inactive cache entry %*s", | |
819 | NGX_HTTP_CACHE_KEY_LEN, key); | |
820 | } | |
821 | ||
822 | if (tries++ < 10) { | |
823 | continue; | |
824 | } | |
825 |