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_set_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;
267 l = vec_len (hcm->free_http_cli_process_node_indices);
270 n = vlib_get_node (vm, hcm->free_http_cli_process_node_indices[l - 1]);
271 vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
272 vec_set_len (hcm->free_http_cli_process_node_indices, l - 1);
276 static vlib_node_registration_t r = {
277 .function = hcs_cli_process,
278 .type = VLIB_NODE_TYPE_PROCESS,
279 .process_log2_n_stack_bytes = 16,
280 .runtime_data_bytes = sizeof (void *),
283 vlib_register_node (vm, &r, "http-cli-%d", l);
285 n = vlib_get_node (vm, r.index);
288 /* Save the node index in the args. It won't be zero. */
289 args->node_index = n->index;
291 /* Save the args (pointer) in the node runtime */
292 save_args = vlib_node_get_runtime_data (vm, n->index);
293 *save_args = clib_mem_alloc (sizeof (*args));
294 clib_memcpy_fast (*save_args, args, sizeof (*args));
296 vlib_start_process (vm, n->runtime_index);
300 alloc_cli_process_callback (void *cb_args)
302 alloc_cli_process ((hcs_cli_args_t *) cb_args);
306 hcs_ts_rx_callback (session_t *ts)
308 hcs_cli_args_t args = {};
313 hs = hcs_session_get (ts->thread_index, ts->opaque);
315 /* Read the http message header */
316 rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
317 ASSERT (rv == sizeof (msg));
319 if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_GET)
322 start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED);
326 /* send the command to a new/recycled vlib process */
327 vec_validate (args.buf, msg.data.len - 1);
328 rv = svm_fifo_dequeue (ts->rx_fifo, msg.data.len, args.buf);
329 ASSERT (rv == msg.data.len);
330 vec_set_len (args.buf, rv);
332 args.hs_index = hs->session_index;
333 args.thread_index = ts->thread_index;
335 /* Send RPC request to main thread */
336 if (vlib_get_thread_index () != 0)
337 vlib_rpc_call_main_thread (alloc_cli_process_callback, (u8 *) &args,
340 alloc_cli_process (&args);
345 hcs_ts_tx_callback (session_t *ts)
351 hs = hcs_session_get (ts->thread_index, ts->opaque);
352 if (!hs || !hs->tx_buf)
355 to_send = vec_len (hs->tx_buf) - hs->tx_offset;
356 rv = svm_fifo_enqueue (ts->tx_fifo, to_send, hs->tx_buf + hs->tx_offset);
360 svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
367 svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
371 vec_free (hs->tx_buf);
374 if (svm_fifo_set_event (ts->tx_fifo))
375 session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
381 hcs_ts_accept_callback (session_t *ts)
385 hs = hcs_session_alloc (ts->thread_index);
386 hs->vpp_session_index = ts->session_index;
388 ts->opaque = hs->session_index;
389 ts->session_state = SESSION_STATE_READY;
395 hcs_ts_connected_callback (u32 app_index, u32 api_context, session_t *s,
398 clib_warning ("called...");
403 hcs_ts_disconnect_callback (session_t *s)
405 hcs_main_t *hcm = &hcs_main;
406 vnet_disconnect_args_t _a = { 0 }, *a = &_a;
408 a->handle = session_handle (s);
409 a->app_index = hcm->app_index;
410 vnet_disconnect_session (a);
414 hcs_ts_reset_callback (session_t *s)
416 hcs_main_t *hcm = &hcs_main;
417 vnet_disconnect_args_t _a = { 0 }, *a = &_a;
419 a->handle = session_handle (s);
420 a->app_index = hcm->app_index;
421 vnet_disconnect_session (a);
425 hcs_ts_cleanup_callback (session_t *s, session_cleanup_ntf_t ntf)
429 if (ntf == SESSION_CLEANUP_TRANSPORT)
432 hs = hcs_session_get (s->thread_index, s->opaque);
436 vec_free (hs->tx_buf);
437 hcs_session_free (hs);
441 hcs_add_segment_callback (u32 client_index, u64 segment_handle)
447 hcs_del_segment_callback (u32 client_index, u64 segment_handle)
452 static session_cb_vft_t hcs_session_cb_vft = {
453 .session_accept_callback = hcs_ts_accept_callback,
454 .session_disconnect_callback = hcs_ts_disconnect_callback,
455 .session_connected_callback = hcs_ts_connected_callback,
456 .add_segment_callback = hcs_add_segment_callback,
457 .del_segment_callback = hcs_del_segment_callback,
458 .builtin_app_rx_callback = hcs_ts_rx_callback,
459 .builtin_app_tx_callback = hcs_ts_tx_callback,
460 .session_reset_callback = hcs_ts_reset_callback,
461 .session_cleanup_callback = hcs_ts_cleanup_callback,
467 vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair;
468 hcs_main_t *hcm = &hcs_main;
469 u64 options[APP_OPTIONS_N_OPTIONS];
470 vnet_app_attach_args_t _a, *a = &_a;
471 u32 segment_size = 128 << 20;
473 clib_memset (a, 0, sizeof (*a));
474 clib_memset (options, 0, sizeof (options));
476 if (hcm->private_segment_size)
477 segment_size = hcm->private_segment_size;
479 a->api_client_index = ~0;
480 a->name = format (0, "http_cli_server");
481 a->session_cb_vft = &hcs_session_cb_vft;
482 a->options = options;
483 a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
484 a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = segment_size;
485 a->options[APP_OPTIONS_RX_FIFO_SIZE] =
486 hcm->fifo_size ? hcm->fifo_size : 8 << 10;
487 a->options[APP_OPTIONS_TX_FIFO_SIZE] =
488 hcm->fifo_size ? hcm->fifo_size : 32 << 10;
489 a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
490 a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = hcm->prealloc_fifos;
492 if (vnet_application_attach (a))
495 clib_warning ("failed to attach server");
499 hcm->app_index = a->app_index;
501 clib_memset (ck_pair, 0, sizeof (*ck_pair));
502 ck_pair->cert = (u8 *) test_srv_crt_rsa;
503 ck_pair->key = (u8 *) test_srv_key_rsa;
504 ck_pair->cert_len = test_srv_crt_rsa_len;
505 ck_pair->key_len = test_srv_key_rsa_len;
506 vnet_app_add_cert_key_pair (ck_pair);
507 hcm->ckpair_index = ck_pair->index;
513 hcs_transport_needs_crypto (transport_proto_t proto)
515 return proto == TRANSPORT_PROTO_TLS || proto == TRANSPORT_PROTO_DTLS ||
516 proto == TRANSPORT_PROTO_QUIC;
522 session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
523 hcs_main_t *hcm = &hcs_main;
524 vnet_listen_args_t _a, *a = &_a;
525 char *uri = "tcp://0.0.0.0/80";
529 clib_memset (a, 0, sizeof (*a));
530 a->app_index = hcm->app_index;
533 uri = (char *) hcm->uri;
535 if (parse_uri (uri, &sep))
538 need_crypto = hcs_transport_needs_crypto (sep.transport_proto);
540 sep.transport_proto = TRANSPORT_PROTO_HTTP;
541 clib_memcpy (&a->sep_ext, &sep, sizeof (sep));
545 session_endpoint_alloc_ext_cfg (&a->sep_ext,
546 TRANSPORT_ENDPT_EXT_CFG_CRYPTO);
547 a->sep_ext.ext_cfg->crypto.ckpair_index = hcm->ckpair_index;
550 rv = vnet_listen (a);
553 clib_mem_free (a->sep_ext.ext_cfg);
561 vnet_app_detach_args_t _a, *a = &_a;
562 hcs_main_t *hcm = &hcs_main;
563 a->app_index = hcm->app_index;
564 a->api_client_index = APP_INVALID_INDEX;
566 vnet_application_detach (a);
570 hcs_create (vlib_main_t *vm)
572 vlib_thread_main_t *vtm = vlib_get_thread_main ();
573 hcs_main_t *hcm = &hcs_main;
576 num_threads = 1 /* main thread */ + vtm->n_threads;
577 vec_validate (hcm->sessions, num_threads - 1);
581 clib_warning ("failed to attach server");
587 clib_warning ("failed to start listening");
594 static clib_error_t *
595 hcs_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
596 vlib_cli_command_t *cmd)
598 unformat_input_t _line_input, *line_input = &_line_input;
599 hcs_main_t *hcm = &hcs_main;
603 hcm->prealloc_fifos = 0;
604 hcm->private_segment_size = 0;
607 /* Get a line of input. */
608 if (!unformat_user (input, unformat_line_input, line_input))
611 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
613 if (unformat (line_input, "prealloc-fifos %d", &hcm->prealloc_fifos))
615 else if (unformat (line_input, "private-segment-size %U",
616 unformat_memory_size, &seg_size))
617 hcm->private_segment_size = seg_size;
618 else if (unformat (line_input, "fifo-size %d", &hcm->fifo_size))
619 hcm->fifo_size <<= 10;
620 else if (unformat (line_input, "uri %s", &hcm->uri))
624 unformat_free (line_input);
625 return clib_error_return (0, "unknown input `%U'",
626 format_unformat_error, line_input);
630 unformat_free (line_input);
634 if (hcm->app_index != (u32) ~0)
635 return clib_error_return (0, "test http server is already running");
637 vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
639 rv = hcs_create (vm);
645 return clib_error_return (0, "server_create returned %d", rv);
651 VLIB_CLI_COMMAND (hcs_create_command, static) = {
652 .path = "http cli server",
653 .short_help = "http cli server [uri <uri>] [fifo-size <nbytes>] "
654 "[private-segment-size <nMG>] [prealloc-fifos <n>]",
655 .function = hcs_create_command_fn,
658 static clib_error_t *
659 hcs_main_init (vlib_main_t *vm)
661 hcs_main_t *hcs = &hcs_main;
668 VLIB_INIT_FUNCTION (hcs_main_init);
671 * fd.io coding-style-patch-verification: ON
674 * eval: (c-set-style "gnu")