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