Basic support for serving static files.

This commit is contained in:
Valentin Bartenev 2019-09-19 02:47:09 +03:00
parent c554941b4f
commit 08a8d1510d
12 changed files with 953 additions and 29 deletions

View file

@ -84,6 +84,7 @@ NXT_LIB_SRCS=" \
src/nxt_http_response.c \
src/nxt_http_error.c \
src/nxt_http_route.c \
src/nxt_http_static.c \
src/nxt_application.c \
src/nxt_external.c \
src/nxt_port_hash.c \

View file

@ -71,6 +71,7 @@ typedef struct {
nxt_conf_value_t *conf;
nxt_mp_t *pool;
nxt_str_t error;
void *ctx;
} nxt_conf_validation_t;

View file

@ -8,6 +8,7 @@
#include <nxt_conf.h>
#include <nxt_cert.h>
#include <nxt_router.h>
#include <nxt_http.h>
typedef enum {
@ -45,6 +46,12 @@ static nxt_int_t nxt_conf_vldt_type(nxt_conf_validation_t *vldt,
static nxt_int_t nxt_conf_vldt_error(nxt_conf_validation_t *vldt,
const char *fmt, ...);
static nxt_int_t nxt_conf_vldt_mtypes(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_mtypes_type(nxt_conf_validation_t *vldt,
nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_mtypes_extension(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_listener(nxt_conf_validation_t *vldt,
nxt_str_t *name, nxt_conf_value_t *value);
#if (NXT_TLS)
@ -130,6 +137,16 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[] = {
};
static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[] = {
{ nxt_string("mime_types"),
NXT_CONF_VLDT_OBJECT,
&nxt_conf_vldt_mtypes,
NULL },
NXT_CONF_VLDT_END
};
static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[] = {
{ nxt_string("header_read_timeout"),
NXT_CONF_VLDT_INTEGER,
@ -161,6 +178,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[] = {
&nxt_conf_vldt_object,
(void *) &nxt_conf_vldt_websocket_members },
{ nxt_string("static"),
NXT_CONF_VLDT_OBJECT,
&nxt_conf_vldt_object,
(void *) &nxt_conf_vldt_static_members },
NXT_CONF_VLDT_END
};
@ -289,6 +311,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_action_members[] = {
&nxt_conf_vldt_pass,
NULL },
{ nxt_string("share"),
NXT_CONF_VLDT_STRING,
NULL,
NULL },
NXT_CONF_VLDT_END
};
@ -735,6 +762,108 @@ nxt_conf_vldt_error(nxt_conf_validation_t *vldt, const char *fmt, ...)
}
typedef struct {
nxt_mp_t *pool;
nxt_str_t *type;
nxt_lvlhsh_t hash;
} nxt_conf_vldt_mtypes_ctx_t;
static nxt_int_t
nxt_conf_vldt_mtypes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
void *data)
{
nxt_int_t ret;
nxt_conf_vldt_mtypes_ctx_t ctx;
ctx.pool = nxt_mp_create(1024, 128, 256, 32);
if (nxt_slow_path(ctx.pool == NULL)) {
return NXT_ERROR;
}
nxt_lvlhsh_init(&ctx.hash);
vldt->ctx = &ctx;
ret = nxt_conf_vldt_object_iterator(vldt, value,
&nxt_conf_vldt_mtypes_type);
vldt->ctx = NULL;
nxt_mp_destroy(ctx.pool);
return ret;
}
static nxt_int_t
nxt_conf_vldt_mtypes_type(nxt_conf_validation_t *vldt, nxt_str_t *name,
nxt_conf_value_t *value)
{
nxt_int_t ret;
nxt_conf_vldt_mtypes_ctx_t *ctx;
ret = nxt_conf_vldt_type(vldt, name, value,
NXT_CONF_VLDT_STRING|NXT_CONF_VLDT_ARRAY);
if (ret != NXT_OK) {
return ret;
}
ctx = vldt->ctx;
ctx->type = nxt_mp_get(ctx->pool, sizeof(nxt_str_t));
if (nxt_slow_path(ctx->type == NULL)) {
return NXT_ERROR;
}
*ctx->type = *name;
if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
return nxt_conf_vldt_array_iterator(vldt, value,
&nxt_conf_vldt_mtypes_extension);
}
/* NXT_CONF_STRING */
return nxt_conf_vldt_mtypes_extension(vldt, value);
}
static nxt_int_t
nxt_conf_vldt_mtypes_extension(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value)
{
nxt_str_t ext, *dup_type;
nxt_conf_vldt_mtypes_ctx_t *ctx;
ctx = vldt->ctx;
if (nxt_conf_type(value) != NXT_CONF_STRING) {
return nxt_conf_vldt_error(vldt, "The \"%V\" MIME type array must "
"contain only strings.", ctx->type);
}
nxt_conf_get_string(value, &ext);
if (ext.length == 0) {
return nxt_conf_vldt_error(vldt, "An empty file extension for "
"the \"%V\" MIME type.", ctx->type);
}
dup_type = nxt_http_static_mtypes_hash_find(&ctx->hash, &ext);
if (dup_type != NULL) {
return nxt_conf_vldt_error(vldt, "The \"%V\" file extension has been "
"declared for \"%V\" and \"%V\" "
"MIME types at the same time.",
&ext, dup_type, ctx->type);
}
return nxt_http_static_mtypes_hash_add(ctx->pool, &ctx->hash,
&ext, ctx->type);
}
static nxt_int_t
nxt_conf_vldt_listener(nxt_conf_validation_t *vldt, nxt_str_t *name,
nxt_conf_value_t *value)

View file

@ -24,7 +24,9 @@ typedef enum {
NXT_HTTP_NOT_MODIFIED = 304,
NXT_HTTP_BAD_REQUEST = 400,
NXT_HTTP_FORBIDDEN = 403,
NXT_HTTP_NOT_FOUND = 404,
NXT_HTTP_METHOD_NOT_ALLOWED = 405,
NXT_HTTP_REQUEST_TIMEOUT = 408,
NXT_HTTP_LENGTH_REQUIRED = 411,
NXT_HTTP_PAYLOAD_TOO_LARGE = 413,
@ -187,6 +189,25 @@ typedef struct {
} nxt_http_proto_table_t;
#define NXT_HTTP_DATE_LEN nxt_length("Wed, 31 Dec 1986 16:40:00 GMT")
nxt_inline u_char *
nxt_http_date(u_char *buf, struct tm *tm)
{
static const char *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
"Sat" };
static const char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
return nxt_sprintf(buf, buf + NXT_HTTP_DATE_LEN,
"%s, %02d %s %4d %02d:%02d:%02d GMT",
week[tm->tm_wday], tm->tm_mday,
month[tm->tm_mon], tm->tm_year + 1900,
tm->tm_hour, tm->tm_min, tm->tm_sec);
}
nxt_int_t nxt_http_init(nxt_task_t *task, nxt_runtime_t *rt);
nxt_int_t nxt_h1p_init(nxt_task_t *task, nxt_runtime_t *rt);
nxt_int_t nxt_http_response_hash_init(nxt_task_t *task, nxt_runtime_t *rt);
@ -225,6 +246,14 @@ nxt_http_pass_t *nxt_http_pass_application(nxt_task_t *task,
void nxt_http_routes_cleanup(nxt_task_t *task, nxt_http_routes_t *routes);
void nxt_http_pass_cleanup(nxt_task_t *task, nxt_http_pass_t *pass);
nxt_http_pass_t *nxt_http_static_handler(nxt_task_t *task,
nxt_http_request_t *r, nxt_http_pass_t *pass);
nxt_int_t nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash);
nxt_int_t nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash,
nxt_str_t *extension, nxt_str_t *type);
nxt_str_t *nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash,
nxt_str_t *extension);
nxt_http_pass_t *nxt_http_request_application(nxt_task_t *task,
nxt_http_request_t *r, nxt_http_pass_t *pass);

