aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/http_static/static_server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/http_static/static_server.c')
-rw-r--r--src/plugins/http_static/static_server.c275
1 files changed, 200 insertions, 75 deletions
diff --git a/src/plugins/http_static/static_server.c b/src/plugins/http_static/static_server.c
index 040cdca9d7a..48e71f51629 100644
--- a/src/plugins/http_static/static_server.c
+++ b/src/plugins/http_static/static_server.c
@@ -19,6 +19,9 @@
#include <sys/stat.h>
#include <unistd.h>
+#include <http/http_header_names.h>
+#include <http/http_content_types.h>
+
/** @file static_server.c
* Static http server, sufficient to serve .html / .css / .js content.
*/
@@ -55,8 +58,6 @@ hss_session_free (hss_session_t *hs)
{
hss_main_t *hsm = &hss_main;
- pool_put (hsm->sessions[hs->thread_index], hs);
-
if (CLIB_DEBUG)
{
u32 save_thread_index;
@@ -65,6 +66,8 @@ hss_session_free (hss_session_t *hs)
memset (hs, 0xfa, sizeof (*hs));
hs->thread_index = save_thread_index;
}
+
+ pool_put (hsm->sessions[hs->thread_index], hs);
}
/** \brief Disconnect a session
@@ -83,48 +86,84 @@ start_send_data (hss_session_t *hs, http_status_code_t status)
{
http_msg_t msg;
session_t *ts;
+ u8 *headers_buf = 0;
+ u32 n_enq;
+ u64 to_send;
int rv;
ts = session_get (hs->vpp_session_index, hs->thread_index);
+ if (vec_len (hs->resp_headers))
+ {
+ headers_buf = http_serialize_headers (hs->resp_headers);
+ vec_free (hs->resp_headers);
+ msg.data.headers_offset = 0;
+ msg.data.headers_len = vec_len (headers_buf);
+ }
+ else
+ {
+ msg.data.headers_offset = 0;
+ msg.data.headers_len = 0;
+ }
+
msg.type = HTTP_MSG_REPLY;
msg.code = status;
- msg.content_type = hs->content_type;
- msg.data.len = hs->data_len;
+ msg.data.body_len = hs->data_len;
+ msg.data.len = msg.data.body_len + msg.data.headers_len;
- if (hs->data_len > hss_main.use_ptr_thresh)
+ if (msg.data.len > hss_main.use_ptr_thresh)
{
msg.data.type = HTTP_MSG_DATA_PTR;
rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
ASSERT (rv == sizeof (msg));
+ if (msg.data.headers_len)
+ {
+ hs->headers_buf = headers_buf;
+ uword headers = pointer_to_uword (hs->headers_buf);
+ rv =
+ svm_fifo_enqueue (ts->tx_fifo, sizeof (headers), (u8 *) &headers);
+ ASSERT (rv == sizeof (headers));
+ }
+
uword data = pointer_to_uword (hs->data);
rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (data), (u8 *) &data);
- ASSERT (rv == sizeof (sizeof (data)));
+ ASSERT (rv == sizeof (data));
goto done;
}
msg.data.type = HTTP_MSG_DATA_INLINE;
+ msg.data.body_offset = msg.data.headers_len;
rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
ASSERT (rv == sizeof (msg));
- if (!msg.data.len)
+ if (msg.data.headers_len)
+ {
+ rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (headers_buf), headers_buf);
+ ASSERT (rv == msg.data.headers_len);
+ vec_free (headers_buf);
+ }
+
+ if (!msg.data.body_len)
goto done;
- rv = svm_fifo_enqueue (ts->tx_fifo, hs->data_len, hs->data);
+ to_send = hs->data_len;
+ n_enq = clib_min (svm_fifo_size (ts->tx_fifo), to_send);
- if (rv != hs->data_len)
+ rv = svm_fifo_enqueue (ts->tx_fifo, n_enq, hs->data);
+
+ if (rv < to_send)
{
- hs->data_offset = rv;
+ hs->data_offset = (rv > 0) ? rv : 0;
svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
}
done:
if (svm_fifo_set_event (ts->tx_fifo))
- session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
+ session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX);
}
__clib_export void
@@ -142,6 +181,15 @@ hss_session_send_data (hss_url_handler_args_t *args)
hs->data = args->data;
hs->data_len = args->data_len;
hs->free_data = args->free_vec_data;
+
+ /* Set content type only if we have some response data */
+ if (hs->data_len)
+ {
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ http_content_type_token (args->ct));
+ }
+
start_send_data (hs, args->sc);
}
@@ -212,30 +260,27 @@ content_type_from_request (u8 *request)
static int
try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
- u8 *request)
+ u8 *target_path, u8 *target_query, u8 *data)
{
http_status_code_t sc = HTTP_STATUS_OK;
hss_url_handler_args_t args = {};
uword *p, *url_table;
- http_content_type_t type;
int rv;
- if (!hsm->enable_url_handlers || !request)
+ if (!hsm->enable_url_handlers || !target_path)
return -1;
/* zero-length? try "index.html" */
- if (vec_len (request) == 0)
+ if (vec_len (target_path) == 0)
{
- request = format (request, "index.html");
+ target_path = format (target_path, "index.html");
}
- type = content_type_from_request (request);
-
/* Look for built-in GET / POST handlers */
url_table =
(rt == HTTP_REQ_GET) ? hsm->get_url_handlers : hsm->post_url_handlers;
- p = hash_get_mem (url_table, request);
+ p = hash_get_mem (url_table, target_path);
if (!p)
return -1;
@@ -244,10 +289,12 @@ try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
hs->cache_pool_index = ~0;
if (hsm->debug_level > 0)
- clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST", request);
+ clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST",
+ target_path);
- args.reqtype = rt;
- args.request = request;
+ args.req_type = rt;
+ args.query = target_query;
+ args.req_data = data;
args.sh.thread_index = hs->thread_index;
args.sh.session_index = hs->session_index;
@@ -260,18 +307,25 @@ try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
if (rv == HSS_URL_HANDLER_ERROR)
{
clib_warning ("builtin handler %llx hit on %s '%s' but failed!", p[0],
- (rt == HTTP_REQ_GET) ? "GET" : "POST", request);
- sc = HTTP_STATUS_NOT_FOUND;
+ (rt == HTTP_REQ_GET) ? "GET" : "POST", target_path);
+ sc = HTTP_STATUS_BAD_GATEWAY;
}
hs->data = args.data;
hs->data_len = args.data_len;
hs->free_data = args.free_vec_data;
- hs->content_type = type;
+
+ /* Set content type only if we have some response data */
+ if (hs->data_len)
+ {
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ http_content_type_token (args.ct));
+ }
start_send_data (hs, sc);
- if (!hs->data)
+ if (!hs->data_len)
hss_session_disconnect_transport (hs);
return 0;
@@ -335,18 +389,20 @@ try_index_file (hss_main_t *hsm, hss_session_t *hs, u8 *path)
}
redirect =
- format (0,
- "Location: http%s://%U%s%s\r\n\r\n",
- proto == TRANSPORT_PROTO_TLS ? "s" : "", format_ip46_address,
- &endpt.ip, endpt.is_ip4, print_port ? port_str : (u8 *) "", path);
+ format (0, "http%s://%U%s%s", proto == TRANSPORT_PROTO_TLS ? "s" : "",
+ format_ip46_address, &endpt.ip, endpt.is_ip4,
+ print_port ? port_str : (u8 *) "", path);
if (hsm->debug_level > 0)
clib_warning ("redirect: %s", redirect);
vec_free (port_str);
- hs->data = redirect;
- hs->data_len = vec_len (redirect);
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_LOCATION),
+ (const char *) redirect, vec_len (redirect));
+ hs->data = redirect; /* TODO: find better way */
+ hs->data_len = 0;
hs->free_data = 1;
return HTTP_STATUS_MOVED;
@@ -354,29 +410,28 @@ try_index_file (hss_main_t *hsm, hss_session_t *hs, u8 *path)
static int
try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
- u8 *request)
+ u8 *target)
{
http_status_code_t sc = HTTP_STATUS_OK;
- u8 *path;
+ u8 *path, *sanitized_path;
u32 ce_index;
http_content_type_t type;
+ u8 *last_modified;
/* Feature not enabled */
if (!hsm->www_root)
return -1;
- type = content_type_from_request (request);
+ /* Remove dot segments to prevent path traversal */
+ sanitized_path = http_path_remove_dot_segments (target);
/*
* Construct the file to open
- * Browsers are capable of sporadically including a leading '/'
*/
- if (!request)
+ if (!target)
path = format (0, "%s%c", hsm->www_root, 0);
- else if (request[0] == '/')
- path = format (0, "%s%s%c", hsm->www_root, request, 0);
else
- path = format (0, "%s/%s%c", hsm->www_root, request, 0);
+ path = format (0, "%s/%s%c", hsm->www_root, sanitized_path, 0);
if (hsm->debug_level > 0)
clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST", path);
@@ -386,8 +441,8 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
hs->data_offset = 0;
- ce_index =
- hss_cache_lookup_and_attach (&hsm->cache, path, &hs->data, &hs->data_len);
+ ce_index = hss_cache_lookup_and_attach (&hsm->cache, path, &hs->data,
+ &hs->data_len, &last_modified);
if (ce_index == ~0)
{
if (!file_path_is_valid (path))
@@ -406,8 +461,8 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
sc = try_index_file (hsm, hs, path);
goto done;
}
- ce_index =
- hss_cache_add_and_attach (&hsm->cache, path, &hs->data, &hs->data_len);
+ ce_index = hss_cache_add_and_attach (&hsm->cache, path, &hs->data,
+ &hs->data_len, &last_modified);
if (ce_index == ~0)
{
sc = HTTP_STATUS_INTERNAL_ERROR;
@@ -418,43 +473,61 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
hs->path = path;
hs->cache_pool_index = ce_index;
-done:
+ /* Set following headers only for happy path:
+ * Content-Type
+ * Cache-Control max-age
+ */
+ type = content_type_from_request (target);
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_CONTENT_TYPE),
+ http_content_type_token (type));
+ http_add_header (
+ &hs->resp_headers, http_header_name_token (HTTP_HEADER_CACHE_CONTROL),
+ (const char *) hsm->max_age_formatted, vec_len (hsm->max_age_formatted));
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_LAST_MODIFIED),
+ (const char *) last_modified, vec_len (last_modified));
- hs->content_type = type;
+done:
+ vec_free (sanitized_path);
start_send_data (hs, sc);
- if (!hs->data)
+ if (!hs->data_len)
hss_session_disconnect_transport (hs);
return 0;
}
-static int
-handle_request (hss_session_t *hs, http_req_method_t rt, u8 *request)
+static void
+handle_request (hss_session_t *hs, http_req_method_t rt, u8 *target_path,
+ u8 *target_query, u8 *data)
{
hss_main_t *hsm = &hss_main;
- if (!try_url_handler (hsm, hs, rt, request))
- return 0;
+ if (!try_url_handler (hsm, hs, rt, target_path, target_query, data))
+ return;
- if (!try_file_handler (hsm, hs, rt, request))
- return 0;
+ if (!try_file_handler (hsm, hs, rt, target_path))
+ return;
/* Handler did not find anything return 404 */
start_send_data (hs, HTTP_STATUS_NOT_FOUND);
hss_session_disconnect_transport (hs);
-
- return 0;
}
static int
hss_ts_rx_callback (session_t *ts)
{
hss_session_t *hs;
- u8 *request = 0;
+ u8 *target_path = 0, *target_query = 0, *data = 0;
http_msg_t msg;
int rv;
hs = hss_session_get (ts->thread_index, ts->opaque);
+ if (hs->free_data)
+ vec_free (hs->data);
+ hs->data = 0;
+ hs->resp_headers = 0;
+ vec_free (hs->headers_buf);
/* Read the http message header */
rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
@@ -463,26 +536,66 @@ hss_ts_rx_callback (session_t *ts)
if (msg.type != HTTP_MSG_REQUEST ||
(msg.method_type != HTTP_REQ_GET && msg.method_type != HTTP_REQ_POST))
{
- hs->data = 0;
+ http_add_header (&hs->resp_headers,
+ http_header_name_token (HTTP_HEADER_ALLOW),
+ http_token_lit ("GET, POST"));
start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED);
- return 0;
+ goto done;
}
- /* Read request */
- if (msg.data.len)
+ if (msg.data.target_form != HTTP_TARGET_ORIGIN_FORM)
{
- vec_validate (request, msg.data.len - 1);
- rv = svm_fifo_dequeue (ts->rx_fifo, msg.data.len, request);
- ASSERT (rv == msg.data.len);
- /* request must be a proper C-string in addition to a vector */
- vec_add1 (request, 0);
+ start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
+ goto done;
}
- /* Find and send data */
- handle_request (hs, msg.method_type, request);
+ /* Read target path */
+ if (msg.data.target_path_len)
+ {
+ vec_validate (target_path, msg.data.target_path_len - 1);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_path_offset,
+ msg.data.target_path_len, target_path);
+ ASSERT (rv == msg.data.target_path_len);
+ if (http_validate_abs_path_syntax (target_path, 0))
+ {
+ start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
+ goto done;
+ }
+ /* Target path must be a proper C-string in addition to a vector */
+ vec_add1 (target_path, 0);
+ }
+
+ /* Read target query */
+ if (msg.data.target_query_len)
+ {
+ vec_validate (target_query, msg.data.target_query_len - 1);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_query_offset,
+ msg.data.target_query_len, target_query);
+ ASSERT (rv == msg.data.target_query_len);
+ if (http_validate_query_syntax (target_query, 0))
+ {
+ start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
+ goto done;
+ }
+ }
- vec_free (request);
+ /* Read body */
+ if (msg.data.body_len)
+ {
+ vec_validate (data, msg.data.body_len - 1);
+ rv = svm_fifo_peek (ts->rx_fifo, msg.data.body_offset, msg.data.body_len,
+ data);
+ ASSERT (rv == msg.data.body_len);
+ }
+
+ /* Find and send data */
+ handle_request (hs, msg.method_type, target_path, target_query, data);
+done:
+ vec_free (target_path);
+ vec_free (target_query);
+ vec_free (data);
+ svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.len);
return 0;
}
@@ -490,7 +603,8 @@ static int
hss_ts_tx_callback (session_t *ts)
{
hss_session_t *hs;
- u32 to_send;
+ u32 n_enq;
+ u64 to_send;
int rv;
hs = hss_session_get (ts->thread_index, ts->opaque);
@@ -498,7 +612,9 @@ hss_ts_tx_callback (session_t *ts)
return 0;
to_send = hs->data_len - hs->data_offset;
- rv = svm_fifo_enqueue (ts->tx_fifo, to_send, hs->data + hs->data_offset);
+ n_enq = clib_min (svm_fifo_size (ts->tx_fifo), to_send);
+
+ rv = svm_fifo_enqueue (ts->tx_fifo, n_enq, hs->data + hs->data_offset);
if (rv <= 0)
{
@@ -513,7 +629,7 @@ hss_ts_tx_callback (session_t *ts)
}
if (svm_fifo_set_event (ts->tx_fifo))
- session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
+ session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX);
return 0;
}
@@ -607,6 +723,7 @@ hss_ts_cleanup (session_t *s, session_cleanup_ntf_t ntf)
hs->data = 0;
hs->data_offset = 0;
hs->free_data = 0;
+ vec_free (hs->headers_buf);
vec_free (hs->path);
hss_session_free (hs);
@@ -757,6 +874,8 @@ hss_create (vlib_main_t *vm)
if (hsm->enable_url_handlers)
hss_url_handlers_init (hsm);
+ hsm->max_age_formatted = format (0, "max-age=%d", hsm->max_age);
+
return 0;
}
@@ -777,6 +896,7 @@ hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
hsm->private_segment_size = 0;
hsm->fifo_size = 0;
hsm->cache_size = 10 << 20;
+ hsm->max_age = HSS_DEFAULT_MAX_AGE;
/* Get a line of input. */
if (!unformat_user (input, unformat_line_input, line_input))
@@ -808,6 +928,8 @@ hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
;
else if (unformat (line_input, "url-handlers"))
hsm->enable_url_handlers = 1;
+ else if (unformat (line_input, "max-age %d", &hsm->max_age))
+ ;
else
{
error = clib_error_return (0, "unknown input `%U'",
@@ -836,7 +958,10 @@ no_input:
goto done;
}
- vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
+ session_enable_disable_args_t args = { .is_en = 1,
+ .rt_engine_type =
+ RT_BACKEND_ENGINE_RULE_TABLE };
+ vnet_session_enable_disable (vm, &args);
if ((rv = hss_create (vm)))
{
@@ -865,8 +990,8 @@ VLIB_CLI_COMMAND (hss_create_command, static) = {
.path = "http static server",
.short_help =
"http static server www-root <path> [prealloc-fifos <nn>]\n"
- "[private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]\n"
- "[ptr-thresh <nn>] [url-handlers] [debug [nn]]\n",
+ "[private-segment-size <nnMG>] [fifo-size <nbytes>] [max-age <nseconds>]\n"
+ "[uri <uri>] [ptr-thresh <nn>] [url-handlers] [debug [nn]]\n",
.function = hss_create_command_fn,
};
@@ -876,7 +1001,7 @@ format_hss_session (u8 *s, va_list *args)
hss_session_t *hs = va_arg (*args, hss_session_t *);
int __clib_unused verbose = va_arg (*args, int);
- s = format (s, "\n path %s, data length %u, data_offset %u",
+ s = format (s, "\n path %s, data length %llu, data_offset %llu",
hs->path ? hs->path : (u8 *) "[none]", hs->data_len,
hs->data_offset);
return s;