From fdb5a5a0e4aad887add68985191be114dcc99e38 Mon Sep 17 00:00:00 2001 From: Florin Coras Date: Fri, 28 Jan 2022 15:26:39 -0800 Subject: http_static: incorporate builtinurl plugin External handlers can still be registered via hss_register_url_handler but url handlers must be enabled when server is created. builtinurl plugin to be removed in a future patch Type: refactor Signed-off-by: Florin Coras Change-Id: I94e103d908b9e118c7927b997a21ce3f67809889 --- src/plugins/http_static/CMakeLists.txt | 1 + src/plugins/http_static/FEATURE.yaml | 20 ++- src/plugins/http_static/builtinurl/json_urls.c | 197 +++++++++++++++++++++++++ src/plugins/http_static/http_static.c | 20 ++- src/plugins/http_static/http_static.h | 13 +- src/plugins/http_static/static_server.c | 90 +++++++---- 6 files changed, 290 insertions(+), 51 deletions(-) create mode 100644 src/plugins/http_static/builtinurl/json_urls.c (limited to 'src/plugins/http_static') diff --git a/src/plugins/http_static/CMakeLists.txt b/src/plugins/http_static/CMakeLists.txt index f9ccb15beae..cf9e41e30e5 100644 --- a/src/plugins/http_static/CMakeLists.txt +++ b/src/plugins/http_static/CMakeLists.txt @@ -17,6 +17,7 @@ add_vpp_plugin(http_static http_static.c static_server.c http_static.h + builtinurl/json_urls.c API_FILES http_static.api diff --git a/src/plugins/http_static/FEATURE.yaml b/src/plugins/http_static/FEATURE.yaml index d40855f2de2..ff4e147c495 100644 --- a/src/plugins/http_static/FEATURE.yaml +++ b/src/plugins/http_static/FEATURE.yaml @@ -1,10 +1,18 @@ --- -name: Static http https server -maintainer: Dave Barach +name: Static HTTP(S) Server +maintainer: + - Dave Barach + - Florin Coras features: - - An extensible static http/https server with caching -description: "A simple caching static http / https server - A built-in vpp host stack application. - Supports HTTP GET and HTTP POST methods." + - HTTP GET/POST handling + - LRU file caching + - pluggable URL handlers + - builtin json URL handles: + - version.json - vpp version info + - interface_list.json - list of interfaces + - interface_stats - single interface via HTTP POST + - interface_stats - all intfcs via HTTP GET." +description: "Static HTTP(S) server implemented as a + built-in vpp host stack application. " state: production properties: [API, CLI, MULTITHREAD] diff --git a/src/plugins/http_static/builtinurl/json_urls.c b/src/plugins/http_static/builtinurl/json_urls.c new file mode 100644 index 00000000000..ccd70fb1799 --- /dev/null +++ b/src/plugins/http_static/builtinurl/json_urls.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2019 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +int +handle_get_version (http_req_method_t reqtype, u8 *request, hss_session_t *hs) +{ + u8 *s = 0; + + s = format (s, "{\"vpp_details\": {"); + s = format (s, " \"version\": \"%s\",", VPP_BUILD_VER); + s = format (s, " \"build_date\": \"%s\"}}\r\n", VPP_BUILD_DATE); + + hs->data = s; + hs->data_offset = 0; + hs->cache_pool_index = ~0; + hs->free_data = 1; + return 0; +} + +void +trim_path_from_request (u8 *s, char *path) +{ + u8 *cp; + int trim_length = strlen (path) + 1 /* remove '?' */; + + /* Get rid of the path and question-mark */ + vec_delete (s, trim_length, 0); + + /* Tail trim irrelevant browser info */ + cp = s; + while ((cp - s) < vec_len (s)) + { + if (*cp == ' ') + { + /* + * Makes request a vector which happens to look + * like a c-string. + */ + *cp = 0; + _vec_len (s) = cp - s; + break; + } + cp++; + } +} + +int +handle_get_interface_stats (http_req_method_t reqtype, u8 *request, + hss_session_t *hs) +{ + u8 *s = 0, *stats = 0; + uword *p; + u32 *sw_if_indices = 0; + vnet_hw_interface_t *hi; + vnet_sw_interface_t *si; + char *q = "\""; + int i; + int need_comma = 0; + u8 *format_vnet_sw_interface_cntrs (u8 * s, vnet_interface_main_t * im, + vnet_sw_interface_t * si, int json); + vnet_main_t *vnm = vnet_get_main (); + vnet_interface_main_t *im = &vnm->interface_main; + + /* Get stats for a single interface via http POST */ + if (reqtype == HTTP_REQ_POST) + { + trim_path_from_request (request, "interface_stats.json"); + + /* Find the sw_if_index */ + p = hash_get (im->hw_interface_by_name, request); + if (!p) + { + s = format (s, "{\"interface_stats\": {[\n"); + s = format (s, " \"name\": \"%s\",", request); + s = format (s, " \"error\": \"%s\"", "UnknownInterface"); + s = format (s, "]}\n"); + goto out; + } + + vec_add1 (sw_if_indices, p[0]); + } + else /* default, HTTP_BUILTIN_METHOD_GET */ + { + pool_foreach (hi, im->hw_interfaces) + { + vec_add1 (sw_if_indices, hi->sw_if_index); + } + } + + s = format (s, "{%sinterface_stats%s: [\n", q, q); + + for (i = 0; i < vec_len (sw_if_indices); i++) + { + si = vnet_get_sw_interface (vnm, sw_if_indices[i]); + if (need_comma) + s = format (s, ",\n"); + + need_comma = 1; + + s = format (s, "{%sname%s: %s%U%s, ", q, q, q, + format_vnet_sw_if_index_name, vnm, sw_if_indices[i], q); + + stats = format_vnet_sw_interface_cntrs (stats, &vnm->interface_main, si, + 1 /* want json */); + if (vec_len (stats)) + s = format (s, "%v}", stats); + else + s = format (s, "%snone%s: %strue%s}", q, q, q, q); + vec_reset_length (stats); + } + + s = format (s, "]}\n"); + +out: + hs->data = s; + hs->data_offset = 0; + hs->cache_pool_index = ~0; + hs->free_data = 1; + vec_free (sw_if_indices); + vec_free (stats); + return 0; +} + +int +handle_get_interface_list (http_req_method_t reqtype, u8 *request, + hss_session_t *hs) +{ + u8 *s = 0; + int i; + vnet_main_t *vnm = vnet_get_main (); + vnet_interface_main_t *im = &vnm->interface_main; + vnet_hw_interface_t *hi; + u32 *hw_if_indices = 0; + int need_comma = 0; + + /* Construct vector of active hw_if_indexes ... */ + pool_foreach (hi, im->hw_interfaces) + { + /* No point in mentioning "local0"... */ + if (hi - im->hw_interfaces) + vec_add1 (hw_if_indices, hi - im->hw_interfaces); + } + + /* Build answer */ + s = format (s, "{\"interface_list\": [\n"); + for (i = 0; i < vec_len (hw_if_indices); i++) + { + if (need_comma) + s = format (s, ",\n"); + hi = pool_elt_at_index (im->hw_interfaces, hw_if_indices[i]); + s = format (s, "\"%v\"", hi->name); + need_comma = 1; + } + s = format (s, "]}\n"); + vec_free (hw_if_indices); + + hs->data = s; + hs->data_offset = 0; + hs->cache_pool_index = ~0; + hs->free_data = 1; + return 0; +} + +void +hss_builtinurl_json_handlers_init (void) +{ + hss_register_url_handler (handle_get_version, "version.json", HTTP_REQ_GET); + hss_register_url_handler (handle_get_interface_list, "interface_list.json", + HTTP_REQ_GET); + hss_register_url_handler (handle_get_interface_stats, "interface_stats.json", + HTTP_REQ_GET); + hss_register_url_handler (handle_get_interface_stats, "interface_stats.json", + HTTP_REQ_POST); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/http_static/http_static.c b/src/plugins/http_static/http_static.c index 077371cac52..b8d6931b4c6 100644 --- a/src/plugins/http_static/http_static.c +++ b/src/plugins/http_static/http_static.c @@ -32,19 +32,17 @@ #define REPLY_MSG_ID_BASE hsm->msg_id_base #include -/** \brief Register a builtin GET or POST handler - */ __clib_export void -http_static_server_register_builtin_handler (void *fp, char *url, - http_req_method_t request_type) +hss_register_url_handler (hss_url_handler_t fp, const char *url, + http_req_method_t request_type) { hss_main_t *hsm = &hss_main; - uword *p, *builtin_table; + uword *p, *url_table; - builtin_table = (request_type == HTTP_REQ_GET) ? hsm->get_url_handlers : - hsm->post_url_handlers; + url_table = (request_type == HTTP_REQ_GET) ? hsm->get_url_handlers : + hsm->post_url_handlers; - p = hash_get_mem (builtin_table, url); + p = hash_get_mem (url_table, url); if (p) { @@ -53,16 +51,16 @@ http_static_server_register_builtin_handler (void *fp, char *url, return; } - hash_set_mem (builtin_table, url, (uword) fp); + hash_set_mem (url_table, url, (uword) fp); /* * Need to update the hash table pointer in http_static_server_main * in case we just expanded it... */ if (request_type == HTTP_REQ_GET) - hsm->get_url_handlers = builtin_table; + hsm->get_url_handlers = url_table; else - hsm->post_url_handlers = builtin_table; + hsm->post_url_handlers = url_table; } /** \brief API helper function for vl_api_http_static_enable_t messages diff --git a/src/plugins/http_static/http_static.h b/src/plugins/http_static/http_static.h index a1eaa6e1144..05d6ee05a4c 100644 --- a/src/plugins/http_static/http_static.h +++ b/src/plugins/http_static/http_static.h @@ -67,9 +67,10 @@ typedef struct int inuse; } hss_cache_entry_t; +typedef int (*hss_url_handler_t) (http_req_method_t, u8 *, hss_session_t *); + /** \brief Main data structure */ - typedef struct { /** Per thread vector of session pools */ @@ -127,14 +128,20 @@ typedef struct u8 *uri; /** Threshold for switching to ptr data in http msgs */ u32 use_ptr_thresh; + /** Enable the use of builtinurls */ + u8 enable_url_handlers; } hss_main_t; extern hss_main_t hss_main; int hss_create (vlib_main_t *vm); -void http_static_server_register_builtin_handler (void *fp, char *url, - http_req_method_t type); +/** + * Register a GET or POST URL handler + */ +void hss_register_url_handler (hss_url_handler_t fp, const char *url, + http_req_method_t type); +void hss_builtinurl_json_handlers_init (void); #endif /* __included_http_static_h__ */ diff --git a/src/plugins/http_static/static_server.c b/src/plugins/http_static/static_server.c index 94124495bd8..a78b5fe4f2b 100644 --- a/src/plugins/http_static/static_server.c +++ b/src/plugins/http_static/static_server.c @@ -294,15 +294,52 @@ done: session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX); } +static int +try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, + u8 *request, http_status_code_t *sc) +{ + uword *p, *url_table; + hss_url_handler_t fp; + int rv; + + if (!hsm->enable_url_handlers) + return -1; + + /* 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); + if (!p) + return -1; + + if (hsm->debug_level > 0) + clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST", request); + + fp = (hss_url_handler_t) p[0]; + hs->path = 0; + rv = fp (rt, request, hs); + if (rv) + { + 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; + } + + return 0; +} + static int find_data (hss_session_t *hs, http_req_method_t rt, u8 *request) { + http_status_code_t sc = HTTP_STATUS_OK; hss_main_t *hsm = &hss_main; - u8 *path; struct stat _sb, *sb = &_sb; clib_error_t *error; - uword *p, *builtin_table; - http_status_code_t sc = HTTP_STATUS_OK; + u8 *path; + + if (!try_url_handler (hsm, hs, rt, request, &sc)) + goto done; /* * Construct the file to open @@ -316,29 +353,6 @@ find_data (hss_session_t *hs, http_req_method_t rt, u8 *request) if (hsm->debug_level > 0) clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST", path); - /* Look for built-in GET / POST handlers */ - builtin_table = - (rt == HTTP_REQ_GET) ? hsm->get_url_handlers : hsm->post_url_handlers; - - p = hash_get_mem (builtin_table, request); - - if (p) - { - int rv; - int (*fp) (http_req_method_t, u8 *, hss_session_t *); - fp = (void *) p[0]; - hs->path = path; - rv = (*fp) (rt, request, hs); - if (rv) - { - 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; - } - goto done; - } - /* Try to find the file. 2x special cases to find index.html */ if (stat ((char *) path, sb) < 0 /* cant even stat the file */ || sb->st_size < 20 /* file too small */ @@ -788,6 +802,18 @@ hss_listen (void) return rv; } +static void +hss_url_handlers_init (hss_main_t *hsm) +{ + if (!hsm->get_url_handlers) + { + hsm->get_url_handlers = hash_create_string (0, sizeof (uword)); + hsm->post_url_handlers = hash_create_string (0, sizeof (uword)); + } + + hss_builtinurl_json_handlers_init (); +} + int hss_create (vlib_main_t *vm) { @@ -814,8 +840,8 @@ hss_create (vlib_main_t *vm) /* Init path-to-cache hash table */ BV (clib_bihash_init) (&hsm->name_to_data, "http cache", 128, 32 << 20); - hsm->get_url_handlers = hash_create_string (0, sizeof (uword)); - hsm->post_url_handlers = hash_create_string (0, sizeof (uword)); + if (hsm->enable_url_handlers) + hss_url_handlers_init (hsm); return 0; } @@ -867,6 +893,8 @@ hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input, else if (unformat (line_input, "ptr-thresh %U", unformat_memory_size, &hsm->use_ptr_thresh)) ; + else if (unformat (line_input, "url-handlers")) + hsm->enable_url_handlers = 1; else { error = clib_error_return (0, "unknown input `%U'", @@ -881,9 +909,9 @@ no_input: if (error) goto done; - if (hsm->www_root == 0) + if (hsm->www_root == 0 && !hsm->enable_url_handlers) { - error = clib_error_return (0, "Must specify www-root "); + error = clib_error_return (0, "Must set www-root or url-handlers"); goto done; } @@ -924,7 +952,7 @@ VLIB_CLI_COMMAND (hss_create_command, static) = { .short_help = "http static server www-root [prealloc-fifos ]\n" "[private-segment-size ] [fifo-size ] [uri ]\n" - "[ptr-thresh ][debug [nn]]\n", + "[ptr-thresh ] [url-handlers] [debug [nn]]\n", .function = hss_create_command_fn, }; -- cgit 1.2.3-korg