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