View file

@ -17,8 +17,8 @@ static void nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj,
void *data);
static void nxt_http_request_done(nxt_task_t *task, void *obj, void *data);
static u_char *nxt_http_date(u_char *buf, nxt_realtime_t *now, struct tm *tm,
size_t size, const char *format);
static u_char *nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now,
struct tm *tm, size_t size, const char *format);
static const nxt_http_request_state_t nxt_http_request_init_state;
@ -27,9 +27,9 @@ static const nxt_http_request_state_t nxt_http_request_body_state;
nxt_time_string_t nxt_http_date_cache = {
(nxt_atomic_uint_t) -1,
nxt_http_date,
"%s, %02d %s %4d %02d:%02d:%02d GMT",
nxt_length("Wed, 31 Dec 1986 16:40:00 GMT"),
nxt_http_date_cache_handler,
NULL,
NXT_HTTP_DATE_LEN,
NXT_THREAD_TIME_GMT,
NXT_THREAD_TIME_SEC,
};
@ -578,17 +578,8 @@ nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data)
static u_char *
nxt_http_date(u_char *buf, nxt_realtime_t *now, struct tm *tm, size_t size,
const char *format)
nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now, struct tm *tm,
size_t size, const char *format)
{
static const char *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
"Sat" };
static const char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
return nxt_sprintf(buf, buf + size, format,
week[tm->tm_wday], tm->tm_mday,
month[tm->tm_mon], tm->tm_year + 1900,
tm->tm_hour, tm->tm_min, tm->tm_sec);
return nxt_http_date(buf, tm);
}

