http: http_read_message improvement
Use svm_fifo_peek in http_read_message and advance rx fifo head by amount of bytes send to app, since not always you won't or can't send all bytes. Type: improvement Change-Id: I84348c9df5c77ba386c9738a754295bb9ea0f7ef Signed-off-by: Matus Fabian <matfabia@cisco.com>
This commit is contained in:
@ -31,7 +31,7 @@ func init() {
|
||||
HttpHeadersTest, HttpStaticFileHandlerTest, HttpClientTest, HttpClientErrRespTest, HttpClientPostFormTest,
|
||||
HttpClientPostFileTest, HttpClientPostFilePtrTest, AuthorityFormTargetTest)
|
||||
RegisterNoTopoSoloTests(HttpStaticPromTest, HttpTpsTest, HttpTpsInterruptModeTest, PromConcurrentConnectionsTest,
|
||||
PromMemLeakTest, HttpClientPostMemLeakTest)
|
||||
PromMemLeakTest, HttpClientPostMemLeakTest, HttpInvalidClientRequestMemLeakTest)
|
||||
}
|
||||
|
||||
const wwwRootPath = "/tmp/www_root"
|
||||
@ -501,6 +501,40 @@ func HttpClientPostMemLeakTest(s *NoTopoSuite) {
|
||||
vpp.MemLeakCheck(traces1, traces2)
|
||||
}
|
||||
|
||||
func HttpInvalidClientRequestMemLeakTest(s *NoTopoSuite) {
|
||||
s.SkipUnlessLeakCheck()
|
||||
|
||||
vpp := s.GetContainerByName("vpp").VppInstance
|
||||
serverAddress := s.GetInterfaceByName(TapInterfaceName).Peer.Ip4AddressString()
|
||||
|
||||
/* no goVPP less noise */
|
||||
vpp.Disconnect()
|
||||
|
||||
vpp.Vppctl("http cli server")
|
||||
|
||||
/* warmup request (FIB) */
|
||||
_, err := TcpSendReceive(serverAddress+":80", "GET / HTTP/1.1\r\n")
|
||||
s.AssertNil(err, fmt.Sprint(err))
|
||||
|
||||
/* let's give it some time to clean up sessions, so local port can be reused and we have less noise */
|
||||
time.Sleep(time.Second * 12)
|
||||
|
||||
vpp.EnableMemoryTrace()
|
||||
traces1, err := vpp.GetMemoryTrace()
|
||||
s.AssertNil(err, fmt.Sprint(err))
|
||||
|
||||
_, err = TcpSendReceive(serverAddress+":80", "GET / HTTP/1.1\r\n")
|
||||
s.AssertNil(err, fmt.Sprint(err))
|
||||
|
||||
/* let's give it some time to clean up sessions */
|
||||
time.Sleep(time.Second * 12)
|
||||
|
||||
traces2, err := vpp.GetMemoryTrace()
|
||||
s.AssertNil(err, fmt.Sprint(err))
|
||||
vpp.MemLeakCheck(traces1, traces2)
|
||||
|
||||
}
|
||||
|
||||
func HttpStaticFileHandlerTest(s *NoTopoSuite) {
|
||||
content := "<html><body><p>Hello</p></body></html>"
|
||||
content2 := "<html><body><p>Page</p></body></html>"
|
||||
|
@ -431,6 +431,7 @@ http_send_error (http_conn_t *hc, http_status_code_t ec)
|
||||
now = clib_timebase_now (&hm->timebase);
|
||||
data = format (0, http_error_template, http_status_code_str[ec],
|
||||
format_clib_timebase_time, now);
|
||||
HTTP_DBG (1, "%v", data);
|
||||
http_send_data (hc, data, vec_len (data), 0);
|
||||
vec_free (data);
|
||||
}
|
||||
@ -438,26 +439,48 @@ http_send_error (http_conn_t *hc, http_status_code_t ec)
|
||||
static int
|
||||
http_read_message (http_conn_t *hc)
|
||||
{
|
||||
u32 max_deq, cursize;
|
||||
u32 max_deq;
|
||||
session_t *ts;
|
||||
int n_read;
|
||||
|
||||
ts = session_get_from_handle (hc->h_tc_session_handle);
|
||||
|
||||
cursize = vec_len (hc->rx_buf);
|
||||
max_deq = svm_fifo_max_dequeue (ts->rx_fifo);
|
||||
if (PREDICT_FALSE (max_deq == 0))
|
||||
return -1;
|
||||
|
||||
vec_validate (hc->rx_buf, cursize + max_deq - 1);
|
||||
n_read = svm_fifo_dequeue (ts->rx_fifo, max_deq, hc->rx_buf + cursize);
|
||||
vec_validate (hc->rx_buf, max_deq - 1);
|
||||
n_read = svm_fifo_peek (ts->rx_fifo, 0, max_deq, hc->rx_buf);
|
||||
ASSERT (n_read == max_deq);
|
||||
HTTP_DBG (1, "read %u bytes from rx_fifo", n_read);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
http_read_message_drop (http_conn_t *hc, u32 len)
|
||||
{
|
||||
session_t *ts;
|
||||
|
||||
ts = session_get_from_handle (hc->h_tc_session_handle);
|
||||
svm_fifo_dequeue_drop (ts->rx_fifo, len);
|
||||
vec_reset_length (hc->rx_buf);
|
||||
|
||||
if (svm_fifo_is_empty (ts->rx_fifo))
|
||||
svm_fifo_unset_event (ts->rx_fifo);
|
||||
}
|
||||
|
||||
vec_set_len (hc->rx_buf, cursize + n_read);
|
||||
return 0;
|
||||
static void
|
||||
http_read_message_drop_all (http_conn_t *hc)
|
||||
{
|
||||
session_t *ts;
|
||||
|
||||
ts = session_get_from_handle (hc->h_tc_session_handle);
|
||||
svm_fifo_dequeue_drop_all (ts->rx_fifo);
|
||||
vec_reset_length (hc->rx_buf);
|
||||
|
||||
if (svm_fifo_is_empty (ts->rx_fifo))
|
||||
svm_fifo_unset_event (ts->rx_fifo);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -575,7 +598,8 @@ http_parse_request_line (http_conn_t *hc, http_status_code_t *ec)
|
||||
return -1;
|
||||
}
|
||||
HTTP_DBG (0, "request line length: %d", i);
|
||||
next_line_offset = i + 2;
|
||||
hc->control_data_len = i + 2;
|
||||
next_line_offset = hc->control_data_len;
|
||||
|
||||
/* there should be at least one more CRLF */
|
||||
if (vec_len (hc->rx_buf) < (next_line_offset + 2))
|
||||
@ -699,7 +723,8 @@ http_parse_status_line (http_conn_t *hc)
|
||||
clib_warning ("status line too short (%d)", i);
|
||||
return -1;
|
||||
}
|
||||
next_line_offset = i + 2;
|
||||
hc->control_data_len = i + 2;
|
||||
next_line_offset = hc->control_data_len;
|
||||
p = hc->rx_buf;
|
||||
end = hc->rx_buf + i;
|
||||
|
||||
@ -776,6 +801,7 @@ http_identify_headers (http_conn_t *hc, http_status_code_t *ec)
|
||||
/* just another CRLF -> no headers */
|
||||
HTTP_DBG (0, "no headers");
|
||||
hc->headers_len = 0;
|
||||
hc->control_data_len += 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -789,6 +815,7 @@ http_identify_headers (http_conn_t *hc, http_status_code_t *ec)
|
||||
}
|
||||
hc->headers_offset = hc->rx_buf_offset;
|
||||
hc->headers_len = i - hc->rx_buf_offset + 2;
|
||||
hc->control_data_len += (hc->headers_len + 2);
|
||||
HTTP_DBG (0, "headers length: %u", hc->headers_len);
|
||||
HTTP_DBG (0, "headers offset: %u", hc->headers_offset);
|
||||
|
||||
@ -866,7 +893,7 @@ http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp)
|
||||
http_msg_t msg = {};
|
||||
app_worker_t *app_wrk;
|
||||
session_t *as;
|
||||
u32 len;
|
||||
u32 len, max_enq;
|
||||
http_status_code_t ec;
|
||||
http_main_t *hm = &http_main;
|
||||
|
||||
@ -899,7 +926,16 @@ http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp)
|
||||
if (rv)
|
||||
goto error;
|
||||
|
||||
len = vec_len (hc->rx_buf);
|
||||
/* send at least "control data" which is necessary minimum,
|
||||
* if there is some space send also portion of body */
|
||||
as = session_get_from_handle (hc->h_pa_session_handle);
|
||||
max_enq = svm_fifo_max_enqueue (as->rx_fifo);
|
||||
if (max_enq < hc->control_data_len)
|
||||
{
|
||||
clib_warning ("not enough room for control data in app's rx fifo");
|
||||
goto error;
|
||||
}
|
||||
len = clib_min (max_enq, vec_len (hc->rx_buf));
|
||||
|
||||
msg.type = HTTP_MSG_REPLY;
|
||||
msg.code = hm->sc_by_u16[hc->status_code];
|
||||
@ -910,27 +946,24 @@ http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp)
|
||||
msg.data.type = HTTP_MSG_DATA_INLINE;
|
||||
msg.data.len = len;
|
||||
|
||||
as = session_get_from_handle (hc->h_pa_session_handle);
|
||||
svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) },
|
||||
{ hc->rx_buf, len } };
|
||||
|
||||
rv = svm_fifo_enqueue_segments (as->rx_fifo, segs, 2, 0 /* allow partial */);
|
||||
if (rv < 0)
|
||||
{
|
||||
clib_warning ("error enqueue");
|
||||
return HTTP_SM_ERROR;
|
||||
}
|
||||
ASSERT (rv == (sizeof (msg) + len));
|
||||
|
||||
vec_free (hc->rx_buf);
|
||||
http_read_message_drop (hc, len);
|
||||
|
||||
if (hc->body_len <= (len - hc->body_offset))
|
||||
if (hc->body_len == 0)
|
||||
{
|
||||
/* no response body, we are done */
|
||||
hc->to_recv = 0;
|
||||
http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD);
|
||||
}
|
||||
else
|
||||
{
|
||||
hc->to_recv = hc->body_len - (len - hc->body_offset);
|
||||
/* stream response body */
|
||||
hc->to_recv = hc->body_len;
|
||||
http_state_change (hc, HTTP_STATE_CLIENT_IO_MORE_DATA);
|
||||
}
|
||||
|
||||
@ -940,6 +973,7 @@ http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp)
|
||||
return HTTP_SM_STOP;
|
||||
|
||||
error:
|
||||
http_read_message_drop_all (hc);
|
||||
session_transport_closing_notify (&hc->connection);
|
||||
session_transport_closed_notify (&hc->connection);
|
||||
http_disconnect_transport (hc);
|
||||
@ -954,7 +988,7 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp)
|
||||
http_msg_t msg;
|
||||
session_t *as;
|
||||
int rv;
|
||||
u32 len;
|
||||
u32 len, max_enq;
|
||||
|
||||
rv = http_read_message (hc);
|
||||
|
||||
@ -982,7 +1016,16 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp)
|
||||
if (rv)
|
||||
goto error;
|
||||
|
||||
len = vec_len (hc->rx_buf);
|
||||
/* send "control data" and request body */
|
||||
as = session_get_from_handle (hc->h_pa_session_handle);
|
||||
len = hc->control_data_len + hc->body_len;
|
||||
max_enq = svm_fifo_max_enqueue (as->rx_fifo);
|
||||
if (max_enq < len)
|
||||
{
|
||||
/* TODO stream body of large POST */
|
||||
clib_warning ("not enough room for data in app's rx fifo");
|
||||
goto error;
|
||||
}
|
||||
|
||||
msg.type = HTTP_MSG_REQUEST;
|
||||
msg.method_type = hc->method;
|
||||
@ -1001,18 +1044,11 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp)
|
||||
svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) },
|
||||
{ hc->rx_buf, len } };
|
||||
|
||||
as = session_get_from_handle (hc->h_pa_session_handle);
|
||||
rv = svm_fifo_enqueue_segments (as->rx_fifo, segs, 2, 0 /* allow partial */);
|
||||
if (rv < 0 || rv != sizeof (msg) + len)
|
||||
{
|
||||
clib_warning ("failed app enqueue");
|
||||
/* This should not happen as we only handle 1 request per session,
|
||||
* and fifo is allocated, but going forward we should consider
|
||||
* rescheduling */
|
||||
return HTTP_SM_ERROR;
|
||||
}
|
||||
ASSERT (rv == (sizeof (msg) + len));
|
||||
|
||||
vec_free (hc->rx_buf);
|
||||
/* drop everything, we do not support pipelining */
|
||||
http_read_message_drop_all (hc);
|
||||
http_state_change (hc, HTTP_STATE_WAIT_APP_REPLY);
|
||||
|
||||
app_wrk = app_worker_get_if_valid (as->app_wrk_index);
|
||||
@ -1022,7 +1058,7 @@ http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp)
|
||||
return HTTP_SM_STOP;
|
||||
|
||||
error:
|
||||
|
||||
http_read_message_drop_all (hc);
|
||||
http_send_error (hc, ec);
|
||||
session_transport_closing_notify (&hc->connection);
|
||||
http_disconnect_transport (hc);
|
||||
@ -1345,11 +1381,7 @@ http_state_client_io_more_data (http_conn_t *hc, transport_send_params_t *sp)
|
||||
HTTP_DBG (1, "drained %d from ts; remains %d", rv, hc->to_recv);
|
||||
|
||||
if (hc->to_recv == 0)
|
||||
{
|
||||
hc->rx_buf_offset = 0;
|
||||
vec_reset_length (hc->rx_buf);
|
||||
http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD);
|
||||
}
|
||||
|
||||
app_wrk = app_worker_get_if_valid (as->app_wrk_index);
|
||||
if (app_wrk)
|
||||
|
@ -402,6 +402,7 @@ typedef struct http_tc_
|
||||
http_buffer_t tx_buf;
|
||||
u32 to_recv;
|
||||
u32 bytes_dequeued;
|
||||
u32 control_data_len; /* start line + headers + empty line */
|
||||
http_target_form_t target_form;
|
||||
u32 target_path_offset;
|
||||
u32 target_path_len;
|
||||
|
Reference in New Issue
Block a user