nxt_http_static.c (1923:9f268a8a1a2f) nxt_http_static.c (1959:45b25ffb2e8c)
1
2/*
3 * Copyright (C) NGINX, Inc.
4 */
5
6#include <nxt_router.h>
7#include <nxt_http.h>
8
9
10typedef struct {
1
2/*
3 * Copyright (C) NGINX, Inc.
4 */
5
6#include <nxt_router.h>
7#include <nxt_http.h>
8
9
10typedef struct {
11 nxt_str_t share;
12 nxt_str_t chroot;
13 nxt_uint_t resolve;
14 nxt_http_route_rule_t *types;
11 nxt_str_t share;
12#if (NXT_HAVE_OPENAT2)
13 nxt_var_t *chroot;
14 nxt_uint_t resolve;
15#endif
16 nxt_http_route_rule_t *types;
17 uint8_t is_const; /* 1 bit */
15} nxt_http_static_conf_t;
16
17
18} nxt_http_static_conf_t;
19
20
21typedef struct {
22 nxt_http_action_t *action;
23#if (NXT_HAVE_OPENAT2)
24 nxt_str_t chroot;
25#endif
26 uint8_t need_body; /* 1 bit */
27} nxt_http_static_ctx_t;
28
29
18#define NXT_HTTP_STATIC_BUF_COUNT 2
19#define NXT_HTTP_STATIC_BUF_SIZE (128 * 1024)
20
21
22static nxt_http_action_t *nxt_http_static(nxt_task_t *task,
23 nxt_http_request_t *r, nxt_http_action_t *action);
30#define NXT_HTTP_STATIC_BUF_COUNT 2
31#define NXT_HTTP_STATIC_BUF_SIZE (128 * 1024)
32
33
34static nxt_http_action_t *nxt_http_static(nxt_task_t *task,
35 nxt_http_request_t *r, nxt_http_action_t *action);
36static void nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data);
37static void nxt_http_static_var_error(nxt_task_t *task, void *obj, void *data);
38#if (NXT_HAVE_OPENAT2)
39static u_char *nxt_http_static_chroot_match(u_char *chr, u_char *shr);
40#endif
24static void nxt_http_static_extract_extension(nxt_str_t *path,
25 nxt_str_t *exten);
26static void nxt_http_static_body_handler(nxt_task_t *task, void *obj,
27 void *data);
28static void nxt_http_static_buf_completion(nxt_task_t *task, void *obj,
29 void *data);
30
31static nxt_int_t nxt_http_static_mtypes_hash_test(nxt_lvlhsh_query_t *lhq,

--- 25 unchanged lines hidden (view full) ---

57
58 nxt_conf_get_string(acf->share, &value);
59
60 str = nxt_str_dup(mp, &conf->share, &value);
61 if (nxt_slow_path(str == NULL)) {
62 return NXT_ERROR;
63 }
64
41static void nxt_http_static_extract_extension(nxt_str_t *path,
42 nxt_str_t *exten);
43static void nxt_http_static_body_handler(nxt_task_t *task, void *obj,
44 void *data);
45static void nxt_http_static_buf_completion(nxt_task_t *task, void *obj,
46 void *data);
47
48static nxt_int_t nxt_http_static_mtypes_hash_test(nxt_lvlhsh_query_t *lhq,

--- 25 unchanged lines hidden (view full) ---

74
75 nxt_conf_get_string(acf->share, &value);
76
77 str = nxt_str_dup(mp, &conf->share, &value);
78 if (nxt_slow_path(str == NULL)) {
79 return NXT_ERROR;
80 }
81
82 conf->is_const = 1;
83
65#if (NXT_HAVE_OPENAT2)
66 if (acf->chroot.length > 0) {
84#if (NXT_HAVE_OPENAT2)
85 if (acf->chroot.length > 0) {
67 u_char *p;
68 nxt_str_t slash;
69
70 if (acf->chroot.start[acf->chroot.length - 1] != '/') {
71 nxt_str_set(&slash, "/");
72
73 } else {
74 nxt_str_set(&slash, "");
86 if (nxt_is_var(&acf->chroot)) {
87 conf->is_const = 0;
75 }
76
88 }
89
77 value.length = acf->chroot.length + slash.length;
78
79 value.start = nxt_mp_alloc(mp, value.length + 1);
80 if (nxt_slow_path(value.start == NULL)) {
90 conf->chroot = nxt_var_compile(&acf->chroot, mp, 1);
91 if (nxt_slow_path(conf->chroot == NULL)) {
81 return NXT_ERROR;
82 }
92 return NXT_ERROR;
93 }
83
84 p = value.start;
85 p = nxt_cpymem(p, acf->chroot.start, acf->chroot.length);
86 p = nxt_cpymem(p, slash.start, slash.length);
87 *p = '\0';
88
89 conf->chroot = value;
90 conf->resolve |= RESOLVE_IN_ROOT;
91 }
92
93 if (acf->follow_symlinks != NULL
94 && !nxt_conf_get_boolean(acf->follow_symlinks))
95 {
96 conf->resolve |= RESOLVE_NO_SYMLINKS;
97 }
98

--- 24 unchanged lines hidden (view full) ---

123 return NXT_OK;
124}
125
126
127static nxt_http_action_t *
128nxt_http_static(nxt_task_t *task, nxt_http_request_t *r,
129 nxt_http_action_t *action)
130{
94 }
95
96 if (acf->follow_symlinks != NULL
97 && !nxt_conf_get_boolean(acf->follow_symlinks))
98 {
99 conf->resolve |= RESOLVE_NO_SYMLINKS;
100 }
101

--- 24 unchanged lines hidden (view full) ---

126 return NXT_OK;
127}
128
129
130static nxt_http_action_t *
131nxt_http_static(nxt_task_t *task, nxt_http_request_t *r,
132 nxt_http_action_t *action)
133{
131 size_t length, encode;
132 u_char *p, *fname;
133 struct tm tm;
134 nxt_buf_t *fb;
135 nxt_int_t ret;
134 nxt_int_t ret;
136 nxt_str_t index, exten, *mtype, *chroot;
137 nxt_uint_t level;
138 nxt_bool_t need_body;
135 nxt_bool_t need_body;
139 nxt_file_t *f, file;
140 nxt_file_info_t fi;
141 nxt_http_field_t *field;
142 nxt_http_status_t status;
143 nxt_router_conf_t *rtcf;
144 nxt_work_handler_t body_handler;
136 nxt_http_static_ctx_t *ctx;
145 nxt_http_static_conf_t *conf;
146
137 nxt_http_static_conf_t *conf;
138
147 conf = action->u.conf;
148
149 nxt_debug(task, "http static: \"%V\"", &conf->share);
150
151 if (nxt_slow_path(!nxt_str_eq(r->method, "GET", 3))) {
152
153 if (!nxt_str_eq(r->method, "HEAD", 4)) {
154 if (action->fallback != NULL) {
155 return action->fallback;
156 }
157
158 nxt_http_request_error(task, r, NXT_HTTP_METHOD_NOT_ALLOWED);
159 return NULL;
160 }
161
162 need_body = 0;
163
164 } else {
165 need_body = 1;
166 }
167
139 if (nxt_slow_path(!nxt_str_eq(r->method, "GET", 3))) {
140
141 if (!nxt_str_eq(r->method, "HEAD", 4)) {
142 if (action->fallback != NULL) {
143 return action->fallback;
144 }
145
146 nxt_http_request_error(task, r, NXT_HTTP_METHOD_NOT_ALLOWED);
147 return NULL;
148 }
149
150 need_body = 0;
151
152 } else {
153 need_body = 1;
154 }
155
156 conf = action->u.conf;
157
158#if (NXT_DEBUG && NXT_HAVE_OPENAT2)
159 nxt_str_t chr;
160
161 if (conf->chroot != NULL) {
162 nxt_var_raw(conf->chroot, &chr);
163
164 } else {
165 nxt_str_set(&chr, "");
166 }
167
168 nxt_debug(task, "http static: \"%V\" (chroot: \"%V\")", &conf->share, &chr);
169
170#else
171 nxt_debug(task, "http static: \"%V\"", &conf->share);
172#endif
173
174 ctx = nxt_mp_zget(r->mem_pool, sizeof(nxt_http_static_ctx_t));
175 if (nxt_slow_path(ctx == NULL)) {
176 goto fail;
177 }
178
179 ctx->action = action;
180 ctx->need_body = need_body;
181
182 if (conf->is_const) {
183#if (NXT_HAVE_OPENAT2)
184 if (conf->chroot != NULL) {
185 nxt_var_raw(conf->chroot, &ctx->chroot);
186 }
187#endif
188
189 nxt_http_static_send_ready(task, r, ctx);
190
191 } else {
192 ret = nxt_var_query_init(&r->var_query, r, r->mem_pool);
193 if (nxt_slow_path(ret != NXT_OK)) {
194 goto fail;
195 }
196
197#if (NXT_HAVE_OPENAT2)
198 nxt_var_query(task, r->var_query, conf->chroot, &ctx->chroot);
199#endif
200
201 nxt_var_query_resolve(task, r->var_query, ctx,
202 nxt_http_static_send_ready,
203 nxt_http_static_var_error);
204 }
205
206 return NULL;
207
208fail:
209
210 nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
211 return NULL;
212}
213
214
215static void
216nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data)
217{
218 size_t length, encode;
219 u_char *p, *fname;
220 struct tm tm;
221 nxt_buf_t *fb;
222 nxt_int_t ret;
223 nxt_str_t index, exten, *mtype;
224 nxt_uint_t level;
225 nxt_file_t *f, file;
226 nxt_file_info_t fi;
227 nxt_http_field_t *field;
228 nxt_http_status_t status;
229 nxt_router_conf_t *rtcf;
230 nxt_http_action_t *action;
231 nxt_http_request_t *r;
232 nxt_work_handler_t body_handler;
233 nxt_http_static_ctx_t *ctx;
234 nxt_http_static_conf_t *conf;
235
236 r = obj;
237 ctx = data;
238 action = ctx->action;
239 conf = action->u.conf;
240
168 if (r->path->start[r->path->length - 1] == '/') {
169 /* TODO: dynamic index setting. */
170 nxt_str_set(&index, "index.html");
171 nxt_str_set(&exten, ".html");
172
173 } else {
174 nxt_str_set(&index, "");
175 nxt_str_null(&exten);
176 }
177
178 f = NULL;
241 if (r->path->start[r->path->length - 1] == '/') {
242 /* TODO: dynamic index setting. */
243 nxt_str_set(&index, "index.html");
244 nxt_str_set(&exten, ".html");
245
246 } else {
247 nxt_str_set(&index, "");
248 nxt_str_null(&exten);
249 }
250
251 f = NULL;
252 status = NXT_HTTP_INTERNAL_SERVER_ERROR;
179
180 rtcf = r->conf->socket_conf->router_conf;
181
182 mtype = NULL;
183
184 if (conf->types != NULL && exten.start == NULL) {
185 nxt_http_static_extract_extension(r->path, &exten);
186 mtype = nxt_http_static_mtype_get(&rtcf->mtypes_hash, &exten);
187
188 ret = nxt_http_route_test_rule(r, conf->types, mtype->start,
189 mtype->length);
190 if (nxt_slow_path(ret == NXT_ERROR)) {
191 goto fail;
192 }
193
194 if (ret == 0) {
253
254 rtcf = r->conf->socket_conf->router_conf;
255
256 mtype = NULL;
257
258 if (conf->types != NULL && exten.start == NULL) {
259 nxt_http_static_extract_extension(r->path, &exten);
260 mtype = nxt_http_static_mtype_get(&rtcf->mtypes_hash, &exten);
261
262 ret = nxt_http_route_test_rule(r, conf->types, mtype->start,
263 mtype->length);
264 if (nxt_slow_path(ret == NXT_ERROR)) {
265 goto fail;
266 }
267
268 if (ret == 0) {
195 if (action->fallback != NULL) {
196 return action->fallback;
197 }
198
199 nxt_http_request_error(task, r, NXT_HTTP_FORBIDDEN);
200 return NULL;
269 status = NXT_HTTP_FORBIDDEN;
270 goto fail;
201 }
202 }
203
204 length = conf->share.length + r->path->length + index.length;
205
206 fname = nxt_mp_nget(r->mem_pool, length + 1);
207 if (nxt_slow_path(fname == NULL)) {
208 goto fail;

--- 4 unchanged lines hidden (view full) ---

213 p = nxt_cpymem(p, r->path->start, r->path->length);
214 p = nxt_cpymem(p, index.start, index.length);
215 *p = '\0';
216
217 nxt_memzero(&file, sizeof(nxt_file_t));
218
219 file.name = fname;
220
271 }
272 }
273
274 length = conf->share.length + r->path->length + index.length;
275
276 fname = nxt_mp_nget(r->mem_pool, length + 1);
277 if (nxt_slow_path(fname == NULL)) {
278 goto fail;

--- 4 unchanged lines hidden (view full) ---

283 p = nxt_cpymem(p, r->path->start, r->path->length);
284 p = nxt_cpymem(p, index.start, index.length);
285 *p = '\0';
286
287 nxt_memzero(&file, sizeof(nxt_file_t));
288
289 file.name = fname;
290
221 chroot = &conf->chroot;
222
223#if (NXT_HAVE_OPENAT2)
291#if (NXT_HAVE_OPENAT2)
224 if (conf->resolve != 0) {
292 if (conf->resolve != 0 || ctx->chroot.length > 0) {
293 nxt_str_t *chr;
294 nxt_uint_t resolve;
225
295
226 if (chroot->length > 0) {
227 file.name = chroot->start;
296 resolve = conf->resolve;
297 chr = &ctx->chroot;
228
298
229 if (length > chroot->length
230 && nxt_memcmp(fname, chroot->start, chroot->length) == 0)
231 {
232 fname += chroot->length;
299 if (chr->length > 0) {
300 resolve |= RESOLVE_IN_ROOT;
301
302 fname = nxt_http_static_chroot_match(chr->start, file.name);
303
304 if (fname != NULL) {
305 file.name = chr->start;
233 ret = nxt_file_open(task, &file, NXT_FILE_SEARCH, NXT_FILE_OPEN,
234 0);
235
236 } else {
237 file.error = NXT_EACCES;
238 ret = NXT_ERROR;
239 }
240

--- 10 unchanged lines hidden (view full) ---

251 if (nxt_fast_path(ret == NXT_OK)) {
252 nxt_file_t af;
253
254 af = file;
255 nxt_memzero(&file, sizeof(nxt_file_t));
256 file.name = fname;
257
258 ret = nxt_file_openat2(task, &file, NXT_FILE_RDONLY,
306 ret = nxt_file_open(task, &file, NXT_FILE_SEARCH, NXT_FILE_OPEN,
307 0);
308
309 } else {
310 file.error = NXT_EACCES;
311 ret = NXT_ERROR;
312 }
313

--- 10 unchanged lines hidden (view full) ---

324 if (nxt_fast_path(ret == NXT_OK)) {
325 nxt_file_t af;
326
327 af = file;
328 nxt_memzero(&file, sizeof(nxt_file_t));
329 file.name = fname;
330
331 ret = nxt_file_openat2(task, &file, NXT_FILE_RDONLY,
259 NXT_FILE_OPEN, 0, af.fd, conf->resolve);
332 NXT_FILE_OPEN, 0, af.fd, resolve);
260
261 if (af.fd != AT_FDCWD) {
262 nxt_file_close(task, &af);
263 }
264 }
265
266 } else {
267 ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);

--- 36 unchanged lines hidden (view full) ---

304
305 default:
306 level = NXT_LOG_ALERT;
307 status = NXT_HTTP_INTERNAL_SERVER_ERROR;
308 break;
309 }
310
311 if (level == NXT_LOG_ERR && action->fallback != NULL) {
333
334 if (af.fd != AT_FDCWD) {
335 nxt_file_close(task, &af);
336 }
337 }
338
339 } else {
340 ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);

--- 36 unchanged lines hidden (view full) ---

377
378 default:
379 level = NXT_LOG_ALERT;
380 status = NXT_HTTP_INTERNAL_SERVER_ERROR;
381 break;
382 }
383
384 if (level == NXT_LOG_ERR && action->fallback != NULL) {
312 return action->fallback;
385 goto fail;
313 }
314
315 if (status != NXT_HTTP_NOT_FOUND) {
386 }
387
388 if (status != NXT_HTTP_NOT_FOUND) {
316 if (chroot->length > 0) {
389#if (NXT_HAVE_OPENAT2)
390 nxt_str_t *chr = &ctx->chroot;
391
392 if (chr->length > 0) {
317 nxt_log(task, level, "opening \"%s\" at \"%V\" failed %E",
393 nxt_log(task, level, "opening \"%s\" at \"%V\" failed %E",
318 fname, chroot, file.error);
394 fname, chr, file.error);
319
320 } else {
321 nxt_log(task, level, "opening \"%s\" failed %E",
322 fname, file.error);
323 }
395
396 } else {
397 nxt_log(task, level, "opening \"%s\" failed %E",
398 fname, file.error);
399 }
400
401#else
402 nxt_log(task, level, "opening \"%s\" failed %E", fname, file.error);
403#endif
324 }
325
404 }
405
326 nxt_http_request_error(task, r, status);
327 return NULL;
406 goto fail;
328 }
329
330 f = nxt_mp_get(r->mem_pool, sizeof(nxt_file_t));
331 if (nxt_slow_path(f == NULL)) {
332 goto fail;
333 }
334
335 *f = file;

--- 59 unchanged lines hidden (view full) ---

395 }
396
397 nxt_http_field_name_set(field, "Content-Type");
398
399 field->value = mtype->start;
400 field->value_length = mtype->length;
401 }
402
407 }
408
409 f = nxt_mp_get(r->mem_pool, sizeof(nxt_file_t));
410 if (nxt_slow_path(f == NULL)) {
411 goto fail;
412 }
413
414 *f = file;

