2 * Copyright (c) 2022 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 #include <vnet/session/application.h>
17 #include <vnet/session/application_interface.h>
18 #include <vnet/session/session.h>
19 #include <http/http.h>
23 CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
28 u32 vpp_session_index;
31 typedef struct hs_main_
33 hts_session_t **sessions;
49 static hts_main_t hts_main;
51 static hts_session_t *
52 hts_session_alloc (u32 thread_index)
54 hts_main_t *htm = &hts_main;
57 pool_get_zero (htm->sessions[thread_index], hs);
58 hs->session_index = hs - htm->sessions[thread_index];
59 hs->thread_index = thread_index;
64 static hts_session_t *
65 hts_session_get (u32 thread_index, u32 hts_index)
67 hts_main_t *htm = &hts_main;
69 if (pool_is_free_index (htm->sessions[thread_index], hts_index))
72 return pool_elt_at_index (htm->sessions[thread_index], hts_index);
76 hts_session_free (hts_session_t *hs)
78 hts_main_t *htm = &hts_main;
79 u32 thread = hs->thread_index;
82 clib_memset (hs, 0xfa, sizeof (*hs));
84 pool_put (htm->sessions[thread], hs);
88 hts_session_tx_zc (hts_session_t *hs, session_t *ts)
94 rv = svm_fifo_fill_chunk_list (ts->tx_fifo);
97 svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
101 max_send = hs->data_len - hs->data_offset;
102 space = svm_fifo_max_enqueue (ts->tx_fifo);
104 to_send = clib_min (space, max_send);
106 svm_fifo_enqueue_nocopy (ts->tx_fifo, to_send);
108 hs->data_offset += to_send;
110 if (to_send < max_send)
111 svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
113 if (svm_fifo_set_event (ts->tx_fifo))
114 session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
118 hts_session_tx_no_zc (hts_session_t *hs, session_t *ts)
120 u32 n_segs, buf_offset, buf_left;
121 u64 max_send = 32 << 10, left;
122 hts_main_t *htm = &hts_main;
123 svm_fifo_seg_t seg[2];
126 left = hs->data_len - hs->data_offset;
127 max_send = clib_min (left, max_send);
128 buf_offset = hs->data_offset % vec_len (htm->test_data);
129 buf_left = vec_len (htm->test_data) - buf_offset;
131 if (buf_left < max_send)
133 seg[0].data = htm->test_data + buf_offset;
134 seg[0].len = buf_left;
135 seg[1].data = htm->test_data;
136 seg[1].len = max_send - buf_left;
141 seg[0].data = htm->test_data + buf_offset;
142 seg[0].len = max_send;
146 sent = svm_fifo_enqueue_segments (ts->tx_fifo, seg, n_segs,
147 1 /* allow partial */);
151 svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
155 hs->data_offset += sent;
158 svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
160 if (svm_fifo_set_event (ts->tx_fifo))
161 session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
165 hts_session_tx (hts_session_t *hs, session_t *ts)
167 hts_main_t *htm = &hts_main;
170 hts_session_tx_zc (hs, ts);
172 hts_session_tx_no_zc (hs, ts);
176 hts_start_send_data (hts_session_t *hs, http_status_code_t status)
182 msg.type = HTTP_MSG_REPLY;
184 msg.content_type = HTTP_CONTENT_TEXT_HTML;
185 msg.data.type = HTTP_MSG_DATA_INLINE;
186 msg.data.len = hs->data_len;
188 ts = session_get (hs->vpp_session_index, hs->thread_index);
189 rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
190 ASSERT (rv == sizeof (msg));
194 if (svm_fifo_set_event (ts->tx_fifo))
195 session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
199 hts_session_tx (hs, ts);
203 try_test_file (hts_session_t *hs, u8 *request)
205 char *test_str = "test_file";
206 hts_main_t *htm = &hts_main;
207 unformat_input_t input;
211 if (memcmp (request, test_str, clib_strnlen (test_str, 9)))
214 unformat_init_vector (&input, vec_dup (request));
215 if (!unformat (&input, "test_file_%U", unformat_memory_size, &file_size))
221 if (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
227 if (htm->debug_level)
228 clib_warning ("Requested file size %U", format_memory_size, file_size);
230 hs->data_len = file_size;
232 hts_start_send_data (hs, HTTP_STATUS_OK);
235 unformat_free (&input);
241 hts_ts_rx_callback (session_t *ts)
248 hs = hts_session_get (ts->thread_index, ts->opaque);
250 /* Read the http message header */
251 rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
252 ASSERT (rv == sizeof (msg));
254 if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_GET)
256 hts_start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED);
262 hts_start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
266 vec_validate (request, msg.data.len - 1);
267 rv = svm_fifo_dequeue (ts->rx_fifo, msg.data.len, request);
269 if (try_test_file (hs, request))
270 hts_start_send_data (hs, HTTP_STATUS_NOT_FOUND);
278 hs_ts_tx_callback (session_t *ts)
282 hs = hts_session_get (ts->thread_index, ts->opaque);
286 hts_session_tx (hs, ts);
292 hts_ts_accept_callback (session_t *ts)
296 hs = hts_session_alloc (ts->thread_index);
297 hs->vpp_session_index = ts->session_index;
299 ts->opaque = hs->session_index;
300 ts->session_state = SESSION_STATE_READY;
306 hts_ts_connected_callback (u32 app_index, u32 api_context, session_t *s,
309 clib_warning ("called...");
314 hts_ts_disconnect_callback (session_t *s)
316 hts_main_t *htm = &hts_main;
317 vnet_disconnect_args_t _a = { 0 }, *a = &_a;
319 a->handle = session_handle (s);
320 a->app_index = htm->app_index;
321 vnet_disconnect_session (a);
325 hts_ts_reset_callback (session_t *s)
327 hts_main_t *htm = &hts_main;
328 vnet_disconnect_args_t _a = { 0 }, *a = &_a;
330 a->handle = session_handle (s);
331 a->app_index = htm->app_index;
332 vnet_disconnect_session (a);
336 hts_ts_cleanup_callback (session_t *s, session_cleanup_ntf_t ntf)
340 if (ntf == SESSION_CLEANUP_TRANSPORT)
343 hs = hts_session_get (s->thread_index, s->opaque);
347 hts_session_free (hs);
351 hts_add_segment_callback (u32 client_index, u64 segment_handle)
357 hts_del_segment_callback (u32 client_index, u64 segment_handle)
362 static session_cb_vft_t hs_session_cb_vft = {
363 .session_accept_callback = hts_ts_accept_callback,
364 .session_disconnect_callback = hts_ts_disconnect_callback,
365 .session_connected_callback = hts_ts_connected_callback,
366 .add_segment_callback = hts_add_segment_callback,
367 .del_segment_callback = hts_del_segment_callback,
368 .builtin_app_rx_callback = hts_ts_rx_callback,
369 .builtin_app_tx_callback = hs_ts_tx_callback,
370 .session_reset_callback = hts_ts_reset_callback,
371 .session_cleanup_callback = hts_ts_cleanup_callback,
375 hts_attach (hts_main_t *hm)
377 vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair;
378 u64 options[APP_OPTIONS_N_OPTIONS];
379 vnet_app_attach_args_t _a, *a = &_a;
381 clib_memset (a, 0, sizeof (*a));
382 clib_memset (options, 0, sizeof (options));
384 a->api_client_index = ~0;
385 a->name = format (0, "http_tps");
386 a->session_cb_vft = &hs_session_cb_vft;
387 a->options = options;
388 a->options[APP_OPTIONS_SEGMENT_SIZE] = hm->segment_size;
389 a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = hm->segment_size;
390 a->options[APP_OPTIONS_RX_FIFO_SIZE] = hm->fifo_size;
391 a->options[APP_OPTIONS_TX_FIFO_SIZE] = hm->fifo_size;
392 a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
394 if (vnet_application_attach (a))
397 clib_warning ("failed to attach server");
401 hm->app_index = a->app_index;
403 clib_memset (ck_pair, 0, sizeof (*ck_pair));
404 ck_pair->cert = (u8 *) test_srv_crt_rsa;
405 ck_pair->key = (u8 *) test_srv_key_rsa;
406 ck_pair->cert_len = test_srv_crt_rsa_len;
407 ck_pair->key_len = test_srv_key_rsa_len;
408 vnet_app_add_cert_key_pair (ck_pair);
409 hm->ckpair_index = ck_pair->index;
415 hts_transport_needs_crypto (transport_proto_t proto)
417 return proto == TRANSPORT_PROTO_TLS || proto == TRANSPORT_PROTO_DTLS ||
418 proto == TRANSPORT_PROTO_QUIC;
422 hts_listen (hts_main_t *htm)
424 session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
425 vnet_listen_args_t _a, *a = &_a;
426 char *uri = "tcp://0.0.0.0/80";
430 clib_memset (a, 0, sizeof (*a));
431 a->app_index = htm->app_index;
434 uri = (char *) htm->uri;
436 if (parse_uri (uri, &sep))
439 need_crypto = hts_transport_needs_crypto (sep.transport_proto);
441 sep.transport_proto = TRANSPORT_PROTO_HTTP;
442 clib_memcpy (&a->sep_ext, &sep, sizeof (sep));
446 session_endpoint_alloc_ext_cfg (&a->sep_ext,
447 TRANSPORT_ENDPT_EXT_CFG_CRYPTO);
448 a->sep_ext.ext_cfg->crypto.ckpair_index = htm->ckpair_index;
451 rv = vnet_listen (a);
454 clib_mem_free (a->sep_ext.ext_cfg);
460 hts_create (vlib_main_t *vm)
462 vlib_thread_main_t *vtm = vlib_get_thread_main ();
463 hts_main_t *htm = &hts_main;
466 num_threads = 1 /* main thread */ + vtm->n_threads;
467 vec_validate (htm->sessions, num_threads - 1);
470 vec_validate (htm->test_data, (64 << 10) - 1);
472 if (hts_attach (htm))
474 clib_warning ("failed to attach server");
477 if (hts_listen (htm))
479 clib_warning ("failed to start listening");
486 static clib_error_t *
487 hts_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
488 vlib_cli_command_t *cmd)
490 unformat_input_t _line_input, *line_input = &_line_input;
491 hts_main_t *htm = &hts_main;
492 clib_error_t *error = 0;
495 /* Get a line of input. */
496 if (!unformat_user (input, unformat_line_input, line_input))
499 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
501 if (unformat (line_input, "private-segment-size %U",
502 unformat_memory_size, &mem_size))
503 htm->segment_size = mem_size;
504 else if (unformat (line_input, "fifo-size %U", unformat_memory_size,
506 htm->fifo_size = mem_size;
507 else if (unformat (line_input, "uri %s", &htm->uri))
509 else if (unformat (line_input, "no-zc"))
511 else if (unformat (line_input, "debug"))
512 htm->debug_level = 1;
515 error = clib_error_return (0, "unknown input `%U'",
516 format_unformat_error, line_input);
521 unformat_free (line_input);
528 if (htm->app_index != (u32) ~0)
529 return clib_error_return (0, "http tps is already running");
531 vnet_session_enable_disable (vm, 1 /* is_enable */);
534 return clib_error_return (0, "http tps create failed");
539 VLIB_CLI_COMMAND (http_tps_command, static) = {
541 .short_help = "http tps [uri <uri>] [fifo-size <nbytes>] "
542 "[segment-size <nMG>] [prealloc-fifos <n>] [debug] [no-zc]",
543 .function = hts_create_command_fn,
546 static clib_error_t *
547 hs_main_init (vlib_main_t *vm)
549 hts_main_t *htm = &hts_main;
552 htm->segment_size = 128 << 20;
553 htm->fifo_size = 64 << 10;
558 VLIB_INIT_FUNCTION (hs_main_init);
561 * fd.io coding-style-patch-verification: ON
564 * eval: (c-set-style "gnu")