1
2 /*
3 * Copyright (C) NGINX, Inc.
4 */
5
6 #include <nxt_router.h>
7 #include <nxt_http.h>
8
9
10 typedef struct {
11 nxt_tstr_t *tstr;
12 #if (NXT_HAVE_OPENAT2)
13 u_char *fname;
14 #endif
15 uint8_t is_const; /* 1 bit */
16 } nxt_http_static_share_t;
17
18
19 typedef struct {
20 nxt_uint_t nshares;
21 nxt_http_static_share_t *shares;
22 nxt_str_t index;
23 #if (NXT_HAVE_OPENAT2)
24 nxt_tstr_t *chroot;
25 nxt_uint_t resolve;
26 #endif
27 nxt_http_route_rule_t *types;
28 } nxt_http_static_conf_t;
29
30
31 typedef struct {
32 nxt_http_action_t *action;
33 nxt_str_t share;
34 #if (NXT_HAVE_OPENAT2)
35 nxt_str_t chroot;
36 #endif
37 uint32_t share_idx;
38 uint8_t need_body; /* 1 bit */
39 } nxt_http_static_ctx_t;
40
41
42 #define NXT_HTTP_STATIC_BUF_COUNT 2
43 #define NXT_HTTP_STATIC_BUF_SIZE (128 * 1024)
44
45
46 static nxt_http_action_t *nxt_http_static(nxt_task_t *task,
47 nxt_http_request_t *r, nxt_http_action_t *action);
48 static void nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r,
49 nxt_http_static_ctx_t *ctx);
50 static void nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data);
51 static void nxt_http_static_send_error(nxt_task_t *task, void *obj, void *data);
52 static void nxt_http_static_next(nxt_task_t *task, nxt_http_request_t *r,
53 nxt_http_static_ctx_t *ctx, nxt_http_status_t status);
54 #if (NXT_HAVE_OPENAT2)
55 static u_char *nxt_http_static_chroot_match(u_char *chr, u_char *shr);
56 #endif
57 static void nxt_http_static_extract_extension(nxt_str_t *path,
58 nxt_str_t *exten);
59 static void nxt_http_static_body_handler(nxt_task_t *task, void *obj,
60 void *data);
61 static void nxt_http_static_buf_completion(nxt_task_t *task, void *obj,
62 void *data);
63
64 static nxt_int_t nxt_http_static_mtypes_hash_test(nxt_lvlhsh_query_t *lhq,
65 void *data);
66 static void *nxt_http_static_mtypes_hash_alloc(void *data, size_t size);
67 static void nxt_http_static_mtypes_hash_free(void *data, void *p);
68
69
70 static const nxt_http_request_state_t nxt_http_static_send_state;
71
72
73 nxt_int_t
nxt_http_static_init(nxt_task_t * task,nxt_router_temp_conf_t * tmcf,nxt_http_action_t * action,nxt_http_action_conf_t * acf)74 nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
75 nxt_http_action_t *action, nxt_http_action_conf_t *acf)
76 {
77 uint32_t i;
78 nxt_mp_t *mp;
79 nxt_str_t str, *ret;
80 nxt_tstr_t *tstr;
81 nxt_conf_value_t *cv;
82 nxt_router_conf_t *rtcf;
83 nxt_http_static_conf_t *conf;
84
85 rtcf = tmcf->router_conf;
86 mp = rtcf->mem_pool;
87
88 conf = nxt_mp_zget(mp, sizeof(nxt_http_static_conf_t));
89 if (nxt_slow_path(conf == NULL)) {
90 return NXT_ERROR;
91 }
92
93 action->handler = nxt_http_static;
94 action->u.conf = conf;
95
96 conf->nshares = nxt_conf_array_elements_count_or_1(acf->share);
97 conf->shares = nxt_mp_zget(mp, sizeof(nxt_http_static_share_t)
98 * conf->nshares);
99 if (nxt_slow_path(conf->shares == NULL)) {
100 return NXT_ERROR;
101 }
102
103 for (i = 0; i < conf->nshares; i++) {
104 cv = nxt_conf_get_array_element_or_itself(acf->share, i);
105 nxt_conf_get_string(cv, &str);
106
107 tstr = nxt_tstr_compile(rtcf->tstr_state, &str, NXT_TSTR_STRZ);
108 if (nxt_slow_path(tstr == NULL)) {
109 return NXT_ERROR;
110 }
111
112 conf->shares[i].tstr = tstr;
113 conf->shares[i].is_const = nxt_tstr_is_const(tstr);
114 }
115
116 if (acf->index == NULL) {
117 nxt_str_set(&conf->index, "index.html");
118
119 } else {
120 ret = nxt_conf_get_string_dup(acf->index, mp, &conf->index);
121 if (nxt_slow_path(ret == NULL)) {
122 return NXT_ERROR;
123 }
124 }
125
126 #if (NXT_HAVE_OPENAT2)
127 if (acf->chroot.length > 0) {
128 nxt_str_t chr, shr;
129 nxt_bool_t is_const;
130
131 conf->chroot = nxt_tstr_compile(rtcf->tstr_state, &acf->chroot,
132 NXT_TSTR_STRZ);
133 if (nxt_slow_path(conf->chroot == NULL)) {
134 return NXT_ERROR;
135 }
136
137 is_const = nxt_tstr_is_const(conf->chroot);
138
139 for (i = 0; i < conf->nshares; i++) {
140 conf->shares[i].is_const &= is_const;
141
142 if (conf->shares[i].is_const) {
143 nxt_tstr_str(conf->chroot, &chr);
144 nxt_tstr_str(conf->shares[i].tstr, &shr);
145
146 conf->shares[i].fname = nxt_http_static_chroot_match(chr.start,
147 shr.start);
148 }
149 }
150 }
151
152 if (acf->follow_symlinks != NULL
153 && !nxt_conf_get_boolean(acf->follow_symlinks))
154 {
155 conf->resolve |= RESOLVE_NO_SYMLINKS;
156 }
157
158 if (acf->traverse_mounts != NULL
159 && !nxt_conf_get_boolean(acf->traverse_mounts))
160 {
161 conf->resolve |= RESOLVE_NO_XDEV;
162 }
163 #endif
164
165 if (acf->types != NULL) {
166 conf->types = nxt_http_route_types_rule_create(task, mp, acf->types);
167 if (nxt_slow_path(conf->types == NULL)) {
168 return NXT_ERROR;
169 }
170 }
171
172 if (acf->fallback != NULL) {
173 action->fallback = nxt_mp_alloc(mp, sizeof(nxt_http_action_t));
174 if (nxt_slow_path(action->fallback == NULL)) {
175 return NXT_ERROR;
176 }
177
178 return nxt_http_action_init(task, tmcf, acf->fallback,
179 action->fallback);
180 }
181
182 return NXT_OK;
183 }
184
185
186 static nxt_http_action_t *
nxt_http_static(nxt_task_t * task,nxt_http_request_t * r,nxt_http_action_t * action)187 nxt_http_static(nxt_task_t *task, nxt_http_request_t *r,
188 nxt_http_action_t *action)
189 {
190 nxt_bool_t need_body;
191 nxt_http_static_ctx_t *ctx;
192
193 if (nxt_slow_path(!nxt_str_eq(r->method, "GET", 3))) {
194
195 if (!nxt_str_eq(r->method, "HEAD", 4)) {
196 if (action->fallback != NULL) {
197 if (nxt_slow_path(r->log_route)) {
198 nxt_log(task, NXT_LOG_NOTICE, "\"fallback\" taken");
199 }
200 return action->fallback;
201 }
202
203 nxt_http_request_error(task, r, NXT_HTTP_METHOD_NOT_ALLOWED);
204 return NULL;
205 }
206
207 need_body = 0;
208
209 } else {
210 need_body = 1;
211 }
212
213 ctx = nxt_mp_zget(r->mem_pool, sizeof(nxt_http_static_ctx_t));
214 if (nxt_slow_path(ctx == NULL)) {
215 nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
216 return NULL;
217 }
218
219 ctx->action = action;
220 ctx->need_body = need_body;
221
222 nxt_http_static_iterate(task, r, ctx);
223
224 return NULL;
225 }
226
227
228 static void
nxt_http_static_iterate(nxt_task_t * task,nxt_http_request_t * r,nxt_http_static_ctx_t * ctx)229 nxt_http_static_iterate(nxt_task_t *task, nxt_http_request_t *r,
230 nxt_http_static_ctx_t *ctx)
231 {
232 nxt_int_t ret;
233 nxt_router_conf_t *rtcf;
234 nxt_http_static_conf_t *conf;
235 nxt_http_static_share_t *share;
236
237 conf = ctx->action->u.conf;
238
239 share = &conf->shares[ctx->share_idx];
240
241 #if (NXT_DEBUG)
242 nxt_str_t shr;
243 nxt_str_t idx;
244
245 nxt_tstr_str(share->tstr, &shr);
246 idx = conf->index;
247
248 #if (NXT_HAVE_OPENAT2)
249 nxt_str_t chr;
250
251 if (conf->chroot != NULL) {
252 nxt_tstr_str(conf->chroot, &chr);
253
254 } else {
255 nxt_str_set(&chr, "");
256 }
257
258 nxt_debug(task, "http static: \"%V\", index: \"%V\" (chroot: \"%V\")",
259 &shr, &idx, &chr);
260 #else
261 nxt_debug(task, "http static: \"%V\", index: \"%V\"", &shr, &idx);
262 #endif
263 #endif /* NXT_DEBUG */
264
265 if (share->is_const) {
266 nxt_tstr_str(share->tstr, &ctx->share);
267
268 #if (NXT_HAVE_OPENAT2)
269 if (conf->chroot != NULL && ctx->share_idx == 0) {
270 nxt_tstr_str(conf->chroot, &ctx->chroot);
271 }
272 #endif
273
274 nxt_http_static_send_ready(task, r, ctx);
275
276 } else {
277 rtcf = r->conf->socket_conf->router_conf;
278
279 ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state,
280 &r->tstr_cache, r, r->mem_pool);
281 if (nxt_slow_path(ret != NXT_OK)) {
282 nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
283 return;
284 }
285
286 nxt_tstr_query(task, r->tstr_query, share->tstr, &ctx->share);
287
288 #if (NXT_HAVE_OPENAT2)
289 if (conf->chroot != NULL && ctx->share_idx == 0) {
290 nxt_tstr_query(task, r->tstr_query, conf->chroot, &ctx->chroot);
291 }
292 #endif
293
294 nxt_tstr_query_resolve(task, r->tstr_query, ctx,
295 nxt_http_static_send_ready,
296 nxt_http_static_send_error);
297 }
298 }
299
300
301 static void
nxt_http_static_send_ready(nxt_task_t * task,void * obj,void * data)302 nxt_http_static_send_ready(nxt_task_t *task, void *obj, void *data)
303 {
304 size_t length, encode;
305 u_char *p, *fname;
306 struct tm tm;
307 nxt_buf_t *fb;
308 nxt_int_t ret;
309 nxt_str_t *shr, *index, exten, *mtype;
310 nxt_uint_t level;
311 nxt_file_t *f, file;
312 nxt_file_info_t fi;
313 nxt_http_field_t *field;
314 nxt_http_status_t status;
315 nxt_router_conf_t *rtcf;
316 nxt_http_action_t *action;
317 nxt_http_request_t *r;
318 nxt_work_handler_t body_handler;
319 nxt_http_static_ctx_t *ctx;
320 nxt_http_static_conf_t *conf;
321
322 r = obj;
323 ctx = data;
324 action = ctx->action;
325 conf = action->u.conf;
326 rtcf = r->conf->socket_conf->router_conf;
327
328 f = NULL;
329 mtype = NULL;
330
331 shr = &ctx->share;
332 index = &conf->index;
333
334 if (shr->start[shr->length - 1] == '/') {
335 nxt_http_static_extract_extension(index, &exten);
336
337 length = shr->length + index->length;
338
339 fname = nxt_mp_nget(r->mem_pool, length + 1);
340 if (nxt_slow_path(fname == NULL)) {
341 goto fail;
342 }
343
344 p = fname;
345 p = nxt_cpymem(p, shr->start, shr->length);
346 p = nxt_cpymem(p, index->start, index->length);
347 *p = '\0';
348
349 } else {
350 if (conf->types == NULL) {
351 nxt_str_null(&exten);
352
353 } else {
354 nxt_http_static_extract_extension(shr, &exten);
355 mtype = nxt_http_static_mtype_get(&rtcf->mtypes_hash, &exten);
356
357 ret = nxt_http_route_test_rule(r, conf->types, mtype->start,
358 mtype->length);
359 if (nxt_slow_path(ret == NXT_ERROR)) {
360 goto fail;
361 }
362
363 if (ret == 0) {
364 nxt_http_static_next(task, r, ctx, NXT_HTTP_FORBIDDEN);
365 return;
366 }
367 }
368
369 fname = ctx->share.start;
370 }
371
372 nxt_memzero(&file, sizeof(nxt_file_t));
373
374 file.name = fname;
375
376 #if (NXT_HAVE_OPENAT2)
377 if (conf->resolve != 0 || ctx->chroot.length > 0) {
378 nxt_str_t *chr;
379 nxt_uint_t resolve;
380 nxt_http_static_share_t *share;
381
382 share = &conf->shares[ctx->share_idx];
383
384 resolve = conf->resolve;
385 chr = &ctx->chroot;
386
387 if (chr->length > 0) {
388 resolve |= RESOLVE_IN_ROOT;
389
390 fname = share->is_const
391 ? share->fname
392 : nxt_http_static_chroot_match(chr->start, file.name);
393
394 if (fname != NULL) {
395 file.name = chr->start;
396 ret = nxt_file_open(task, &file, NXT_FILE_SEARCH, NXT_FILE_OPEN,
397 0);
398
399 } else {
400 file.error = NXT_EACCES;
401 ret = NXT_ERROR;
402 }
403
404 } else if (fname[0] == '/') {
405 file.name = (u_char *) "/";
406 ret = nxt_file_open(task, &file, NXT_FILE_SEARCH, NXT_FILE_OPEN, 0);
407
408 } else {
409 file.name = (u_char *) ".";
410 file.fd = AT_FDCWD;
411 ret = NXT_OK;
412 }
413
414 if (nxt_fast_path(ret == NXT_OK)) {
415 nxt_file_t af;
416
417 af = file;
418 nxt_memzero(&file, sizeof(nxt_file_t));
419 file.name = fname;
420
421 ret = nxt_file_openat2(task, &file, NXT_FILE_RDONLY,
422 NXT_FILE_OPEN, 0, af.fd, resolve);
423
424 if (af.fd != AT_FDCWD) {
425 nxt_file_close(task, &af);
426 }
427 }
428
429 } else {
430 ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);
431 }
432
433 #else
434 ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);
435 #endif
436
437 if (nxt_slow_path(ret != NXT_OK)) {
438
439 switch (file.error) {
440
441 /*
442 * For Unix domain sockets "errno" is set to:
443 * - ENXIO on Linux;
444 * - EOPNOTSUPP on *BSD, MacOSX, and Solaris.
445 */
446
447 case NXT_ENOENT:
448 case NXT_ENOTDIR:
449 case NXT_ENAMETOOLONG:
450 #if (NXT_LINUX)
451 case NXT_ENXIO:
452 #else
453 case NXT_EOPNOTSUPP:
454 #endif
455 level = NXT_LOG_ERR;
456 status = NXT_HTTP_NOT_FOUND;
457 break;
458
459 case NXT_EACCES:
460 #if (NXT_HAVE_OPENAT2)
461 case NXT_ELOOP:
462 case NXT_EXDEV:
463 #endif
464 level = NXT_LOG_ERR;
465 status = NXT_HTTP_FORBIDDEN;
466 break;
467
468 default:
469 level = NXT_LOG_ALERT;
470 status = NXT_HTTP_INTERNAL_SERVER_ERROR;
471 break;
472 }
473
474 if (status != NXT_HTTP_NOT_FOUND) {
475 #if (NXT_HAVE_OPENAT2)
476 nxt_str_t *chr = &ctx->chroot;
477
478 if (chr->length > 0) {
479 nxt_log(task, level, "opening \"%s\" at \"%V\" failed %E",
480 fname, chr, file.error);
481
482 } else {
483 nxt_log(task, level, "opening \"%s\" failed %E",
484 fname, file.error);
485 }
486
487 #else
488 nxt_log(task, level, "opening \"%s\" failed %E", fname, file.error);
489 #endif
490 }
491
492 if (level == NXT_LOG_ERR) {
493 nxt_http_static_next(task, r, ctx, status);
494 return;
495 }
496
497 goto fail;
498 }
499
500 f = nxt_mp_get(r->mem_pool, sizeof(nxt_file_t));
501 if (nxt_slow_path(f == NULL)) {
502 nxt_file_close(task, &file);
503 goto fail;
504 }
505
506 *f = file;
507
508 ret = nxt_file_info(f, &fi);
509 if (nxt_slow_path(ret != NXT_OK)) {
510 goto fail;
511 }
512
513 if (nxt_fast_path(nxt_is_file(&fi))) {
514 r->status = NXT_HTTP_OK;
515 r->resp.content_length_n = nxt_file_size(&fi);
516
517 field = nxt_list_zero_add(r->resp.fields);
518 if (nxt_slow_path(field == NULL)) {
519 goto fail;
520 }
521
522 nxt_http_field_name_set(field, "Last-Modified");
523
524 p = nxt_mp_nget(r->mem_pool, NXT_HTTP_DATE_LEN);
525 if (nxt_slow_path(p == NULL)) {
526 goto fail;
527 }
528
529 nxt_localtime(nxt_file_mtime(&fi), &tm);
530
531 field->value = p;
532 field->value_length = nxt_http_date(p, &tm) - p;
533
534 field = nxt_list_zero_add(r->resp.fields);
535 if (nxt_slow_path(field == NULL)) {
536 goto fail;
537 }
538
539 nxt_http_field_name_set(field, "ETag");
540
541 length = NXT_TIME_T_HEXLEN + NXT_OFF_T_HEXLEN + 3;
542
543 p = nxt_mp_nget(r->mem_pool, length);
544 if (nxt_slow_path(p == NULL)) {
545 goto fail;
546 }
547
548 field->value = p;
549 field->value_length = nxt_sprintf(p, p + length, "\"%xT-%xO\"",
550 nxt_file_mtime(&fi),
551 nxt_file_size(&fi))
552 - p;
553
554 if (exten.start == NULL) {
555 nxt_http_static_extract_extension(shr, &exten);
556 }
557
558 if (mtype == NULL) {
559 mtype = nxt_http_static_mtype_get(&rtcf->mtypes_hash, &exten);
560 }
561
562 if (mtype->length != 0) {
563 field = nxt_list_zero_add(r->resp.fields);
564 if (nxt_slow_path(field == NULL)) {
565 goto fail;
566 }
567
568 nxt_http_field_name_set(field, "Content-Type");
569
570 field->value = mtype->start;
571 field->value_length = mtype->length;
572 }
573
574 if (ctx->need_body && nxt_file_size(&fi) > 0) {
575 fb = nxt_mp_zget(r->mem_pool, NXT_BUF_FILE_SIZE);
576 if (nxt_slow_path(fb == NULL)) {
577 goto fail;
578 }
579
580 fb->file = f;
581 fb->file_end = nxt_file_size(&fi);
582
583 r->out = fb;
584
585 body_handler = &nxt_http_static_body_handler;
586
587 } else {
588 nxt_file_close(task, f);
589 body_handler = NULL;
590 }
591
592 } else {
593 /* Not a file. */
594 nxt_file_close(task, f);
595
596 if (nxt_slow_path(!nxt_is_dir(&fi)
597 || shr->start[shr->length - 1] == '/'))
598 {
599 nxt_log(task, NXT_LOG_ERR, "\"%FN\" is not a regular file",
600 f->name);
601
602 nxt_http_static_next(task, r, ctx, NXT_HTTP_NOT_FOUND);
603 return;
604 }
605
606 f = NULL;
607
608 r->status = NXT_HTTP_MOVED_PERMANENTLY;
609 r->resp.content_length_n = 0;
610
611 field = nxt_list_zero_add(r->resp.fields);
612 if (nxt_slow_path(field == NULL)) {
613 goto fail;
614 }
615
616 nxt_http_field_name_set(field, "Location");
617
618 encode = nxt_encode_uri(NULL, r->path->start, r->path->length);
619 length = r->path->length + encode * 2 + 1;
620
621 if (r->args->length > 0) {
622 length += 1 + r->args->length;
623 }
624
625 p = nxt_mp_nget(r->mem_pool, length);
626 if (nxt_slow_path(p == NULL)) {
627 goto fail;
628 }
629
630 field->value = p;
631 field->value_length = length;
632
633 if (encode > 0) {
634 p = (u_char *) nxt_encode_uri(p, r->path->start, r->path->length);
635
636 } else {
637 p = nxt_cpymem(p, r->path->start, r->path->length);
638 }
639
640 *p++ = '/';
641
642 if (r->args->length > 0) {
643 *p++ = '?';
644 nxt_memcpy(p, r->args->start, r->args->length);
645 }
646
647 body_handler = NULL;
648 }
649
650 nxt_http_request_header_send(task, r, body_handler, NULL);
651
652 r->state = &nxt_http_static_send_state;
653 return;
654
655 fail:
656
657 if (f != NULL) {
658 nxt_file_close(task, f);
659 }
660
661 nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
662 }
663
664
665 static void
nxt_http_static_send_error(nxt_task_t * task,void * obj,void * data)666 nxt_http_static_send_error(nxt_task_t *task, void *obj, void *data)
667 {
668 nxt_http_request_t *r;
669
670 r = obj;
671
672 nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
673 }
674
675
676 static void
nxt_http_static_next(nxt_task_t * task,nxt_http_request_t * r,nxt_http_static_ctx_t * ctx,nxt_http_status_t status)677 nxt_http_static_next(nxt_task_t *task, nxt_http_request_t *r,
678 nxt_http_static_ctx_t *ctx, nxt_http_status_t status)
679 {
680 nxt_http_action_t *action;
681 nxt_http_static_conf_t *conf;
682
683 action = ctx->action;
684 conf = action->u.conf;
685
686 ctx->share_idx++;
687
688 if (ctx->share_idx < conf->nshares) {
689 nxt_http_static_iterate(task, r, ctx);
690 return;
691 }
692
693 if (action->fallback != NULL) {
694 if (nxt_slow_path(r->log_route)) {
695 nxt_log(task, NXT_LOG_NOTICE, "\"fallback\" taken");
696 }
697
698 r->action = action->fallback;
699 nxt_http_request_action(task, r, action->fallback);
700 return;
701 }
702
703 nxt_http_request_error(task, r, status);
704 }
705
706
707 #if (NXT_HAVE_OPENAT2)
708
709 static u_char *
nxt_http_static_chroot_match(u_char * chr,u_char * shr)710 nxt_http_static_chroot_match(u_char *chr, u_char *shr)
711 {
712 if (*chr != *shr) {
713 return NULL;
714 }
715
716 chr++;
717 shr++;
718
719 for ( ;; ) {
720 if (*shr == '\0') {
721 return NULL;
722 }
723
724 if (*chr == *shr) {
725 chr++;
726 shr++;
727 continue;
728 }
729
730 if (*chr == '\0') {
731 break;
732 }
733
734 if (*chr == '/') {
735 if (chr[-1] == '/') {
736 chr++;
737 continue;
738 }
739
740 } else if (*shr == '/') {
741 if (shr[-1] == '/') {
742 shr++;
743 continue;
744 }
745 }
746
747 return NULL;
748 }
749
750 if (shr[-1] != '/' && *shr != '/') {
751 return NULL;
752 }
753
754 while (*shr == '/') {
755 shr++;
756 }
757
758 return (*shr != '\0') ? shr : NULL;
759 }
760
761 #endif
762
763
764 static void
nxt_http_static_extract_extension(nxt_str_t * path,nxt_str_t * exten)765 nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *exten)
766 {
767 u_char ch, *p, *end;
768
769 end = path->start + path->length;
770 p = end;
771
772 while (p > path->start) {
773 p--;
774 ch = *p;
775
776 switch (ch) {
777 case '/':
778 p++;
779 /* Fall through. */
780 case '.':
781 goto extension;
782 }
783 }
784
785 extension:
786
787 exten->length = end - p;
788 exten->start = p;
789 }
790
791
792 static void
nxt_http_static_body_handler(nxt_task_t * task,void * obj,void * data)793 nxt_http_static_body_handler(nxt_task_t *task, void *obj, void *data)
794 {
795 size_t alloc;
796 nxt_buf_t *fb, *b, **next, *out;
797 nxt_off_t rest;
798 nxt_int_t n;
799 nxt_work_queue_t *wq;
800 nxt_http_request_t *r;
801
802 r = obj;
803 fb = r->out;
804
805 rest = fb->file_end - fb->file_pos;
806 out = NULL;
807 next = &out;
808 n = 0;
809
810 do {
811 alloc = nxt_min(rest, NXT_HTTP_STATIC_BUF_SIZE);
812
813 b = nxt_buf_mem_alloc(r->mem_pool, alloc, 0);
814 if (nxt_slow_path(b == NULL)) {
815 goto fail;
816 }
817
818 b->completion_handler = nxt_http_static_buf_completion;
819 b->parent = r;
820
821 nxt_mp_retain(r->mem_pool);
822
823 *next = b;
824 next = &b->next;
825
826 rest -= alloc;
827
828 } while (rest > 0 && ++n < NXT_HTTP_STATIC_BUF_COUNT);
829
830 wq = &task->thread->engine->fast_work_queue;
831
832 nxt_sendbuf_drain(task, wq, out);
833 return;
834
835 fail:
836
837 while (out != NULL) {
838 b = out;
839 out = b->next;
840
841 nxt_mp_free(r->mem_pool, b);
842 nxt_mp_release(r->mem_pool);
843 }
844 }
845
846
847 static const nxt_http_request_state_t nxt_http_static_send_state
848 nxt_aligned(64) =
849 {
850 .error_handler = nxt_http_request_error_handler,
851 };
852
853
854 static void
nxt_http_static_buf_completion(nxt_task_t * task,void * obj,void * data)855 nxt_http_static_buf_completion(nxt_task_t *task, void *obj, void *data)
856 {
857 ssize_t n, size;
858 nxt_buf_t *b, *fb, *next;
859 nxt_off_t rest;
860 nxt_http_request_t *r;
861
862 b = obj;
863 r = data;
864
865 complete_buf:
866
867 fb = r->out;
868
869 if (nxt_slow_path(fb == NULL || r->error)) {
870 goto clean;
871 }
872
873 rest = fb->file_end - fb->file_pos;
874 size = nxt_buf_mem_size(&b->mem);
875
876 size = nxt_min(rest, (nxt_off_t) size);
877
878 n = nxt_file_read(fb->file, b->mem.start, size, fb->file_pos);
879
880 if (nxt_slow_path(n == NXT_ERROR)) {
881 nxt_http_request_error_handler(task, r, r->proto.any);
882 goto clean;
883 }
884
885 next = b->next;
886
887 if (n == rest) {
888 nxt_file_close(task, fb->file);
889 r->out = NULL;
890
891 b->next = nxt_http_buf_last(r);
892
893 } else {
894 fb->file_pos += n;
895 b->next = NULL;
896 }
897
898 b->mem.pos = b->mem.start;
899 b->mem.free = b->mem.pos + n;
900
901 nxt_http_request_send(task, r, b);
902
903 if (next != NULL) {
904 b = next;
905 goto complete_buf;
906 }
907
908 return;
909
910 clean:
911
912 do {
913 next = b->next;
914
915 nxt_mp_free(r->mem_pool, b);
916 nxt_mp_release(r->mem_pool);
917
918 b = next;
919 } while (b != NULL);
920
921 if (fb != NULL) {
922 nxt_file_close(task, fb->file);
923 r->out = NULL;
924 }
925 }
926
927
928 nxt_int_t
nxt_http_static_mtypes_init(nxt_mp_t * mp,nxt_lvlhsh_t * hash)929 nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash)
930 {
931 nxt_str_t *type, exten;
932 nxt_int_t ret;
933 nxt_uint_t i;
934
935 static const struct {
936 nxt_str_t type;
937 const char *exten;
938 } default_types[] = {
939
940 { nxt_string("text/html"), ".html" },
941 { nxt_string("text/html"), ".htm" },
942 { nxt_string("text/css"), ".css" },
943
944 { nxt_string("image/svg+xml"), ".svg" },
945 { nxt_string("image/webp"), ".webp" },
946 { nxt_string("image/png"), ".png" },
947 { nxt_string("image/apng"), ".apng" },
948 { nxt_string("image/jpeg"), ".jpeg" },
949 { nxt_string("image/jpeg"), ".jpg" },
950 { nxt_string("image/gif"), ".gif" },
951 { nxt_string("image/x-icon"), ".ico" },
952
953 { nxt_string("image/avif"), ".avif" },
954 { nxt_string("image/avif-sequence"), ".avifs" },
955
956 { nxt_string("font/woff"), ".woff" },
957 { nxt_string("font/woff2"), ".woff2" },
958 { nxt_string("font/otf"), ".otf" },
959 { nxt_string("font/ttf"), ".ttf" },
960
961 { nxt_string("text/plain"), ".txt" },
962 { nxt_string("text/markdown"), ".md" },
963 { nxt_string("text/x-rst"), ".rst" },
964
965 { nxt_string("application/javascript"), ".js" },
966 { nxt_string("application/json"), ".json" },
967 { nxt_string("application/xml"), ".xml" },
968 { nxt_string("application/rss+xml"), ".rss" },
969 { nxt_string("application/atom+xml"), ".atom" },
970 { nxt_string("application/pdf"), ".pdf" },
971
972 { nxt_string("application/zip"), ".zip" },
973
974 { nxt_string("audio/mpeg"), ".mp3" },
975 { nxt_string("audio/ogg"), ".ogg" },
976 { nxt_string("audio/midi"), ".midi" },
977 { nxt_string("audio/midi"), ".mid" },
978 { nxt_string("audio/flac"), ".flac" },
979 { nxt_string("audio/aac"), ".aac" },
980 { nxt_string("audio/wav"), ".wav" },
981
982 { nxt_string("video/mpeg"), ".mpeg" },
983 { nxt_string("video/mpeg"), ".mpg" },
984 { nxt_string("video/mp4"), ".mp4" },
985 { nxt_string("video/webm"), ".webm" },
986 { nxt_string("video/x-msvideo"), ".avi" },
987
988 { nxt_string("application/octet-stream"), ".exe" },
989 { nxt_string("application/octet-stream"), ".bin" },
990 { nxt_string("application/octet-stream"), ".dll" },
991 { nxt_string("application/octet-stream"), ".iso" },
992 { nxt_string("application/octet-stream"), ".img" },
993 { nxt_string("application/octet-stream"), ".msi" },
994
995 { nxt_string("application/octet-stream"), ".deb" },
996 { nxt_string("application/octet-stream"), ".rpm" },
997
998 { nxt_string("application/x-httpd-php"), ".php" },
999 };
1000
1001 for (i = 0; i < nxt_nitems(default_types); i++) {
1002 type = (nxt_str_t *) &default_types[i].type;
1003
1004 exten.start = (u_char *) default_types[i].exten;
1005 exten.length = nxt_strlen(exten.start);
1006
1007 ret = nxt_http_static_mtypes_hash_add(mp, hash, &exten, type);
1008 if (nxt_slow_path(ret != NXT_OK)) {
1009 return NXT_ERROR;
1010 }
1011 }
1012
1013 return NXT_OK;
1014 }
1015
1016
1017 static const nxt_lvlhsh_proto_t nxt_http_static_mtypes_hash_proto
1018 nxt_aligned(64) =
1019 {
1020 NXT_LVLHSH_DEFAULT,
1021 nxt_http_static_mtypes_hash_test,
1022 nxt_http_static_mtypes_hash_alloc,
1023 nxt_http_static_mtypes_hash_free,
1024 };
1025
1026
1027 typedef struct {
1028 nxt_str_t exten;
1029 nxt_str_t *type;
1030 } nxt_http_static_mtype_t;
1031
1032
1033 nxt_int_t
nxt_http_static_mtypes_hash_add(nxt_mp_t * mp,nxt_lvlhsh_t * hash,const nxt_str_t * exten,nxt_str_t * type)1034 nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash,
1035 const nxt_str_t *exten, nxt_str_t *type)
1036 {
1037 nxt_lvlhsh_query_t lhq;
1038 nxt_http_static_mtype_t *mtype;
1039
1040 mtype = nxt_mp_get(mp, sizeof(nxt_http_static_mtype_t));
1041 if (nxt_slow_path(mtype == NULL)) {
1042 return NXT_ERROR;
1043 }
1044
1045 mtype->exten = *exten;
1046 mtype->type = type;
1047
1048 lhq.key = *exten;
1049 lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length);
1050 lhq.replace = 1;
1051 lhq.value = mtype;
1052 lhq.proto = &nxt_http_static_mtypes_hash_proto;
1053 lhq.pool = mp;
1054
1055 return nxt_lvlhsh_insert(hash, &lhq);
1056 }
1057
1058
1059 nxt_str_t *
nxt_http_static_mtype_get(nxt_lvlhsh_t * hash,const nxt_str_t * exten)1060 nxt_http_static_mtype_get(nxt_lvlhsh_t *hash, const nxt_str_t *exten)
1061 {
1062 nxt_lvlhsh_query_t lhq;
1063 nxt_http_static_mtype_t *mtype;
1064
1065 static nxt_str_t empty = nxt_string("");
1066
1067 lhq.key = *exten;
1068 lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length);
1069 lhq.proto = &nxt_http_static_mtypes_hash_proto;
1070
1071 if (nxt_lvlhsh_find(hash, &lhq) == NXT_OK) {
1072 mtype = lhq.value;
1073 return mtype->type;
1074 }
1075
1076 return ∅
1077 }
1078
1079
1080 static nxt_int_t
nxt_http_static_mtypes_hash_test(nxt_lvlhsh_query_t * lhq,void * data)1081 nxt_http_static_mtypes_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
1082 {
1083 nxt_http_static_mtype_t *mtype;
1084
1085 mtype = data;
1086
1087 return nxt_strcasestr_eq(&lhq->key, &mtype->exten) ? NXT_OK : NXT_DECLINED;
1088 }
1089
1090
1091 static void *
nxt_http_static_mtypes_hash_alloc(void * data,size_t size)1092 nxt_http_static_mtypes_hash_alloc(void *data, size_t size)
1093 {
1094 return nxt_mp_align(data, size, size);
1095 }
1096
1097
1098 static void
nxt_http_static_mtypes_hash_free(void * data,void * p)1099 nxt_http_static_mtypes_hash_free(void *data, void *p)
1100 {
1101 nxt_mp_free(data, p);
1102 }
1103