xref: /unit/src/nxt_http_static.c (revision 1859)
11183Svbart@nginx.com 
21183Svbart@nginx.com /*
31183Svbart@nginx.com  * Copyright (C) NGINX, Inc.
41183Svbart@nginx.com  */
51183Svbart@nginx.com 
61183Svbart@nginx.com #include <nxt_router.h>
71183Svbart@nginx.com #include <nxt_http.h>
81183Svbart@nginx.com 
91183Svbart@nginx.com 
101183Svbart@nginx.com #define NXT_HTTP_STATIC_BUF_COUNT  2
111183Svbart@nginx.com #define NXT_HTTP_STATIC_BUF_SIZE   (128 * 1024)
121183Svbart@nginx.com 
131183Svbart@nginx.com 
141183Svbart@nginx.com static void nxt_http_static_extract_extension(nxt_str_t *path,
151183Svbart@nginx.com     nxt_str_t *extension);
161183Svbart@nginx.com static void nxt_http_static_body_handler(nxt_task_t *task, void *obj,
171183Svbart@nginx.com     void *data);
181183Svbart@nginx.com static void nxt_http_static_buf_completion(nxt_task_t *task, void *obj,
191183Svbart@nginx.com     void *data);
201183Svbart@nginx.com 
211183Svbart@nginx.com static nxt_int_t nxt_http_static_mtypes_hash_test(nxt_lvlhsh_query_t *lhq,
221183Svbart@nginx.com     void *data);
231183Svbart@nginx.com static void *nxt_http_static_mtypes_hash_alloc(void *data, size_t size);
241183Svbart@nginx.com static void nxt_http_static_mtypes_hash_free(void *data, void *p);
251183Svbart@nginx.com 
261183Svbart@nginx.com 
271183Svbart@nginx.com static const nxt_http_request_state_t  nxt_http_static_send_state;
281183Svbart@nginx.com 
291183Svbart@nginx.com 
301264Sigor@sysoev.ru nxt_http_action_t *
311183Svbart@nginx.com nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
321264Sigor@sysoev.ru     nxt_http_action_t *action)
331183Svbart@nginx.com {
341855Sz.hong@f5.com     size_t              length, encode;
351855Sz.hong@f5.com     u_char              *p, *fname;
361183Svbart@nginx.com     struct tm           tm;
371183Svbart@nginx.com     nxt_buf_t           *fb;
381183Svbart@nginx.com     nxt_int_t           ret;
391855Sz.hong@f5.com     nxt_str_t           index, extension, *mtype, *chroot;
401183Svbart@nginx.com     nxt_uint_t          level;
411183Svbart@nginx.com     nxt_bool_t          need_body;
421856Sz.hong@f5.com     nxt_file_t          *f, file;
431183Svbart@nginx.com     nxt_file_info_t     fi;
441183Svbart@nginx.com     nxt_http_field_t    *field;
451183Svbart@nginx.com     nxt_http_status_t   status;
461183Svbart@nginx.com     nxt_router_conf_t   *rtcf;
471183Svbart@nginx.com     nxt_work_handler_t  body_handler;
481183Svbart@nginx.com 
491183Svbart@nginx.com     if (nxt_slow_path(!nxt_str_eq(r->method, "GET", 3))) {
501183Svbart@nginx.com 
511183Svbart@nginx.com         if (!nxt_str_eq(r->method, "HEAD", 4)) {
521854Sz.hong@f5.com             if (action->u.share.fallback != NULL) {
531854Sz.hong@f5.com                 return action->u.share.fallback;
541378Svbart@nginx.com             }
551378Svbart@nginx.com 
561183Svbart@nginx.com             nxt_http_request_error(task, r, NXT_HTTP_METHOD_NOT_ALLOWED);
571183Svbart@nginx.com             return NULL;
581183Svbart@nginx.com         }
591183Svbart@nginx.com 
601183Svbart@nginx.com         need_body = 0;
611183Svbart@nginx.com 
621183Svbart@nginx.com     } else {
631183Svbart@nginx.com         need_body = 1;
641183Svbart@nginx.com     }
651183Svbart@nginx.com 
661183Svbart@nginx.com     if (r->path->start[r->path->length - 1] == '/') {
671183Svbart@nginx.com         /* TODO: dynamic index setting. */
681183Svbart@nginx.com         nxt_str_set(&index, "index.html");
691183Svbart@nginx.com         nxt_str_set(&extension, ".html");
701183Svbart@nginx.com 
711183Svbart@nginx.com     } else {
721483Svbart@nginx.com         nxt_str_set(&index, "");
731183Svbart@nginx.com         nxt_str_null(&extension);
741183Svbart@nginx.com     }
751183Svbart@nginx.com 
761855Sz.hong@f5.com     f = NULL;
771183Svbart@nginx.com 
78*1859So.canty@f5.com     rtcf = r->conf->socket_conf->router_conf;
79*1859So.canty@f5.com 
80*1859So.canty@f5.com     mtype = NULL;
81*1859So.canty@f5.com 
82*1859So.canty@f5.com     if (action->u.share.types != NULL && extension.start == NULL) {
83*1859So.canty@f5.com         nxt_http_static_extract_extension(r->path, &extension);
84*1859So.canty@f5.com         mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash,
85*1859So.canty@f5.com                                                  &extension);
86*1859So.canty@f5.com 
87*1859So.canty@f5.com         if (mtype != NULL) {
88*1859So.canty@f5.com             ret = nxt_http_route_test_rule(r, action->u.share.types,
89*1859So.canty@f5.com                                            mtype->start, mtype->length);
90*1859So.canty@f5.com             if (ret == 1) {
91*1859So.canty@f5.com                 goto mime_ok;
92*1859So.canty@f5.com             }
93*1859So.canty@f5.com 
94*1859So.canty@f5.com             if (nxt_slow_path(ret == NXT_ERROR)) {
95*1859So.canty@f5.com                 goto fail;
96*1859So.canty@f5.com             }
97*1859So.canty@f5.com         }
98*1859So.canty@f5.com 
99*1859So.canty@f5.com         if (action->u.share.fallback != NULL) {
100*1859So.canty@f5.com             return action->u.share.fallback;
101*1859So.canty@f5.com         }
102*1859So.canty@f5.com 
103*1859So.canty@f5.com         nxt_http_request_error(task, r, NXT_HTTP_FORBIDDEN);
104*1859So.canty@f5.com         return NULL;
105*1859So.canty@f5.com     }
106*1859So.canty@f5.com 
107*1859So.canty@f5.com mime_ok:
108*1859So.canty@f5.com 
1091855Sz.hong@f5.com     length = action->name.length + r->path->length + index.length;
1101855Sz.hong@f5.com 
1111855Sz.hong@f5.com     fname = nxt_mp_nget(r->mem_pool, length + 1);
1121855Sz.hong@f5.com     if (nxt_slow_path(fname == NULL)) {
1131183Svbart@nginx.com         goto fail;
1141183Svbart@nginx.com     }
1151183Svbart@nginx.com 
1161855Sz.hong@f5.com     p = fname;
1171264Sigor@sysoev.ru     p = nxt_cpymem(p, action->name.start, action->name.length);
1181183Svbart@nginx.com     p = nxt_cpymem(p, r->path->start, r->path->length);
1191183Svbart@nginx.com     p = nxt_cpymem(p, index.start, index.length);
1201183Svbart@nginx.com     *p = '\0';
1211183Svbart@nginx.com 
1221855Sz.hong@f5.com     nxt_memzero(&file, sizeof(nxt_file_t));
1231855Sz.hong@f5.com 
1241855Sz.hong@f5.com     file.name = fname;
1251855Sz.hong@f5.com 
1261855Sz.hong@f5.com     chroot = &action->u.share.chroot;
1271855Sz.hong@f5.com 
1281855Sz.hong@f5.com #if (NXT_HAVE_OPENAT2)
1291855Sz.hong@f5.com 
1301855Sz.hong@f5.com     if (action->u.share.resolve != 0) {
1311855Sz.hong@f5.com 
1321855Sz.hong@f5.com         if (chroot->length > 0) {
1331855Sz.hong@f5.com             file.name = chroot->start;
1341855Sz.hong@f5.com 
1351855Sz.hong@f5.com             if (length > chroot->length
1361855Sz.hong@f5.com                 && nxt_memcmp(fname, chroot->start, chroot->length) == 0)
1371855Sz.hong@f5.com             {
1381855Sz.hong@f5.com                 fname += chroot->length;
1391855Sz.hong@f5.com                 ret = nxt_file_open(task, &file, NXT_FILE_SEARCH, NXT_FILE_OPEN,
1401855Sz.hong@f5.com                                     0);
1411855Sz.hong@f5.com 
1421855Sz.hong@f5.com             } else {
1431855Sz.hong@f5.com                 file.error = NXT_EACCES;
1441855Sz.hong@f5.com                 ret = NXT_ERROR;
1451855Sz.hong@f5.com             }
1461855Sz.hong@f5.com 
1471855Sz.hong@f5.com         } else if (fname[0] == '/') {
1481855Sz.hong@f5.com             file.name = (u_char *) "/";
1491855Sz.hong@f5.com             ret = nxt_file_open(task, &file, NXT_FILE_SEARCH, NXT_FILE_OPEN, 0);
1501855Sz.hong@f5.com 
1511855Sz.hong@f5.com         } else {
1521855Sz.hong@f5.com             file.name = (u_char *) ".";
1531855Sz.hong@f5.com             file.fd = AT_FDCWD;
1541855Sz.hong@f5.com             ret = NXT_OK;
1551855Sz.hong@f5.com         }
1561855Sz.hong@f5.com 
1571855Sz.hong@f5.com         if (nxt_fast_path(ret == NXT_OK)) {
1581856Sz.hong@f5.com             nxt_file_t  af;
1591856Sz.hong@f5.com 
1601855Sz.hong@f5.com             af = file;
1611855Sz.hong@f5.com             nxt_memzero(&file, sizeof(nxt_file_t));
1621855Sz.hong@f5.com             file.name = fname;
1631855Sz.hong@f5.com 
1641855Sz.hong@f5.com             ret = nxt_file_openat2(task, &file, NXT_FILE_RDONLY,
1651855Sz.hong@f5.com                                    NXT_FILE_OPEN, 0, af.fd,
1661855Sz.hong@f5.com                                    action->u.share.resolve);
1671855Sz.hong@f5.com 
1681855Sz.hong@f5.com             if (af.fd != AT_FDCWD) {
1691855Sz.hong@f5.com                 nxt_file_close(task, &af);
1701855Sz.hong@f5.com             }
1711855Sz.hong@f5.com         }
1721855Sz.hong@f5.com 
1731855Sz.hong@f5.com     } else {
1741855Sz.hong@f5.com         ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);
1751855Sz.hong@f5.com     }
1761855Sz.hong@f5.com 
1771855Sz.hong@f5.com #else
1781855Sz.hong@f5.com 
1791855Sz.hong@f5.com     ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);
1801855Sz.hong@f5.com 
1811855Sz.hong@f5.com #endif
1821183Svbart@nginx.com 
1831183Svbart@nginx.com     if (nxt_slow_path(ret != NXT_OK)) {
1841855Sz.hong@f5.com 
1851855Sz.hong@f5.com         switch (file.error) {
1861183Svbart@nginx.com 
1871200Svbart@nginx.com         /*
1881200Svbart@nginx.com          * For Unix domain sockets "errno" is set to:
1891200Svbart@nginx.com          *  - ENXIO on Linux;
1901200Svbart@nginx.com          *  - EOPNOTSUPP on *BSD, MacOSX, and Solaris.
1911200Svbart@nginx.com          */
1921200Svbart@nginx.com 
1931183Svbart@nginx.com         case NXT_ENOENT:
1941183Svbart@nginx.com         case NXT_ENOTDIR:
1951183Svbart@nginx.com         case NXT_ENAMETOOLONG:
1961200Svbart@nginx.com #if (NXT_LINUX)
1971200Svbart@nginx.com         case NXT_ENXIO:
1981200Svbart@nginx.com #else
1991200Svbart@nginx.com         case NXT_EOPNOTSUPP:
2001200Svbart@nginx.com #endif
2011183Svbart@nginx.com             level = NXT_LOG_ERR;
2021183Svbart@nginx.com             status = NXT_HTTP_NOT_FOUND;
2031183Svbart@nginx.com             break;
2041183Svbart@nginx.com 
2051183Svbart@nginx.com         case NXT_EACCES:
2061855Sz.hong@f5.com #if (NXT_HAVE_OPENAT2)
2071855Sz.hong@f5.com         case NXT_ELOOP:
2081855Sz.hong@f5.com         case NXT_EXDEV:
2091855Sz.hong@f5.com #endif
2101183Svbart@nginx.com             level = NXT_LOG_ERR;
2111183Svbart@nginx.com             status = NXT_HTTP_FORBIDDEN;
2121183Svbart@nginx.com             break;
2131183Svbart@nginx.com 
2141183Svbart@nginx.com         default:
2151183Svbart@nginx.com             level = NXT_LOG_ALERT;
2161183Svbart@nginx.com             status = NXT_HTTP_INTERNAL_SERVER_ERROR;
2171183Svbart@nginx.com             break;
2181183Svbart@nginx.com         }
2191183Svbart@nginx.com 
2201854Sz.hong@f5.com         if (level == NXT_LOG_ERR && action->u.share.fallback != NULL) {
2211854Sz.hong@f5.com             return action->u.share.fallback;
2221378Svbart@nginx.com         }
2231378Svbart@nginx.com 
2241183Svbart@nginx.com         if (status != NXT_HTTP_NOT_FOUND) {
2251855Sz.hong@f5.com             if (chroot->length > 0) {
2261857Sz.hong@f5.com                 nxt_log(task, level, "opening \"%s\" at \"%V\" failed %E",
2271855Sz.hong@f5.com                         fname, chroot, file.error);
2281855Sz.hong@f5.com 
2291855Sz.hong@f5.com             } else {
2301857Sz.hong@f5.com                 nxt_log(task, level, "opening \"%s\" failed %E",
2311855Sz.hong@f5.com                         fname, file.error);
2321855Sz.hong@f5.com             }
2331183Svbart@nginx.com         }
2341183Svbart@nginx.com 
2351183Svbart@nginx.com         nxt_http_request_error(task, r, status);
2361183Svbart@nginx.com         return NULL;
2371183Svbart@nginx.com     }
2381183Svbart@nginx.com 
2391855Sz.hong@f5.com     f = nxt_mp_get(r->mem_pool, sizeof(nxt_file_t));
2401855Sz.hong@f5.com     if (nxt_slow_path(f == NULL)) {
2411855Sz.hong@f5.com         goto fail;
2421855Sz.hong@f5.com     }
2431855Sz.hong@f5.com 
2441855Sz.hong@f5.com     *f = file;
2451855Sz.hong@f5.com 
2461183Svbart@nginx.com     ret = nxt_file_info(f, &fi);
2471183Svbart@nginx.com     if (nxt_slow_path(ret != NXT_OK)) {
2481183Svbart@nginx.com         goto fail;
2491183Svbart@nginx.com     }
2501183Svbart@nginx.com 
2511183Svbart@nginx.com     if (nxt_fast_path(nxt_is_file(&fi))) {
2521183Svbart@nginx.com         r->status = NXT_HTTP_OK;
2531183Svbart@nginx.com         r->resp.content_length_n = nxt_file_size(&fi);
2541183Svbart@nginx.com 
2551183Svbart@nginx.com         field = nxt_list_zero_add(r->resp.fields);
2561183Svbart@nginx.com         if (nxt_slow_path(field == NULL)) {
2571183Svbart@nginx.com             goto fail;
2581183Svbart@nginx.com         }
2591183Svbart@nginx.com 
2601183Svbart@nginx.com         nxt_http_field_name_set(field, "Last-Modified");
2611183Svbart@nginx.com 
2621183Svbart@nginx.com         p = nxt_mp_nget(r->mem_pool, NXT_HTTP_DATE_LEN);
2631183Svbart@nginx.com         if (nxt_slow_path(p == NULL)) {
2641183Svbart@nginx.com             goto fail;
2651183Svbart@nginx.com         }
2661183Svbart@nginx.com 
2671183Svbart@nginx.com         nxt_localtime(nxt_file_mtime(&fi), &tm);
2681183Svbart@nginx.com 
2691183Svbart@nginx.com         field->value = p;
2701183Svbart@nginx.com         field->value_length = nxt_http_date(p, &tm) - p;
2711183Svbart@nginx.com 
2721183Svbart@nginx.com         field = nxt_list_zero_add(r->resp.fields);
2731183Svbart@nginx.com         if (nxt_slow_path(field == NULL)) {
2741183Svbart@nginx.com             goto fail;
2751183Svbart@nginx.com         }
2761183Svbart@nginx.com 
2771183Svbart@nginx.com         nxt_http_field_name_set(field, "ETag");
2781183Svbart@nginx.com 
2791855Sz.hong@f5.com         length = NXT_TIME_T_HEXLEN + NXT_OFF_T_HEXLEN + 3;
2801183Svbart@nginx.com 
2811855Sz.hong@f5.com         p = nxt_mp_nget(r->mem_pool, length);
2821183Svbart@nginx.com         if (nxt_slow_path(p == NULL)) {
2831183Svbart@nginx.com             goto fail;
2841183Svbart@nginx.com         }
2851183Svbart@nginx.com 
2861183Svbart@nginx.com         field->value = p;
2871855Sz.hong@f5.com         field->value_length = nxt_sprintf(p, p + length, "\"%xT-%xO\"",
2881183Svbart@nginx.com                                           nxt_file_mtime(&fi),
2891183Svbart@nginx.com                                           nxt_file_size(&fi))
2901183Svbart@nginx.com                               - p;
2911183Svbart@nginx.com 
2921183Svbart@nginx.com         if (extension.start == NULL) {
2931183Svbart@nginx.com             nxt_http_static_extract_extension(r->path, &extension);
2941183Svbart@nginx.com         }
2951183Svbart@nginx.com 
296*1859So.canty@f5.com         if (mtype == NULL) {
297*1859So.canty@f5.com             mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash,
298*1859So.canty@f5.com                                                      &extension);
299*1859So.canty@f5.com         }
3001183Svbart@nginx.com 
3011183Svbart@nginx.com         if (mtype != NULL) {
3021183Svbart@nginx.com             field = nxt_list_zero_add(r->resp.fields);
3031183Svbart@nginx.com             if (nxt_slow_path(field == NULL)) {
3041183Svbart@nginx.com                 goto fail;
3051183Svbart@nginx.com             }
3061183Svbart@nginx.com 
3071183Svbart@nginx.com             nxt_http_field_name_set(field, "Content-Type");
3081183Svbart@nginx.com 
3091183Svbart@nginx.com             field->value = mtype->start;
3101183Svbart@nginx.com             field->value_length = mtype->length;
3111183Svbart@nginx.com         }
3121183Svbart@nginx.com 
3131183Svbart@nginx.com         if (need_body && nxt_file_size(&fi) > 0) {
3141183Svbart@nginx.com             fb = nxt_mp_zget(r->mem_pool, NXT_BUF_FILE_SIZE);
3151183Svbart@nginx.com             if (nxt_slow_path(fb == NULL)) {
3161183Svbart@nginx.com                 goto fail;
3171183Svbart@nginx.com             }
3181183Svbart@nginx.com 
3191183Svbart@nginx.com             fb->file = f;
3201183Svbart@nginx.com             fb->file_end = nxt_file_size(&fi);
3211183Svbart@nginx.com 
3221183Svbart@nginx.com             r->out = fb;
3231183Svbart@nginx.com 
3241183Svbart@nginx.com             body_handler = &nxt_http_static_body_handler;
3251183Svbart@nginx.com 
3261183Svbart@nginx.com         } else {
3271183Svbart@nginx.com             nxt_file_close(task, f);
3281183Svbart@nginx.com             body_handler = NULL;
3291183Svbart@nginx.com         }
3301183Svbart@nginx.com 
3311183Svbart@nginx.com     } else {
3321183Svbart@nginx.com         /* Not a file. */
3331183Svbart@nginx.com 
3341183Svbart@nginx.com         nxt_file_close(task, f);
3351183Svbart@nginx.com 
3361183Svbart@nginx.com         if (nxt_slow_path(!nxt_is_dir(&fi))) {
3371854Sz.hong@f5.com             if (action->u.share.fallback != NULL) {
3381854Sz.hong@f5.com                 return action->u.share.fallback;
3391378Svbart@nginx.com             }
3401378Svbart@nginx.com 
3411183Svbart@nginx.com             nxt_log(task, NXT_LOG_ERR, "\"%FN\" is not a regular file",
3421183Svbart@nginx.com                     f->name);
3431378Svbart@nginx.com 
3441183Svbart@nginx.com             nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND);
3451183Svbart@nginx.com             return NULL;
3461183Svbart@nginx.com         }
3471183Svbart@nginx.com 
3481198Svbart@nginx.com         f = NULL;
3491198Svbart@nginx.com 
3501183Svbart@nginx.com         r->status = NXT_HTTP_MOVED_PERMANENTLY;
3511183Svbart@nginx.com         r->resp.content_length_n = 0;
3521183Svbart@nginx.com 
3531183Svbart@nginx.com         field = nxt_list_zero_add(r->resp.fields);
3541183Svbart@nginx.com         if (nxt_slow_path(field == NULL)) {
3551183Svbart@nginx.com             goto fail;
3561183Svbart@nginx.com         }
3571183Svbart@nginx.com 
3581183Svbart@nginx.com         nxt_http_field_name_set(field, "Location");
3591183Svbart@nginx.com 
3601183Svbart@nginx.com         encode = nxt_encode_uri(NULL, r->path->start, r->path->length);
3611855Sz.hong@f5.com         length = r->path->length + encode * 2 + 1;
3621183Svbart@nginx.com 
3631183Svbart@nginx.com         if (r->args->length > 0) {
3641855Sz.hong@f5.com             length += 1 + r->args->length;
3651183Svbart@nginx.com         }
3661183Svbart@nginx.com 
3671855Sz.hong@f5.com         p = nxt_mp_nget(r->mem_pool, length);
3681183Svbart@nginx.com         if (nxt_slow_path(p == NULL)) {
3691183Svbart@nginx.com             goto fail;
3701183Svbart@nginx.com         }
3711183Svbart@nginx.com 
3721183Svbart@nginx.com         field->value = p;
3731855Sz.hong@f5.com         field->value_length = length;
3741183Svbart@nginx.com 
3751183Svbart@nginx.com         if (encode > 0) {
3761183Svbart@nginx.com             p = (u_char *) nxt_encode_uri(p, r->path->start, r->path->length);
3771183Svbart@nginx.com 
3781183Svbart@nginx.com         } else {
3791183Svbart@nginx.com             p = nxt_cpymem(p, r->path->start, r->path->length);
3801183Svbart@nginx.com         }
3811183Svbart@nginx.com 
3821183Svbart@nginx.com         *p++ = '/';
3831183Svbart@nginx.com 
3841183Svbart@nginx.com         if (r->args->length > 0) {
3851183Svbart@nginx.com             *p++ = '?';
3861183Svbart@nginx.com             nxt_memcpy(p, r->args->start, r->args->length);
3871183Svbart@nginx.com         }
3881183Svbart@nginx.com 
3891183Svbart@nginx.com         body_handler = NULL;
3901183Svbart@nginx.com     }
3911183Svbart@nginx.com 
3921270Sigor@sysoev.ru     nxt_http_request_header_send(task, r, body_handler, NULL);
3931183Svbart@nginx.com 
3941183Svbart@nginx.com     r->state = &nxt_http_static_send_state;
3951183Svbart@nginx.com     return NULL;
3961183Svbart@nginx.com 
3971183Svbart@nginx.com fail:
3981183Svbart@nginx.com 
3991183Svbart@nginx.com     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
4001183Svbart@nginx.com 
4011855Sz.hong@f5.com     if (f != NULL) {
4021183Svbart@nginx.com         nxt_file_close(task, f);
4031183Svbart@nginx.com     }
4041183Svbart@nginx.com 
4051183Svbart@nginx.com     return NULL;
4061183Svbart@nginx.com }
4071183Svbart@nginx.com 
4081183Svbart@nginx.com 
4091183Svbart@nginx.com static void
4101183Svbart@nginx.com nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *extension)
4111183Svbart@nginx.com {
4121183Svbart@nginx.com     u_char  ch, *p, *end;
4131183Svbart@nginx.com 
4141183Svbart@nginx.com     end = path->start + path->length;
4151183Svbart@nginx.com     p = end;
4161183Svbart@nginx.com 
4171183Svbart@nginx.com     for ( ;; ) {
4181183Svbart@nginx.com         /* There's always '/' in the beginning of the request path. */
4191183Svbart@nginx.com 
4201183Svbart@nginx.com         p--;
4211183Svbart@nginx.com         ch = *p;
4221183Svbart@nginx.com 
4231183Svbart@nginx.com         switch (ch) {
4241183Svbart@nginx.com         case '/':
4251183Svbart@nginx.com             p++;
4261183Svbart@nginx.com             /* Fall through. */
4271183Svbart@nginx.com         case '.':
4281183Svbart@nginx.com             extension->length = end - p;
4291183Svbart@nginx.com             extension->start = p;
4301183Svbart@nginx.com             return;
4311183Svbart@nginx.com         }
4321183Svbart@nginx.com     }
4331183Svbart@nginx.com }
4341183Svbart@nginx.com 
4351183Svbart@nginx.com 
4361183Svbart@nginx.com static void
4371183Svbart@nginx.com nxt_http_static_body_handler(nxt_task_t *task, void *obj, void *data)
4381183Svbart@nginx.com {
4391183Svbart@nginx.com     size_t              alloc;
4401183Svbart@nginx.com     nxt_buf_t           *fb, *b, **next, *out;
4411183Svbart@nginx.com     nxt_off_t           rest;
4421183Svbart@nginx.com     nxt_int_t           n;
4431183Svbart@nginx.com     nxt_work_queue_t    *wq;
4441183Svbart@nginx.com     nxt_http_request_t  *r;
4451183Svbart@nginx.com 
4461183Svbart@nginx.com     r = obj;
4471183Svbart@nginx.com     fb = r->out;
4481183Svbart@nginx.com 
4491183Svbart@nginx.com     rest = fb->file_end - fb->file_pos;
4501183Svbart@nginx.com     out = NULL;
4511183Svbart@nginx.com     next = &out;
4521183Svbart@nginx.com     n = 0;
4531183Svbart@nginx.com 
4541183Svbart@nginx.com     do {
4551183Svbart@nginx.com         alloc = nxt_min(rest, NXT_HTTP_STATIC_BUF_SIZE);
4561183Svbart@nginx.com 
4571183Svbart@nginx.com         b = nxt_buf_mem_alloc(r->mem_pool, alloc, 0);
4581183Svbart@nginx.com         if (nxt_slow_path(b == NULL)) {
4591183Svbart@nginx.com             goto fail;
4601183Svbart@nginx.com         }
4611183Svbart@nginx.com 
4621183Svbart@nginx.com         b->completion_handler = nxt_http_static_buf_completion;
4631183Svbart@nginx.com         b->parent = r;
4641183Svbart@nginx.com 
4651183Svbart@nginx.com         nxt_mp_retain(r->mem_pool);
4661183Svbart@nginx.com 
4671183Svbart@nginx.com         *next = b;
4681183Svbart@nginx.com         next = &b->next;
4691183Svbart@nginx.com 
4701183Svbart@nginx.com         rest -= alloc;
4711183Svbart@nginx.com 
4721183Svbart@nginx.com     } while (rest > 0 && ++n < NXT_HTTP_STATIC_BUF_COUNT);
4731183Svbart@nginx.com 
4741183Svbart@nginx.com     wq = &task->thread->engine->fast_work_queue;
4751183Svbart@nginx.com 
4761183Svbart@nginx.com     nxt_sendbuf_drain(task, wq, out);
4771183Svbart@nginx.com     return;
4781183Svbart@nginx.com 
4791183Svbart@nginx.com fail:
4801183Svbart@nginx.com 
4811183Svbart@nginx.com     while (out != NULL) {
4821183Svbart@nginx.com         b = out;
4831183Svbart@nginx.com         out = b->next;
4841183Svbart@nginx.com 
4851183Svbart@nginx.com         nxt_mp_free(r->mem_pool, b);
4861183Svbart@nginx.com         nxt_mp_release(r->mem_pool);
4871183Svbart@nginx.com     }
4881183Svbart@nginx.com }
4891183Svbart@nginx.com 
4901183Svbart@nginx.com 
4911183Svbart@nginx.com static const nxt_http_request_state_t  nxt_http_static_send_state
4921183Svbart@nginx.com     nxt_aligned(64) =
4931183Svbart@nginx.com {
4941183Svbart@nginx.com     .error_handler = nxt_http_request_error_handler,
4951183Svbart@nginx.com };
4961183Svbart@nginx.com 
4971183Svbart@nginx.com 
4981183Svbart@nginx.com static void
4991183Svbart@nginx.com nxt_http_static_buf_completion(nxt_task_t *task, void *obj, void *data)
5001183Svbart@nginx.com {
5011183Svbart@nginx.com     ssize_t             n, size;
5021760Smax.romanov@nginx.com     nxt_buf_t           *b, *fb, *next;
5031183Svbart@nginx.com     nxt_off_t           rest;
5041183Svbart@nginx.com     nxt_http_request_t  *r;
5051183Svbart@nginx.com 
5061183Svbart@nginx.com     b = obj;
5071183Svbart@nginx.com     r = data;
5081760Smax.romanov@nginx.com 
5091760Smax.romanov@nginx.com complete_buf:
5101760Smax.romanov@nginx.com 
5111183Svbart@nginx.com     fb = r->out;
5121183Svbart@nginx.com 
5131183Svbart@nginx.com     if (nxt_slow_path(fb == NULL || r->error)) {
5141183Svbart@nginx.com         goto clean;
5151183Svbart@nginx.com     }
5161183Svbart@nginx.com 
5171183Svbart@nginx.com     rest = fb->file_end - fb->file_pos;
5181183Svbart@nginx.com     size = nxt_buf_mem_size(&b->mem);
5191183Svbart@nginx.com 
5201183Svbart@nginx.com     size = nxt_min(rest, (nxt_off_t) size);
5211183Svbart@nginx.com 
5221183Svbart@nginx.com     n = nxt_file_read(fb->file, b->mem.start, size, fb->file_pos);
5231183Svbart@nginx.com 
5241183Svbart@nginx.com     if (n != size) {
5251183Svbart@nginx.com         if (n >= 0) {
5261183Svbart@nginx.com             nxt_log(task, NXT_LOG_ERR, "file \"%FN\" has changed "
5271183Svbart@nginx.com                     "while sending response to a client", fb->file->name);
5281183Svbart@nginx.com         }
5291183Svbart@nginx.com 
5301183Svbart@nginx.com         nxt_http_request_error_handler(task, r, r->proto.any);
5311183Svbart@nginx.com         goto clean;
5321183Svbart@nginx.com     }
5331183Svbart@nginx.com 
5341760Smax.romanov@nginx.com     next = b->next;
5351760Smax.romanov@nginx.com 
5361183Svbart@nginx.com     if (n == rest) {
5371183Svbart@nginx.com         nxt_file_close(task, fb->file);
5381183Svbart@nginx.com         r->out = NULL;
5391183Svbart@nginx.com 
5401183Svbart@nginx.com         b->next = nxt_http_buf_last(r);
5411183Svbart@nginx.com 
5421183Svbart@nginx.com     } else {
5431183Svbart@nginx.com         fb->file_pos += n;
5441183Svbart@nginx.com         b->next = NULL;
5451183Svbart@nginx.com     }
5461183Svbart@nginx.com 
5471183Svbart@nginx.com     b->mem.pos = b->mem.start;
5481183Svbart@nginx.com     b->mem.free = b->mem.pos + n;
5491183Svbart@nginx.com 
5501183Svbart@nginx.com     nxt_http_request_send(task, r, b);
5511760Smax.romanov@nginx.com 
5521760Smax.romanov@nginx.com     if (next != NULL) {
5531760Smax.romanov@nginx.com         b = next;
5541760Smax.romanov@nginx.com         goto complete_buf;
5551760Smax.romanov@nginx.com     }
5561760Smax.romanov@nginx.com 
5571183Svbart@nginx.com     return;
5581183Svbart@nginx.com 
5591183Svbart@nginx.com clean:
5601183Svbart@nginx.com 
5611760Smax.romanov@nginx.com     do {
5621760Smax.romanov@nginx.com         next = b->next;
5631760Smax.romanov@nginx.com 
5641760Smax.romanov@nginx.com         nxt_mp_free(r->mem_pool, b);
5651760Smax.romanov@nginx.com         nxt_mp_release(r->mem_pool);
5661760Smax.romanov@nginx.com 
5671760Smax.romanov@nginx.com         b = next;
5681760Smax.romanov@nginx.com     } while (b != NULL);
5691183Svbart@nginx.com 
5701183Svbart@nginx.com     if (fb != NULL) {
5711183Svbart@nginx.com         nxt_file_close(task, fb->file);
5721183Svbart@nginx.com         r->out = NULL;
5731183Svbart@nginx.com     }
5741183Svbart@nginx.com }
5751183Svbart@nginx.com 
5761183Svbart@nginx.com 
5771183Svbart@nginx.com nxt_int_t
5781183Svbart@nginx.com nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash)
5791183Svbart@nginx.com {
5801183Svbart@nginx.com     nxt_str_t   *type, extension;
5811183Svbart@nginx.com     nxt_int_t   ret;
5821183Svbart@nginx.com     nxt_uint_t  i;
5831183Svbart@nginx.com 
5841183Svbart@nginx.com     static const struct {
5851183Svbart@nginx.com         nxt_str_t   type;
5861183Svbart@nginx.com         const char  *extension;
5871183Svbart@nginx.com     } default_types[] = {
5881183Svbart@nginx.com 
5891183Svbart@nginx.com         { nxt_string("text/html"),      ".html"  },
5901183Svbart@nginx.com         { nxt_string("text/html"),      ".htm"   },
5911183Svbart@nginx.com         { nxt_string("text/css"),       ".css"   },
5921183Svbart@nginx.com 
5931183Svbart@nginx.com         { nxt_string("image/svg+xml"),  ".svg"   },
5941183Svbart@nginx.com         { nxt_string("image/webp"),     ".webp"  },
5951183Svbart@nginx.com         { nxt_string("image/png"),      ".png"   },
5961617Svbart@nginx.com         { nxt_string("image/apng"),     ".apng"  },
5971183Svbart@nginx.com         { nxt_string("image/jpeg"),     ".jpeg"  },
5981183Svbart@nginx.com         { nxt_string("image/jpeg"),     ".jpg"   },
5991183Svbart@nginx.com         { nxt_string("image/gif"),      ".gif"   },
6001183Svbart@nginx.com         { nxt_string("image/x-icon"),   ".ico"   },
6011183Svbart@nginx.com 
6021617Svbart@nginx.com         { nxt_string("image/avif"),           ".avif"  },
6031617Svbart@nginx.com         { nxt_string("image/avif-sequence"),  ".avifs" },
6041617Svbart@nginx.com 
6051183Svbart@nginx.com         { nxt_string("font/woff"),      ".woff"  },
6061183Svbart@nginx.com         { nxt_string("font/woff2"),     ".woff2" },
6071183Svbart@nginx.com         { nxt_string("font/otf"),       ".otf"   },
6081183Svbart@nginx.com         { nxt_string("font/ttf"),       ".ttf"   },
6091183Svbart@nginx.com 
6101183Svbart@nginx.com         { nxt_string("text/plain"),     ".txt"   },
6111183Svbart@nginx.com         { nxt_string("text/markdown"),  ".md"    },
6121183Svbart@nginx.com         { nxt_string("text/x-rst"),     ".rst"   },
6131183Svbart@nginx.com 
6141183Svbart@nginx.com         { nxt_string("application/javascript"),  ".js"   },
6151183Svbart@nginx.com         { nxt_string("application/json"),        ".json" },
6161183Svbart@nginx.com         { nxt_string("application/xml"),         ".xml"  },
6171183Svbart@nginx.com         { nxt_string("application/rss+xml"),     ".rss"  },
6181183Svbart@nginx.com         { nxt_string("application/atom+xml"),    ".atom" },
6191183Svbart@nginx.com         { nxt_string("application/pdf"),         ".pdf"  },
6201183Svbart@nginx.com 
6211183Svbart@nginx.com         { nxt_string("application/zip"),         ".zip"  },
6221183Svbart@nginx.com 
6231183Svbart@nginx.com         { nxt_string("audio/mpeg"),       ".mp3"  },
6241183Svbart@nginx.com         { nxt_string("audio/ogg"),        ".ogg"  },
6251183Svbart@nginx.com         { nxt_string("audio/midi"),       ".midi" },
6261183Svbart@nginx.com         { nxt_string("audio/midi"),       ".mid"  },
6271183Svbart@nginx.com         { nxt_string("audio/flac"),       ".flac" },
6281183Svbart@nginx.com         { nxt_string("audio/aac"),        ".aac"  },
6291183Svbart@nginx.com         { nxt_string("audio/wav"),        ".wav"  },
6301183Svbart@nginx.com 
6311183Svbart@nginx.com         { nxt_string("video/mpeg"),       ".mpeg" },
6321183Svbart@nginx.com         { nxt_string("video/mpeg"),       ".mpg"  },
6331183Svbart@nginx.com         { nxt_string("video/mp4"),        ".mp4"  },
6341183Svbart@nginx.com         { nxt_string("video/webm"),       ".webm" },
6351183Svbart@nginx.com         { nxt_string("video/x-msvideo"),  ".avi"  },
6361183Svbart@nginx.com 
6371183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".exe" },
6381183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".bin" },
6391183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".dll" },
6401183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".iso" },
6411183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".img" },
6421183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".msi" },
6431183Svbart@nginx.com 
6441183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".deb" },
6451183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".rpm" },
6461183Svbart@nginx.com     };
6471183Svbart@nginx.com 
6481183Svbart@nginx.com     for (i = 0; i < nxt_nitems(default_types); i++) {
6491183Svbart@nginx.com         type = (nxt_str_t *) &default_types[i].type;
6501183Svbart@nginx.com 
6511183Svbart@nginx.com         extension.start = (u_char *) default_types[i].extension;
6521183Svbart@nginx.com         extension.length = nxt_strlen(extension.start);
6531183Svbart@nginx.com 
6541183Svbart@nginx.com         ret = nxt_http_static_mtypes_hash_add(mp, hash, &extension, type);
6551183Svbart@nginx.com         if (nxt_slow_path(ret != NXT_OK)) {
6561183Svbart@nginx.com             return NXT_ERROR;
6571183Svbart@nginx.com         }
6581183Svbart@nginx.com     }
6591183Svbart@nginx.com 
6601183Svbart@nginx.com     return NXT_OK;
6611183Svbart@nginx.com }
6621183Svbart@nginx.com 
6631183Svbart@nginx.com 
6641183Svbart@nginx.com static const nxt_lvlhsh_proto_t  nxt_http_static_mtypes_hash_proto
6651183Svbart@nginx.com     nxt_aligned(64) =
6661183Svbart@nginx.com {
6671183Svbart@nginx.com     NXT_LVLHSH_DEFAULT,
6681183Svbart@nginx.com     nxt_http_static_mtypes_hash_test,
6691183Svbart@nginx.com     nxt_http_static_mtypes_hash_alloc,
6701183Svbart@nginx.com     nxt_http_static_mtypes_hash_free,
6711183Svbart@nginx.com };
6721183Svbart@nginx.com 
6731183Svbart@nginx.com 
6741183Svbart@nginx.com typedef struct {
6751183Svbart@nginx.com     nxt_str_t  extension;
6761183Svbart@nginx.com     nxt_str_t  *type;
6771183Svbart@nginx.com } nxt_http_static_mtype_t;
6781183Svbart@nginx.com 
6791183Svbart@nginx.com 
6801183Svbart@nginx.com nxt_int_t
6811183Svbart@nginx.com nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash,
6821183Svbart@nginx.com     nxt_str_t *extension, nxt_str_t *type)
6831183Svbart@nginx.com {
6841183Svbart@nginx.com     nxt_lvlhsh_query_t       lhq;
6851183Svbart@nginx.com     nxt_http_static_mtype_t  *mtype;
6861183Svbart@nginx.com 
6871183Svbart@nginx.com     mtype = nxt_mp_get(mp, sizeof(nxt_http_static_mtype_t));
6881183Svbart@nginx.com     if (nxt_slow_path(mtype == NULL)) {
6891183Svbart@nginx.com         return NXT_ERROR;
6901183Svbart@nginx.com     }
6911183Svbart@nginx.com 
6921183Svbart@nginx.com     mtype->extension = *extension;
6931183Svbart@nginx.com     mtype->type = type;
6941183Svbart@nginx.com 
6951183Svbart@nginx.com     lhq.key = *extension;
6961183Svbart@nginx.com     lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length);
6971183Svbart@nginx.com     lhq.replace = 1;
6981183Svbart@nginx.com     lhq.value = mtype;
6991183Svbart@nginx.com     lhq.proto = &nxt_http_static_mtypes_hash_proto;
7001183Svbart@nginx.com     lhq.pool = mp;
7011183Svbart@nginx.com 
7021183Svbart@nginx.com     return nxt_lvlhsh_insert(hash, &lhq);
7031183Svbart@nginx.com }
7041183Svbart@nginx.com 
7051183Svbart@nginx.com 
7061183Svbart@nginx.com nxt_str_t *
7071183Svbart@nginx.com nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash, nxt_str_t *extension)
7081183Svbart@nginx.com {
7091183Svbart@nginx.com     nxt_lvlhsh_query_t       lhq;
7101183Svbart@nginx.com     nxt_http_static_mtype_t  *mtype;
7111183Svbart@nginx.com 
7121183Svbart@nginx.com     lhq.key = *extension;
7131183Svbart@nginx.com     lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length);
7141183Svbart@nginx.com     lhq.proto = &nxt_http_static_mtypes_hash_proto;
7151183Svbart@nginx.com 
7161183Svbart@nginx.com     if (nxt_lvlhsh_find(hash, &lhq) == NXT_OK) {
7171183Svbart@nginx.com         mtype = lhq.value;
7181183Svbart@nginx.com         return mtype->type;
7191183Svbart@nginx.com     }
7201183Svbart@nginx.com 
7211183Svbart@nginx.com     return NULL;
7221183Svbart@nginx.com }
7231183Svbart@nginx.com 
7241183Svbart@nginx.com 
7251183Svbart@nginx.com static nxt_int_t
7261183Svbart@nginx.com nxt_http_static_mtypes_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
7271183Svbart@nginx.com {
7281183Svbart@nginx.com     nxt_http_static_mtype_t  *mtype;
7291183Svbart@nginx.com 
7301183Svbart@nginx.com     mtype = data;
7311183Svbart@nginx.com 
7321183Svbart@nginx.com     return nxt_strcasestr_eq(&lhq->key, &mtype->extension) ? NXT_OK
7331183Svbart@nginx.com                                                            : NXT_DECLINED;
7341183Svbart@nginx.com }
7351183Svbart@nginx.com 
7361183Svbart@nginx.com 
7371183Svbart@nginx.com static void *
7381183Svbart@nginx.com nxt_http_static_mtypes_hash_alloc(void *data, size_t size)
7391183Svbart@nginx.com {
7401183Svbart@nginx.com     return nxt_mp_align(data, size, size);
7411183Svbart@nginx.com }
7421183Svbart@nginx.com 
7431183Svbart@nginx.com 
7441183Svbart@nginx.com static void
7451183Svbart@nginx.com nxt_http_static_mtypes_hash_free(void *data, void *p)
7461183Svbart@nginx.com {
7471183Svbart@nginx.com     nxt_mp_free(data, p);
7481183Svbart@nginx.com }
749