RegisterNoTopoTests(HeaderServerTest, HttpPersistentConnectionTest, HttpPipeliningTest,
HttpStaticMovedTest, HttpStaticNotFoundTest, HttpCliMethodNotAllowedTest, HttpAbsoluteFormUriTest,
HttpCliBadRequestTest, HttpStaticBuildInUrlGetIfStatsTest, HttpStaticBuildInUrlPostIfStatsTest,
- HttpInvalidRequestLineTest, HttpMethodNotImplementedTest, HttpInvalidHeadersTest,
+ HttpInvalidRequestLineTest, HttpMethodNotImplementedTest, HttpInvalidHeadersTest, HttpStaticPostTest,
HttpContentLengthTest, HttpStaticBuildInUrlGetIfListTest, HttpStaticBuildInUrlGetVersionTest,
HttpStaticMacTimeTest, HttpStaticBuildInUrlGetVersionVerboseTest, HttpVersionNotSupportedTest,
HttpInvalidContentLengthTest, HttpInvalidTargetSyntaxTest, HttpStaticPathSanitizationTest, HttpUriDecodeTest,
s.AssertMatchError(err, os.ErrDeadlineExceeded, "second request response received")
}
+func HttpStaticPostTest(s *NoTopoSuite) {
+ // testing url handler app do not support multi-thread
+ s.SkipIfMultiWorker()
+ vpp := s.Containers.Vpp.VppInstance
+ serverAddress := s.VppAddr()
+ s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers debug max-body-size 1m"))
+ s.Log(vpp.Vppctl("test-url-handler enable"))
+
+ body := make([]byte, 131072)
+ _, err := rand.Read(body)
+ client := NewHttpClient(defaultHttpTimeout)
+ req, err := http.NewRequest("POST", "http://"+serverAddress+":80/test3", bytes.NewBuffer(body))
+ s.AssertNil(err, fmt.Sprint(err))
+ resp, err := client.Do(req)
+ s.AssertNil(err, fmt.Sprint(err))
+ defer resp.Body.Close()
+ s.AssertHttpStatus(resp, 200)
+ _, err = io.ReadAll(resp.Body)
+ s.AssertNil(err, fmt.Sprint(err))
+}
+
func HttpCliTest(s *VethsSuite) {
s.Containers.ServerVpp.VppInstance.Vppctl("http cli server")
This file defines static http server control-plane API messages
*/
-option version = "2.4.0";
-
-/** \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 {
- 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;
- 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];
-};
+option version = "2.5.0";
/** \brief Configure and enable the static http server
@param client_index - opaque cookie to identify the sender
@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 max_body_size - maximum size of a request body (in bytes)
@param keepalive_timeout - timeout during which client connection will stay open (in seconds)
@param prealloc_fifos - number of preallocated fifos (usually 0)
@param private_segment_size - fifo segment size (usually 0)
@param uri - bind URI, defaults to "tcp://0.0.0.0/80"
*/
-autoreply define http_static_enable_v3 {
+autoreply define http_static_enable_v4 {
option deprecated;
/* Client identifier, set from api_main.my_client_index */
u32 cache_size_limit;
u32 max_age [default=600];
u32 keepalive_timeout [default=60];
+ u64 max_body_size [default=8000];
/* Unusual options */
u32 prealloc_fifos;
u32 private_segment_size;
};
/** \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 max_body_size - maximum size of a request body (in bytes)
+ @param rx_buff_thresh - maximum size of a large memory allocation (in bytes)
@param keepalive_timeout - timeout during which client connection will stay open (in seconds)
@param prealloc_fifos - number of preallocated fifos (usually 0)
@param private_segment_size - fifo segment size (usually 0)
@param uri - bind URI, defaults to "tcp://0.0.0.0/80"
*/
-autoreply define http_static_enable_v4 {
+autoreply define http_static_enable_v5 {
/* Client identifier, set from api_main.my_client_index */
u32 client_index;
u32 cache_size_limit;
u32 max_age [default=600];
u32 keepalive_timeout [default=60];
- u64 max_body_size [default=8000];
+ u64 max_body_size [default=8192];
+ u32 rx_buff_thresh [default=1048576];
/* Unusual options */
u32 prealloc_fifos;
u32 private_segment_size;
static int
hss_enable_api (u32 fifo_size, u32 cache_limit, u32 prealloc_fifos,
u32 private_segment_size, u8 *www_root, u8 *uri, u32 max_age,
- u32 keepalive_timeout, u64 max_body_size)
+ u32 keepalive_timeout, u64 max_body_size, u32 rx_buff_thresh)
{
hss_main_t *hsm = &hss_main;
int rv;
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.rx_buff_thresh = rx_buff_thresh;
hsm->default_listener.keepalive_timeout = keepalive_timeout;
hsm->have_default_listener = 1;
/* 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), HSS_DEFAULT_KEEPALIVE_TIMEOUT,
- HSS_DEFAULT_MAX_BODY_SIZE);
-
- REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V2_REPLY);
-}
-
-/* API message handler */
-static void
-vl_api_http_static_enable_v3_t_handler (vl_api_http_static_enable_v3_t *mp)
+vl_api_http_static_enable_v4_t_handler (vl_api_http_static_enable_v4_t *mp)
{
- vl_api_http_static_enable_v3_reply_t *rmp;
+ vl_api_http_static_enable_v4_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), ntohl (mp->keepalive_timeout),
- HSS_DEFAULT_MAX_BODY_SIZE);
+ 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), ntohl (mp->keepalive_timeout),
+ ntohl (mp->max_body_size), HSS_DEFAULT_RX_BUFFER_THRESH);
- REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V3_REPLY);
+ REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V4_REPLY);
}
/* API message handler */
static void
-vl_api_http_static_enable_v4_t_handler (vl_api_http_static_enable_v4_t *mp)
+vl_api_http_static_enable_v5_t_handler (vl_api_http_static_enable_v5_t *mp)
{
- vl_api_http_static_enable_v4_reply_t *rmp;
+ vl_api_http_static_enable_v5_reply_t *rmp;
hss_main_t *hsm = &hss_main;
int rv;
ntohl (mp->prealloc_fifos),
ntohl (mp->private_segment_size), mp->www_root, mp->uri,
ntohl (mp->max_age), ntohl (mp->keepalive_timeout),
- ntohl (mp->max_body_size));
+ ntohl (mp->max_body_size), ntohl (mp->rx_buff_thresh));
- REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V4_REPLY);
+ REPLY_MACRO (VL_API_HTTP_STATIC_ENABLE_V5_REPLY);
}
#include <http_static/http_static.api.c>
#define HSS_DEFAULT_MAX_AGE 600
#define HSS_DEFAULT_MAX_BODY_SIZE 8192
+#define HSS_DEFAULT_RX_BUFFER_THRESH 1 << 20
#define HSS_DEFAULT_KEEPALIVE_TIMEOUT 60
/** @file http_static.h
http_headers_ctx_t resp_headers;
/** Response header buffer */
u8 *headers_buf;
+ /** RX buffer (POST body) */
+ u8 *rx_buff;
+ /** Current RX buffer offset */
+ u64 rx_buff_offset;
/** POST body left to receive */
u64 left_recv;
/** threshold for switching to pointers */
u64 cache_size;
/** Maximum size of a request body (in bytes) **/
u64 max_body_size;
+ /** Maximum size of a large memory allocation */
+ u32 rx_buff_thresh;
/** Timeout during which client connection will stay open */
u32 keepalive_timeout;
/** How long a response is considered fresh (in seconds) */
#include <vlibapi/vat_helper_macros.h>
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;
-}
-
-static int
-api_http_static_enable_v3 (vat_main_t *vam)
+api_http_static_enable_v4 (vat_main_t *vam)
{
unformat_input_t *line_input = vam->input;
- vl_api_http_static_enable_v3_t *mp;
+ vl_api_http_static_enable_v4_t *mp;
u64 tmp;
u8 *www_root = 0;
u8 *uri = 0;
u32 cache_size_limit = 1 << 20;
u32 max_age = HSS_DEFAULT_MAX_AGE;
u32 keepalive_timeout = HSS_DEFAULT_KEEPALIVE_TIMEOUT;
+ u64 max_body_size = HSS_DEFAULT_MAX_BODY_SIZE;
int ret;
/* Parse args required to build the message */
;
else if (unformat (line_input, "uri %s", &uri))
;
+ else if (unformat (line_input, "max-body-size %llu", &max_body_size))
+ ;
else
{
errmsg ("unknown input `%U'", format_unformat_error, line_input);
uri = format (0, "tcp://0.0.0.0/80%c", 0);
/* Construct the API message */
- M (HTTP_STATIC_ENABLE_V3, mp);
+ M (HTTP_STATIC_ENABLE_V4, 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->private_segment_size = ntohl (private_segment_size);
mp->max_age = ntohl (max_age);
mp->keepalive_timeout = ntohl (keepalive_timeout);
+ mp->max_body_size = ntohl (max_body_size);
+
/* send it... */
S (mp);
}
static int
-api_http_static_enable_v4 (vat_main_t *vam)
+api_http_static_enable_v5 (vat_main_t *vam)
{
unformat_input_t *line_input = vam->input;
- vl_api_http_static_enable_v4_t *mp;
+ vl_api_http_static_enable_v5_t *mp;
u64 tmp;
u8 *www_root = 0;
u8 *uri = 0;
u32 max_age = HSS_DEFAULT_MAX_AGE;
u32 keepalive_timeout = HSS_DEFAULT_KEEPALIVE_TIMEOUT;
u64 max_body_size = HSS_DEFAULT_MAX_BODY_SIZE;
+ u32 rx_buff_thresh = HSS_DEFAULT_RX_BUFFER_THRESH;
int ret;
/* Parse args required to build the message */
;
else if (unformat (line_input, "uri %s", &uri))
;
- else if (unformat (line_input, "max-body-size %llu", &max_body_size))
+ else if (unformat (line_input, "max-body-size %U", unformat_memory_size,
+ &max_body_size))
+ ;
+ else if (unformat (line_input, "rx-buff-thresh %U", unformat_memory_size,
+ &rx_buff_thresh))
;
else
{
#include <unistd.h>
#include <http/http_content_types.h>
+#include <http/http_status_codes.h>
/** @file static_server.c
* Static http server, sufficient to serve .html / .css / .js content.
hss_main_t hss_main;
static int file_handler_discard_body (hss_session_t *hs, session_t *ts);
-static int url_handler_wait_body (hss_session_t *hs, session_t *ts);
+static int url_handler_read_body (hss_session_t *hs, session_t *ts);
static int
hss_add_header (hss_session_t *hs, http_header_name_t name, const char *value,
return 0;
}
+static_always_inline void
+hss_confirm_data_read (hss_session_t *hs, u32 n_last_deq)
+{
+ session_t *ts;
+
+ ts = session_get (hs->vpp_session_index, hs->thread_index);
+ if (svm_fifo_needs_deq_ntf (ts->rx_fifo, n_last_deq))
+ {
+ svm_fifo_clear_deq_ntf (ts->rx_fifo);
+ session_program_transport_io_evt (ts->handle, SESSION_IO_EVT_RX);
+ }
+}
+
static hss_session_t *
hss_session_alloc (u32 thread_index)
{
static void
start_send_data (hss_session_t *hs, http_status_code_t status)
{
+ hss_main_t *hsm = &hss_main;
http_msg_t msg;
session_t *ts;
u32 n_enq;
ts = session_get (hs->vpp_session_index, hs->thread_index);
+ if (hsm->debug_level > 0)
+ clib_warning ("status code: %U", format_http_status_code, status);
+
msg.type = HTTP_MSG_REPLY;
msg.code = status;
msg.data.body_len = hs->data_len;
hss_url_handler_args_t args = {};
uword *p, *url_table;
session_t *ts;
- u8 *data = 0, *target_path;
+ u32 max_deq;
+ u8 *target_path;
int rv;
target_path = hs->target_path;
if (!p)
return -1;
+ hs->rx_buff = 0;
+
/* Read request body */
if (hs->left_recv)
{
+ hss_listener_t *l = hss_listener_get (hs->listener_index);
+ if (hs->left_recv > l->rx_buff_thresh)
+ {
+ /* TODO: large body (not buffered in memory) */
+ clib_warning ("data length %u above threshold %u", hs->left_recv,
+ l->rx_buff_thresh);
+ hs->left_recv = 0;
+ start_send_data (hs, HTTP_STATUS_INTERNAL_ERROR);
+ hss_session_disconnect_transport (hs);
+ }
+ hs->rx_buff_offset = 0;
+ vec_validate (hs->rx_buff, hs->left_recv - 1);
ts = session_get (hs->vpp_session_index, hs->thread_index);
- if (svm_fifo_max_dequeue (ts->rx_fifo) < hs->left_recv)
+ max_deq = svm_fifo_max_dequeue (ts->rx_fifo);
+ if (max_deq < hs->left_recv)
{
- hs->read_body_handler = url_handler_wait_body;
+ hs->read_body_handler = url_handler_read_body;
+ if (max_deq == 0)
+ return 0;
+ rv = svm_fifo_dequeue (ts->rx_fifo, max_deq, hs->rx_buff);
+ ASSERT (rv == max_deq);
+ hs->rx_buff_offset = max_deq;
+ hs->left_recv -= max_deq;
+ hss_confirm_data_read (hs, max_deq);
return 0;
}
- vec_validate (data, hs->left_recv - 1);
- rv = svm_fifo_dequeue (ts->rx_fifo, hs->left_recv, data);
+ rv = svm_fifo_dequeue (ts->rx_fifo, hs->left_recv,
+ hs->rx_buff + hs->rx_buff_offset);
ASSERT (rv == hs->left_recv);
+ hss_confirm_data_read (hs, hs->left_recv);
hs->left_recv = 0;
}
args.req_type = hs->rt;
args.query = hs->target_query;
- args.req_data = data;
+ args.req_data = hs->rx_buff;
args.sh.thread_index = hs->thread_index;
args.sh.session_index = hs->session_index;
rv = ((hss_url_handler_fn) p[0]) (&args);
- vec_free (data);
+ vec_free (hs->rx_buff);
/* Wait for data from handler */
if (rv == HSS_URL_HANDLER_ASYNC)
svm_fifo_dequeue_drop (ts->rx_fifo, max_dequeue);
hs->left_recv -= max_dequeue;
hs->read_body_handler = file_handler_discard_body;
+ hss_confirm_data_read (hs, max_dequeue);
return 0;
}
svm_fifo_dequeue_drop (ts->rx_fifo, hs->left_recv);
+ hss_confirm_data_read (hs, hs->left_recv);
hs->left_recv = 0;
}
to_discard = clib_min (max_dequeue, hs->left_recv);
svm_fifo_dequeue_drop (ts->rx_fifo, to_discard);
hs->left_recv -= to_discard;
+ hss_confirm_data_read (hs, to_discard);
if (hs->left_recv == 0)
return try_file_handler (hs);
return 0;
}
static int
-url_handler_wait_body (hss_session_t *hs, session_t *ts)
+url_handler_read_body (hss_session_t *hs, session_t *ts)
{
- /* TODO: add support for large content (buffer or stream data) */
- if (svm_fifo_max_dequeue (ts->rx_fifo) < hs->left_recv)
- {
- clib_warning ("not all data in fifo, max deq %u, left recv %u",
- svm_fifo_max_dequeue (ts->rx_fifo), hs->left_recv);
- hs->left_recv = 0;
- start_send_data (hs, HTTP_STATUS_INTERNAL_ERROR);
- hss_session_disconnect_transport (hs);
- return 0;
- }
- hs->left_recv = 0;
- return try_url_handler (hs);
+ u32 max_dequeue, to_read;
+ int rv;
+
+ max_dequeue = svm_fifo_max_dequeue (ts->rx_fifo);
+ to_read = clib_min (max_dequeue, hs->left_recv);
+ rv =
+ svm_fifo_dequeue (ts->rx_fifo, to_read, hs->rx_buff + hs->rx_buff_offset);
+ ASSERT (rv == to_read);
+ hs->rx_buff_offset += to_read;
+ hs->left_recv -= to_read;
+ hss_confirm_data_read (hs, to_read);
+ if (hs->left_recv == 0)
+ return try_url_handler (hs);
+ return 0;
}
static int
l->cache_size = 10 << 20;
l->max_age = HSS_DEFAULT_MAX_AGE;
l->max_body_size = HSS_DEFAULT_MAX_BODY_SIZE;
+ l->rx_buff_thresh = HSS_DEFAULT_RX_BUFFER_THRESH;
l->keepalive_timeout = HSS_DEFAULT_KEEPALIVE_TIMEOUT;
/* Get a line of input. */
else if (unformat (line_input, "max-body-size %U", unformat_memory_size,
&l->max_body_size))
;
+ else if (unformat (line_input, "rx-buff-thresh %U", unformat_memory_size,
+ &l->rx_buff_thresh))
+ ;
else if (unformat (line_input, "keepalive-timeout %d",
&l->keepalive_timeout))
;
if (!unformat_user (input, unformat_line_input, line_input))
return clib_error_return (0, "No input provided");
+ l->cache_size = 10 << 20;
+ l->max_age = HSS_DEFAULT_MAX_AGE;
+ l->max_body_size = HSS_DEFAULT_MAX_BODY_SIZE;
+ l->rx_buff_thresh = HSS_DEFAULT_RX_BUFFER_THRESH;
+ l->keepalive_timeout = HSS_DEFAULT_KEEPALIVE_TIMEOUT;
+
while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (line_input, "add"))
else if (unformat (line_input, "max-body-size %U", unformat_memory_size,
&l->max_body_size))
;
+ else if (unformat (line_input, "rx-buff-thresh %U", unformat_memory_size,
+ &l->rx_buff_thresh))
+ ;
else
{
error = clib_error_return (0, "unknown input `%U'",
super(TestHttpStaticVapi, cls).tearDownClass()
def test_http_static_vapi(self):
- self.vapi.http_static_enable_v3(
+ self.vapi.http_static_enable_v5(
www_root="/tmp",
uri="tcp://0.0.0.0/80",
)