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