Basic support for serving static files.
This commit is contained in:
parent
c554941b4f
commit
08a8d1510d
12 changed files with 953 additions and 29 deletions
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
599
src/nxt_http_static.c
Normal 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);
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
Loading…
Reference in a new issue