--- 59 unchanged lines hidden (view full) ---

474 }
475
476 nxt_http_field_name_set(field, "Content-Type");
477
478 field->value = mtype->start;
479 field->value_length = mtype->length;
480 }
481
403 if (need_body && nxt_file_size(&fi) > 0) {
482 if (ctx->need_body && nxt_file_size(&fi) > 0) {
404 fb = nxt_mp_zget(r->mem_pool, NXT_BUF_FILE_SIZE);
405 if (nxt_slow_path(fb == NULL)) {
406 goto fail;
407 }
408
409 fb->file = f;
410 fb->file_end = nxt_file_size(&fi);
411

--- 4 unchanged lines hidden (view full) ---

416 } else {
417 nxt_file_close(task, f);
418 body_handler = NULL;
419 }
420
421 } else {
422 /* Not a file. */
423
483 fb = nxt_mp_zget(r->mem_pool, NXT_BUF_FILE_SIZE);
484 if (nxt_slow_path(fb == NULL)) {
485 goto fail;
486 }
487
488 fb->file = f;
489 fb->file_end = nxt_file_size(&fi);
490

--- 4 unchanged lines hidden (view full) ---

495 } else {
496 nxt_file_close(task, f);
497 body_handler = NULL;
498 }
499
500 } else {
501 /* Not a file. */
502
424 nxt_file_close(task, f);
425
426 if (nxt_slow_path(!nxt_is_dir(&fi))) {
503 if (nxt_slow_path(!nxt_is_dir(&fi))) {
427 if (action->fallback != NULL) {
428 return action->fallback;
504 if (action->fallback == NULL) {
505 nxt_log(task, NXT_LOG_ERR, "\"%FN\" is not a regular file",
506 f->name);
429 }
430
507 }
508
431 nxt_log(task, NXT_LOG_ERR, "\"%FN\" is not a regular file",
432 f->name);
433
434 nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND);
435 return NULL;
509 status = NXT_HTTP_NOT_FOUND;
510 goto fail;
436 }
437
511 }
512
513 nxt_file_close(task, f);
438 f = NULL;
439
440 r->status = NXT_HTTP_MOVED_PERMANENTLY;
441 r->resp.content_length_n = 0;
442
443 field = nxt_list_zero_add(r->resp.fields);
444 if (nxt_slow_path(field == NULL)) {
445 goto fail;

--- 31 unchanged lines hidden (view full) ---

477 }
478
479 body_handler = NULL;
480 }
481
482 nxt_http_request_header_send(task, r, body_handler, NULL);
483
484 r->state = &nxt_http_static_send_state;
514 f = NULL;
515
516 r->status = NXT_HTTP_MOVED_PERMANENTLY;
517 r->resp.content_length_n = 0;
518
519 field = nxt_list_zero_add(r->resp.fields);
520 if (nxt_slow_path(field == NULL)) {
521 goto fail;

--- 31 unchanged lines hidden (view full) ---

553 }
554
555 body_handler = NULL;
556 }
557
558 nxt_http_request_header_send(task, r, body_handler, NULL);
559
560 r->state = &nxt_http_static_send_state;
485 return NULL;
561 return;
486
487fail:
488
562
563fail:
564
489 nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
490
491 if (f != NULL) {
492 nxt_file_close(task, f);
493 }
494
565 if (f != NULL) {
566 nxt_file_close(task, f);
567 }
568
495 return NULL;
569 if (status != NXT_HTTP_INTERNAL_SERVER_ERROR
570 && action->fallback != NULL)
571 {
572 nxt_http_request_action(task, r, action->fallback);
573 return;
574 }
575
576 nxt_http_request_error(task, r, status);
496}
497
498
499static void
577}
578
579
580static void
581nxt_http_static_var_error(nxt_task_t *task, void *obj, void *data)
582{
583 nxt_http_request_t *r;
584
585 r = obj;
586
587 nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
588}
589
590
591#if (NXT_HAVE_OPENAT2)
592
593static u_char *
594nxt_http_static_chroot_match(u_char *chr, u_char *shr)
595{
596 if (*chr != *shr) {
597 return NULL;
598 }
599
600 chr++;
601 shr++;
602
603 for ( ;; ) {
604 if (*shr == '\0') {
605 return NULL;
606 }
607
608 if (*chr == *shr) {
609 chr++;
610 shr++;
611 continue;
612 }
613
614 if (*chr == '\0') {
615 break;
616 }
617
618 if (*chr == '/') {
619 if (chr[-1] == '/') {
620 chr++;
621 continue;
622 }
623
624 } else if (*shr == '/') {
625 if (shr[-1] == '/') {
626 shr++;
627 continue;
628 }
629 }
630
631 return NULL;
632 }
633
634 if (shr[-1] != '/' && *shr != '/') {
635 return NULL;
636 }
637
638 while (*shr == '/') {
639 shr++;
640 }
641
642 return (*shr != '\0') ? shr : NULL;
643}
644
645#endif
646
647
648static void
500nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *exten)
501{
502 u_char ch, *p, *end;
503
504 end = path->start + path->length;
505 p = end;
506
507 for ( ;; ) {

--- 334 unchanged lines hidden ---
649nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *exten)
650{
651 u_char ch, *p, *end;
652
653 end = path->start + path->length;
654 p = end;
655
656 for ( ;; ) {

--- 334 unchanged lines hidden ---