View file

@ -376,15 +376,9 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_http_route_match_conf_t mtcf;
static nxt_str_t pass_path = nxt_string("/action/pass");
static nxt_str_t share_path = nxt_string("/action/share");
static nxt_str_t match_path = nxt_string("/match");
pass_conf = nxt_conf_get_path(cv, &pass_path);
if (nxt_slow_path(pass_conf == NULL)) {
return NULL;
}
nxt_conf_get_string(pass_conf, &pass);
match_conf = nxt_conf_get_path(cv, &match_path);
n = (match_conf != NULL) ? nxt_conf_object_members_count(match_conf) : 0;
@ -401,6 +395,19 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
match->pass.handler = NULL;
match->items = n;
pass_conf = nxt_conf_get_path(cv, &pass_path);
if (pass_conf == NULL) {
pass_conf = nxt_conf_get_path(cv, &share_path);
if (nxt_slow_path(pass_conf == NULL)) {
return NULL;
}
match->pass.handler = nxt_http_static_handler;
}
nxt_conf_get_string(pass_conf, &pass);
string = nxt_str_dup(mp, &match->pass.name, &pass);
if (nxt_slow_path(string == NULL)) {
return NULL;
@ -870,13 +877,18 @@ static void
nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_http_route_t *route)
{
nxt_http_pass_t *pass;
nxt_http_route_match_t **match, **end;
match = &route->match[0];
end = match + route->items;
while (match < end) {
nxt_http_pass_resolve(task, tmcf, &(*match)->pass);
pass = &(*match)->pass;
if (pass->handler == NULL) {
nxt_http_pass_resolve(task, tmcf, &(*match)->pass);
}
match++;
}

599
src/nxt_http_static.c Normal file
View file

@ -0,0 +1,599 @@
/*
* Copyright (C) NGINX, Inc.
*/
#include <nxt_router.h>
#include <nxt_http.h>
#define NXT_HTTP_STATIC_BUF_COUNT 2
#define NXT_HTTP_STATIC_BUF_SIZE (128 * 1024)
static void nxt_http_static_extract_extension(nxt_str_t *path,
nxt_str_t *extension);
static void nxt_http_static_body_handler(nxt_task_t *task, void *obj,
void *data);
static void nxt_http_static_buf_completion(nxt_task_t *task, void *obj,
void *data);
static nxt_int_t nxt_http_static_mtypes_hash_test(nxt_lvlhsh_query_t *lhq,
void *data);
static void *nxt_http_static_mtypes_hash_alloc(void *data, size_t size);
static void nxt_http_static_mtypes_hash_free(void *data, void *p);
static const nxt_http_request_state_t nxt_http_static_send_state;
nxt_http_pass_t *
nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r,
nxt_http_pass_t *pass)
{
size_t alloc, encode;
u_char *p;
struct tm tm;
nxt_buf_t *fb;
nxt_int_t ret;
nxt_str_t index, extension, *mtype;
nxt_uint_t level;
nxt_bool_t need_body;
nxt_file_t *f;
nxt_file_info_t fi;
nxt_http_field_t *field;
nxt_http_status_t status;
nxt_router_conf_t *rtcf;
nxt_work_handler_t body_handler;
if (nxt_slow_path(!nxt_str_eq(r->method, "GET", 3))) {
if (!nxt_str_eq(r->method, "HEAD", 4)) {
nxt_http_request_error(task, r, NXT_HTTP_METHOD_NOT_ALLOWED);
return NULL;
}
need_body = 0;
} else {
need_body = 1;
}
f = nxt_mp_zget(r->mem_pool, sizeof(nxt_file_t));
if (nxt_slow_path(f == NULL)) {
goto fail;
}
f->fd = NXT_FILE_INVALID;
if (r->path->start[r->path->length - 1] == '/') {
/* TODO: dynamic index setting. */
nxt_str_set(&index, "index.html");
nxt_str_set(&extension, ".html");
} else {
nxt_str_null(&index);
nxt_str_null(&extension);
}
alloc = pass->name.length + r->path->length + index.length + 1;
f->name = nxt_mp_nget(r->mem_pool, alloc);
if (nxt_slow_path(f->name == NULL)) {
goto fail;
}
p = f->name;
p = nxt_cpymem(p, pass->name.start, pass->name.length);
p = nxt_cpymem(p, r->path->start, r->path->length);
p = nxt_cpymem(p, index.start, index.length);
*p = '\0';
ret = nxt_file_open(task, f, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);
if (nxt_slow_path(ret != NXT_OK)) {
switch (f->error) {
case NXT_ENOENT:
case NXT_ENOTDIR:
case NXT_ENAMETOOLONG:
level = NXT_LOG_ERR;
status = NXT_HTTP_NOT_FOUND;
break;
case NXT_EACCES:
level = NXT_LOG_ERR;
status = NXT_HTTP_FORBIDDEN;
break;
default:
level = NXT_LOG_ALERT;
status = NXT_HTTP_INTERNAL_SERVER_ERROR;
break;
}
if (status != NXT_HTTP_NOT_FOUND) {
nxt_log(task, level, "open(\"%FN\") failed %E", f->name, f->error);
}
nxt_http_request_error(task, r, status);
return NULL;
}
ret = nxt_file_info(f, &fi);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
if (nxt_fast_path(nxt_is_file(&fi))) {
r->status = NXT_HTTP_OK;
r->resp.content_length_n = nxt_file_size(&fi);
field = nxt_list_zero_add(r->resp.fields);
if (nxt_slow_path(field == NULL)) {
goto fail;
}
nxt_http_field_name_set(field, "Last-Modified");
p = nxt_mp_nget(r->mem_pool, NXT_HTTP_DATE_LEN);
if (nxt_slow_path(p == NULL)) {
goto fail;
}
nxt_localtime(nxt_file_mtime(&fi), &tm);
field->value = p;
field->value_length = nxt_http_date(p, &tm) - p;
field = nxt_list_zero_add(r->resp.fields);
if (nxt_slow_path(field == NULL)) {
goto fail;
}
nxt_http_field_name_set(field, "ETag");
alloc = NXT_TIME_T_HEXLEN + NXT_OFF_T_HEXLEN + 3;
p = nxt_mp_nget(r->mem_pool, alloc);
if (nxt_slow_path(p == NULL)) {
goto fail;
}
field->value = p;
field->value_length = nxt_sprintf(p, p + alloc, "\"%xT-%xO\"",
nxt_file_mtime(&fi),
nxt_file_size(&fi))
- p;
if (extension.start == NULL) {
nxt_http_static_extract_extension(r->path, &extension);
}
rtcf = r->conf->socket_conf->router_conf;
mtype = nxt_http_static_mtypes_hash_find(&rtcf->mtypes_hash,
&extension);
if (mtype != NULL) {
field = nxt_list_zero_add(r->resp.fields);
if (nxt_slow_path(field == NULL)) {
goto fail;
}
nxt_http_field_name_set(field, "Content-Type");
field->value = mtype->start;
field->value_length = mtype->length;
}
if (need_body && nxt_file_size(&fi) > 0) {
fb = nxt_mp_zget(r->mem_pool, NXT_BUF_FILE_SIZE);
if (nxt_slow_path(fb == NULL)) {
goto fail;
}
fb->file = f;
fb->file_end = nxt_file_size(&fi);
r->out = fb;
body_handler = &nxt_http_static_body_handler;
} else {
nxt_file_close(task, f);
body_handler = NULL;
}
} else {
/* Not a file. */
nxt_file_close(task, f);
f = NULL;
if (nxt_slow_path(!nxt_is_dir(&fi))) {
nxt_log(task, NXT_LOG_ERR, "\"%FN\" is not a regular file",
f->name);
nxt_http_request_error(task, r, NXT_HTTP_NOT_FOUND);
return NULL;
}
r->status = NXT_HTTP_MOVED_PERMANENTLY;
r->resp.content_length_n = 0;
field = nxt_list_zero_add(r->resp.fields);
if (nxt_slow_path(field == NULL)) {
goto fail;
}
nxt_http_field_name_set(field, "Location");
encode = nxt_encode_uri(NULL, r->path->start, r->path->length);
alloc = r->path->length + encode * 2 + 1;
if (r->args->length > 0) {
alloc += 1 + r->args->length;
}
p = nxt_mp_nget(r->mem_pool, alloc);
if (nxt_slow_path(p == NULL)) {
goto fail;
}
field->value = p;
field->value_length = alloc;
if (encode > 0) {
p = (u_char *) nxt_encode_uri(p, r->path->start, r->path->length);
} else {
p = nxt_cpymem(p, r->path->start, r->path->length);
}
*p++ = '/';
if (r->args->length > 0) {
*p++ = '?';
nxt_memcpy(p, r->args->start, r->args->length);
}
body_handler = NULL;
}
nxt_http_request_header_send(task, r, body_handler);
r->state = &nxt_http_static_send_state;
return NULL;
fail:
nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
if (f != NULL && f->fd != NXT_FILE_INVALID) {
nxt_file_close(task, f);
}
return NULL;
}
static void
nxt_http_static_extract_extension(nxt_str_t *path, nxt_str_t *extension)
{
u_char ch, *p, *end;
end = path->start + path->length;
p = end;
for ( ;; ) {
/* There's always '/' in the beginning of the request path. */
p--;
ch = *p;
switch (ch) {
case '/':
p++;
/* Fall through. */
case '.':
extension->length = end - p;
extension->start = p;
return;
}
}
}
static void
nxt_http_static_body_handler(nxt_task_t *task, void *obj, void *data)
{
size_t alloc;
nxt_buf_t *fb, *b, **next, *out;
nxt_off_t rest;
nxt_int_t n;
nxt_work_queue_t *wq;
nxt_http_request_t *r;
r = obj;
fb = r->out;
rest = fb->file_end - fb->file_pos;
out = NULL;
next = &out;
n = 0;
do {
alloc = nxt_min(rest, NXT_HTTP_STATIC_BUF_SIZE);
b = nxt_buf_mem_alloc(r->mem_pool, alloc, 0);
if (nxt_slow_path(b == NULL)) {
goto fail;
}
b->completion_handler = nxt_http_static_buf_completion;
b->parent = r;
nxt_mp_retain(r->mem_pool);
*next = b;
next = &b->next;
rest -= alloc;
} while (rest > 0 && ++n < NXT_HTTP_STATIC_BUF_COUNT);
wq = &task->thread->engine->fast_work_queue;
nxt_sendbuf_drain(task, wq, out);
return;
fail:
while (out != NULL) {
b = out;
out = b->next;
nxt_mp_free(r->mem_pool, b);
nxt_mp_release(r->mem_pool);
}
}
static const nxt_http_request_state_t nxt_http_static_send_state
nxt_aligned(64) =
{
.error_handler = nxt_http_request_error_handler,
};
static void
nxt_http_static_buf_completion(nxt_task_t *task, void *obj, void *data)
{
ssize_t n, size;
nxt_buf_t *b, *fb;
nxt_off_t rest;
nxt_http_request_t *r;
b = obj;
r = data;
fb = r->out;
if (nxt_slow_path(fb == NULL || r->error)) {
goto clean;
}
rest = fb->file_end - fb->file_pos;
size = nxt_buf_mem_size(&b->mem);
size = nxt_min(rest, (nxt_off_t) size);
n = nxt_file_read(fb->file, b->mem.start, size, fb->file_pos);
if (n != size) {
if (n >= 0) {
nxt_log(task, NXT_LOG_ERR, "file \"%FN\" has changed "
"while sending response to a client", fb->file->name);
}
nxt_http_request_error_handler(task, r, r->proto.any);
goto clean;
}
if (n == rest) {
nxt_file_close(task, fb->file);
r->out = NULL;
b->next = nxt_http_buf_last(r);
} else {
fb->file_pos += n;
b->next = NULL;
}
b->mem.pos = b->mem.start;
b->mem.free = b->mem.pos + n;
nxt_http_request_send(task, r, b);
return;
clean:
nxt_mp_free(r->mem_pool, b);
nxt_mp_release(r->mem_pool);
if (fb != NULL) {
nxt_file_close(task, fb->file);
r->out = NULL;
}
}
nxt_int_t
nxt_http_static_mtypes_init(nxt_mp_t *mp, nxt_lvlhsh_t *hash)
{
nxt_str_t *type, extension;
nxt_int_t ret;
nxt_uint_t i;
static const struct {
nxt_str_t type;
const char *extension;
} default_types[] = {
{ nxt_string("text/html"), ".html" },
{ nxt_string("text/html"), ".htm" },
{ nxt_string("text/css"), ".css" },
{ nxt_string("image/svg+xml"), ".svg" },
{ nxt_string("image/svg+xml"), ".svg" },
{ nxt_string("image/webp"), ".webp" },
{ nxt_string("image/png"), ".png" },
{ nxt_string("image/jpeg"), ".jpeg" },
{ nxt_string("image/jpeg"), ".jpg" },
{ nxt_string("image/gif"), ".gif" },
{ nxt_string("image/x-icon"), ".ico" },
{ nxt_string("font/woff"), ".woff" },
{ nxt_string("font/woff2"), ".woff2" },
{ nxt_string("font/otf"), ".otf" },
{ nxt_string("font/ttf"), ".ttf" },
{ nxt_string("text/plain"), ".txt" },
{ nxt_string("text/markdown"), ".md" },
{ nxt_string("text/x-rst"), ".rst" },
{ nxt_string("application/javascript"), ".js" },
{ nxt_string("application/json"), ".json" },
{ nxt_string("application/xml"), ".xml" },
{ nxt_string("application/rss+xml"), ".rss" },
{ nxt_string("application/atom+xml"), ".atom" },
{ nxt_string("application/pdf"), ".pdf" },
{ nxt_string("application/zip"), ".zip" },
{ nxt_string("audio/mpeg"), ".mp3" },
{ nxt_string("audio/ogg"), ".ogg" },
{ nxt_string("audio/midi"), ".midi" },
{ nxt_string("audio/midi"), ".mid" },
{ nxt_string("audio/flac"), ".flac" },
{ nxt_string("audio/aac"), ".aac" },
{ nxt_string("audio/wav"), ".wav" },
{ nxt_string("video/mpeg"), ".mpeg" },
{ nxt_string("video/mpeg"), ".mpg" },
{ nxt_string("video/mp4"), ".mp4" },
{ nxt_string("video/webm"), ".webm" },
{ nxt_string("video/x-msvideo"), ".avi" },
{ nxt_string("application/octet-stream"), ".exe" },
{ nxt_string("application/octet-stream"), ".bin" },
{ nxt_string("application/octet-stream"), ".dll" },
{ nxt_string("application/octet-stream"), ".iso" },
{ nxt_string("application/octet-stream"), ".img" },
{ nxt_string("application/octet-stream"), ".msi" },
{ nxt_string("application/octet-stream"), ".deb" },
{ nxt_string("application/octet-stream"), ".rpm" },
};
for (i = 0; i < nxt_nitems(default_types); i++) {
type = (nxt_str_t *) &default_types[i].type;
extension.start = (u_char *) default_types[i].extension;
extension.length = nxt_strlen(extension.start);
ret = nxt_http_static_mtypes_hash_add(mp, hash, &extension, type);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
}
return NXT_OK;
}
static const nxt_lvlhsh_proto_t nxt_http_static_mtypes_hash_proto
nxt_aligned(64) =
{
NXT_LVLHSH_DEFAULT,
nxt_http_static_mtypes_hash_test,
nxt_http_static_mtypes_hash_alloc,
nxt_http_static_mtypes_hash_free,
};
typedef struct {
nxt_str_t extension;
nxt_str_t *type;
} nxt_http_static_mtype_t;
nxt_int_t
nxt_http_static_mtypes_hash_add(nxt_mp_t *mp, nxt_lvlhsh_t *hash,
nxt_str_t *extension, nxt_str_t *type)
{
nxt_lvlhsh_query_t lhq;
nxt_http_static_mtype_t *mtype;
mtype = nxt_mp_get(mp, sizeof(nxt_http_static_mtype_t));
if (nxt_slow_path(mtype == NULL)) {
return NXT_ERROR;
}
mtype->extension = *extension;
mtype->type = type;
lhq.key = *extension;
lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length);
lhq.replace = 1;
lhq.value = mtype;
lhq.proto = &nxt_http_static_mtypes_hash_proto;
lhq.pool = mp;
return nxt_lvlhsh_insert(hash, &lhq);
}
nxt_str_t *
nxt_http_static_mtypes_hash_find(nxt_lvlhsh_t *hash, nxt_str_t *extension)
{
nxt_lvlhsh_query_t lhq;
nxt_http_static_mtype_t *mtype;
lhq.key = *extension;
lhq.key_hash = nxt_djb_hash_lowcase(lhq.key.start, lhq.key.length);
lhq.proto = &nxt_http_static_mtypes_hash_proto;
if (nxt_lvlhsh_find(hash, &lhq) == NXT_OK) {
mtype = lhq.value;
return mtype->type;
}
return NULL;
}
static nxt_int_t
nxt_http_static_mtypes_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
{
nxt_http_static_mtype_t *mtype;
mtype = data;
return nxt_strcasestr_eq(&lhq->key, &mtype->extension) ? NXT_OK
: NXT_DECLINED;
}
static void *
nxt_http_static_mtypes_hash_alloc(void *data, size_t size)
{
return nxt_mp_align(data, size, size);
}
static void
nxt_http_static_mtypes_hash_free(void *data, void *p)
{
nxt_mp_free(data, p);
}

