X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fplugins%2Fhttp%2Fhttp.c;h=036e6929987798e534fb974101ac6df4e3eba9af;hb=bf40da413f8dc1d2d3a2ca355541d6b3648ba532;hp=f40715f055d543f2a74dc3a3207dc505a9942dca;hpb=8bea589cfe0fca1a6f560e16ca66a4cf199041a2;p=vpp.git diff --git a/src/plugins/http/http.c b/src/plugins/http/http.c index f40715f055d..036e6929987 100644 --- a/src/plugins/http/http.c +++ b/src/plugins/http/http.c @@ -20,6 +20,15 @@ static http_main_t http_main; #define HTTP_FIFO_THRESH (16 << 10) +#define CONTENT_LEN_STR "Content-Length: " + +/* HTTP state machine result */ +typedef enum http_sm_result_t_ +{ + HTTP_SM_STOP = 0, + HTTP_SM_CONTINUE = 1, + HTTP_SM_ERROR = -1, +} http_sm_result_t; const char *http_status_code_str[] = { #define _(c, s, str) str, @@ -28,7 +37,7 @@ const char *http_status_code_str[] = { }; const char *http_content_type_str[] = { -#define _(s, str) str, +#define _(s, ext, str) str, foreach_http_content_type #undef _ }; @@ -38,6 +47,42 @@ const http_buffer_type_t msg_to_buf_type[] = { [HTTP_MSG_DATA_PTR] = HTTP_BUFFER_PTR, }; +u8 * +format_http_state (u8 *s, va_list *va) +{ + http_state_t state = va_arg (*va, http_state_t); + + switch (state) + { + case HTTP_STATE_IDLE: + return format (s, "idle"); + case HTTP_STATE_WAIT_APP_METHOD: + return format (s, "wait app method"); + case HTTP_STATE_WAIT_SERVER_REPLY: + return format (s, "wait server reply"); + case HTTP_STATE_CLIENT_IO_MORE_DATA: + return format (s, "client io more data"); + case HTTP_STATE_WAIT_CLIENT_METHOD: + return format (s, "wait client method"); + case HTTP_STATE_WAIT_APP_REPLY: + return format (s, "wait app reply"); + case HTTP_STATE_APP_IO_MORE_DATA: + return format (s, "app io more data"); + default: + break; + } + return format (s, "unknown"); +} + +static inline void +http_state_change (http_conn_t *hc, http_state_t state) +{ + HTTP_DBG (1, "changing http state %U -> %U", format_http_state, + hc->http_state, format_http_state, state); + ASSERT (hc->http_state != state); + hc->http_state = state; +} + static inline http_worker_t * http_worker_get (u32 thread_index) { @@ -50,7 +95,8 @@ http_conn_alloc_w_thread (u32 thread_index) http_worker_t *wrk = http_worker_get (thread_index); http_conn_t *hc; - pool_get_zero (wrk->conn_pool, hc); + pool_get_aligned_safe (wrk->conn_pool, hc, CLIB_CACHE_LINE_BYTES); + clib_memset (hc, 0, sizeof (*hc)); hc->c_thread_index = thread_index; hc->h_hc_index = hc - wrk->conn_pool; hc->h_pa_session_handle = SESSION_INVALID_HANDLE; @@ -147,14 +193,14 @@ http_ts_accept_callback (session_t *ts) hc_index = http_conn_alloc_w_thread (ts->thread_index); hc = http_conn_get_w_thread (hc_index, ts->thread_index); clib_memcpy_fast (hc, lhc, sizeof (*lhc)); - hc->c_thread_index = vlib_get_thread_index (); + hc->c_thread_index = ts->thread_index; hc->h_hc_index = hc_index; hc->h_tc_session_handle = session_handle (ts); hc->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP; hc->state = HTTP_CONN_STATE_ESTABLISHED; - hc->req_state = HTTP_REQ_STATE_WAIT_METHOD; + http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD); ts->session_state = SESSION_STATE_READY; ts->opaque = hc_index; @@ -163,7 +209,6 @@ http_ts_accept_callback (session_t *ts) * Alloc session and initialize */ as = session_alloc (hc->c_thread_index); - as->session_state = SESSION_STATE_CREATED; hc->c_s_index = as->session_index; as->app_wrk_index = hc->h_pa_wrk_index; @@ -212,10 +257,70 @@ http_ts_accept_callback (session_t *ts) } static int -http_ts_connected_callback (u32 http_app_index, u32 hc_index, session_t *ts, +http_ts_connected_callback (u32 http_app_index, u32 ho_hc_index, session_t *ts, session_error_t err) { - clib_warning ("not supported"); + u32 new_hc_index; + session_t *as; + http_conn_t *hc, *ho_hc; + app_worker_t *app_wrk; + int rv; + + if (err) + { + clib_warning ("ERROR: %d", err); + return 0; + } + + new_hc_index = http_conn_alloc_w_thread (ts->thread_index); + hc = http_conn_get_w_thread (new_hc_index, ts->thread_index); + ho_hc = http_conn_get_w_thread (ho_hc_index, 0); + + ASSERT (ho_hc->state == HTTP_CONN_STATE_CONNECTING); + + clib_memcpy_fast (hc, ho_hc, sizeof (*hc)); + + hc->c_thread_index = ts->thread_index; + hc->h_tc_session_handle = session_handle (ts); + hc->c_c_index = new_hc_index; + hc->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP; + hc->state = HTTP_CONN_STATE_ESTABLISHED; + http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD); + + ts->session_state = SESSION_STATE_READY; + ts->opaque = new_hc_index; + + /* allocate app session and initialize */ + + as = session_alloc (hc->c_thread_index); + hc->c_s_index = as->session_index; + as->connection_index = hc->c_c_index; + as->app_wrk_index = hc->h_pa_wrk_index; + as->session_state = SESSION_STATE_READY; + as->opaque = hc->h_pa_app_api_ctx; + as->session_type = session_type_from_proto_and_ip ( + TRANSPORT_PROTO_HTTP, session_type_is_ip4 (ts->session_type)); + + HTTP_DBG (1, "half-open hc index %d, hc index %d", ho_hc_index, + new_hc_index); + + app_wrk = app_worker_get (hc->h_pa_wrk_index); + if (!app_wrk) + { + clib_warning ("no app worker"); + return -1; + } + + if ((rv = app_worker_init_connected (app_wrk, as))) + { + HTTP_DBG (1, "failed to allocate fifos"); + session_free (as); + return rv; + } + app_worker_connect_notify (app_wrk, as, err, hc->h_pa_app_api_ctx); + hc->h_pa_session_handle = session_handle (as); + http_conn_timer_start (hc); + return 0; } @@ -243,7 +348,7 @@ http_ts_reset_callback (session_t *ts) hc->state = HTTP_CONN_STATE_CLOSED; http_buffer_free (&hc->tx_buf); - hc->req_state = HTTP_REQ_STATE_WAIT_METHOD; + http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD); session_transport_reset_notify (&hc->connection); http_disconnect_transport (hc); @@ -259,18 +364,24 @@ static const char *http_error_template = "HTTP/1.1 %s\r\n" "Pragma: no-cache\r\n" "Content-Length: 0\r\n\r\n"; +static const char *http_redirect_template = "HTTP/1.1 %s\r\n"; + /** * http response boilerplate */ -static const char *http_response_template = "HTTP/1.1 200 OK\r\n" +static const char *http_response_template = "HTTP/1.1 %s\r\n" "Date: %U GMT\r\n" "Expires: %U GMT\r\n" "Server: VPP Static\r\n" "Content-Type: %s\r\n" "Content-Length: %lu\r\n\r\n"; +static const char *http_request_template = "GET %s HTTP/1.1\r\n" + "User-Agent: VPP HTTP client\r\n" + "Accept: */*\r\n"; + static u32 -send_data (http_conn_t *hc, u8 *data, u32 length, u32 offset) +http_send_data (http_conn_t *hc, u8 *data, u32 length, u32 offset) { const u32 max_burst = 64 << 10; session_t *ts; @@ -292,7 +403,7 @@ send_data (http_conn_t *hc, u8 *data, u32 length, u32 offset) } static void -send_error (http_conn_t *hc, http_status_code_t ec) +http_send_error (http_conn_t *hc, http_status_code_t ec) { http_main_t *hm = &http_main; u8 *data; @@ -304,12 +415,12 @@ 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); - send_data (hc, data, vec_len (data), 0); + http_send_data (hc, data, vec_len (data), 0); vec_free (data); } static int -read_request (http_conn_t *hc) +http_read_message (http_conn_t *hc) { u32 max_deq, cursize; session_t *ts; @@ -337,7 +448,7 @@ static int v_find_index (u8 *vec, u32 offset, char *str) { int start_index = offset; - u32 slen = (u32) strnlen_s_inline (str, 8); + u32 slen = (u32) strnlen_s_inline (str, 16); u32 vlen = vec_len (vec); ASSERT (slen > 0); @@ -354,11 +465,148 @@ v_find_index (u8 *vec, u32 offset, char *str) return -1; } -/** - * waiting for request method from peer - parse request method and data - */ static int -state_wait_method (http_conn_t *hc, transport_send_params_t *sp) +http_parse_header (http_conn_t *hc, int *content_length) +{ + unformat_input_t input; + int i, len; + u8 *line; + + i = v_find_index (hc->rx_buf, hc->rx_buf_offset, CONTENT_LEN_STR); + if (i < 0) + { + clib_warning ("cannot find '%s' in the header!", CONTENT_LEN_STR); + return -1; + } + + hc->rx_buf_offset = i; + + i = v_find_index (hc->rx_buf, hc->rx_buf_offset, "\n"); + if (i < 0) + { + clib_warning ("end of line missing; incomplete data"); + return -1; + } + + len = i - hc->rx_buf_offset; + line = vec_new (u8, len); + clib_memcpy (line, hc->rx_buf + hc->rx_buf_offset, len); + + unformat_init_vector (&input, line); + if (!unformat (&input, CONTENT_LEN_STR "%d", content_length)) + { + clib_warning ("failed to unformat content length!"); + return -1; + } + unformat_free (&input); + + /* skip rest of the header */ + hc->rx_buf_offset += len; + i = v_find_index (hc->rx_buf, hc->rx_buf_offset, ""); + if (i < 0) + { + clib_warning (" tag not found"); + return -1; + } + hc->rx_buf_offset = i; + + return 0; +} + +static http_sm_result_t +http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp) +{ + int i, rv, content_length; + http_msg_t msg = {}; + app_worker_t *app_wrk; + session_t *as; + http_status_code_t ec; + + rv = http_read_message (hc); + + /* Nothing yet, wait for data or timer expire */ + if (rv) + return HTTP_SM_STOP; + + if (vec_len (hc->rx_buf) < 8) + { + ec = HTTP_STATUS_BAD_REQUEST; + goto error; + } + + if ((i = v_find_index (hc->rx_buf, 0, "200 OK")) >= 0) + { + msg.type = HTTP_MSG_REPLY; + msg.content_type = HTTP_CONTENT_TEXT_HTML; + msg.code = HTTP_STATUS_OK; + msg.data.type = HTTP_MSG_DATA_INLINE; + msg.data.len = 0; + + rv = http_parse_header (hc, &content_length); + if (rv) + { + clib_warning ("failed to parse http reply"); + session_transport_closing_notify (&hc->connection); + http_disconnect_transport (hc); + return -1; + } + msg.data.len = content_length; + u32 dlen = vec_len (hc->rx_buf) - hc->rx_buf_offset; + as = session_get_from_handle (hc->h_pa_session_handle); + svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) }, + { &hc->rx_buf[hc->rx_buf_offset], dlen } }; + + rv = svm_fifo_enqueue_segments (as->rx_fifo, segs, 2, + 0 /* allow partial */); + if (rv < 0) + { + clib_warning ("error enqueue"); + return HTTP_SM_ERROR; + } + + hc->rx_buf_offset += dlen; + hc->to_recv = content_length - dlen; + + if (hc->rx_buf_offset == vec_len (hc->rx_buf)) + { + vec_reset_length (hc->rx_buf); + hc->rx_buf_offset = 0; + } + + if (hc->to_recv == 0) + { + hc->rx_buf_offset = 0; + vec_reset_length (hc->rx_buf); + http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD); + } + else + { + http_state_change (hc, HTTP_STATE_CLIENT_IO_MORE_DATA); + } + + app_wrk = app_worker_get_if_valid (as->app_wrk_index); + app_worker_rx_notify (app_wrk, as); + return HTTP_SM_STOP; + } + else + { + HTTP_DBG (0, "Unknown http method %v", hc->rx_buf); + ec = HTTP_STATUS_METHOD_NOT_ALLOWED; + goto error; + } + return HTTP_SM_STOP; + +error: + + http_send_error (hc, ec); + session_transport_closing_notify (&hc->connection); + http_disconnect_transport (hc); + + return HTTP_SM_ERROR; +} + +static http_sm_result_t +http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp) { http_status_code_t ec; app_worker_t *app_wrk; @@ -368,11 +616,11 @@ state_wait_method (http_conn_t *hc, transport_send_params_t *sp) u32 len; u8 *buf; - rv = read_request (hc); + rv = http_read_message (hc); /* Nothing yet, wait for data or timer expire */ if (rv) - return 0; + return HTTP_SM_STOP; if (vec_len (hc->rx_buf) < 8) { @@ -392,6 +640,7 @@ state_wait_method (http_conn_t *hc, transport_send_params_t *sp) goto error; } + HTTP_DBG (0, "GET method %v", hc->rx_buf); len = i - hc->rx_buf_offset - 1; } else if ((i = v_find_index (hc->rx_buf, 0, "POST ")) >= 0) @@ -399,10 +648,11 @@ state_wait_method (http_conn_t *hc, transport_send_params_t *sp) hc->method = HTTP_REQ_POST; hc->rx_buf_offset = i + 6; len = vec_len (hc->rx_buf) - hc->rx_buf_offset - 1; + HTTP_DBG (0, "POST method %v", hc->rx_buf); } else { - HTTP_DBG (0, "Unknown http method"); + HTTP_DBG (0, "Unknown http method %v", hc->rx_buf); ec = HTTP_STATUS_METHOD_NOT_ALLOWED; goto error; } @@ -425,39 +675,37 @@ state_wait_method (http_conn_t *hc, transport_send_params_t *sp) /* This should not happen as we only handle 1 request per session, * and fifo is allocated, but going forward we should consider * rescheduling */ - return -1; + return HTTP_SM_ERROR; } vec_free (hc->rx_buf); - hc->req_state = HTTP_REQ_STATE_WAIT_APP; + http_state_change (hc, HTTP_STATE_WAIT_APP_REPLY); app_wrk = app_worker_get_if_valid (as->app_wrk_index); - app_worker_lock_and_send_event (app_wrk, as, SESSION_IO_EVT_RX); + if (app_wrk) + app_worker_rx_notify (app_wrk, as); - return 0; + return HTTP_SM_STOP; error: - send_error (hc, ec); + http_send_error (hc, ec); session_transport_closing_notify (&hc->connection); http_disconnect_transport (hc); - return -1; + return HTTP_SM_ERROR; } -/** - * waiting for data from app - */ -static int -state_wait_app (http_conn_t *hc, transport_send_params_t *sp) +static http_sm_result_t +http_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp) { http_main_t *hm = &http_main; - http_status_code_t ec; - http_msg_t msg; - session_t *as; u8 *header; u32 offset; f64 now; + session_t *as; + http_status_code_t sc; + http_msg_t msg; int rv; as = session_get_from_handle (hc->h_pa_session_handle); @@ -465,16 +713,17 @@ state_wait_app (http_conn_t *hc, transport_send_params_t *sp) rv = svm_fifo_dequeue (as->tx_fifo, sizeof (msg), (u8 *) &msg); ASSERT (rv == sizeof (msg)); - if (msg.type != HTTP_MSG_REPLY || msg.data.type > HTTP_MSG_DATA_PTR) + if (msg.data.type > HTTP_MSG_DATA_PTR) { - clib_warning ("unexpected msg type from app %u", msg.type); - ec = HTTP_STATUS_INTERNAL_ERROR; + clib_warning ("no data"); + sc = HTTP_STATUS_INTERNAL_ERROR; goto error; } - if (msg.code != HTTP_STATUS_OK) + if (msg.type != HTTP_MSG_REPLY) { - ec = msg.code; + clib_warning ("unexpected message type %d", msg.type); + sc = HTTP_STATUS_INTERNAL_ERROR; goto error; } @@ -489,46 +738,186 @@ state_wait_app (http_conn_t *hc, transport_send_params_t *sp) * - data length */ now = clib_timebase_now (&hm->timebase); - header = format (0, http_response_template, - /* Date */ - format_clib_timebase_time, now, - /* Expires */ - format_clib_timebase_time, now + 600.0, - /* Content type */ - http_content_type_str[msg.content_type], - /* Length */ - msg.data.len); - - offset = send_data (hc, header, vec_len (header), 0); + + switch (msg.code) + { + case HTTP_STATUS_OK: + header = + format (0, http_response_template, http_status_code_str[msg.code], + /* Date */ + format_clib_timebase_time, now, + /* Expires */ + format_clib_timebase_time, now + 600.0, + /* Content type */ + http_content_type_str[msg.content_type], + /* Length */ + msg.data.len); + break; + case HTTP_STATUS_MOVED: + header = + format (0, http_redirect_template, http_status_code_str[msg.code]); + /* Location: http(s)://new-place already queued up as data */ + break; + default: + return HTTP_SM_ERROR; + } + + offset = http_send_data (hc, header, vec_len (header), 0); if (offset != vec_len (header)) { clib_warning ("couldn't send response header!"); - ec = HTTP_STATUS_INTERNAL_ERROR; + sc = HTTP_STATUS_INTERNAL_ERROR; + vec_free (header); goto error; } vec_free (header); /* Start sending the actual data */ - hc->req_state = HTTP_REQ_STATE_SEND_MORE_DATA; + http_state_change (hc, HTTP_STATE_APP_IO_MORE_DATA); ASSERT (sp->max_burst_size >= offset); sp->max_burst_size -= offset; - - return 1; + return HTTP_SM_CONTINUE; error: + clib_warning ("unexpected msg type from app %u", msg.type); + http_send_error (hc, sc); + http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD); + session_transport_closing_notify (&hc->connection); + http_disconnect_transport (hc); + return HTTP_SM_STOP; +} + +static http_sm_result_t +http_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp) +{ + http_status_code_t sc; + http_msg_t msg; + session_t *as; + u8 *buf = 0, *request; + u32 offset; + int rv; - send_error (hc, ec); - hc->req_state = HTTP_REQ_STATE_WAIT_METHOD; + as = session_get_from_handle (hc->h_pa_session_handle); + + rv = svm_fifo_dequeue (as->tx_fifo, sizeof (msg), (u8 *) &msg); + ASSERT (rv == sizeof (msg)); + + if (msg.data.type > HTTP_MSG_DATA_PTR) + { + clib_warning ("no data"); + sc = HTTP_STATUS_INTERNAL_ERROR; + goto error; + } + + if (msg.type != HTTP_MSG_REQUEST) + { + clib_warning ("unexpected message type %d", msg.type); + sc = HTTP_STATUS_INTERNAL_ERROR; + goto error; + } + + sc = msg.code; + + vec_validate (buf, msg.data.len - 1); + rv = svm_fifo_dequeue (as->tx_fifo, msg.data.len, buf); + ASSERT (rv == msg.data.len); + + request = format (0, http_request_template, buf); + offset = http_send_data (hc, request, vec_len (request), 0); + if (offset != vec_len (request)) + { + clib_warning ("sending request failed!"); + sc = HTTP_STATUS_INTERNAL_ERROR; + goto error; + } + + http_state_change (hc, HTTP_STATE_WAIT_SERVER_REPLY); + + vec_free (buf); + vec_free (request); + + return HTTP_SM_CONTINUE; + +error: + clib_warning ("unexpected msg type from app %u", msg.type); + http_send_error (hc, sc); session_transport_closing_notify (&hc->connection); http_disconnect_transport (hc); + return HTTP_SM_STOP; +} - /* stop state machine processing */ - return 0; +static void +http_app_enqueue (http_conn_t *hc, session_t *as) +{ + app_worker_t *app_wrk; + u32 dlen, max_enq, n_enq; + int rv; + + dlen = vec_len (hc->rx_buf) - hc->rx_buf_offset; + if (!dlen) + return; + + max_enq = svm_fifo_max_enqueue (as->rx_fifo); + n_enq = clib_min (max_enq, dlen); + rv = svm_fifo_enqueue (as->rx_fifo, n_enq, &hc->rx_buf[hc->rx_buf_offset]); + if (rv < 0) + return; + + hc->rx_buf_offset += rv; + if (hc->rx_buf_offset >= vec_len (hc->rx_buf)) + { + vec_reset_length (hc->rx_buf); + hc->rx_buf_offset = 0; + } + + app_wrk = app_worker_get_if_valid (as->app_wrk_index); + ASSERT (app_wrk); + app_worker_rx_notify (app_wrk, as); } -static int -state_send_more_data (http_conn_t *hc, transport_send_params_t *sp) +static http_sm_result_t +http_state_client_io_more_data (http_conn_t *hc, transport_send_params_t *sp) +{ + session_t *as, *ts; + u32 max_deq; + int n_read; + + as = session_get_from_handle (hc->h_pa_session_handle); + ts = session_get_from_handle (hc->h_tc_session_handle); + + http_app_enqueue (hc, as); + + if (hc->to_recv == 0) + { + http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD); + return HTTP_SM_STOP; + } + + max_deq = svm_fifo_max_dequeue (ts->rx_fifo); + if (max_deq > 0) + { + vec_validate (hc->rx_buf, max_deq - 1); + n_read = svm_fifo_dequeue (ts->rx_fifo, max_deq, hc->rx_buf); + ASSERT (n_read == max_deq); + + if (svm_fifo_is_empty (ts->rx_fifo)) + svm_fifo_unset_event (ts->rx_fifo); + + hc->to_recv -= n_read; + vec_set_len (hc->rx_buf, n_read); + } + + if (hc->rx_buf_offset < vec_len (hc->rx_buf) || + svm_fifo_max_dequeue_cons (ts->rx_fifo)) + { + session_enqueue_notify (ts); + } + return HTTP_SM_CONTINUE; +} + +static http_sm_result_t +http_state_app_io_more_data (http_conn_t *hc, transport_send_params_t *sp) { u32 max_send = 64 << 10, n_segs; http_buffer_t *hb = &hc->tx_buf; @@ -569,37 +958,41 @@ state_send_more_data (http_conn_t *hc, transport_send_params_t *sp) if (sent && svm_fifo_set_event (ts->tx_fifo)) session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX_FLUSH); - /* Finished transaction, back to HTTP_REQ_STATE_WAIT_METHOD */ - hc->req_state = HTTP_REQ_STATE_WAIT_METHOD; + /* Finished transaction, back to HTTP_STATE_WAIT_METHOD */ + http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD); http_buffer_free (&hc->tx_buf); } - return 0; + return HTTP_SM_STOP; } -typedef int (*http_sm_handler) (http_conn_t *, transport_send_params_t *sp); - -static http_sm_handler req_state_funcs[HTTP_REQ_N_STATES] = { - /* Waiting for GET, POST, etc. */ - state_wait_method, - /* Wait for data from app */ - state_wait_app, - /* Send more data */ - state_send_more_data, +typedef http_sm_result_t (*http_sm_handler) (http_conn_t *, + transport_send_params_t *sp); + +static http_sm_handler state_funcs[HTTP_N_STATES] = { + 0, /* idle state */ + http_state_wait_app_method, + http_state_wait_client_method, + http_state_wait_server_reply, + http_state_wait_app_reply, + http_state_client_io_more_data, + http_state_app_io_more_data, }; static void http_req_run_state_machine (http_conn_t *hc, transport_send_params_t *sp) { - int rv; - + http_sm_result_t res; do { - rv = req_state_funcs[hc->req_state](hc, sp); - if (rv < 0) - return; + res = state_funcs[hc->http_state](hc, sp); + if (res == HTTP_SM_ERROR) + { + HTTP_DBG (1, "error in state machine %d", res); + return; + } } - while (rv); + while (res == HTTP_SM_CONTINUE); /* Reset the session expiration timer */ http_conn_timer_update (hc); @@ -611,11 +1004,10 @@ http_ts_rx_callback (session_t *ts) http_conn_t *hc; hc = http_conn_get_w_thread (ts->opaque, ts->thread_index); - - if (hc->req_state != HTTP_REQ_STATE_WAIT_METHOD) + if (!hc) { - clib_warning ("tcp data in req state %u", hc->req_state); - return 0; + clib_warning ("http connection not found (ts %d)", ts->opaque); + return -1; } http_req_run_state_machine (hc, 0); @@ -738,7 +1130,35 @@ http_transport_enable (vlib_main_t *vm, u8 is_en) static int http_transport_connect (transport_endpoint_cfg_t *tep) { - return -1; + vnet_connect_args_t _cargs, *cargs = &_cargs; + http_main_t *hm = &http_main; + session_endpoint_cfg_t *sep = (session_endpoint_cfg_t *) tep; + application_t *app; + http_conn_t *hc; + int error; + u32 hc_index; + app_worker_t *app_wrk = app_worker_get (sep->app_wrk_index); + + clib_memset (cargs, 0, sizeof (*cargs)); + clib_memcpy (&cargs->sep_ext, sep, sizeof (session_endpoint_cfg_t)); + cargs->sep.transport_proto = TRANSPORT_PROTO_TCP; + cargs->app_index = hm->app_index; + app = application_get (app_wrk->app_index); + cargs->sep_ext.ns_index = app->ns_index; + + hc_index = http_conn_alloc_w_thread (0 /* ts->thread_index */); + hc = http_conn_get_w_thread (hc_index, 0); + hc->h_pa_wrk_index = sep->app_wrk_index; + hc->h_pa_app_api_ctx = sep->opaque; + hc->state = HTTP_CONN_STATE_CONNECTING; + cargs->api_context = hc_index; + + HTTP_DBG (1, "hc ho_index %x", hc_index); + + if ((error = vnet_connect (cargs))) + return error; + + return 0; } static u32 @@ -819,6 +1239,13 @@ http_transport_close (u32 hc_index, u32 thread_index) HTTP_DBG (1, "App disconnecting %x", hc_index); hc = http_conn_get_w_thread (hc_index, thread_index); + if (hc->state == HTTP_CONN_STATE_CONNECTING) + { + hc->state = HTTP_CONN_STATE_APP_CLOSED; + http_disconnect_transport (hc); + return; + } + as = session_get_from_handle (hc->h_pa_session_handle); /* Nothing more to send, confirm close */ @@ -855,12 +1282,14 @@ http_app_tx_callback (void *session, transport_send_params_t *sp) u32 max_burst_sz, sent; http_conn_t *hc; + HTTP_DBG (1, "app session conn index %x", as->connection_index); + hc = http_conn_get_w_thread (as->connection_index, as->thread_index); - if (hc->req_state < HTTP_REQ_STATE_WAIT_APP) + if (!http_state_is_tx_valid (hc)) { if (hc->state != HTTP_CONN_STATE_CLOSED) - clib_warning ("app data req state %u session state %u", hc->req_state, - hc->state); + clib_warning ("app data req state '%U' session state %u", + format_http_state, hc->http_state, hc->state); svm_fifo_dequeue_drop_all (as->tx_fifo); return 0; } @@ -1064,7 +1493,7 @@ http_config_fn (vlib_main_t *vm, unformat_input_t *input) return 0; } -VLIB_EARLY_CONFIG_FUNCTION (http_config_fn, "http"); +VLIB_CONFIG_FUNCTION (http_config_fn, "http"); VLIB_PLUGIN_REGISTER () = { .version = VPP_BUILD_VER,