From 0a897eb8d83ca2fbbb315f023d638dc319831c45 Mon Sep 17 00:00:00 2001 From: Florin Coras Date: Fri, 4 Apr 2025 17:56:18 -0400 Subject: [PATCH] http_static: support multiple listeners Add support for multiple listeners that can be added and removed via "http static listener" cli. Type: feature Change-Id: I024d1bf8b02b2046f1757656944b2d48662a1254 Signed-off-by: Florin Coras --- src/plugins/http_static/http_cache.c | 8 + src/plugins/http_static/http_cache.h | 1 + src/plugins/http_static/http_static.c | 21 +- src/plugins/http_static/http_static.h | 71 ++++-- src/plugins/http_static/static_server.c | 425 ++++++++++++++++++++++++-------- test/asf/test_http_static.py | 17 +- 6 files changed, 413 insertions(+), 130 deletions(-) diff --git a/src/plugins/http_static/http_cache.c b/src/plugins/http_static/http_cache.c index 2e63e335d47..61f1f50ea3b 100644 --- a/src/plugins/http_static/http_cache.c +++ b/src/plugins/http_static/http_cache.c @@ -400,6 +400,14 @@ hss_cache_init (hss_cache_t *hc, uword cache_size, u8 debug_level) hc->first_index = hc->last_index = ~0; } +void +hss_cache_free (hss_cache_t *hc) +{ + hss_cache_clear (hc); + BV (clib_bihash_free) (&hc->name_to_data); + clib_spinlock_free (&hc->cache_lock); +} + /** \brief format a file cache entry */ static u8 * diff --git a/src/plugins/http_static/http_cache.h b/src/plugins/http_static/http_cache.h index 21f71a924d5..c1e363443ee 100644 --- a/src/plugins/http_static/http_cache.h +++ b/src/plugins/http_static/http_cache.h @@ -67,6 +67,7 @@ u32 hss_cache_add_and_attach (hss_cache_t *hc, u8 *path, u8 **data, void hss_cache_detach_entry (hss_cache_t *hc, u32 ce_index); u32 hss_cache_clear (hss_cache_t *hc); void hss_cache_init (hss_cache_t *hc, uword cache_size, u8 debug_level); +void hss_cache_free (hss_cache_t *hc); u8 *format_hss_cache (u8 *s, va_list *args); diff --git a/src/plugins/http_static/http_static.c b/src/plugins/http_static/http_static.c index 7a12f37b8d3..2574f65c223 100644 --- a/src/plugins/http_static/http_static.c +++ b/src/plugins/http_static/http_static.c @@ -73,16 +73,18 @@ hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos, int rv; hsm->fifo_size = fifo_size; - hsm->cache_size = cache_limit; hsm->prealloc_fifos = prealloc_fifos; hsm->private_segment_size = private_segment_size; - hsm->www_root = format (0, "%s%c", www_root, 0); - hsm->uri = format (0, "%s%c", uri, 0); - hsm->max_age = max_age; - hsm->max_body_size = max_body_size; - hsm->keepalive_timeout = keepalive_timeout; - - if (vec_len (hsm->www_root) < 2) + if (uri && parse_uri ((char *) uri, &hsm->default_listener.sep)) + return VNET_API_ERROR_INVALID_VALUE; + hsm->default_listener.www_root = format (0, "%s%c", www_root, 0); + hsm->default_listener.cache_size = cache_limit; + hsm->default_listener.max_age = max_age; + hsm->default_listener.max_body_size = max_body_size; + hsm->default_listener.keepalive_timeout = keepalive_timeout; + hsm->have_default_listener = 1; + + if (vec_len (hsm->default_listener.www_root) < 2) return VNET_API_ERROR_INVALID_VALUE; if (hsm->app_index != ~0) @@ -99,8 +101,7 @@ hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos, case 0: break; default: - vec_free (hsm->www_root); - vec_free (hsm->uri); + vec_free (hsm->default_listener.www_root); return VNET_API_ERROR_INIT_FAILED; } return 0; diff --git a/src/plugins/http_static/http_static.h b/src/plugins/http_static/http_static.h index 2b6de03c71a..25ee19548d2 100644 --- a/src/plugins/http_static/http_static.h +++ b/src/plugins/http_static/http_static.h @@ -42,6 +42,8 @@ typedef struct hss_session_ /** vpp session index, handle */ u32 vpp_session_index; session_handle_t vpp_session_handle; + /** Index of listener for which connection was accepted */ + u32 listener_index; u8 *target_path; u8 *target_query; http_req_method_t rt; @@ -63,6 +65,8 @@ typedef struct hss_session_ u8 *headers_buf; /** POST body left to receive */ u64 left_recv; + /** threshold for switching to pointers */ + u64 use_ptr_thresh; int (*read_body_handler) (struct hss_session_ *hs, session_t *ts); } hss_session_t; @@ -119,6 +123,34 @@ typedef hss_url_handler_rc_t (*hss_url_handler_fn) (hss_url_handler_args_t *); typedef void (*hss_register_url_fn) (hss_url_handler_fn, char *, int); typedef void (*hss_session_send_fn) (hss_url_handler_args_t *args); +typedef struct hss_listener_ +{ + /** Path to file hash table */ + hss_cache_t cache; + /** The bind session endpoint e.g., tcp://0.0.0.0:80 */ + session_endpoint_cfg_t sep; + /** root path to be served */ + u8 *www_root; + /** Threshold for switching to ptr data in http msgs */ + u64 use_ptr_thresh; + /** Max cache size before LRU occurs */ + u64 cache_size; + /** Maximum size of a request body (in bytes) **/ + u64 max_body_size; + /** Timeout during which client connection will stay open */ + u32 keepalive_timeout; + /** How long a response is considered fresh (in seconds) */ + u32 max_age; + /** Formatted max_age: "max-age=xyz" */ + u8 *max_age_formatted; + /** Enable the use of builtinurls */ + u8 enable_url_handlers; + /** Index in listener pool */ + u32 l_index; + /** Listener session handle */ + session_handle_t session_handle; +} hss_listener_t; + /** \brief Main data structure */ typedef struct @@ -126,15 +158,13 @@ typedef struct /** Per thread vector of session pools */ hss_session_t **sessions; + /** Listeners pool */ + hss_listener_t *listeners; + /** Hash tables for built-in GET and POST handlers */ uword *get_url_handlers; uword *post_url_handlers; - hss_cache_t cache; - - /** root path to be served */ - u8 *www_root; - /** Application index */ u32 app_index; @@ -150,6 +180,11 @@ typedef struct * Config */ + /** Listener configured with server, if any */ + hss_listener_t default_listener; + u8 have_default_listener; + u8 is_init; + /** Enable debug messages */ int debug_level; /** Number of preallocated fifos, usually 0 */ @@ -158,22 +193,6 @@ typedef struct u64 private_segment_size; /** Size of the allocated rx, tx fifos, roughly 8K or so */ u32 fifo_size; - /** The bind URI, defaults to tcp://0.0.0.0/80 */ - u8 *uri; - /** Threshold for switching to ptr data in http msgs */ - u64 use_ptr_thresh; - /** Enable the use of builtinurls */ - u8 enable_url_handlers; - /** Max cache size before LRU occurs */ - u64 cache_size; - /** How long a response is considered fresh (in seconds) */ - u32 max_age; - /** Maximum size of a request body (in bytes) **/ - u64 max_body_size; - /** Formatted max_age: "max-age=xyz" */ - u8 *max_age_formatted; - /** Timeout during which client connection will stay open */ - u32 keepalive_timeout; /** hash table of file extensions to mime types string indices */ uword *mime_type_indices_by_file_extensions; @@ -183,6 +202,16 @@ extern hss_main_t hss_main; int hss_create (vlib_main_t *vm); +static inline hss_listener_t * +hss_listener_get (u32 l_index) +{ + hss_main_t *hsm = &hss_main; + + if (pool_is_free_index (hsm->listeners, l_index)) + return 0; + return pool_elt_at_index (hsm->listeners, l_index); +} + /** * Register a GET or POST URL handler */ diff --git a/src/plugins/http_static/static_server.c b/src/plugins/http_static/static_server.c index 75481f840b2..c21658ce925 100644 --- a/src/plugins/http_static/static_server.c +++ b/src/plugins/http_static/static_server.c @@ -14,6 +14,7 @@ */ #include +#include #include #include @@ -134,7 +135,7 @@ start_send_data (hss_session_t *hs, http_status_code_t status) msg.data.headers_len = hs->resp_headers.tail_offset; msg.data.len = msg.data.body_len + msg.data.headers_len; - if (msg.data.len > hss_main.use_ptr_thresh) + if (msg.data.len > hs->use_ptr_thresh) { msg.data.type = HTTP_MSG_DATA_PTR; rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg); @@ -294,7 +295,7 @@ try_url_handler (hss_session_t *hs) target_path = hs->target_path; - if (!hsm->enable_url_handlers || !target_path) + if (!target_path) return -1; /* zero-length? try "index.html" */ @@ -389,8 +390,9 @@ file_path_is_valid (u8 *path) } static u32 -try_index_file (hss_main_t *hsm, hss_session_t *hs, u8 *path) +try_index_file (hss_listener_t *l, hss_session_t *hs, u8 *path) { + hss_main_t *hsm = &hss_main; u8 *port_str = 0, *redirect; transport_endpoint_t endpt; transport_proto_t proto; @@ -418,7 +420,7 @@ try_index_file (hss_main_t *hsm, hss_session_t *hs, u8 *path) /* * We found an index.html file, build a redirect */ - vec_delete (path, vec_len (hsm->www_root) - 1, 0); + vec_delete (path, vec_len (l->www_root) - 1, 0); ts = session_get (hs->vpp_session_index, hs->thread_index); session_get_endpoint (ts, &endpt, 1 /* is_local */); @@ -463,10 +465,13 @@ try_file_handler (hss_session_t *hs) u32 ce_index, max_dequeue; http_content_type_t type; u8 *last_modified; + hss_listener_t *l; session_t *ts; + l = hss_listener_get (hs->listener_index); + /* Feature not enabled */ - if (!hsm->www_root) + if (!l->www_root) return -1; /* Discard request body */ @@ -492,9 +497,9 @@ try_file_handler (hss_session_t *hs) * Construct the file to open */ if (!sanitized_path) - path = format (0, "%s%c", hsm->www_root, 0); + path = format (0, "%s%c", l->www_root, 0); else - path = format (0, "%s/%s%c", hsm->www_root, sanitized_path, 0); + path = format (0, "%s/%s%c", l->www_root, sanitized_path, 0); if (hsm->debug_level > 0) clib_warning ("%s '%s'", (hs->rt == HTTP_REQ_GET) ? "GET" : "POST", path); @@ -504,7 +509,7 @@ try_file_handler (hss_session_t *hs) hs->data_offset = 0; - ce_index = hss_cache_lookup_and_attach (&hsm->cache, path, &hs->data, + ce_index = hss_cache_lookup_and_attach (&l->cache, path, &hs->data, &hs->data_len, &last_modified); if (ce_index == ~0) { @@ -521,10 +526,10 @@ try_file_handler (hss_session_t *hs) sc = HTTP_STATUS_NOT_FOUND; goto done; } - sc = try_index_file (hsm, hs, path); + sc = try_index_file (l, hs, path); goto done; } - ce_index = hss_cache_add_and_attach (&hsm->cache, path, &hs->data, + ce_index = hss_cache_add_and_attach (&l->cache, path, &hs->data, &hs->data_len, &last_modified); if (ce_index == ~0) { @@ -545,8 +550,8 @@ try_file_handler (hss_session_t *hs) if (hss_add_header (hs, HTTP_HEADER_CONTENT_TYPE, http_content_type_token (type)) || hss_add_header (hs, HTTP_HEADER_CACHE_CONTROL, - (const char *) hsm->max_age_formatted, - vec_len (hsm->max_age_formatted)) || + (const char *) l->max_age_formatted, + vec_len (l->max_age_formatted)) || hss_add_header (hs, HTTP_HEADER_LAST_MODIFIED, (const char *) last_modified, vec_len (last_modified))) { @@ -565,7 +570,18 @@ done: static void handle_request (hss_session_t *hs) { - if (!try_url_handler (hs)) + hss_listener_t *l; + + l = hss_listener_get (hs->listener_index); + + if (hs->left_recv > l->max_body_size) + { + start_send_data (hs, HTTP_STATUS_CONTENT_TOO_LARGE); + hss_session_disconnect_transport (hs); + return; + } + + if (l->enable_url_handlers && !try_url_handler (hs)) return; if (!try_file_handler (hs)) @@ -593,7 +609,6 @@ file_handler_discard_body (hss_session_t *hs, session_t *ts) static int hss_ts_rx_callback (session_t *ts) { - hss_main_t *hsm = &hss_main; hss_session_t *hs; http_msg_t msg; int rv; @@ -663,12 +678,6 @@ hss_ts_rx_callback (session_t *ts) if (msg.data.body_len && msg.method_type == HTTP_REQ_POST) { - if (msg.data.body_len > hsm->max_body_size) - { - start_send_data (hs, HTTP_STATUS_CONTENT_TOO_LARGE); - goto err_done; - } - hs->left_recv = msg.data.body_len; /* drop everything up to body */ svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.body_offset); @@ -726,6 +735,7 @@ static int hss_ts_accept_callback (session_t *ts) { hss_session_t *hs; + session_t *ls; u32 thresh; hs = hss_session_alloc (ts->thread_index); @@ -733,6 +743,11 @@ hss_ts_accept_callback (session_t *ts) hs->vpp_session_index = ts->session_index; hs->vpp_session_handle = session_handle (ts); + /* Link to listener context */ + ls = listen_session_get_from_handle (ts->listener_handle); + hs->listener_index = ls->opaque; + hs->use_ptr_thresh = hss_listener_get (hs->listener_index)->use_ptr_thresh; + /* The application sets a threshold for it's fifo to get notified when * additional data can be enqueued. We want to keep the TX fifo reasonably * full, however avoid entering a state where the @@ -788,7 +803,6 @@ hss_add_segment_callback (u32 client_index, u64 segment_handle) static void hss_ts_cleanup (session_t *s, session_cleanup_ntf_t ntf) { - hss_main_t *hsm = &hss_main; hss_session_t *hs; if (ntf == SESSION_CLEANUP_TRANSPORT) @@ -800,7 +814,9 @@ hss_ts_cleanup (session_t *s, session_cleanup_ntf_t ntf) if (hs->cache_pool_index != ~0) { - hss_cache_detach_entry (&hsm->cache, hs->cache_pool_index); + hss_listener_t *l = hss_listener_get (hs->listener_index); + if (l) + hss_cache_detach_entry (&l->cache, hs->cache_pool_index); hs->cache_pool_index = ~0; } @@ -885,30 +901,22 @@ hss_transport_needs_crypto (transport_proto_t proto) } static int -hss_listen (void) +hss_listen (hss_listener_t *l, session_handle_t *lh) { hss_main_t *hsm = &hss_main; - session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL; vnet_listen_args_t _a, *a = &_a; - char *uri = "tcp://0.0.0.0/80"; u8 need_crypto; transport_endpt_ext_cfg_t *ext_cfg; int rv; - transport_endpt_cfg_http_t http_cfg = { hsm->keepalive_timeout, 0 }; + transport_endpt_cfg_http_t http_cfg = { l->keepalive_timeout, 0 }; clib_memset (a, 0, sizeof (*a)); a->app_index = hsm->app_index; - if (hsm->uri) - uri = (char *) hsm->uri; - - if (parse_uri (uri, &sep)) - return -1; - - need_crypto = hss_transport_needs_crypto (sep.transport_proto); + need_crypto = hss_transport_needs_crypto (l->sep.transport_proto); - sep.transport_proto = TRANSPORT_PROTO_HTTP; - clib_memcpy (&a->sep_ext, &sep, sizeof (sep)); + l->sep.transport_proto = TRANSPORT_PROTO_HTTP; + clib_memcpy (&a->sep_ext, &l->sep, sizeof (l->sep)); ext_cfg = session_endpoint_add_ext_cfg ( &a->sep_ext, TRANSPORT_ENDPT_EXT_CFG_HTTP, sizeof (http_cfg)); @@ -922,7 +930,8 @@ hss_listen (void) ext_cfg->crypto.ckpair_index = hsm->ckpair_index; } - rv = vnet_listen (a); + if (!(rv = vnet_listen (a))) + *lh = a->handle; session_endpoint_free_ext_cfgs (&a->sep_ext); @@ -932,13 +941,75 @@ hss_listen (void) static void hss_url_handlers_init (hss_main_t *hsm) { - if (!hsm->get_url_handlers) + if (hsm->get_url_handlers) + return; + + 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_listener_add (hss_listener_t *l_cfg) +{ + hss_main_t *hsm = &hss_main; + session_handle_t lh; + app_listener_t *al; + hss_listener_t *l; + session_t *ls; + + if (hss_listen (l_cfg, &lh)) { - hsm->get_url_handlers = hash_create_string (0, sizeof (uword)); - hsm->post_url_handlers = hash_create_string (0, sizeof (uword)); + clib_warning ("failed to start listening"); + return -1; } - hss_builtinurl_json_handlers_init (); + pool_get (hsm->listeners, l); + *l = *l_cfg; + l->l_index = l - hsm->listeners; + l->session_handle = lh; + + al = app_listener_get_w_handle (lh); + ls = app_listener_get_session (al); + ls->opaque = l->l_index; + + if (l->www_root) + hss_cache_init (&l->cache, l->cache_size, hsm->debug_level); + if (l->enable_url_handlers) + hss_url_handlers_init (hsm); + + l->max_age_formatted = format (0, "max-age=%d", l->max_age); + + return 0; +} + +int +hss_listener_del (hss_listener_t *l_cfg) +{ + hss_main_t *hsm = &hss_main; + hss_listener_t *l; + u8 found = 0; + + pool_foreach (l, hsm->listeners) + { + if (clib_memcmp (&l_cfg->sep, &l->sep, sizeof (l_cfg->sep)) == 0) + { + found = 1; + break; + } + } + + if (!found) + return -1; + + vnet_unlisten_args_t args = { .handle = l->session_handle, hsm->app_index }; + + vec_free (l->www_root); + vec_free (l->max_age_formatted); + hss_cache_free (&l->cache); + pool_put (hsm->listeners, l); + + return vnet_unlisten (&args); } int @@ -951,24 +1022,25 @@ hss_create (vlib_main_t *vm) num_threads = 1 /* main thread */ + vtm->n_threads; vec_validate (hsm->sessions, num_threads - 1); + /* Make sure session layer is enabled */ + session_enable_disable_args_t args = { .is_en = 1, + .rt_engine_type = + RT_BACKEND_ENGINE_RULE_TABLE }; + vnet_session_enable_disable (vm, &args); + if (hss_attach ()) { clib_warning ("failed to attach server"); return -1; } - if (hss_listen ()) + + if (hsm->have_default_listener && hss_listener_add (&hsm->default_listener)) { clib_warning ("failed to start listening"); return -1; } - if (hsm->www_root) - hss_cache_init (&hsm->cache, hsm->cache_size, hsm->debug_level); - - if (hsm->enable_url_handlers) - hss_url_handlers_init (hsm); - - hsm->max_age_formatted = format (0, "max-age=%d", hsm->max_age); + hsm->is_init = 1; return 0; } @@ -979,20 +1051,23 @@ hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input, { unformat_input_t _line_input, *line_input = &_line_input; hss_main_t *hsm = &hss_main; + hss_listener_t *l = &hsm->default_listener; clib_error_t *error = 0; + char *uri = 0; u64 seg_size; int rv; if (hsm->app_index != (u32) ~0) - return clib_error_return (0, "http server already running..."); + return clib_error_return (0, "http static server already initialized..."); hsm->prealloc_fifos = 0; hsm->private_segment_size = 0; hsm->fifo_size = 0; - hsm->cache_size = 10 << 20; - hsm->max_age = HSS_DEFAULT_MAX_AGE; - hsm->max_body_size = HSS_DEFAULT_MAX_BODY_SIZE; - hsm->keepalive_timeout = HSS_DEFAULT_KEEPALIVE_TIMEOUT; + + l->cache_size = 10 << 20; + l->max_age = HSS_DEFAULT_MAX_AGE; + l->max_body_size = HSS_DEFAULT_MAX_BODY_SIZE; + l->keepalive_timeout = HSS_DEFAULT_KEEPALIVE_TIMEOUT; /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) @@ -1000,38 +1075,40 @@ hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input, while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { - if (unformat (line_input, "www-root %s", &hsm->www_root)) - ; - else - if (unformat (line_input, "prealloc-fifos %d", &hsm->prealloc_fifos)) - ; - else if (unformat (line_input, "private-segment-size %U", - unformat_memory_size, &seg_size)) + /* Server config */ + if (unformat (line_input, "private-segment-size %U", + unformat_memory_size, &seg_size)) hsm->private_segment_size = seg_size; else if (unformat (line_input, "fifo-size %U", unformat_memory_size, &hsm->fifo_size)) ; - else if (unformat (line_input, "cache-size %U", unformat_memory_size, - &hsm->cache_size)) - ; - else if (unformat (line_input, "uri %s", &hsm->uri)) + else if (unformat (line_input, "prealloc-fifos %d", + &hsm->prealloc_fifos)) ; else if (unformat (line_input, "debug %d", &hsm->debug_level)) ; - else if (unformat (line_input, "keepalive-timeout %d", - &hsm->keepalive_timeout)) - ; else if (unformat (line_input, "debug")) hsm->debug_level = 1; - else if (unformat (line_input, "ptr-thresh %U", unformat_memory_size, - &hsm->use_ptr_thresh)) + /* Default listener parameters */ + else if (unformat (line_input, "uri %s", &uri)) + ; + else if (unformat (line_input, "www-root %s", &l->www_root)) ; else if (unformat (line_input, "url-handlers")) - hsm->enable_url_handlers = 1; - else if (unformat (line_input, "max-age %d", &hsm->max_age)) + l->enable_url_handlers = 1; + else if (unformat (line_input, "cache-size %U", unformat_memory_size, + &l->cache_size)) + ; + else if (unformat (line_input, "max-age %d", &l->max_age)) ; else if (unformat (line_input, "max-body-size %U", unformat_memory_size, - &hsm->max_body_size)) + &l->max_body_size)) + ; + else if (unformat (line_input, "keepalive-timeout %d", + &l->keepalive_timeout)) + ; + else if (unformat (line_input, "ptr-thresh %U", unformat_memory_size, + &l->use_ptr_thresh)) ; else { @@ -1048,28 +1125,33 @@ no_input: if (error) goto done; - if (hsm->www_root == 0 && !hsm->enable_url_handlers) + if (l->www_root) { - error = clib_error_return (0, "Must set www-root or url-handlers"); - goto done; + /* Maintain legacy default uri behavior */ + if (!uri) + uri = "tcp://0.0.0.0:80"; + if (l->cache_size < (128 << 10)) + { + error = clib_error_return (0, "cache-size must be at least 128kb"); + vec_free (l->www_root); + goto done; + } } - if (hsm->cache_size < (128 << 10)) + if (uri) { - error = clib_error_return (0, "cache-size must be at least 128kb"); - vec_free (hsm->www_root); - goto done; + if (parse_uri (uri, &l->sep)) + { + error = clib_error_return (0, "failed to parse uri %s", uri); + goto done; + } + hsm->have_default_listener = 1; } - 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))) { error = clib_error_return (0, "server_create returned %d", rv); - vec_free (hsm->www_root); + vec_free (l->www_root); } done: @@ -1093,13 +1175,114 @@ done: VLIB_CLI_COMMAND (hss_create_command, static) = { .path = "http static server", .short_help = - "http static server www-root [prealloc-fifos ]\n" + "http static server [www-root ] [url-handlers]\n" "[private-segment-size ] [fifo-size ] [max-age ]\n" - "[uri ] [ptr-thresh ] [url-handlers] [debug [nn]]\n" + "[uri ] [ptr-thresh ] [prealloc-fifos ] [debug [nn]]\n" "[keepalive-timeout ] [max-body-size ]\n", .function = hss_create_command_fn, }; +static clib_error_t * +hss_add_del_listener_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + hss_main_t *hsm = &hss_main; + clib_error_t *error = 0; + hss_listener_t _l = {}, *l = &_l; + u8 is_add = 1; + char *uri = 0; + + if (!hsm->is_init) + return clib_error_return (0, "Static server not initialized"); + + if (!unformat_user (input, unformat_line_input, line_input)) + return clib_error_return (0, "No input provided"); + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "add")) + is_add = 1; + else if (unformat (line_input, "del")) + is_add = 0; + else if (unformat (line_input, "uri %s", &uri)) + ; + else if (unformat (line_input, "www-root %s", &l->www_root)) + ; + else if (unformat (line_input, "url-handlers")) + l->enable_url_handlers = 1; + else if (unformat (line_input, "cache-size %U", unformat_memory_size, + &l->cache_size)) + ; + else if (unformat (line_input, "keepalive-timeout %d", + &l->keepalive_timeout)) + ; + else if (unformat (line_input, "ptr-thresh %U", unformat_memory_size, + &l->use_ptr_thresh)) + ; + else if (unformat (line_input, "max-age %d", &l->max_age)) + ; + else if (unformat (line_input, "max-body-size %U", unformat_memory_size, + &l->max_body_size)) + ; + else + { + error = clib_error_return (0, "unknown input `%U'", + format_unformat_error, line_input); + break; + } + } + unformat_free (line_input); + + if (!uri) + { + error = clib_error_return (0, "Must set uri"); + goto done; + } + + if (parse_uri (uri, &l->sep)) + { + error = clib_error_return (0, "failed to parse uri %s", uri); + goto done; + } + + if (!is_add) + { + hss_listener_del (l); + goto done; + } + + if (l->www_root == 0 && !l->enable_url_handlers) + { + error = clib_error_return (0, "Must set www-root or url-handlers"); + goto done; + } + + if (l->cache_size < (128 << 10)) + { + error = clib_error_return (0, "cache-size must be at least 128kb"); + goto done; + } + + if (hss_listener_add (l)) + { + error = clib_error_return (0, "failed to create listener"); + goto done; + } + +done: + + vec_free (uri); + return error; +} + +VLIB_CLI_COMMAND (hss_add_del_listener_command, static) = { + .path = "http static listener", + .short_help = "http static listener [add|del] uri \n" + "[www-root ] [url-handlers] \n", + .function = hss_add_del_listener_command_fn, +}; + static u8 * format_hss_session (u8 *s, va_list *args) { @@ -1112,14 +1295,29 @@ format_hss_session (u8 *s, va_list *args) return s; } +static u8 * +format_hss_listener (u8 *s, va_list *args) +{ + hss_listener_t *l = va_arg (*args, hss_listener_t *); + int __clib_unused verbose = va_arg (*args, int); + + s = format ( + s, "listener %d, uri %U:%u, www-root %s, cache-size %U url-handlers %d", + l->l_index, format_ip46_address, &l->sep.ip, l->sep.is_ip4, + clib_net_to_host_u16 (l->sep.port), l->www_root, format_memory_size, + l->cache_size, l->enable_url_handlers); + return s; +} + static clib_error_t * hss_show_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) { - int verbose = 0, show_cache = 0, show_sessions = 0; + int verbose = 0, show_cache = 0, show_sessions = 0, show_listeners = 0; + u32 l_index = 0; hss_main_t *hsm = &hss_main; - if (hsm->www_root == 0) + if (!hsm->is_init) return clib_error_return (0, "Static server disabled"); while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) @@ -1130,17 +1328,26 @@ hss_show_command_fn (vlib_main_t *vm, unformat_input_t *input, verbose = 1; else if (unformat (input, "cache")) show_cache = 1; + else if (unformat (input, "cache %u", &l_index)) + show_cache = 1; else if (unformat (input, "sessions")) show_sessions = 1; + else if (unformat (input, "listeners")) + show_listeners = 1; else break; } - if ((show_cache + show_sessions) == 0) + if ((show_cache + show_sessions + show_listeners) == 0) return clib_error_return (0, "specify one or more of cache, sessions"); if (show_cache) - vlib_cli_output (vm, "%U", format_hss_cache, &hsm->cache, verbose); + { + hss_listener_t *l = hss_listener_get (l_index); + if (l == 0) + return clib_error_return (0, "listener %d not found", l_index); + vlib_cli_output (vm, "%U", format_hss_cache, &l->cache, verbose); + } if (show_sessions) { @@ -1165,6 +1372,15 @@ hss_show_command_fn (vlib_main_t *vm, unformat_input_t *input, } vec_free (session_indices); } + + if (show_listeners) + { + hss_listener_t *l; + pool_foreach (l, hsm->listeners) + { + vlib_cli_output (vm, "%U", format_hss_listener, l, verbose); + } + } return 0; } @@ -1180,7 +1396,8 @@ hss_show_command_fn (vlib_main_t *vm, unformat_input_t *input, ?*/ VLIB_CLI_COMMAND (hss_show_command, static) = { .path = "show http static server", - .short_help = "show http static server sessions cache [verbose []]", + .short_help = "show http static server [sessions] [cache] [listeners] " + "[verbose []]", .function = hss_show_command_fn, }; @@ -1189,12 +1406,28 @@ hss_clear_cache_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) { hss_main_t *hsm = &hss_main; - u32 busy_items = 0; + u32 busy_items = 0, l_index = 0; + hss_listener_t *l; - if (hsm->www_root == 0) + if (!hsm->is_init) return clib_error_return (0, "Static server disabled"); - busy_items = hss_cache_clear (&hsm->cache); + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "index %u", &l_index)) + ; + else + { + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + } + } + + l = hss_listener_get (l_index); + if (l == 0) + return clib_error_return (0, "listener %d not found", l_index); + + busy_items = hss_cache_clear (&l->cache); if (busy_items > 0) vlib_cli_output (vm, "Note: %d busy items still in cache...", busy_items); @@ -1216,7 +1449,7 @@ hss_clear_cache_command_fn (vlib_main_t *vm, unformat_input_t *input, ?*/ VLIB_CLI_COMMAND (clear_hss_cache_command, static) = { .path = "clear http static cache", - .short_help = "clear http static cache", + .short_help = "clear http static cache [index ]", .function = hss_clear_cache_command_fn, }; diff --git a/test/asf/test_http_static.py b/test/asf/test_http_static.py index 368826f75d4..8d488cb4b22 100644 --- a/test/asf/test_http_static.py +++ b/test/asf/test_http_static.py @@ -77,11 +77,20 @@ class TestHttpStaticVapi(VppAsfTestCase): "exec", self.ns_name, "curl", + "--noproxy", + "10.10.1.2", "-v", f"10.10.1.2/{self.temp.name[5:]}", ], capture_output=True, ) + if process.returncode != 0: + self.logger.error( + f"Subprocess failed with return code {process.returncode}" + ) + self.logger.error(f"stderr: {process.stderr.decode()}") + raise RuntimeError("Subprocess execution failed") + self.logger.info(self.vapi.cli("sh session verbose")) self.assertIn(b"Hello world", process.stdout) self.assertIn(b"max-age=600", process.stderr) @@ -93,6 +102,8 @@ class TestHttpStaticVapi(VppAsfTestCase): "exec", self.ns_name, "curl", + "--noproxy", + "10.10.1.2", f"10.10.1.2/{self.temp2.name[5:]}", ], capture_output=True, @@ -183,9 +194,9 @@ class TestHttpStaticCli(VppAsfTestCase): ) self.assertIn(b"Hello world2", process.stdout) - self.vapi.cli("show http static server cache") - self.vapi.cli("clear http static cache") - self.vapi.cli("show http static server sessions") + self.logger.info(self.vapi.cli("show http static server cache")) + self.logger.info(self.vapi.cli("clear http static cache")) + self.logger.info(self.vapi.cli("show http static server sessions")) if __name__ == "__main__": -- 2.16.6