diff options
-rw-r--r-- | extras/hs-test/http_test.go | 26 | ||||
-rw-r--r-- | src/plugins/http_static/http_static.api | 37 | ||||
-rw-r--r-- | src/plugins/http_static/http_static.c | 30 | ||||
-rw-r--r-- | src/plugins/http_static/http_static.h | 6 | ||||
-rw-r--r-- | src/plugins/http_static/http_static_test.c | 91 | ||||
-rw-r--r-- | src/plugins/http_static/static_server.c | 16 | ||||
-rw-r--r-- | test/asf/test_http_static.py | 2 |
7 files changed, 191 insertions, 17 deletions
diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go index 5b2b65f4eb2..f8b0fb79615 100644 --- a/extras/hs-test/http_test.go +++ b/extras/hs-test/http_test.go @@ -28,7 +28,7 @@ func init() { HttpContentLengthTest, HttpStaticBuildInUrlGetIfListTest, HttpStaticBuildInUrlGetVersionTest, HttpStaticMacTimeTest, HttpStaticBuildInUrlGetVersionVerboseTest, HttpVersionNotSupportedTest, HttpInvalidContentLengthTest, HttpInvalidTargetSyntaxTest, HttpStaticPathTraversalTest, HttpUriDecodeTest, - HttpHeadersTest, HttpStaticFileHandlerTest, HttpClientTest, HttpClientErrRespTest, HttpClientPostFormTest, + HttpHeadersTest, HttpStaticFileHandlerTest, HttpStaticFileHandlerDefaultMaxAgeTest, HttpClientTest, HttpClientErrRespTest, HttpClientPostFormTest, HttpClientPostFileTest, HttpClientPostFilePtrTest, AuthorityFormTargetTest) RegisterNoTopoSoloTests(HttpStaticPromTest, HttpTpsTest, HttpTpsInterruptModeTest, PromConcurrentConnectionsTest, PromMemLeakTest, HttpClientPostMemLeakTest, HttpInvalidClientRequestMemLeakTest) @@ -535,7 +535,23 @@ func HttpInvalidClientRequestMemLeakTest(s *NoTopoSuite) { } +func HttpStaticFileHandlerDefaultMaxAgeTest(s *NoTopoSuite) { + HttpStaticFileHandlerTestFunction(s, "default") +} + func HttpStaticFileHandlerTest(s *NoTopoSuite) { + HttpStaticFileHandlerTestFunction(s, "123") +} + +func HttpStaticFileHandlerTestFunction(s *NoTopoSuite, max_age string) { + var max_age_formatted string + if max_age == "default" { + max_age_formatted = "" + max_age = "600" + } else { + max_age_formatted = "max-age " + max_age + } + content := "<html><body><p>Hello</p></body></html>" content2 := "<html><body><p>Page</p></body></html>" vpp := s.GetContainerByName("vpp").VppInstance @@ -545,7 +561,7 @@ func HttpStaticFileHandlerTest(s *NoTopoSuite) { err = vpp.Container.CreateFile(wwwRootPath+"/page.html", content2) s.AssertNil(err, fmt.Sprint(err)) serverAddress := s.GetInterfaceByName(TapInterfaceName).Peer.Ip4AddressString() - s.Log(vpp.Vppctl("http static server www-root " + wwwRootPath + " uri tcp://" + serverAddress + "/80 debug cache-size 2m")) + s.Log(vpp.Vppctl("http static server www-root " + wwwRootPath + " uri tcp://" + serverAddress + "/80 debug cache-size 2m " + max_age_formatted)) client := NewHttpClient() req, err := http.NewRequest("GET", "http://"+serverAddress+":80/index.html", nil) @@ -556,7 +572,7 @@ func HttpStaticFileHandlerTest(s *NoTopoSuite) { s.Log(DumpHttpResp(resp, true)) s.AssertEqual(200, resp.StatusCode) s.AssertContains(resp.Header.Get("Content-Type"), "html") - s.AssertContains(resp.Header.Get("Cache-Control"), "max-age=") + s.AssertContains(resp.Header.Get("Cache-Control"), "max-age="+max_age) s.AssertEqual(int64(len([]rune(content))), resp.ContentLength) body, err := io.ReadAll(resp.Body) s.AssertNil(err, fmt.Sprint(err)) @@ -572,7 +588,7 @@ func HttpStaticFileHandlerTest(s *NoTopoSuite) { s.Log(DumpHttpResp(resp, true)) s.AssertEqual(200, resp.StatusCode) s.AssertContains(resp.Header.Get("Content-Type"), "html") - s.AssertContains(resp.Header.Get("Cache-Control"), "max-age=") + s.AssertContains(resp.Header.Get("Cache-Control"), "max-age="+max_age) s.AssertEqual(int64(len([]rune(content))), resp.ContentLength) body, err = io.ReadAll(resp.Body) s.AssertNil(err, fmt.Sprint(err)) @@ -586,7 +602,7 @@ func HttpStaticFileHandlerTest(s *NoTopoSuite) { s.Log(DumpHttpResp(resp, true)) s.AssertEqual(200, resp.StatusCode) s.AssertContains(resp.Header.Get("Content-Type"), "html") - s.AssertContains(resp.Header.Get("Cache-Control"), "max-age=") + s.AssertContains(resp.Header.Get("Cache-Control"), "max-age="+max_age) s.AssertEqual(int64(len([]rune(content2))), resp.ContentLength) body, err = io.ReadAll(resp.Body) s.AssertNil(err, fmt.Sprint(err)) diff --git a/src/plugins/http_static/http_static.api b/src/plugins/http_static/http_static.api index 4d6d8bfe9b5..dd4f513a420 100644 --- a/src/plugins/http_static/http_static.api +++ b/src/plugins/http_static/http_static.api @@ -2,7 +2,8 @@ /** \file This file defines static http server control-plane API messages */ -option version = "2.1.0"; + +option version = "2.2.0"; /** \brief Configure and enable the static http server @param client_index - opaque cookie to identify the sender @@ -16,6 +17,39 @@ option version = "2.1.0"; */ autoreply define http_static_enable { + option deprecated; + + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + /* Typical options */ + u32 fifo_size; + u32 cache_size_limit; + /* Unusual options */ + u32 prealloc_fifos; + u32 private_segment_size; + + /* Root of the html path */ + string www_root[256]; + /* The bind URI */ + string uri[256]; +}; + +/** \brief Configure and enable the static http server + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param fifo_size - size (in bytes) of the session FIFOs + @param cache_size_limit - size (in bytes) of the in-memory file data cache + @param max_age - how long a response is considered fresh (in seconds) + @param prealloc_fifos - number of preallocated fifos (usually 0) + @param private_segment_size - fifo segment size (usually 0) + @param www_root - html root path + @param uri - bind URI, defaults to "tcp://0.0.0.0/80" +*/ + +autoreply define http_static_enable_v2 { /* Client identifier, set from api_main.my_client_index */ u32 client_index; @@ -24,6 +58,7 @@ autoreply define http_static_enable { /* Typical options */ u32 fifo_size; u32 cache_size_limit; + u32 max_age [default=600]; /* Unusual options */ u32 prealloc_fifos; u32 private_segment_size; diff --git a/src/plugins/http_static/http_static.c b/src/plugins/http_static/http_static.c index 8f8fe37b7c1..9a98763b312 100644 --- a/src/plugins/http_static/http_static.c +++ b/src/plugins/http_static/http_static.c @@ -66,7 +66,7 @@ hss_register_url_handler (hss_url_handler_fn fp, const char *url, */ static int hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos, - u32 private_segment_size, u8 *www_root, u8 *uri) + u32 private_segment_size, u8 *www_root, u8 *uri, u32 max_age) { hss_main_t *hsm = &hss_main; int rv; @@ -77,6 +77,7 @@ hss_enable_api (u32 fifo_size, u32 cache_limit, u32 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; if (vec_len (hsm->www_root) < 2) return VNET_API_ERROR_INVALID_VALUE; @@ -110,14 +111,33 @@ static void vl_api_http_static_enable_t_handler mp->uri[ARRAY_LEN (mp->uri) - 1] = 0; mp->www_root[ARRAY_LEN (mp->www_root) - 1] = 0; - rv = - hss_enable_api (ntohl (mp->fifo_size), ntohl (mp->cache_size_limit), - ntohl (mp->prealloc_fifos), - ntohl (mp->private_segment_size), mp->www_root, mp->uri); + rv = hss_enable_api (ntohl (mp->fifo_size), ntohl (mp->cache_size_limit), + ntohl (mp->prealloc_fifos), + ntohl (mp->private_segment_size), mp->www_root, mp->uri, + HSS_DEFAULT_MAX_AGE); REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_REPLY); } +/* API message handler */ +static void +vl_api_http_static_enable_v2_t_handler (vl_api_http_static_enable_v2_t *mp) +{ + vl_api_http_static_enable_v2_reply_t *rmp; + hss_main_t *hsm = &hss_main; + int rv; + + mp->uri[ARRAY_LEN (mp->uri) - 1] = 0; + mp->www_root[ARRAY_LEN (mp->www_root) - 1] = 0; + + rv = hss_enable_api (ntohl (mp->fifo_size), ntohl (mp->cache_size_limit), + ntohl (mp->prealloc_fifos), + ntohl (mp->private_segment_size), mp->www_root, mp->uri, + ntohl (mp->max_age)); + + REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V2_REPLY); +} + #include <http_static/http_static.api.c> static clib_error_t * hss_api_init (vlib_main_t *vm) diff --git a/src/plugins/http_static/http_static.h b/src/plugins/http_static/http_static.h index 4c22e1d7ed7..f9364801a71 100644 --- a/src/plugins/http_static/http_static.h +++ b/src/plugins/http_static/http_static.h @@ -23,6 +23,8 @@ #include <vppinfra/error.h> #include <http_static/http_cache.h> +#define HSS_DEFAULT_MAX_AGE 600 + /** @file http_static.h * Static http server definitions */ @@ -156,6 +158,10 @@ typedef struct 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; + /** Formatted max_age: "max-age=xyz" */ + u8 *max_age_formatted; /** hash table of file extensions to mime types string indices */ uword *mime_type_indices_by_file_extensions; diff --git a/src/plugins/http_static/http_static_test.c b/src/plugins/http_static/http_static_test.c index 3503a1b0812..f701c8b9ee7 100644 --- a/src/plugins/http_static/http_static_test.c +++ b/src/plugins/http_static/http_static_test.c @@ -18,6 +18,7 @@ #include <vlibapi/api.h> #include <vlibmemory/api.h> #include <vppinfra/error.h> +#include <http_static/http_static.h> uword unformat_sw_if_index (unformat_input_t * input, va_list * args); @@ -126,6 +127,96 @@ api_http_static_enable (vat_main_t * vam) return ret; } +static int +api_http_static_enable_v2 (vat_main_t *vam) +{ + unformat_input_t *line_input = vam->input; + vl_api_http_static_enable_v2_t *mp; + u64 tmp; + u8 *www_root = 0; + u8 *uri = 0; + u32 prealloc_fifos = 0; + u32 private_segment_size = 0; + u32 fifo_size = 8 << 10; + u32 cache_size_limit = 1 << 20; + u32 max_age = HSS_DEFAULT_MAX_AGE; + int ret; + + /* Parse args required to build the message */ + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "www-root %s", &www_root)) + ; + else if (unformat (line_input, "prealloc-fifos %d", &prealloc_fifos)) + ; + else if (unformat (line_input, "private-segment-size %U", + unformat_memory_size, &tmp)) + { + if (tmp >= 0x100000000ULL) + { + errmsg ("private segment size %llu, too large", tmp); + return -99; + } + private_segment_size = (u32) tmp; + } + else if (unformat (line_input, "fifo-size %U", unformat_memory_size, + &tmp)) + { + if (tmp >= 0x100000000ULL) + { + errmsg ("fifo-size %llu, too large", tmp); + return -99; + } + fifo_size = (u32) tmp; + } + else if (unformat (line_input, "cache-size %U", unformat_memory_size, + &tmp)) + { + if (tmp < (128ULL << 10)) + { + errmsg ("cache-size must be at least 128kb"); + return -99; + } + cache_size_limit = (u32) tmp; + } + else if (unformat (line_input, "max-age %d", &max_age)) + ; + else if (unformat (line_input, "uri %s", &uri)) + ; + else + { + errmsg ("unknown input `%U'", format_unformat_error, line_input); + return -99; + } + } + + if (www_root == 0) + { + errmsg ("Must specify www-root"); + return -99; + } + + if (uri == 0) + uri = format (0, "tcp://0.0.0.0/80%c", 0); + + /* Construct the API message */ + M (HTTP_STATIC_ENABLE_V2, mp); + strncpy_s ((char *) mp->www_root, 256, (const char *) www_root, 256); + strncpy_s ((char *) mp->uri, 256, (const char *) uri, 256); + mp->fifo_size = ntohl (fifo_size); + mp->cache_size_limit = ntohl (cache_size_limit); + mp->prealloc_fifos = ntohl (prealloc_fifos); + mp->private_segment_size = ntohl (private_segment_size); + mp->max_age = ntohl (max_age); + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + return ret; +} + #include <http_static/http_static.api_test.c> /* diff --git a/src/plugins/http_static/static_server.c b/src/plugins/http_static/static_server.c index b47fc9c3f0b..40de6cb578d 100644 --- a/src/plugins/http_static/static_server.c +++ b/src/plugins/http_static/static_server.c @@ -475,10 +475,9 @@ try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt, http_add_header (&hs->resp_headers, http_header_name_token (HTTP_HEADER_CONTENT_TYPE), http_content_type_token (type)); - /* TODO configurable max-age value */ - http_add_header (&hs->resp_headers, - http_header_name_token (HTTP_HEADER_CACHE_CONTROL), - http_token_lit ("max-age=600")); + 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)); done: vec_free (sanitized_path); @@ -863,6 +862,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; } @@ -883,6 +884,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)) @@ -914,6 +916,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'", @@ -971,8 +975,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, }; diff --git a/test/asf/test_http_static.py b/test/asf/test_http_static.py index 18e8ba56a1e..73a95e992da 100644 --- a/test/asf/test_http_static.py +++ b/test/asf/test_http_static.py @@ -63,11 +63,13 @@ class TestHttpStaticVapi(VppAsfTestCase): "exec", "HttpStatic", "curl", + "-v", f"10.10.1.2/{self.temp.name[5:]}", ], capture_output=True, ) self.assertIn(b"Hello world", process.stdout) + self.assertIn(b"max-age=600", process.stderr) self.temp2.seek(0) process = subprocess.run( |