http_static: make max-age configurable
Type: improvement Change-Id: I629add6e3f4219d56610c3785013f69dbe847844 Signed-off-by: Adrian Villin <avillin@cisco.com>
This commit is contained in:

committed by
Florin Coras

parent
3601b322a0
commit
86fa943725
@ -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))
|
||||
|
@ -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,8 @@ option version = "2.1.0";
|
||||
*/
|
||||
|
||||
autoreply define http_static_enable {
|
||||
option deprecated;
|
||||
|
||||
/* Client identifier, set from api_main.my_client_index */
|
||||
u32 client_index;
|
||||
|
||||
@ -33,3 +36,35 @@ autoreply define http_static_enable {
|
||||
/* 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;
|
||||
|
||||
/* Arbitrary context, so client can match reply to request */
|
||||
u32 context;
|
||||
/* Typical options */
|
||||
u32 fifo_size;
|
||||
u32 cache_size_limit;
|
||||
u32 max_age [default=600];
|
||||
/* Unusual options */
|
||||
u32 prealloc_fifos;
|
||||
u32 private_segment_size;
|
||||
|
||||
/* Root of the html path */
|
||||
string www_root[256];
|
||||
/* The bind URI */
|
||||
string uri[256];
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
||||
/*
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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(
|
||||
|
Reference in New Issue
Block a user