http: CONNECT method for tunnelling
Type: improvement Change-Id: I6af16ddcc6734bb831227ce65cb39e87294fc4cd Signed-off-by: Matus Fabian <matfabia@cisco.com>
This commit is contained in:

committed by
Florin Coras

parent
7e70b1f0c4
commit
afce287645
@ -7,7 +7,8 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterVppProxyTests(VppProxyHttpGetTcpTest, VppProxyHttpGetTlsTest, VppProxyHttpPutTcpTest, VppProxyHttpPutTlsTest)
|
||||
RegisterVppProxyTests(VppProxyHttpGetTcpTest, VppProxyHttpGetTlsTest, VppProxyHttpPutTcpTest, VppProxyHttpPutTlsTest,
|
||||
VppConnectProxyGetTest, VppConnectProxyPutTest)
|
||||
RegisterEnvoyProxyTests(EnvoyProxyHttpGetTcpTest, EnvoyProxyHttpPutTcpTest)
|
||||
RegisterNginxProxyTests(NginxMirroringTest)
|
||||
RegisterNginxProxySoloTests(MirrorMultiThreadTest)
|
||||
@ -15,14 +16,11 @@ func init() {
|
||||
|
||||
func configureVppProxy(s *VppProxySuite, proto string, proxyPort uint16) {
|
||||
vppProxy := s.GetContainerByName(VppProxyContainerName).VppInstance
|
||||
output := vppProxy.Vppctl(
|
||||
"test proxy server server-uri %s://%s/%d client-uri tcp://%s/%d",
|
||||
proto,
|
||||
s.VppProxyAddr(),
|
||||
proxyPort,
|
||||
s.NginxAddr(),
|
||||
s.NginxPort(),
|
||||
)
|
||||
cmd := fmt.Sprintf("test proxy server fifo-size 512k server-uri %s://%s/%d", proto, s.VppProxyAddr(), proxyPort)
|
||||
if proto != "http" {
|
||||
cmd += fmt.Sprintf(" client-uri tcp://%s/%d", s.NginxAddr(), s.NginxPort())
|
||||
}
|
||||
output := vppProxy.Vppctl(cmd)
|
||||
s.Log("proxy configured: " + output)
|
||||
}
|
||||
|
||||
@ -83,3 +81,23 @@ func nginxMirroring(s *NginxProxySuite, multiThreadWorkers bool) {
|
||||
uri := fmt.Sprintf("http://%s:%d/httpTestFile", s.ProxyAddr(), s.ProxyPort())
|
||||
s.CurlDownloadResource(uri)
|
||||
}
|
||||
|
||||
func VppConnectProxyGetTest(s *VppProxySuite) {
|
||||
var proxyPort uint16 = 8080
|
||||
|
||||
configureVppProxy(s, "http", proxyPort)
|
||||
|
||||
targetUri := fmt.Sprintf("http://%s:%d/httpTestFile", s.NginxAddr(), s.NginxPort())
|
||||
proxyUri := fmt.Sprintf("http://%s:%d", s.VppProxyAddr(), proxyPort)
|
||||
s.CurlDownloadResourceViaTunnel(targetUri, proxyUri)
|
||||
}
|
||||
|
||||
func VppConnectProxyPutTest(s *VppProxySuite) {
|
||||
var proxyPort uint16 = 8080
|
||||
|
||||
configureVppProxy(s, "http", proxyPort)
|
||||
|
||||
proxyUri := fmt.Sprintf("http://%s:%d", s.VppProxyAddr(), proxyPort)
|
||||
targetUri := fmt.Sprintf("http://%s:%d/upload/testFile", s.NginxAddr(), s.NginxPort())
|
||||
s.CurlUploadResourceViaTunnel(targetUri, proxyUri, CurlContainerTestFile)
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -51,6 +51,7 @@ typedef struct proxy_session_side_ctx_
|
||||
proxy_session_side_state_t state;
|
||||
u32 sc_index;
|
||||
u32 ps_index;
|
||||
u8 is_http;
|
||||
} proxy_session_side_ctx_t;
|
||||
|
||||
typedef struct
|
||||
|
@ -447,9 +447,9 @@ static const char *http_error_template = "HTTP/1.1 %s\r\n"
|
||||
*/
|
||||
static const char *http_response_template = "HTTP/1.1 %s\r\n"
|
||||
"Date: %U GMT\r\n"
|
||||
"Server: %v\r\n"
|
||||
"Content-Length: %llu\r\n"
|
||||
"%s";
|
||||
"Server: %v\r\n";
|
||||
|
||||
static const char *content_len_template = "Content-Length: %llu\r\n";
|
||||
|
||||
/**
|
||||
* http request boilerplate
|
||||
@ -705,6 +705,13 @@ http_parse_request_line (http_conn_t *hc, http_status_code_t *ec)
|
||||
hc->method = HTTP_REQ_POST;
|
||||
hc->target_path_offset = method_offset + 5;
|
||||
}
|
||||
else if (!memcmp (hc->rx_buf + method_offset, "CONNECT ", 8))
|
||||
{
|
||||
HTTP_DBG (0, "CONNECT method");
|
||||
hc->method = HTTP_REQ_CONNECT;
|
||||
hc->target_path_offset = method_offset + 8;
|
||||
hc->is_tunnel = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hc->rx_buf[method_offset] - 'A' <= 'Z' - 'A')
|
||||
@ -930,6 +937,11 @@ http_identify_message_body (http_conn_t *hc, http_status_code_t *ec)
|
||||
HTTP_DBG (2, "no header, no message-body");
|
||||
return 0;
|
||||
}
|
||||
if (hc->is_tunnel)
|
||||
{
|
||||
HTTP_DBG (2, "tunnel, no message-body");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO check for chunked transfer coding */
|
||||
|
||||
@ -1271,11 +1283,21 @@ http_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp)
|
||||
/* Date */
|
||||
format_clib_timebase_time, now,
|
||||
/* Server */
|
||||
hc->app_name,
|
||||
/* Length */
|
||||
msg.data.body_len,
|
||||
/* Any headers from app? */
|
||||
msg.data.headers_len ? "" : "\r\n");
|
||||
hc->app_name);
|
||||
|
||||
/* RFC9110 9.3.6: A server MUST NOT send Content-Length header field in a
|
||||
* 2xx (Successful) response to CONNECT. */
|
||||
if (hc->is_tunnel && http_status_code_str[msg.code][0] == '2')
|
||||
{
|
||||
ASSERT (msg.data.body_len == 0);
|
||||
hc->state = HTTP_CONN_STATE_TUNNEL;
|
||||
/* cleanup some stuff we don't need anymore in tunnel mode */
|
||||
http_conn_timer_stop (hc);
|
||||
vec_free (hc->rx_buf);
|
||||
http_buffer_free (&hc->tx_buf);
|
||||
}
|
||||
else
|
||||
response = format (response, content_len_template, msg.data.body_len);
|
||||
|
||||
/* Add headers from app (if any) */
|
||||
if (msg.data.headers_len)
|
||||
@ -1298,6 +1320,11 @@ http_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp)
|
||||
ASSERT (rv == msg.data.headers_len);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No headers from app */
|
||||
response = format (response, "\r\n");
|
||||
}
|
||||
HTTP_DBG (3, "%v", response);
|
||||
|
||||
sent = http_send_data (hc, response, vec_len (response));
|
||||
@ -1649,6 +1676,47 @@ http_req_run_state_machine (http_conn_t *hc, transport_send_params_t *sp)
|
||||
http_conn_timer_update (hc);
|
||||
}
|
||||
|
||||
static int
|
||||
http_tunnel_rx (session_t *ts, http_conn_t *hc)
|
||||
{
|
||||
u32 max_deq, max_enq, max_read, n_segs = 2;
|
||||
svm_fifo_seg_t segs[n_segs];
|
||||
int n_written = 0;
|
||||
session_t *as;
|
||||
app_worker_t *app_wrk;
|
||||
|
||||
HTTP_DBG (1, "tunnel received data from client");
|
||||
|
||||
as = session_get_from_handle (hc->h_pa_session_handle);
|
||||
|
||||
max_deq = svm_fifo_max_dequeue (ts->rx_fifo);
|
||||
if (PREDICT_FALSE (max_deq == 0))
|
||||
{
|
||||
HTTP_DBG (1, "max_deq == 0");
|
||||
return 0;
|
||||
}
|
||||
max_enq = svm_fifo_max_enqueue (as->rx_fifo);
|
||||
if (max_enq == 0)
|
||||
{
|
||||
HTTP_DBG (1, "app's rx fifo full");
|
||||
svm_fifo_add_want_deq_ntf (as->rx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
|
||||
return 0;
|
||||
}
|
||||
max_read = clib_min (max_enq, max_deq);
|
||||
svm_fifo_segments (ts->rx_fifo, 0, segs, &n_segs, max_read);
|
||||
n_written = svm_fifo_enqueue_segments (as->rx_fifo, segs, n_segs, 0);
|
||||
ASSERT (n_written > 0);
|
||||
HTTP_DBG (1, "transfered %u bytes", n_written);
|
||||
svm_fifo_dequeue_drop (ts->rx_fifo, n_written);
|
||||
app_wrk = app_worker_get_if_valid (as->app_wrk_index);
|
||||
if (app_wrk)
|
||||
app_worker_rx_notify (app_wrk, as);
|
||||
if (svm_fifo_max_dequeue_cons (ts->rx_fifo))
|
||||
session_program_rx_io_evt (session_handle (ts));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
http_ts_rx_callback (session_t *ts)
|
||||
{
|
||||
@ -1665,6 +1733,9 @@ http_ts_rx_callback (session_t *ts)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hc->state == HTTP_CONN_STATE_TUNNEL)
|
||||
return http_tunnel_rx (ts, hc);
|
||||
|
||||
if (!http_state_is_rx_valid (hc))
|
||||
{
|
||||
if (hc->state != HTTP_CONN_STATE_CLOSED)
|
||||
@ -1691,6 +1762,7 @@ http_ts_builtin_tx_callback (session_t *ts)
|
||||
http_conn_t *hc;
|
||||
|
||||
hc = http_conn_get_w_thread (ts->opaque, ts->thread_index);
|
||||
HTTP_DBG (1, "transport connection reschedule");
|
||||
transport_connection_reschedule (&hc->connection);
|
||||
|
||||
return 0;
|
||||
@ -2017,6 +2089,54 @@ http_transport_get_listener (u32 listener_index)
|
||||
return &lhc->connection;
|
||||
}
|
||||
|
||||
static int
|
||||
http_tunnel_tx (http_conn_t *hc, session_t *as, transport_send_params_t *sp)
|
||||
{
|
||||
u32 max_deq, max_enq, max_read, n_segs = 2;
|
||||
svm_fifo_seg_t segs[n_segs];
|
||||
session_t *ts;
|
||||
int n_written = 0;
|
||||
|
||||
HTTP_DBG (1, "tunnel received data from target");
|
||||
|
||||
ts = session_get_from_handle (hc->h_tc_session_handle);
|
||||
|
||||
max_deq = svm_fifo_max_dequeue_cons (as->tx_fifo);
|
||||
if (PREDICT_FALSE (max_deq == 0))
|
||||
{
|
||||
HTTP_DBG (1, "max_deq == 0");
|
||||
goto check_fifo;
|
||||
}
|
||||
max_enq = svm_fifo_max_enqueue_prod (ts->tx_fifo);
|
||||
if (max_enq == 0)
|
||||
{
|
||||
HTTP_DBG (1, "ts tx fifo full");
|
||||
goto check_fifo;
|
||||
}
|
||||
max_read = clib_min (max_enq, max_deq);
|
||||
max_read = clib_min (max_read, sp->max_burst_size);
|
||||
svm_fifo_segments (as->tx_fifo, 0, segs, &n_segs, max_read);
|
||||
n_written = svm_fifo_enqueue_segments (ts->tx_fifo, segs, n_segs, 0);
|
||||
ASSERT (n_written > 0);
|
||||
HTTP_DBG (1, "transfered %u bytes", n_written);
|
||||
sp->bytes_dequeued += n_written;
|
||||
sp->max_burst_size -= n_written;
|
||||
svm_fifo_dequeue_drop (as->tx_fifo, n_written);
|
||||
if (svm_fifo_set_event (ts->tx_fifo))
|
||||
session_program_tx_io_evt (ts->handle, SESSION_IO_EVT_TX);
|
||||
|
||||
check_fifo:
|
||||
/* Deschedule and wait for deq notification if ts fifo is almost full */
|
||||
if (svm_fifo_max_enqueue (ts->tx_fifo) < HTTP_FIFO_THRESH)
|
||||
{
|
||||
svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
|
||||
transport_connection_deschedule (&hc->connection);
|
||||
sp->flags |= TRANSPORT_SND_F_DESCHED;
|
||||
}
|
||||
|
||||
return n_written > 0 ? clib_max (n_written / TRANSPORT_PACER_MIN_MSS, 1) : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
http_app_tx_callback (void *session, transport_send_params_t *sp)
|
||||
{
|
||||
@ -2027,6 +2147,13 @@ http_app_tx_callback (void *session, transport_send_params_t *sp)
|
||||
HTTP_DBG (1, "hc [%u]%x", as->thread_index, as->connection_index);
|
||||
|
||||
hc = http_conn_get_w_thread (as->connection_index, as->thread_index);
|
||||
|
||||
max_burst_sz = sp->max_burst_size * TRANSPORT_PACER_MIN_MSS;
|
||||
sp->max_burst_size = max_burst_sz;
|
||||
|
||||
if (hc->state == HTTP_CONN_STATE_TUNNEL)
|
||||
return http_tunnel_tx (hc, as, sp);
|
||||
|
||||
if (!http_state_is_tx_valid (hc))
|
||||
{
|
||||
if (hc->state != HTTP_CONN_STATE_CLOSED)
|
||||
@ -2040,9 +2167,6 @@ http_app_tx_callback (void *session, transport_send_params_t *sp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
max_burst_sz = sp->max_burst_size * TRANSPORT_PACER_MIN_MSS;
|
||||
sp->max_burst_size = max_burst_sz;
|
||||
|
||||
HTTP_DBG (1, "run state machine");
|
||||
http_req_run_state_machine (hc, sp);
|
||||
|
||||
@ -2057,6 +2181,19 @@ http_app_tx_callback (void *session, transport_send_params_t *sp)
|
||||
return sent > 0 ? clib_max (sent / TRANSPORT_PACER_MIN_MSS, 1) : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
http_app_rx_evt_cb (transport_connection_t *tc)
|
||||
{
|
||||
http_conn_t *hc = (http_conn_t *) tc;
|
||||
HTTP_DBG (1, "hc [%u]%x", vlib_get_thread_index (), hc->h_hc_index);
|
||||
session_t *ts = session_get_from_handle (hc->h_tc_session_handle);
|
||||
|
||||
if (hc->state == HTTP_CONN_STATE_TUNNEL)
|
||||
return http_tunnel_rx (ts, hc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
http_transport_get_endpoint (u32 hc_index, u32 thread_index,
|
||||
transport_endpoint_t *tep, u8 is_lcl)
|
||||
@ -2114,6 +2251,9 @@ format_http_conn_state (u8 *s, va_list *args)
|
||||
case HTTP_CONN_STATE_ESTABLISHED:
|
||||
s = format (s, "ESTABLISHED");
|
||||
break;
|
||||
case HTTP_CONN_STATE_TUNNEL:
|
||||
s = format (s, "TUNNEL");
|
||||
break;
|
||||
case HTTP_CONN_STATE_TRANSPORT_CLOSED:
|
||||
s = format (s, "TRANSPORT_CLOSED");
|
||||
break;
|
||||
@ -2212,6 +2352,7 @@ static const transport_proto_vft_t http_proto = {
|
||||
.close = http_transport_close,
|
||||
.cleanup_ho = http_transport_cleanup_ho,
|
||||
.custom_tx = http_app_tx_callback,
|
||||
.app_rx_evt = http_app_rx_evt_cb,
|
||||
.get_connection = http_transport_get_connection,
|
||||
.get_listener = http_transport_get_listener,
|
||||
.get_half_open = http_transport_get_ho,
|
||||
|
@ -64,6 +64,7 @@ typedef enum http_conn_state_
|
||||
HTTP_CONN_STATE_LISTEN,
|
||||
HTTP_CONN_STATE_CONNECTING,
|
||||
HTTP_CONN_STATE_ESTABLISHED,
|
||||
HTTP_CONN_STATE_TUNNEL,
|
||||
HTTP_CONN_STATE_TRANSPORT_CLOSED,
|
||||
HTTP_CONN_STATE_APP_CLOSED,
|
||||
HTTP_CONN_STATE_CLOSED
|
||||
@ -85,6 +86,7 @@ typedef enum http_req_method_
|
||||
{
|
||||
HTTP_REQ_GET = 0,
|
||||
HTTP_REQ_POST,
|
||||
HTTP_REQ_CONNECT,
|
||||
} http_req_method_t;
|
||||
|
||||
typedef enum http_msg_type_
|
||||
@ -415,6 +417,7 @@ typedef struct http_tc_
|
||||
u32 body_offset;
|
||||
u64 body_len;
|
||||
u16 status_code;
|
||||
u8 is_tunnel;
|
||||
} http_conn_t;
|
||||
|
||||
typedef struct http_worker_
|
||||
|
Reference in New Issue
Block a user