2 * Copyright (c) 2017-2019 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>
31 CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
36 u32 vpp_session_index;
41 hcs_session_t **sessions;
42 u32 *free_http_cli_process_node_indices;
45 /* Cert key pair for tls */
49 u32 private_segment_size;
52 vlib_main_t *vlib_main;
55 static hcs_main_t hcs_main;
57 static hcs_session_t *
58 hcs_session_alloc (u32 thread_index)
60 hcs_main_t *hcm = &hcs_main;
62 pool_get (hcm->sessions[thread_index], hs);
63 memset (hs, 0, sizeof (*hs));
64 hs->session_index = hs - hcm->sessions[thread_index];
65 hs->thread_index = thread_index;
69 static hcs_session_t *
70 hcs_session_get (u32 thread_index, u32 hs_index)
72 hcs_main_t *hcm = &hcs_main;
73 if (pool_is_free_index (hcm->sessions[thread_index], hs_index))
75 return pool_elt_at_index (hcm->sessions[thread_index], hs_index);
79 hcs_session_free (hcs_session_t *hs)
81 hcs_main_t *hcm = &hcs_main;
82 u32 thread = hs->thread_index;
84 memset (hs, 0xfa, sizeof (*hs));
85 pool_put (hcm->sessions[thread], hs);
89 hcs_cli_process_free (hcs_cli_args_t *args)
91 vlib_main_t *vm = vlib_get_first_main ();
92 hcs_main_t *hcm = &hcs_main;
93 hcs_cli_args_t **save_args;
94 vlib_node_runtime_t *rt;
98 node_index = args->node_index;
99 ASSERT (node_index != 0);
101 n = vlib_get_node (vm, node_index);
102 rt = vlib_node_get_runtime (vm, n->index);
103 save_args = vlib_node_get_runtime_data (vm, n->index);
105 /* Reset process session pointer */
106 clib_mem_free (*save_args);
109 /* Turn off the process node */
110 vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
112 /* add node index to the freelist */
113 vec_add1 (hcm->free_http_cli_process_node_indices, node_index);
116 /* Header, including incantation to suppress favicon.ico requests */
117 static const char *html_header_template =
118 "<html><head><title>%v</title></head>"
119 "<link rel=\"icon\" href=\"data:,\">"
122 static const char *html_footer =
123 "</pre></body></html>\r\n";
126 hcs_cli_output (uword arg, u8 *buffer, uword buffer_bytes)
128 u8 **output_vecp = (u8 **) arg;
132 output_vec = *output_vecp;
134 offset = vec_len (output_vec);
135 vec_validate (output_vec, offset + buffer_bytes - 1);
136 clib_memcpy_fast (output_vec + offset, buffer, buffer_bytes);
138 *output_vecp = output_vec;
142 start_send_data (hcs_session_t *hs, http_status_code_t status)
148 msg.type = HTTP_MSG_REPLY;
150 msg.content_type = HTTP_CONTENT_TEXT_HTML;
151 msg.data.type = HTTP_MSG_DATA_INLINE;
152 msg.data.len = vec_len (hs->tx_buf);
154 ts = session_get (hs->vpp_session_index, hs->thread_index);
155 rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
156 ASSERT (rv == sizeof (msg));
161 rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (hs->tx_buf), hs->tx_buf);
163 if (rv != vec_len (hs->tx_buf))
166 svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
170 vec_free (hs->tx_buf);
175 if (svm_fifo_set_event (ts->tx_fifo))
176 session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
180 send_data_to_http (void *rpc_args)
182 hcs_cli_args_t *args = (hcs_cli_args_t *) rpc_args;
185 hs = hcs_session_get (args->thread_index, args->hs_index);
188 vec_free (args->buf);
192 hs->tx_buf = args->buf;
193 start_send_data (hs, HTTP_STATUS_OK);
197 clib_mem_free (rpc_args);
201 hcs_cli_process (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
203 u8 *request = 0, *reply = 0, *html = 0;
204 hcs_cli_args_t *args, *rpc_args;
205 hcs_main_t *hcm = &hcs_main;
206 hcs_cli_args_t **save_args;
207 unformat_input_t input;
210 save_args = vlib_node_get_runtime_data (hcm->vlib_main, rt->node_index);
215 /* Replace slashes with spaces, stop at the end of the path */
217 while (i < vec_len (request))
219 if (request[i] == '/')
221 else if (request[i] == ' ')
223 /* vlib_cli_input is vector-based, no need for a NULL */
224 _vec_len (request) = i;
230 /* Generate the html header */
231 html = format (0, html_header_template, request /* title */ );
233 /* Run the command */
234 unformat_init_vector (&input, vec_dup (request));
235 vlib_cli_input (vm, &input, hcs_cli_output, (uword) &reply);
236 unformat_free (&input);
239 /* Generate the html page */
240 html = format (html, "%v", reply);
241 html = format (html, html_footer);
244 rpc_args = clib_mem_alloc (sizeof (*args));
245 clib_memcpy_fast (rpc_args, args, sizeof (*args));
246 rpc_args->buf = html;
248 session_send_rpc_evt_to_thread_force (args->thread_index, send_data_to_http,
252 vec_free (args->buf);
253 hcs_cli_process_free (args);
259 alloc_cli_process (hcs_cli_args_t *args)
261 hcs_main_t *hcm = &hcs_main;
262 vlib_main_t *vm = hcm->vlib_main;
263 hcs_cli_args_t **save_args;
268 l = vec_len (hcm->free_http_cli_process_node_indices);
271 n = vlib_get_node (vm, hcm->free_http_cli_process_node_indices[l - 1]);
272 vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
273 _vec_len (hcm->free_http_cli_process_node_indices) = l - 1;
277 static vlib_node_registration_t r = {
278 .function = hcs_cli_process,
279 .type = VLIB_NODE_TYPE_PROCESS,
280 .process_log2_n_stack_bytes = 16,
281 .runtime_data_bytes = sizeof (void *),
284 name = (char *) format (0, "http-cli-%d", l);
286 vlib_register_node (vm, &r);
289 n = vlib_get_node (vm, r.index);
292 /* Save the node index in the args. It won't be zero. */
293 args->node_index = n->index;
295 /* Save the args (pointer) in the node runtime */
296 save_args = vlib_node_get_runtime_data (vm, n->index);
297 *save_args = clib_mem_alloc (sizeof (*args));
298 clib_memcpy_fast (*save_args, args, sizeof (*args));
300 vlib_start_process (vm, n->runtime_index);
304 alloc_cli_process_callback (void *cb_args)
306 alloc_cli_process ((hcs_cli_args_t *) cb_args);
310 hcs_ts_rx_callback (session_t *ts)
312 hcs_cli_args_t args = {};
317 hs = hcs_session_get (ts->thread_index, ts->opaque);
319 /* Read the http message header */
320 rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
321 ASSERT (rv == sizeof (msg));
323 if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_GET)
326 start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED);
330 /* send the command to a new/recycled vlib process */
331 vec_validate (args.buf, msg.data.len - 1);
332 rv = svm_fifo_dequeue (ts->rx_fifo, msg.data.len, args.buf);
333 ASSERT (rv == msg.data.len);
334 vec_set_len (args.buf, rv);
336 args.hs_index = hs->session_index;
337 args.thread_index = ts->thread_index;
339 /* Send RPC request to main thread */
340 if (vlib_get_thread_index () != 0)
341 vlib_rpc_call_main_thread (alloc_cli_process_callback, (u8 *) &args,
344 alloc_cli_process (&args);
349 hcs_ts_tx_callback (session_t *ts)
355 hs = hcs_session_get (ts->thread_index, ts->opaque);
356 if (!hs || !hs->tx_buf)
359 to_send = vec_len (hs->tx_buf) - hs->tx_offset;
360 rv = svm_fifo_enqueue (ts->tx_fifo, to_send, hs->tx_buf + hs->tx_offset);
364 svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
371 svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
375 vec_free (hs->tx_buf);
378 if (svm_fifo_set_event (ts->tx_fifo))
379 session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
385 hcs_ts_accept_callback (session_t *ts)
389 hs = hcs_session_alloc (ts->thread_index);
390 hs->vpp_session_index = ts->session_index;
392 ts->opaque = hs->session_index;
393 ts->session_state = SESSION_STATE_READY;
399 hcs_ts_connected_callback (u32 app_index, u32 api_context, session_t *s,
402 clib_warning ("called...");
407 hcs_ts_disconnect_callback (session_t *s)
409 hcs_main_t *hcm = &hcs_main;
410 vnet_disconnect_args_t _a = { 0 }, *a = &_a;
412 a->handle = session_handle (s);
413 a->app_index = hcm->app_index;
414 vnet_disconnect_session (a);
418 hcs_ts_reset_callback (session_t *s)
420 hcs_main_t *hcm = &hcs_main;
421 vnet_disconnect_args_t _a = { 0 }, *a = &_a;
423 a->handle = session_handle (s);
424 a->app_index = hcm->app_index;
425 vnet_disconnect_session (a);
429 hcs_ts_cleanup_callback (session_t *s, session_cleanup_ntf_t ntf)
433 if (ntf == SESSION_CLEANUP_TRANSPORT)
436 hs = hcs_session_get (s->thread_index, s->opaque);
440 vec_free (hs->tx_buf);
441 hcs_session_free (hs);
445 hcs_add_segment_callback (u32 client_index, u64 segment_handle)
451 hcs_del_segment_callback (u32 client_index, u64 segment_handle)
456 static session_cb_vft_t hcs_session_cb_vft = {
457 .session_accept_callback = hcs_ts_accept_callback,
458 .session_disconnect_callback = hcs_ts_disconnect_callback,
459 .session_connected_callback = hcs_ts_connected_callback,
460 .add_segment_callback = hcs_add_segment_callback,
461 .del_segment_callback = hcs_del_segment_callback,
462 .builtin_app_rx_callback = hcs_ts_rx_callback,
463 .builtin_app_tx_callback = hcs_ts_tx_callback,
464 .session_reset_callback = hcs_ts_reset_callback,
465 .session_cleanup_callback = hcs_ts_cleanup_callback,
471 vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair;
472 hcs_main_t *hcm = &hcs_main;
473 u64 options[APP_OPTIONS_N_OPTIONS];
474 vnet_app_attach_args_t _a, *a = &_a;
475 u32 segment_size = 128 << 20;
477 clib_memset (a, 0, sizeof (*a));
478 clib_memset (options, 0, sizeof (options));
480 if (hcm->private_segment_size)
481 segment_size = hcm->private_segment_size;
483 a->api_client_index = ~0;
484 a->name = format (0, "http_cli_server");
485 a->session_cb_vft = &hcs_session_cb_vft;
486 a->options = options;
487 a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
488 a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = segment_size;
489 a->options[APP_OPTIONS_RX_FIFO_SIZE] =
490 hcm->fifo_size ? hcm->fifo_size : 8 << 10;
491 a->options[APP_OPTIONS_TX_FIFO_SIZE] =
492 hcm->fifo_size ? hcm->fifo_size : 32 << 10;
493 a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
494 a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = hcm->prealloc_fifos;
496 if (vnet_application_attach (a))
499 clib_warning ("failed to attach server");
503 hcm->app_index = a->app_index;
505 clib_memset (ck_pair, 0, sizeof (*ck_pair));
506 ck_pair->cert = (u8 *) test_srv_crt_rsa;
507 ck_pair->key = (u8 *) test_srv_key_rsa;
508 ck_pair->cert_len = test_srv_crt_rsa_len;
509 ck_pair->key_len = test_srv_key_rsa_len;
510 vnet_app_add_cert_key_pair (ck_pair);
511 hcm->ckpair_index = ck_pair->index;
517 hcs_transport_needs_crypto (transport_proto_t proto)
519 return proto == TRANSPORT_PROTO_TLS || proto == TRANSPORT_PROTO_DTLS ||
520 proto == TRANSPORT_PROTO_QUIC;
526 session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
527 hcs_main_t *hcm = &hcs_main;
528 vnet_listen_args_t _a, *a = &_a;
529 char *uri = "tcp://0.0.0.0/80";
533 clib_memset (a, 0, sizeof (*a));
534 a->app_index = hcm->app_index;
537 uri = (char *) hcm->uri;
539 if (parse_uri (uri, &sep))
542 need_crypto = hcs_transport_needs_crypto (sep.transport_proto);
544 sep.transport_proto = TRANSPORT_PROTO_HTTP;
545 clib_memcpy (&a->sep_ext, &sep, sizeof (sep));
549 session_endpoint_alloc_ext_cfg (&a->sep_ext,
550 TRANSPORT_ENDPT_EXT_CFG_CRYPTO);
551 a->sep_ext.ext_cfg->crypto.ckpair_index = hcm->ckpair_index;
554 rv = vnet_listen (a);
557 clib_mem_free (a->sep_ext.ext_cfg);
563 hcs_create (vlib_main_t *vm)
565 vlib_thread_main_t *vtm = vlib_get_thread_main ();
566 hcs_main_t *hcm = &hcs_main;
569 num_threads = 1 /* main thread */ + vtm->n_threads;
570 vec_validate (hcm->sessions, num_threads - 1);
574 clib_warning ("failed to attach server");
579 clib_warning ("failed to start listening");
586 static clib_error_t *
587 hcs_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
588 vlib_cli_command_t *cmd)
590 unformat_input_t _line_input, *line_input = &_line_input;
591 hcs_main_t *hcm = &hcs_main;
595 hcm->prealloc_fifos = 0;
596 hcm->private_segment_size = 0;
599 /* Get a line of input. */
600 if (!unformat_user (input, unformat_line_input, line_input))
603 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
605 if (unformat (line_input, "prealloc-fifos %d", &hcm->prealloc_fifos))
607 else if (unformat (line_input, "private-segment-size %U",
608 unformat_memory_size, &seg_size))
609 hcm->private_segment_size = seg_size;
610 else if (unformat (line_input, "fifo-size %d", &hcm->fifo_size))
611 hcm->fifo_size <<= 10;
612 else if (unformat (line_input, "uri %s", &hcm->uri))
616 unformat_free (line_input);
617 return clib_error_return (0, "unknown input `%U'",
618 format_unformat_error, line_input);
622 unformat_free (line_input);
626 if (hcm->app_index != (u32) ~0)
627 return clib_error_return (0, "test http server is already running");
629 vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
631 rv = hcs_create (vm);
637 return clib_error_return (0, "server_create returned %d", rv);
643 VLIB_CLI_COMMAND (hcs_create_command, static) = {
644 .path = "http cli server",
645 .short_help = "http cli server [uri <uri>] [fifo-size <nbytes>] "
646 "[private-segment-size <nMG>] [prealloc-fifos <n>]",
647 .function = hcs_create_command_fn,
650 static clib_error_t *
651 hcs_main_init (vlib_main_t *vm)
653 hcs_main_t *hcs = &hcs_main;
660 VLIB_INIT_FUNCTION (hcs_main_init);
663 * fd.io coding-style-patch-verification: ON
666 * eval: (c-set-style "gnu")