xref: /unit/src/nxt_http_static.c (revision 1200)
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 
301183Svbart@nginx.com nxt_http_pass_t *
311183Svbart@nginx.com nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
321183Svbart@nginx.com     nxt_http_pass_t *pass)
331183Svbart@nginx.com {
341183Svbart@nginx.com     size_t              alloc, encode;
351183Svbart@nginx.com     u_char              *p;
361183Svbart@nginx.com     struct tm           tm;
371183Svbart@nginx.com     nxt_buf_t           *fb;
381183Svbart@nginx.com     nxt_int_t           ret;
391183Svbart@nginx.com     nxt_str_t           index, extension, *mtype;
401183Svbart@nginx.com     nxt_uint_t          level;
411183Svbart@nginx.com     nxt_bool_t          need_body;
421183Svbart@nginx.com     nxt_file_t          *f;
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)) {
521183Svbart@nginx.com             nxt_http_request_error(task, r, NXT_HTTP_METHOD_NOT_ALLOWED);
531183Svbart@nginx.com             return NULL;
541183Svbart@nginx.com         }
551183Svbart@nginx.com 
561183Svbart@nginx.com         need_body = 0;
571183Svbart@nginx.com 
581183Svbart@nginx.com     } else {
591183Svbart@nginx.com         need_body = 1;
601183Svbart@nginx.com     }
611183Svbart@nginx.com 
621183Svbart@nginx.com     f = nxt_mp_zget(r->mem_pool, sizeof(nxt_file_t));
631183Svbart@nginx.com     if (nxt_slow_path(f == NULL)) {
641183Svbart@nginx.com         goto fail;
651183Svbart@nginx.com     }
661183Svbart@nginx.com 
671183Svbart@nginx.com     f->fd = NXT_FILE_INVALID;
681183Svbart@nginx.com 
691183Svbart@nginx.com     if (r->path->start[r->path->length - 1] == '/') {
701183Svbart@nginx.com         /* TODO: dynamic index setting. */
711183Svbart@nginx.com         nxt_str_set(&index, "index.html");
721183Svbart@nginx.com         nxt_str_set(&extension, ".html");
731183Svbart@nginx.com 
741183Svbart@nginx.com     } else {
751183Svbart@nginx.com         nxt_str_null(&index);
761183Svbart@nginx.com         nxt_str_null(&extension);
771183Svbart@nginx.com     }
781183Svbart@nginx.com 
791183Svbart@nginx.com     alloc = pass->name.length + r->path->length + index.length + 1;
801183Svbart@nginx.com 
811183Svbart@nginx.com     f->name = nxt_mp_nget(r->mem_pool, alloc);
821183Svbart@nginx.com     if (nxt_slow_path(f->name == NULL)) {
831183Svbart@nginx.com         goto fail;
841183Svbart@nginx.com     }
851183Svbart@nginx.com 
861183Svbart@nginx.com     p = f->name;
871183Svbart@nginx.com     p = nxt_cpymem(p, pass->name.start, pass->name.length);
881183Svbart@nginx.com     p = nxt_cpymem(p, r->path->start, r->path->length);
891183Svbart@nginx.com     p = nxt_cpymem(p, index.start, index.length);
901183Svbart@nginx.com     *p = '\0';
911183Svbart@nginx.com 
921183Svbart@nginx.com     ret = nxt_file_open(task, f, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);
931183Svbart@nginx.com 
941183Svbart@nginx.com     if (nxt_slow_path(ret != NXT_OK)) {
951183Svbart@nginx.com         switch (f->error) {
961183Svbart@nginx.com 
97*1200Svbart@nginx.com         /*
98*1200Svbart@nginx.com          * For Unix domain sockets "errno" is set to:
99*1200Svbart@nginx.com          *  - ENXIO on Linux;
100*1200Svbart@nginx.com          *  - EOPNOTSUPP on *BSD, MacOSX, and Solaris.
101*1200Svbart@nginx.com          */
102*1200Svbart@nginx.com 
1031183Svbart@nginx.com         case NXT_ENOENT:
1041183Svbart@nginx.com         case NXT_ENOTDIR:
1051183Svbart@nginx.com         case NXT_ENAMETOOLONG:
106*1200Svbart@nginx.com #if (NXT_LINUX)
107*1200Svbart@nginx.com         case NXT_ENXIO:
108*1200Svbart@nginx.com #else
109*1200Svbart@nginx.com         case NXT_EOPNOTSUPP:
110*1200Svbart@nginx.com #endif
1111183Svbart@nginx.com             level = NXT_LOG_ERR;
1121183Svbart@nginx.com             status = NXT_HTTP_NOT_FOUND;
1131183Svbart@nginx.com             break;
1141183Svbart@nginx.com 
1151183Svbart@nginx.com         case NXT_EACCES:
1161183Svbart@nginx.com             level = NXT_LOG_ERR;
1171183Svbart@nginx.com             status = NXT_HTTP_FORBIDDEN;
1181183Svbart@nginx.com             break;
1191183Svbart@nginx.com 
1201183Svbart@nginx.com         default:
1211183Svbart@nginx.com             level = NXT_LOG_ALERT;
1221183Svbart@nginx.com             status = NXT_HTTP_INTERNAL_SERVER_ERROR;
1231183Svbart@nginx.com             break;
1241183Svbart@nginx.com         }
1251183Svbart@nginx.com 
1261183Svbart@nginx.com         if (status != NXT_HTTP_NOT_FOUND) {
1271183Svbart@nginx.com             nxt_log(task, level, "open(\"%FN\") failed %E", f->name, f->error);
1281183Svbart@nginx.com         }
1291183Svbart@nginx.com 
1301183Svbart@nginx.com         nxt_http_request_error(task, r, status);
1311183Svbart@nginx.com         return NULL;
1321183Svbart@nginx.com     }
1331183Svbart@nginx.com 
1341183Svbart@nginx.com     ret = nxt_file_info(f, &fi);
1351183Svbart@nginx.com     if (nxt_slow_path(ret != NXT_OK)) {
1361183Svbart@nginx.com         goto fail;
1371183Svbart@nginx.com     }
1381183Svbart@nginx.com 
1391183Svbart@nginx.com     if (nxt_fast_path(nxt_is_file(&fi))) {
1401183Svbart@nginx.com         r->status = NXT_HTTP_OK;
1411183Svbart@nginx.com         r->resp.content_length_n = nxt_file_size(&fi);
1421183Svbart@nginx.com 
1431183Svbart@nginx.com         field = nxt_list_zero_add(r->resp.fields);
1441183Svbart@nginx.com         if (nxt_slow_path(field == NULL)) {
1451183Svbart@nginx.com             goto fail;
1461183Svbart@nginx.com         }
1471183Svbart@nginx.com 
1481183Svbart@nginx.com         nxt_http_field_name_set(field, "Last-Modified");
1491183Svbart@nginx.com 
1501183Svbart@nginx.com         p = nxt_mp_nget(r->mem_pool, NXT_HTTP_DATE_LEN);
1511183Svbart@nginx.com         if (nxt_slow_path(p == NULL)) {
1521183Svbart@nginx.com             goto fail;
1531183Svbart@nginx.com         }
1541183Svbart@nginx.com 
1551183Svbart@nginx.com         nxt_localtime(nxt_file_mtime(&fi), &tm);
1561183Svbart@nginx.com 
1571183Svbart@nginx.com         field->value = p;
1581183Svbart@nginx.com         field->value_length = nxt_http_date(p, &tm) - p;
1591183Svbart@nginx.com 
1601183Svbart@nginx.com         field = nxt_list_zero_add(r->resp.fields);
1611183Svbart@nginx.com         if (nxt_slow_path(field == NULL)) {
1621183Svbart@nginx.com             goto fail;
1631183Svbart@nginx.com         }
1641183Svbart@nginx.com 
1651183Svbart@nginx.com         nxt_http_field_name_set(field, "ETag");
1661183Svbart@nginx.com 
1671183Svbart@nginx.com         alloc = NXT_TIME_T_HEXLEN + NXT_OFF_T_HEXLEN + 3;
1681183Svbart@nginx.com 
1691183Svbart@nginx.com         p = nxt_mp_nget(r->mem_pool, alloc);
1701183Svbart@nginx.com         if (nxt_slow_path(p == NULL)) {
1711183Svbart@nginx.com             goto fail;
1721183Svbart@nginx.com         }
1731183Svbart@nginx.com 
1741183Svbart@nginx.com         field->value = p;
1751183Svbart@nginx.com         field->value_length = nxt_sprintf(p, p + alloc, "\"%xT-%xO\"",
1761183Svbart@nginx.com                                           nxt_file_mtime(&fi),
1771183Svbart@nginx.com                                           nxt_file_size(&fi))
1781183Svbart@nginx.com                               - p;
1791183Svbart@nginx.com 
1801183Svbart@nginx.com         if (extension.start == NULL) {
1811183Svbart@nginx.com             nxt_http_static_extract_extension(r->path, &extension);
1821183Svbart@nginx.com         }
1831183Svbart@nginx.com 
1841183Svbart@nginx.com         rtcf = r->conf->socket_conf->router_conf;
1851183Svbart@nginx.com 
1861183Svbart@nginx.com         mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash,
1871183Svbart@nginx.com                                                  &extension);
1881183Svbart@nginx.com 
1891183Svbart@nginx.com         if (mtype != NULL) {
1901183Svbart@nginx.com             field = nxt_list_zero_add(r->resp.fields);
1911183Svbart@nginx.com             if (nxt_slow_path(field == NULL)) {
1921183Svbart@nginx.com                 goto fail;
1931183Svbart@nginx.com             }
1941183Svbart@nginx.com 
1951183Svbart@nginx.com             nxt_http_field_name_set(field, "Content-Type");
1961183Svbart@nginx.com 
1971183Svbart@nginx.com             field->value = mtype->start;
1981183Svbart@nginx.com             field->value_length = mtype->length;
1991183Svbart@nginx.com         }
2001183Svbart@nginx.com 
2011183Svbart@nginx.com         if (need_body && nxt_file_size(&fi) > 0) {
2021183Svbart@nginx.com             fb = nxt_mp_zget(r->mem_pool, NXT_BUF_FILE_SIZE);
2031183Svbart@nginx.com             if (nxt_slow_path(fb == NULL)) {
2041183Svbart@nginx.com                 goto fail;
2051183Svbart@nginx.com             }
2061183Svbart@nginx.com 
2071183Svbart@nginx.com             fb->file = f;
2081183Svbart@nginx.com             fb->file_end = nxt_file_size(&fi);
2091183Svbart@nginx.com 
2101183Svbart@nginx.com             r->out = fb;
2111183Svbart@nginx.com 
2121183Svbart@nginx.com             body_handler = &nxt_http_static_body_handler;
2131183Svbart@nginx.com 
2141183Svbart@nginx.com         } else {
2151183Svbart@nginx.com             nxt_file_close(task, f);
2161183Svbart@nginx.com             body_handler = NULL;
2171183Svbart@nginx.com         }
2181183Svbart@nginx.com 
2191183Svbart@nginx.com     } else {
2201183Svbart@nginx.com         /* Not a file. */
2211183Svbart@nginx.com 
2221183Svbart@nginx.com         nxt_file_close(task, f);
2231183Svbart@nginx.com 
2241183Svbart@nginx.com         if (nxt_slow_path(!nxt_is_dir(&fi))) {
2251183Svbart@nginx.com             nxt_log(task, NXT_LOG_ERR, "\"%FN\" is not a regular file",
2261183Svbart@nginx.com                     f->name);
2271183Svbart@nginx.com             nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND);
2281183Svbart@nginx.com             return NULL;
2291183Svbart@nginx.com         }
2301183Svbart@nginx.com 
2311198Svbart@nginx.com         f = NULL;
2321198Svbart@nginx.com 
2331183Svbart@nginx.com         r->status = NXT_HTTP_MOVED_PERMANENTLY;
2341183Svbart@nginx.com         r->resp.content_length_n = 0;
2351183Svbart@nginx.com 
2361183Svbart@nginx.com         field = nxt_list_zero_add(r->resp.fields);
2371183Svbart@nginx.com         if (nxt_slow_path(field == NULL)) {
2381183Svbart@nginx.com             goto fail;
2391183Svbart@nginx.com         }
2401183Svbart@nginx.com 
2411183Svbart@nginx.com         nxt_http_field_name_set(field, "Location");
2421183Svbart@nginx.com 
2431183Svbart@nginx.com         encode = nxt_encode_uri(NULL, r->path->start, r->path->length);
2441183Svbart@nginx.com         alloc = r->path->length + encode * 2 + 1;
2451183Svbart@nginx.com 
2461183Svbart@nginx.com         if (r->args->length > 0) {
2471183Svbart@nginx.com             alloc += 1 + r->args->length;
2481183Svbart@nginx.com         }
2491183Svbart@nginx.com 
2501183Svbart@nginx.com         p = nxt_mp_nget(r->mem_pool, alloc);
2511183Svbart@nginx.com         if (nxt_slow_path(p == NULL)) {
2521183Svbart@nginx.com             goto fail;
2531183Svbart@nginx.com         }
2541183Svbart@nginx.com 
2551183Svbart@nginx.com         field->value = p;
2561183Svbart@nginx.com         field->value_length = alloc;
2571183Svbart@nginx.com 
2581183Svbart@nginx.com         if (encode > 0) {
2591183Svbart@nginx.com             p = (u_char *) nxt_encode_uri(p, r->path->start, r->path->length);
2601183Svbart@nginx.com 
2611183Svbart@nginx.com         } else {
2621183Svbart@nginx.com             p = nxt_cpymem(p, r->path->start, r->path->length);
2631183Svbart@nginx.com         }
2641183Svbart@nginx.com 
2651183Svbart@nginx.com         *p++ = '/';
2661183Svbart@nginx.com 
2671183Svbart@nginx.com         if (r->args->length > 0) {
2681183Svbart@nginx.com             *p++ = '?';
2691183Svbart@nginx.com             nxt_memcpy(p, r->args->start, r->args->length);
2701183Svbart@nginx.com         }
2711183Svbart@nginx.com 
2721183Svbart@nginx.com         body_handler = NULL;
2731183Svbart@nginx.com     }
2741183Svbart@nginx.com 
2751183Svbart@nginx.com     nxt_http_request_header_send(task, r, body_handler);
2761183Svbart@nginx.com 
2771183Svbart@nginx.com     r->state = &nxt_http_static_send_state;
2781183Svbart@nginx.com     return NULL;
2791183Svbart@nginx.com 
2801183Svbart@nginx.com fail:
2811183Svbart@nginx.com 
2821183Svbart@nginx.com     nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
2831183Svbart@nginx.com 
2841183Svbart@nginx.com     if (f != NULL && f->fd != NXT_FILE_INVALID) {
2851183Svbart@nginx.com         nxt_file_close(task, f);
2861183Svbart@nginx.com     }
2871183Svbart@nginx.com 
2881183Svbart@nginx.com     return NULL;
2891183Svbart@nginx.com }
2901183Svbart@nginx.com 
2911183Svbart@nginx.com 
2921183Svbart@nginx.com static void
2931183Svbart@nginx.com nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *extension)
2941183Svbart@nginx.com {
2951183Svbart@nginx.com     u_char  ch, *p, *end;
2961183Svbart@nginx.com 
2971183Svbart@nginx.com     end = path->start + path->length;
2981183Svbart@nginx.com     p = end;
2991183Svbart@nginx.com 
3001183Svbart@nginx.com     for ( ;; ) {
3011183Svbart@nginx.com         /* There's always '/' in the beginning of the request path. */
3021183Svbart@nginx.com 
3031183Svbart@nginx.com         p--;
3041183Svbart@nginx.com         ch = *p;
3051183Svbart@nginx.com 
3061183Svbart@nginx.com         switch (ch) {
3071183Svbart@nginx.com         case '/':
3081183Svbart@nginx.com             p++;
3091183Svbart@nginx.com             /* Fall through. */
3101183Svbart@nginx.com         case '.':
3111183Svbart@nginx.com             extension->length = end - p;
3121183Svbart@nginx.com             extension->start = p;
3131183Svbart@nginx.com             return;
3141183Svbart@nginx.com         }
3151183Svbart@nginx.com     }
3161183Svbart@nginx.com }
3171183Svbart@nginx.com 
3181183Svbart@nginx.com 
3191183Svbart@nginx.com static void
3201183Svbart@nginx.com nxt_http_static_body_handler(nxt_task_t *task, void *obj, void *data)
3211183Svbart@nginx.com {
3221183Svbart@nginx.com     size_t              alloc;
3231183Svbart@nginx.com     nxt_buf_t           *fb, *b, **next, *out;
3241183Svbart@nginx.com     nxt_off_t           rest;
3251183Svbart@nginx.com     nxt_int_t           n;
3261183Svbart@nginx.com     nxt_work_queue_t    *wq;
3271183Svbart@nginx.com     nxt_http_request_t  *r;
3281183Svbart@nginx.com 
3291183Svbart@nginx.com     r = obj;
3301183Svbart@nginx.com     fb = r->out;
3311183Svbart@nginx.com 
3321183Svbart@nginx.com     rest = fb->file_end - fb->file_pos;
3331183Svbart@nginx.com     out = NULL;
3341183Svbart@nginx.com     next = &out;
3351183Svbart@nginx.com     n = 0;
3361183Svbart@nginx.com 
3371183Svbart@nginx.com     do {
3381183Svbart@nginx.com         alloc = nxt_min(rest, NXT_HTTP_STATIC_BUF_SIZE);
3391183Svbart@nginx.com 
3401183Svbart@nginx.com         b = nxt_buf_mem_alloc(r->mem_pool, alloc, 0);
3411183Svbart@nginx.com         if (nxt_slow_path(b == NULL)) {
3421183Svbart@nginx.com             goto fail;
3431183Svbart@nginx.com         }
3441183Svbart@nginx.com 
3451183Svbart@nginx.com         b->completion_handler = nxt_http_static_buf_completion;
3461183Svbart@nginx.com         b->parent = r;
3471183Svbart@nginx.com 
3481183Svbart@nginx.com         nxt_mp_retain(r->mem_pool);
3491183Svbart@nginx.com 
3501183Svbart@nginx.com         *next = b;
3511183Svbart@nginx.com         next = &b->next;
3521183Svbart@nginx.com 
3531183Svbart@nginx.com         rest -= alloc;
3541183Svbart@nginx.com 
3551183Svbart@nginx.com     } while (rest > 0 && ++n < NXT_HTTP_STATIC_BUF_COUNT);
3561183Svbart@nginx.com 
3571183Svbart@nginx.com     wq = &task->thread->engine->fast_work_queue;
3581183Svbart@nginx.com 
3591183Svbart@nginx.com     nxt_sendbuf_drain(task, wq, out);
3601183Svbart@nginx.com     return;
3611183Svbart@nginx.com 
3621183Svbart@nginx.com fail:
3631183Svbart@nginx.com 
3641183Svbart@nginx.com     while (out != NULL) {
3651183Svbart@nginx.com         b = out;
3661183Svbart@nginx.com         out = b->next;
3671183Svbart@nginx.com 
3681183Svbart@nginx.com         nxt_mp_free(r->mem_pool, b);
3691183Svbart@nginx.com         nxt_mp_release(r->mem_pool);
3701183Svbart@nginx.com     }
3711183Svbart@nginx.com }
3721183Svbart@nginx.com 
3731183Svbart@nginx.com 
3741183Svbart@nginx.com static const nxt_http_request_state_t  nxt_http_static_send_state
3751183Svbart@nginx.com     nxt_aligned(64) =
3761183Svbart@nginx.com {
3771183Svbart@nginx.com     .error_handler = nxt_http_request_error_handler,
3781183Svbart@nginx.com };
3791183Svbart@nginx.com 
3801183Svbart@nginx.com 
3811183Svbart@nginx.com static void
3821183Svbart@nginx.com nxt_http_static_buf_completion(nxt_task_t *task, void *obj, void *data)
3831183Svbart@nginx.com {
3841183Svbart@nginx.com     ssize_t             n, size;
3851183Svbart@nginx.com     nxt_buf_t           *b, *fb;
3861183Svbart@nginx.com     nxt_off_t           rest;
3871183Svbart@nginx.com     nxt_http_request_t  *r;
3881183Svbart@nginx.com 
3891183Svbart@nginx.com     b = obj;
3901183Svbart@nginx.com     r = data;
3911183Svbart@nginx.com     fb = r->out;
3921183Svbart@nginx.com 
3931183Svbart@nginx.com     if (nxt_slow_path(fb == NULL || r->error)) {
3941183Svbart@nginx.com         goto clean;
3951183Svbart@nginx.com     }
3961183Svbart@nginx.com 
3971183Svbart@nginx.com     rest = fb->file_end - fb->file_pos;
3981183Svbart@nginx.com     size = nxt_buf_mem_size(&b->mem);
3991183Svbart@nginx.com 
4001183Svbart@nginx.com     size = nxt_min(rest, (nxt_off_t) size);
4011183Svbart@nginx.com 
4021183Svbart@nginx.com     n = nxt_file_read(fb->file, b->mem.start, size, fb->file_pos);
4031183Svbart@nginx.com 
4041183Svbart@nginx.com     if (n != size) {
4051183Svbart@nginx.com         if (n >= 0) {
4061183Svbart@nginx.com             nxt_log(task, NXT_LOG_ERR, "file \"%FN\" has changed "
4071183Svbart@nginx.com                     "while sending response to a client", fb->file->name);
4081183Svbart@nginx.com         }
4091183Svbart@nginx.com 
4101183Svbart@nginx.com         nxt_http_request_error_handler(task, r, r->proto.any);
4111183Svbart@nginx.com         goto clean;
4121183Svbart@nginx.com     }
4131183Svbart@nginx.com 
4141183Svbart@nginx.com     if (n == rest) {
4151183Svbart@nginx.com         nxt_file_close(task, fb->file);
4161183Svbart@nginx.com         r->out = NULL;
4171183Svbart@nginx.com 
4181183Svbart@nginx.com         b->next = nxt_http_buf_last(r);
4191183Svbart@nginx.com 
4201183Svbart@nginx.com     } else {
4211183Svbart@nginx.com         fb->file_pos += n;
4221183Svbart@nginx.com         b->next = NULL;
4231183Svbart@nginx.com     }
4241183Svbart@nginx.com 
4251183Svbart@nginx.com     b->mem.pos = b->mem.start;
4261183Svbart@nginx.com     b->mem.free = b->mem.pos + n;
4271183Svbart@nginx.com 
4281183Svbart@nginx.com     nxt_http_request_send(task, r, b);
4291183Svbart@nginx.com     return;
4301183Svbart@nginx.com 
4311183Svbart@nginx.com clean:
4321183Svbart@nginx.com 
4331183Svbart@nginx.com     nxt_mp_free(r->mem_pool, b);
4341183Svbart@nginx.com     nxt_mp_release(r->mem_pool);
4351183Svbart@nginx.com 
4361183Svbart@nginx.com     if (fb != NULL) {
4371183Svbart@nginx.com         nxt_file_close(task, fb->file);
4381183Svbart@nginx.com         r->out = NULL;
4391183Svbart@nginx.com     }
4401183Svbart@nginx.com }
4411183Svbart@nginx.com 
4421183Svbart@nginx.com 
4431183Svbart@nginx.com nxt_int_t
4441183Svbart@nginx.com nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash)
4451183Svbart@nginx.com {
4461183Svbart@nginx.com     nxt_str_t   *type, extension;
4471183Svbart@nginx.com     nxt_int_t   ret;
4481183Svbart@nginx.com     nxt_uint_t  i;
4491183Svbart@nginx.com 
4501183Svbart@nginx.com     static const struct {
4511183Svbart@nginx.com         nxt_str_t   type;
4521183Svbart@nginx.com         const char  *extension;
4531183Svbart@nginx.com     } default_types[] = {
4541183Svbart@nginx.com 
4551183Svbart@nginx.com         { nxt_string("text/html"),      ".html"  },
4561183Svbart@nginx.com         { nxt_string("text/html"),      ".htm"   },
4571183Svbart@nginx.com         { nxt_string("text/css"),       ".css"   },
4581183Svbart@nginx.com 
4591183Svbart@nginx.com         { nxt_string("image/svg+xml"),  ".svg"   },
4601183Svbart@nginx.com         { nxt_string("image/svg+xml"),  ".svg"   },
4611183Svbart@nginx.com         { nxt_string("image/webp"),     ".webp"  },
4621183Svbart@nginx.com         { nxt_string("image/png"),      ".png"   },
4631183Svbart@nginx.com         { nxt_string("image/jpeg"),     ".jpeg"  },
4641183Svbart@nginx.com         { nxt_string("image/jpeg"),     ".jpg"   },
4651183Svbart@nginx.com         { nxt_string("image/gif"),      ".gif"   },
4661183Svbart@nginx.com         { nxt_string("image/x-icon"),   ".ico"   },
4671183Svbart@nginx.com 
4681183Svbart@nginx.com         { nxt_string("font/woff"),      ".woff"  },
4691183Svbart@nginx.com         { nxt_string("font/woff2"),     ".woff2" },
4701183Svbart@nginx.com         { nxt_string("font/otf"),       ".otf"   },
4711183Svbart@nginx.com         { nxt_string("font/ttf"),       ".ttf"   },
4721183Svbart@nginx.com 
4731183Svbart@nginx.com         { nxt_string("text/plain"),     ".txt"   },
4741183Svbart@nginx.com         { nxt_string("text/markdown"),  ".md"    },
4751183Svbart@nginx.com         { nxt_string("text/x-rst"),     ".rst"   },
4761183Svbart@nginx.com 
4771183Svbart@nginx.com         { nxt_string("application/javascript"),  ".js"   },
4781183Svbart@nginx.com         { nxt_string("application/json"),        ".json" },
4791183Svbart@nginx.com         { nxt_string("application/xml"),         ".xml"  },
4801183Svbart@nginx.com         { nxt_string("application/rss+xml"),     ".rss"  },
4811183Svbart@nginx.com         { nxt_string("application/atom+xml"),    ".atom" },
4821183Svbart@nginx.com         { nxt_string("application/pdf"),         ".pdf"  },
4831183Svbart@nginx.com 
4841183Svbart@nginx.com         { nxt_string("application/zip"),         ".zip"  },
4851183Svbart@nginx.com 
4861183Svbart@nginx.com         { nxt_string("audio/mpeg"),       ".mp3"  },
4871183Svbart@nginx.com         { nxt_string("audio/ogg"),        ".ogg"  },
4881183Svbart@nginx.com         { nxt_string("audio/midi"),       ".midi" },
4891183Svbart@nginx.com         { nxt_string("audio/midi"),       ".mid"  },
4901183Svbart@nginx.com         { nxt_string("audio/flac"),       ".flac" },
4911183Svbart@nginx.com         { nxt_string("audio/aac"),        ".aac"  },
4921183Svbart@nginx.com         { nxt_string("audio/wav"),        ".wav"  },
4931183Svbart@nginx.com 
4941183Svbart@nginx.com         { nxt_string("video/mpeg"),       ".mpeg" },
4951183Svbart@nginx.com         { nxt_string("video/mpeg"),       ".mpg"  },
4961183Svbart@nginx.com         { nxt_string("video/mp4"),        ".mp4"  },
4971183Svbart@nginx.com         { nxt_string("video/webm"),       ".webm" },
4981183Svbart@nginx.com         { nxt_string("video/x-msvideo"),  ".avi"  },
4991183Svbart@nginx.com 
5001183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".exe" },
5011183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".bin" },
5021183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".dll" },
5031183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".iso" },
5041183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".img" },
5051183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".msi" },
5061183Svbart@nginx.com 
5071183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".deb" },
5081183Svbart@nginx.com         { nxt_string("application/octet-stream"),  ".rpm" },
5091183Svbart@nginx.com     };
5101183Svbart@nginx.com 
5111183Svbart@nginx.com     for (i = 0; i < nxt_nitems(default_types); i++) {
5121183Svbart@nginx.com         type = (nxt_str_t *) &default_types[i].type;
5131183Svbart@nginx.com 
5141183Svbart@nginx.com         extension.start = (u_char *) default_types[i].extension;
5151183Svbart@nginx.com         extension.length = nxt_strlen(extension.start);
5161183Svbart@nginx.com 
5171183Svbart@nginx.com         ret = nxt_http_static_mtypes_hash_add(mp, hash, &extension, type);
5181183Svbart@nginx.com         if (nxt_slow_path(ret != NXT_OK)) {
5191183Svbart@nginx.com             return NXT_ERROR;
5201183Svbart@nginx.com         }
5211183Svbart@nginx.com     }
5221183Svbart@nginx.com 
5231183Svbart@nginx.com     return NXT_OK;
5241183Svbart@nginx.com }
5251183Svbart@nginx.com 
5261183Svbart@nginx.com 
5271183Svbart@nginx.com static const nxt_lvlhsh_proto_t  nxt_http_static_mtypes_hash_proto
5281183Svbart@nginx.com     nxt_aligned(64) =
5291183Svbart@nginx.com {
5301183Svbart@nginx.com     NXT_LVLHSH_DEFAULT,
5311183Svbart@nginx.com     nxt_http_static_mtypes_hash_test,
5321183Svbart@nginx.com     nxt_http_static_mtypes_hash_alloc,
5331183Svbart@nginx.com     nxt_http_static_mtypes_hash_free,
5341183Svbart@nginx.com };
5351183Svbart@nginx.com 
5361183Svbart@nginx.com 
5371183Svbart@nginx.com typedef struct {
5381183Svbart@nginx.com     nxt_str_t  extension;
5391183Svbart@nginx.com     nxt_str_t  *type;
5401183Svbart@nginx.com } nxt_http_static_mtype_t;
5411183Svbart@nginx.com 
5421183Svbart@nginx.com 
5431183Svbart@nginx.com nxt_int_t
5441183Svbart@nginx.com nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash,
5451183Svbart@nginx.com     nxt_str_t *extension, nxt_str_t *type)
5461183Svbart@nginx.com {
5471183Svbart@nginx.com     nxt_lvlhsh_query_t       lhq;
5481183Svbart@nginx.com     nxt_http_static_mtype_t  *mtype;
5491183Svbart@nginx.com 
5501183Svbart@nginx.com     mtype = nxt_mp_get(mp, sizeof(nxt_http_static_mtype_t));
5511183Svbart@nginx.com     if (nxt_slow_path(mtype == NULL)) {
5521183Svbart@nginx.com         return NXT_ERROR;
5531183Svbart@nginx.com     }
5541183Svbart@nginx.com 
5551183Svbart@nginx.com     mtype->extension = *extension;
5561183Svbart@nginx.com     mtype->type = type;
5571183Svbart@nginx.com 
5581183Svbart@nginx.com     lhq.key = *extension;
5591183Svbart@nginx.com     lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length);
5601183Svbart@nginx.com     lhq.replace = 1;
5611183Svbart@nginx.com     lhq.value = mtype;
5621183Svbart@nginx.com     lhq.proto = &nxt_http_static_mtypes_hash_proto;
5631183Svbart@nginx.com     lhq.pool = mp;
5641183Svbart@nginx.com 
5651183Svbart@nginx.com     return nxt_lvlhsh_insert(hash, &lhq);
5661183Svbart@nginx.com }
5671183Svbart@nginx.com 
5681183Svbart@nginx.com 
5691183Svbart@nginx.com nxt_str_t *
5701183Svbart@nginx.com nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash, nxt_str_t *extension)
5711183Svbart@nginx.com {
5721183Svbart@nginx.com     nxt_lvlhsh_query_t       lhq;
5731183Svbart@nginx.com     nxt_http_static_mtype_t  *mtype;
5741183Svbart@nginx.com 
5751183Svbart@nginx.com     lhq.key = *extension;
5761183Svbart@nginx.com     lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length);
5771183Svbart@nginx.com     lhq.proto = &nxt_http_static_mtypes_hash_proto;
5781183Svbart@nginx.com 
5791183Svbart@nginx.com     if (nxt_lvlhsh_find(hash, &lhq) == NXT_OK) {
5801183Svbart@nginx.com         mtype = lhq.value;
5811183Svbart@nginx.com         return mtype->type;
5821183Svbart@nginx.com     }
5831183Svbart@nginx.com 
5841183Svbart@nginx.com     return NULL;
5851183Svbart@nginx.com }
5861183Svbart@nginx.com 
5871183Svbart@nginx.com 
5881183Svbart@nginx.com static nxt_int_t
5891183Svbart@nginx.com nxt_http_static_mtypes_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
5901183Svbart@nginx.com {
5911183Svbart@nginx.com     nxt_http_static_mtype_t  *mtype;
5921183Svbart@nginx.com 
5931183Svbart@nginx.com     mtype = data;
5941183Svbart@nginx.com 
5951183Svbart@nginx.com     return nxt_strcasestr_eq(&lhq->key, &mtype->extension) ? NXT_OK
5961183Svbart@nginx.com                                                            : NXT_DECLINED;
5971183Svbart@nginx.com }
5981183Svbart@nginx.com 
5991183Svbart@nginx.com 
6001183Svbart@nginx.com static void *
6011183Svbart@nginx.com nxt_http_static_mtypes_hash_alloc(void *data, size_t size)
6021183Svbart@nginx.com {
6031183Svbart@nginx.com     return nxt_mp_align(data, size, size);
6041183Svbart@nginx.com }
6051183Svbart@nginx.com 
6061183Svbart@nginx.com 
6071183Svbart@nginx.com static void
6081183Svbart@nginx.com nxt_http_static_mtypes_hash_free(void *data, void *p)
6091183Svbart@nginx.com {
6101183Svbart@nginx.com     nxt_mp_free(data, p);
6111183Svbart@nginx.com }
612