View file

@ -122,6 +122,8 @@ static void nxt_router_conf_send(nxt_task_t *task,
static nxt_int_t nxt_router_conf_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, u_char *start, u_char *end);
static nxt_int_t nxt_router_conf_process_static(nxt_task_t *task,
nxt_router_conf_t *rtcf, nxt_conf_value_t *conf);
static nxt_app_t *nxt_router_app_find(nxt_queue_t *queue, nxt_str_t *name);
static void nxt_router_listen_socket_rpc_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_socket_conf_t *skcf);
@ -1399,7 +1401,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_conf_value_t *conf, *http, *value, *websocket;
nxt_conf_value_t *applications, *application;
nxt_conf_value_t *listeners, *listener;
nxt_conf_value_t *routes_conf;
nxt_conf_value_t *routes_conf, *static_conf;
nxt_socket_conf_t *skcf;
nxt_http_routes_t *routes;
nxt_event_engine_t *engine;
@ -1419,6 +1421,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
#if (NXT_TLS)
static nxt_str_t certificate_path = nxt_string("/tls/certificate");
#endif
static nxt_str_t static_path = nxt_string("/settings/http/static");
static nxt_str_t websocket_path = nxt_string("/settings/http/websocket");
conf = nxt_conf_json_parse(tmcf->mem_pool, start, end, NULL);
@ -1440,6 +1443,13 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
tmcf->router_conf->threads = nxt_ncpu;
}
static_conf = nxt_conf_get_path(conf, &static_path);
ret = nxt_router_conf_process_static(task, tmcf->router_conf, static_conf);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
router = tmcf->router_conf->router;
applications = nxt_conf_get_path(conf, &applications_path);
@ -1788,6 +1798,87 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
}
static nxt_int_t
nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf,
nxt_conf_value_t *conf)
{
uint32_t next, i;
nxt_mp_t *mp;
nxt_str_t *type, extension, str;
nxt_int_t ret;
nxt_uint_t exts;
nxt_conf_value_t *mtypes_conf, *ext_conf, *value;
static nxt_str_t mtypes_path = nxt_string("/mime_types");
mp = rtcf->mem_pool;
ret = nxt_http_static_mtypes_init(mp, &rtcf->mtypes_hash);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
if (conf == NULL) {
return NXT_OK;
}
mtypes_conf = nxt_conf_get_path(conf, &mtypes_path);
if (mtypes_conf != NULL) {
next = 0;
for ( ;; ) {
ext_conf = nxt_conf_next_object_member(mtypes_conf, &str, &next);
if (ext_conf == NULL) {
break;
}
type = nxt_str_dup(mp, NULL, &str);
if (nxt_slow_path(type == NULL)) {
return NXT_ERROR;
}
if (nxt_conf_type(ext_conf) == NXT_CONF_STRING) {
nxt_conf_get_string(ext_conf, &str);
if (nxt_slow_path(nxt_str_dup(mp, &extension, &str) == NULL)) {
return NXT_ERROR;
}
ret = nxt_http_static_mtypes_hash_add(mp, &rtcf->mtypes_hash,
&extension, type);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
continue;
}
exts = nxt_conf_array_elements_count(ext_conf);
for (i = 0; i < exts; i++) {
value = nxt_conf_get_array_element(ext_conf, i);
nxt_conf_get_string(value, &str);
if (nxt_slow_path(nxt_str_dup(mp, &extension, &str) == NULL)) {
return NXT_ERROR;
}
ret = nxt_http_static_mtypes_hash_add(mp, &rtcf->mtypes_hash,
&extension, type);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
}
}
}
return NXT_OK;
}
static nxt_app_t *
nxt_router_app_find(nxt_queue_t *queue, nxt_str_t *name)
{

View file

@ -38,9 +38,13 @@ typedef struct {
typedef struct {
uint32_t count;
uint32_t threads;
nxt_mp_t *mem_pool;
nxt_router_t *router;
nxt_http_routes_t *routes;
nxt_mp_t *mem_pool;
nxt_lvlhsh_t mtypes_hash;
nxt_router_access_log_t *access_log;
} nxt_router_conf_t;

View file

@ -12,8 +12,8 @@
/*
* Supported formats:
*
* %[0][width][x][X]O nxt_off_t
* %[0][width]T nxt_time_t
* %[0][width][x|X]O nxt_off_t
* %[0][width][x|X]T nxt_time_t
* %[0][width][u][x|X]z ssize_t/size_t
* %[0][width][u][x|X]d int/u_int
* %[0][width][u][x|X]l long

View file

@ -507,3 +507,69 @@ nxt_decode_uri(u_char *dst, u_char *src, size_t length)
return dst;
}
uintptr_t
nxt_encode_uri(u_char *dst, u_char *src, size_t length)
{
u_char *end;
nxt_uint_t n;
static const u_char hex[16] = "0123456789ABCDEF";
/* " ", "#", "%", "?", %00-%1F, %7F-%FF */
static const uint32_t escape[] = {
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
/* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
0x80000029, /* 1000 0000 0000 0000 0000 0000 0010 1001 */
/* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
/* ~}| {zyx wvut srqp onml kjih gfed cba` */
0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
};
end = src + length;
if (dst == NULL) {
/* Find the number of the characters to be escaped. */
n = 0;
while (src < end) {
if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
n++;
}
src++;
}
return (uintptr_t) n;
}
while (src < end) {
if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
*dst++ = '%';
*dst++ = hex[*src >> 4];
*dst++ = hex[*src & 0xf];
} else {
*dst++ = *src;
}
src++;
}
return (uintptr_t) dst;
}

View file

@ -169,6 +169,7 @@ NXT_EXPORT nxt_bool_t nxt_strvers_match(u_char *version, u_char *prefix,
size_t length);
NXT_EXPORT u_char *nxt_decode_uri(u_char *dst, u_char *src, size_t length);
NXT_EXPORT uintptr_t nxt_encode_uri(u_char *dst, u_char *src, size_t length);
#endif /* _NXT_STRING_H_INCLUDED_ */