comparison src/http/modules/ngx_http_static_handler.c @ 0:f0b350454894 NGINX_0_1_0

nginx 0.1.0 *) The first public version.
author Igor Sysoev <http://sysoev.ru>
date Mon, 04 Oct 2004 00:00:00 +0400
parents
children 46833bd150cb
comparison
equal deleted inserted replaced
-1:000000000000 0:f0b350454894
1
2 /*
3 * Copyright (C) Igor Sysoev
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10
11
12 typedef struct {
13 ngx_http_cache_hash_t *redirect_cache;
14 } ngx_http_static_loc_conf_t;
15
16
17 static ngx_int_t ngx_http_static_handler(ngx_http_request_t *r);
18 static void *ngx_http_static_create_loc_conf(ngx_conf_t *cf);
19 static char *ngx_http_static_merge_loc_conf(ngx_conf_t *cf,
20 void *parent, void *child);
21 static ngx_int_t ngx_http_static_init(ngx_cycle_t *cycle);
22
23
24 static ngx_command_t ngx_http_static_commands[] = {
25
26 #if (NGX_HTTP_CACHE)
27
28 { ngx_string("redirect_cache"),
29 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE3,
30 ngx_http_set_cache_slot,
31 NGX_HTTP_LOC_CONF_OFFSET,
32 offsetof(ngx_http_static_loc_conf_t, redirect_cache),
33 NULL },
34
35 #endif
36
37 ngx_null_command
38 };
39
40
41
42 ngx_http_module_t ngx_http_static_module_ctx = {
43 NULL, /* pre conf */
44
45 NULL, /* create main configuration */
46 NULL, /* init main configuration */
47
48 NULL, /* create server configuration */
49 NULL, /* merge server configuration */
50
51 ngx_http_static_create_loc_conf, /* create location configuration */
52 ngx_http_static_merge_loc_conf /* merge location configuration */
53 };
54
55
56 ngx_module_t ngx_http_static_module = {
57 NGX_MODULE,
58 &ngx_http_static_module_ctx, /* module context */
59 ngx_http_static_commands, /* module directives */
60 NGX_HTTP_MODULE, /* module type */
61 ngx_http_static_init, /* init module */
62 NULL /* init child */
63 };
64
65
66 static ngx_int_t ngx_http_static_handler(ngx_http_request_t *r)
67 {
68 u_char *last;
69 ngx_fd_t fd;
70 ngx_int_t rc;
71 ngx_uint_t level;
72 ngx_str_t name, location;
73 ngx_err_t err;
74 ngx_log_t *log;
75 ngx_buf_t *b;
76 ngx_chain_t out;
77 ngx_file_info_t fi;
78 ngx_http_cleanup_t *file_cleanup, *redirect_cleanup;
79 ngx_http_log_ctx_t *ctx;
80 ngx_http_core_loc_conf_t *clcf;
81 ngx_http_static_loc_conf_t *slcf;
82 #if (NGX_HTTP_CACHE)
83 uint32_t file_crc, redirect_crc;
84 ngx_http_cache_t *file, *redirect;
85 #endif
86
87 if (r->uri.data[r->uri.len - 1] == '/') {
88 return NGX_DECLINED;
89 }
90
91 if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) {
92 return NGX_HTTP_NOT_ALLOWED;
93 }
94
95 rc = ngx_http_discard_body(r);
96
97 if (rc != NGX_OK && rc != NGX_AGAIN) {
98 return rc;
99 }
100
101 #if (NGX_HTTP_CACHE)
102
103 /*
104 * there is a valid cached open file, i.e by the index handler,
105 * and it should be already registered in r->cleanup
106 */
107
108 if (r->cache && !r->cache->expired) {
109 return ngx_http_send_cached(r);
110 }
111
112 #endif
113
114 log = r->connection->log;
115
116 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
117
118 /*
119 * make a file name, reserve 2 bytes for a trailing '/'
120 * in a possible redirect and for the last '\0'
121 */
122
123 if (clcf->alias) {
124 name.data = ngx_palloc(r->pool, clcf->root.len + r->uri.len + 2
125 - clcf->name.len);
126 if (name.data == NULL) {
127 return NGX_HTTP_INTERNAL_SERVER_ERROR;
128 }
129
130 last = ngx_cpymem(name.data, clcf->root.data, clcf->root.len);
131 last = ngx_cpystrn(last, r->uri.data + clcf->name.len,
132 r->uri.len + 1 - clcf->name.len);
133
134 name.len = last - name.data;
135
136 location.data = ngx_palloc(r->pool, r->uri.len + 2);
137 if (location.data == NULL) {
138 return NGX_HTTP_INTERNAL_SERVER_ERROR;
139 }
140
141 last = ngx_cpystrn(location.data, r->uri.data, r->uri.len + 1);
142
143 #if 0
144 /*
145 * aliases usually have trailling "/",
146 * set it in the start of the possible redirect
147 */
148
149 if (*location.data != '/') {
150 location.data--;
151 }
152 #endif
153
154 location.len = last - location.data + 1;
155
156 } else {
157 name.data = ngx_palloc(r->pool, clcf->root.len + r->uri.len + 2);
158 if (name.data == NULL) {
159 return NGX_HTTP_INTERNAL_SERVER_ERROR;
160 }
161
162 location.data = ngx_cpymem(name.data, clcf->root.data, clcf->root.len);
163 last = ngx_cpystrn(location.data, r->uri.data, r->uri.len + 1);
164
165 name.len = last - name.data;
166 location.len = last - location.data + 1;
167 }
168
169 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
170 "http filename: \"%s\"", name.data);
171
172
173 /* allocate cleanups */
174
175 if (!(file_cleanup = ngx_push_array(&r->cleanup))) {
176 return NGX_HTTP_INTERNAL_SERVER_ERROR;
177 }
178 file_cleanup->valid = 0;
179
180 slcf = ngx_http_get_module_loc_conf(r, ngx_http_static_module);
181 if (slcf->redirect_cache) {
182 if (!(redirect_cleanup = ngx_push_array(&r->cleanup))) {
183 return NGX_HTTP_INTERNAL_SERVER_ERROR;
184 }
185 redirect_cleanup->valid = 0;
186
187 } else {
188 redirect_cleanup = NULL;
189 }
190
191 #if (NGX_HTTP_CACHE)
192
193 /* look up an open files cache */
194
195 if (clcf->open_files) {
196 file = ngx_http_cache_get(clcf->open_files, file_cleanup,
197 &name, &file_crc);
198
199 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
200 "http open file cache get: " PTR_FMT, file);
201
202 if (file && !file->expired) {
203 r->cache = file;
204 return ngx_http_send_cached(r);
205 }
206
207 } else {
208 file = NULL;
209 }
210
211
212 /* look up an redirect cache */
213
214 if (slcf->redirect_cache) {
215 redirect = ngx_http_cache_get(slcf->redirect_cache, redirect_cleanup,
216 &name, &redirect_crc);
217
218 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
219 "http redirect cache get: " PTR_FMT, redirect);
220
221 if (redirect && !redirect->expired) {
222
223 /*
224 * We do not copy a cached value so the cache entry is locked
225 * until the end of the request. In a single threaded model
226 * the redirected request should complete before other event
227 * will be processed. In a multithreaded model this locking
228 * should keep more popular redirects in cache.
229 */
230
231 if (!(r->headers_out.location =
232 ngx_http_add_header(&r->headers_out, ngx_http_headers_out)))
233 {
234 return NGX_HTTP_INTERNAL_SERVER_ERROR;
235 }
236
237 r->headers_out.location->value = redirect->data.value;
238
239 return NGX_HTTP_MOVED_PERMANENTLY;
240 }
241
242 } else {
243 redirect = NULL;
244 }
245
246 #endif
247
248 /* open file */
249
250 #if (WIN9X)
251
252 /* TODO: redirect cache */
253
254 if (ngx_win32_version < NGX_WIN_NT) {
255
256 /*
257 * there is no way to open a file or a directory in Win9X with
258 * one syscall because Win9X has no FILE_FLAG_BACKUP_SEMANTICS flag
259 * so we need to check its type before the opening
260 */
261
262 if (ngx_file_info(name.data, &fi) == NGX_FILE_ERROR) {
263 err = ngx_errno;
264 ngx_log_error(NGX_LOG_ERR, log, err,
265 ngx_file_info_n " \"%s\" failed", name.data);
266
267 if (err == NGX_ENOENT || err == NGX_ENOTDIR) {
268 return NGX_HTTP_NOT_FOUND;
269
270 } else if (err == NGX_EACCES) {
271 return NGX_HTTP_FORBIDDEN;
272
273 } else {
274 return NGX_HTTP_INTERNAL_SERVER_ERROR;
275 }
276 }
277
278 if (ngx_is_dir(&fi)) {
279 ngx_log_debug(log, "HTTP DIR: '%s'" _ name.data);
280
281 if (!(r->headers_out.location =
282 ngx_http_add_header(&r->headers_out, ngx_http_headers_out)))
283 {
284 return NGX_HTTP_INTERNAL_SERVER_ERROR;
285 }
286
287 *last++ = '/';
288 *last = '\0';
289 r->headers_out.location->value.len = last - location;
290 r->headers_out.location->value.data = location;
291
292 return NGX_HTTP_MOVED_PERMANENTLY;
293 }
294 }
295
296 #endif
297
298
299 fd = ngx_open_file(name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN);
300
301 if (fd == NGX_INVALID_FILE) {
302 err = ngx_errno;
303
304 if (err == NGX_ENOENT || err == NGX_ENOTDIR) {
305 level = NGX_LOG_ERR;
306 rc = NGX_HTTP_NOT_FOUND;
307
308 } else if (err == NGX_EACCES) {
309 level = NGX_LOG_ERR;
310 rc = NGX_HTTP_FORBIDDEN;
311
312 } else {
313 level = NGX_LOG_CRIT;
314 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
315 }
316
317 ngx_log_error(level, log, err,
318 ngx_open_file_n " \"%s\" failed", name.data);
319
320 return rc;
321 }
322
323 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", fd);
324
325 if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
326 ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
327 ngx_fd_info_n " \"%s\" failed", name.data);
328
329 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
330 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
331 ngx_close_file_n " \"%s\" failed", name.data);
332 }
333
334 return NGX_HTTP_INTERNAL_SERVER_ERROR;
335 }
336
337 if (ngx_is_dir(&fi)) {
338
339 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");
340
341 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
342 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
343 ngx_close_file_n " \"%s\" failed", name.data);
344 }
345
346 *last++ = '/';
347 *last = '\0';
348
349 r->headers_out.location = ngx_list_push(&r->headers_out.headers);
350 if (r->headers_out.location == NULL) {
351 return NGX_HTTP_INTERNAL_SERVER_ERROR;
352 }
353
354 r->headers_out.location->value = location;
355
356 #if (NGX_HTTP_CACHE)
357
358 if (slcf->redirect_cache) {
359 if (redirect) {
360 if (location.len == redirect->data.value.len
361 && ngx_memcmp(redirect->data.value.data, location.data,
362 location.len) == 0)
363 {
364 redirect->accessed = ngx_cached_time;
365 redirect->updated = ngx_cached_time;
366
367 /*
368 * we can unlock the cache entry because
369 * we have the local copy anyway
370 */
371
372 ngx_http_cache_unlock(slcf->redirect_cache, redirect, log);
373 redirect_cleanup->valid = 0;
374
375 return NGX_HTTP_MOVED_PERMANENTLY;
376 }
377 }
378
379 location.len++;
380 redirect = ngx_http_cache_alloc(slcf->redirect_cache, redirect,
381 redirect_cleanup,
382 &name, redirect_crc,
383 &location, log);
384 location.len--;
385
386 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
387 "http redirect cache alloc: " PTR_FMT, redirect);
388
389 if (redirect) {
390 redirect->fd = NGX_INVALID_FILE;
391 redirect->accessed = ngx_cached_time;
392 redirect->last_modified = 0;
393 redirect->updated = ngx_cached_time;
394 redirect->memory = 1;
395 ngx_http_cache_unlock(slcf->redirect_cache, redirect, log);
396 redirect_cleanup->valid = 0;
397 }
398
399 }
400
401 #endif
402
403 return NGX_HTTP_MOVED_PERMANENTLY;
404 }
405
406 #if !(WIN32) /* the not regular files are probably Unix specific */
407
408 if (!ngx_is_file(&fi)) {
409 ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
410 "%s is not a regular file", name.data);
411
412 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
413 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
414 ngx_close_file_n " \"%s\" failed", name.data);
415 }
416
417 return NGX_HTTP_NOT_FOUND;
418 }
419
420 #endif
421
422
423 #if (NGX_HTTP_CACHE)
424
425 if (clcf->open_files) {
426
427 #if (NGX_USE_HTTP_FILE_CACHE_UNIQ)
428
429 if (file && file->uniq == ngx_file_uniq(&fi)) {
430 if (ngx_close_file(fd) == NGX_FILE_ERROR) {
431 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
432 ngx_close_file_n " \"%s\" failed", name.data);
433 }
434 file->accessed = ngx_cached_time;
435 file->updated = ngx_cached_time;
436 file->expired = 0;
437 r->cache = file;
438
439 return ngx_http_send_cached(r);
440
441 } else {
442 if (file) {
443 ngx_http_cache_unlock(clcf->open_files, file, log);
444 file = NULL;
445 }
446
447 file = ngx_http_cache_alloc(clcf->open_files, file,
448 file_cleanup,
449 &name, file_crc, NULL, log);
450 if (file) {
451 file->uniq = ngx_file_uniq(&fi);
452 }
453 }
454
455 #else
456 file = ngx_http_cache_alloc(clcf->open_files, file,
457 file_cleanup,
458 &name, file_crc, NULL, log);
459 #endif
460
461 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
462 "http open file cache alloc: " PTR_FMT, file);
463
464 if (file) {
465 file->fd = fd;
466 file->data.size = ngx_file_size(&fi);
467 file->accessed = ngx_cached_time;
468 file->last_modified = ngx_file_mtime(&fi);
469 file->updated = ngx_cached_time;
470 r->cache = file;
471 }
472
473 return ngx_http_send_cached(r);
474 }
475
476 #endif
477
478 ctx = log->data;
479 ctx->action = "sending response to client";
480
481 file_cleanup->data.file.fd = fd;
482 file_cleanup->data.file.name = name.data;
483 file_cleanup->valid = 1;
484 file_cleanup->cache = 0;
485
486 r->headers_out.status = NGX_HTTP_OK;
487 r->headers_out.content_length_n = ngx_file_size(&fi);
488 r->headers_out.last_modified_time = ngx_file_mtime(&fi);
489
490 if (r->headers_out.content_length_n == 0) {
491 r->header_only = 1;
492 }
493
494 if (ngx_http_set_content_type(r) != NGX_OK) {
495 return NGX_HTTP_INTERNAL_SERVER_ERROR;
496 }
497
498 #if (NGX_SUPPRESS_WARN)
499 b = NULL;
500 #endif
501
502 if (!r->header_only) {
503 /* we need to allocate all before the header would be sent */
504
505 if (!(b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)))) {
506 return NGX_HTTP_INTERNAL_SERVER_ERROR;
507 }
508
509 if (!(b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)))) {
510 return NGX_HTTP_INTERNAL_SERVER_ERROR;
511 }
512
513 r->filter_allow_ranges = 1;
514 }
515
516 rc = ngx_http_send_header(r);
517
518 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
519 return rc;
520 }
521
522 b->in_file = 1;
523
524 if (!r->main) {
525 b->last_buf = 1;
526 }
527
528 b->file_pos = 0;
529 b->file_last = ngx_file_size(&fi);
530
531 b->file->fd = fd;
532 b->file->log = log;
533
534 out.buf = b;
535 out.next = NULL;
536
537 return ngx_http_output_filter(r, &out);
538 }
539
540
541 static void *ngx_http_static_create_loc_conf(ngx_conf_t *cf)
542 {
543 ngx_http_static_loc_conf_t *conf;
544
545 if (!(conf = ngx_palloc(cf->pool, sizeof(ngx_http_static_loc_conf_t)))) {
546 return NGX_CONF_ERROR;
547 }
548
549 conf->redirect_cache = NULL;
550
551 return conf;
552 }
553
554
555 static char *ngx_http_static_merge_loc_conf(ngx_conf_t *cf,
556 void *parent, void *child)
557 {
558 ngx_http_static_loc_conf_t *prev = parent;
559 ngx_http_static_loc_conf_t *conf = child;
560
561 if (conf->redirect_cache == NULL) {
562 conf->redirect_cache = prev->redirect_cache;
563 }
564
565 return NGX_CONF_OK;
566 }
567
568
569 static ngx_int_t ngx_http_static_init(ngx_cycle_t *cycle)
570 {
571 ngx_http_handler_pt *h;
572 ngx_http_core_main_conf_t *cmcf;
573
574 cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module);
575
576 h = ngx_push_array(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
577 if (h == NULL) {
578 return NGX_ERROR;
579 }
580
581 *h = ngx_http_static_handler;
582
583 return NGX_OK;
584 }