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>
24 #define HCS_DBG(_fmt, _args...) clib_warning (_fmt, ##_args)
26 #define HCS_DBG(_fmt, _args...)
40 CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
45 u32 vpp_session_index;
50 hcs_session_t **sessions;
51 u32 *free_http_cli_process_node_indices;
54 /* Cert key pair for tls */
58 u32 private_segment_size;
61 vlib_main_t *vlib_main;
64 static hcs_main_t hcs_main;
66 static hcs_session_t *
67 hcs_session_alloc (u32 thread_index)
69 hcs_main_t *hcm = &hcs_main;
71 pool_get (hcm->sessions[thread_index], hs);
72 memset (hs, 0, sizeof (*hs));
73 hs->session_index = hs - hcm->sessions[thread_index];
74 hs->thread_index = thread_index;
78 static hcs_session_t *
79 hcs_session_get (u32 thread_index, u32 hs_index)
81 hcs_main_t *hcm = &hcs_main;
82 if (pool_is_free_index (hcm->sessions[thread_index], hs_index))
84 return pool_elt_at_index (hcm->sessions[thread_index], hs_index);
88 hcs_session_free (hcs_session_t *hs)
90 hcs_main_t *hcm = &hcs_main;
91 u32 thread = hs->thread_index;
93 memset (hs, 0xfa, sizeof (*hs));
94 pool_put (hcm->sessions[thread], hs);
98 hcs_cli_process_free (hcs_cli_args_t *args)
100 vlib_main_t *vm = vlib_get_first_main ();
101 hcs_main_t *hcm = &hcs_main;
102 hcs_cli_args_t **save_args;
103 vlib_node_runtime_t *rt;
107 node_index = args->node_index;
108 ASSERT (node_index != 0);
110 n = vlib_get_node (vm, node_index);
111 rt = vlib_node_get_runtime (vm, n->index);
112 save_args = vlib_node_get_runtime_data (vm, n->index);
114 /* Reset process session pointer */
115 clib_mem_free (*save_args);
118 /* Turn off the process node */
119 vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
121 /* add node index to the freelist */
122 vec_add1 (hcm->free_http_cli_process_node_indices, node_index);
125 /* Header, including incantation to suppress favicon.ico requests */
126 static const char *html_header_template =
127 "<html><head><title>%v</title></head>"
128 "<link rel=\"icon\" href=\"data:,\">"
131 static const char *html_footer =
132 "</pre></body></html>\r\n";
135 hcs_cli_output (uword arg, u8 *buffer, uword buffer_bytes)
137 u8 **output_vecp = (u8 **) arg;
141 output_vec = *output_vecp;
143 offset = vec_len (output_vec);
144 vec_validate (output_vec, offset + buffer_bytes - 1);
145 clib_memcpy_fast (output_vec + offset, buffer, buffer_bytes);
147 *output_vecp = output_vec;
151 start_send_data (hcs_session_t *hs, http_status_code_t status,
152 http_content_type_t type)
158 msg.type = HTTP_MSG_REPLY;
160 msg.content_type = type;
161 msg.data.type = HTTP_MSG_DATA_INLINE;
162 msg.data.len = vec_len (hs->tx_buf);
164 ts = session_get (hs->vpp_session_index, hs->thread_index);
165 rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
166 ASSERT (rv == sizeof (msg));
171 rv = svm_fifo_enqueue (ts->tx_fifo, vec_len (hs->tx_buf), hs->tx_buf);
173 if (rv != vec_len (hs->tx_buf))
176 svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
180 vec_free (hs->tx_buf);
185 if (svm_fifo_set_event (ts->tx_fifo))
186 session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
190 send_data_to_http (void *rpc_args)
192 hcs_cli_args_t *args = (hcs_cli_args_t *) rpc_args;
194 http_content_type_t type = HTTP_CONTENT_TEXT_HTML;
196 hs = hcs_session_get (args->thread_index, args->hs_index);
199 vec_free (args->buf);
203 hs->tx_buf = args->buf;
204 if (args->plain_text)
205 type = HTTP_CONTENT_TEXT_PLAIN;
206 start_send_data (hs, HTTP_STATUS_OK, type);
210 clib_mem_free (rpc_args);
214 hcs_cli_process (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
216 u8 *request = 0, *reply = 0, *html = 0;
217 hcs_cli_args_t *args, *rpc_args;
218 hcs_main_t *hcm = &hcs_main;
219 hcs_cli_args_t **save_args;
220 unformat_input_t input;
223 save_args = vlib_node_get_runtime_data (hcm->vlib_main, rt->node_index);
228 /* Replace slashes with spaces, stop at the end of the path */
230 while (i < vec_len (request))
232 if (request[i] == '/')
236 HCS_DBG ("%v", request);
238 /* Run the command */
239 unformat_init_vector (&input, vec_dup (request));
240 vlib_cli_input (vm, &input, hcs_cli_output, (uword) &reply);
241 unformat_free (&input);
244 if (args->plain_text)
246 html = format (0, "%v", reply);
250 /* Generate the html page */
251 html = format (0, html_header_template, request /* title */);
252 html = format (html, "%v", reply);
253 html = format (html, html_footer);
257 rpc_args = clib_mem_alloc (sizeof (*args));
258 clib_memcpy_fast (rpc_args, args, sizeof (*args));
259 rpc_args->buf = html;
261 session_send_rpc_evt_to_thread_force (args->thread_index, send_data_to_http,
265 vec_free (args->buf);
266 hcs_cli_process_free (args);
272 alloc_cli_process (hcs_cli_args_t *args)
274 hcs_main_t *hcm = &hcs_main;
275 vlib_main_t *vm = hcm->vlib_main;
276 hcs_cli_args_t **save_args;
280 l = vec_len (hcm->free_http_cli_process_node_indices);
283 n = vlib_get_node (vm, hcm->free_http_cli_process_node_indices[l - 1]);
284 vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
285 vec_set_len (hcm->free_http_cli_process_node_indices, l - 1);
289 static vlib_node_registration_t r = {
290 .function = hcs_cli_process,
291 .type = VLIB_NODE_TYPE_PROCESS,
292 .process_log2_n_stack_bytes = 16,
293 .runtime_data_bytes = sizeof (void *),
296 vlib_register_node (vm, &r, "http-cli-%d", l);
298 n = vlib_get_node (vm, r.index);
301 /* Save the node index in the args. It won't be zero. */
302 args->node_index = n->index;
304 /* Save the args (pointer) in the node runtime */
305 save_args = vlib_node_get_runtime_data (vm, n->index);
306 *save_args = clib_mem_alloc (sizeof (*args));
307 clib_memcpy_fast (*save_args, args, sizeof (*args));
309 vlib_start_process (vm, n->runtime_index);
313 alloc_cli_process_callback (void *cb_args)
315 alloc_cli_process ((hcs_cli_args_t *) cb_args);
319 hcs_ts_rx_callback (session_t *ts)
321 hcs_cli_args_t args = {};
324 int rv, is_encoded = 0;
326 hs = hcs_session_get (ts->thread_index, ts->opaque);
329 /* Read the http message header */
330 rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
331 ASSERT (rv == sizeof (msg));
333 if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_GET)
335 start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED,
336 HTTP_CONTENT_TEXT_HTML);
340 if (msg.data.target_path_len == 0 ||
341 msg.data.target_form != HTTP_TARGET_ORIGIN_FORM)
344 start_send_data (hs, HTTP_STATUS_BAD_REQUEST, HTTP_CONTENT_TEXT_HTML);
348 /* send the command to a new/recycled vlib process */
349 vec_validate (args.buf, msg.data.target_path_len - 1);
350 rv = svm_fifo_peek (ts->rx_fifo, msg.data.target_path_offset,
351 msg.data.target_path_len, args.buf);
352 ASSERT (rv == msg.data.target_path_len);
353 HCS_DBG ("%v", args.buf);
354 if (http_validate_abs_path_syntax (args.buf, &is_encoded))
356 start_send_data (hs, HTTP_STATUS_BAD_REQUEST, HTTP_CONTENT_TEXT_HTML);
362 u8 *decoded = http_percent_decode (args.buf);
367 if (msg.data.headers_len)
370 http_header_table_t *ht;
371 vec_validate (headers, msg.data.headers_len - 1);
372 rv = svm_fifo_peek (ts->rx_fifo, msg.data.headers_offset,
373 msg.data.headers_len, headers);
374 ASSERT (rv == msg.data.headers_len);
375 if (http_parse_headers (headers, &ht))
377 start_send_data (hs, HTTP_STATUS_BAD_REQUEST,
378 HTTP_CONTENT_TEXT_HTML);
383 const char *accept_value = http_get_header (ht, HTTP_HEADER_ACCEPT);
386 HCS_DBG ("client accept: %s", accept_value);
387 /* just for testing purpose, we don't care about precedence */
388 if (strstr (accept_value, "text/plain"))
391 http_free_header_table (ht);
395 args.hs_index = hs->session_index;
396 args.thread_index = ts->thread_index;
398 /* Send RPC request to main thread */
399 if (vlib_get_thread_index () != 0)
400 vlib_rpc_call_main_thread (alloc_cli_process_callback, (u8 *) &args,
403 alloc_cli_process (&args);
406 svm_fifo_dequeue_drop (ts->rx_fifo, msg.data.len);
411 hcs_ts_tx_callback (session_t *ts)
417 hs = hcs_session_get (ts->thread_index, ts->opaque);
418 if (!hs || !hs->tx_buf)
421 to_send = vec_len (hs->tx_buf) - hs->tx_offset;
422 rv = svm_fifo_enqueue (ts->tx_fifo, to_send, hs->tx_buf + hs->tx_offset);
426 svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
433 svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
437 vec_free (hs->tx_buf);
440 if (svm_fifo_set_event (ts->tx_fifo))
441 session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
447 hcs_ts_accept_callback (session_t *ts)
451 hs = hcs_session_alloc (ts->thread_index);
452 hs->vpp_session_index = ts->session_index;
454 ts->opaque = hs->session_index;
455 ts->session_state = SESSION_STATE_READY;
461 hcs_ts_connected_callback (u32 app_index, u32 api_context, session_t *s,
464 clib_warning ("called...");
469 hcs_ts_disconnect_callback (session_t *s)
471 hcs_main_t *hcm = &hcs_main;
472 vnet_disconnect_args_t _a = { 0 }, *a = &_a;
474 a->handle = session_handle (s);
475 a->app_index = hcm->app_index;
476 vnet_disconnect_session (a);
480 hcs_ts_reset_callback (session_t *s)
482 hcs_main_t *hcm = &hcs_main;
483 vnet_disconnect_args_t _a = { 0 }, *a = &_a;
485 a->handle = session_handle (s);
486 a->app_index = hcm->app_index;
487 vnet_disconnect_session (a);
491 hcs_ts_cleanup_callback (session_t *s, session_cleanup_ntf_t ntf)
495 if (ntf == SESSION_CLEANUP_TRANSPORT)
498 hs = hcs_session_get (s->thread_index, s->opaque);
502 vec_free (hs->tx_buf);
503 hcs_session_free (hs);
507 hcs_add_segment_callback (u32 client_index, u64 segment_handle)
513 hcs_del_segment_callback (u32 client_index, u64 segment_handle)
518 static session_cb_vft_t hcs_session_cb_vft = {
519 .session_accept_callback = hcs_ts_accept_callback,
520 .session_disconnect_callback = hcs_ts_disconnect_callback,
521 .session_connected_callback = hcs_ts_connected_callback,
522 .add_segment_callback = hcs_add_segment_callback,
523 .del_segment_callback = hcs_del_segment_callback,
524 .builtin_app_rx_callback = hcs_ts_rx_callback,
525 .builtin_app_tx_callback = hcs_ts_tx_callback,
526 .session_reset_callback = hcs_ts_reset_callback,
527 .session_cleanup_callback = hcs_ts_cleanup_callback,
533 vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair;
534 hcs_main_t *hcm = &hcs_main;
535 u64 options[APP_OPTIONS_N_OPTIONS];
536 vnet_app_attach_args_t _a, *a = &_a;
537 u32 segment_size = 128 << 20;
539 clib_memset (a, 0, sizeof (*a));
540 clib_memset (options, 0, sizeof (options));
542 if (hcm->private_segment_size)
543 segment_size = hcm->private_segment_size;
545 a->api_client_index = ~0;
546 a->name = format (0, "http_cli_server");
547 a->session_cb_vft = &hcs_session_cb_vft;
548 a->options = options;
549 a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
550 a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = segment_size;
551 a->options[APP_OPTIONS_RX_FIFO_SIZE] =
552 hcm->fifo_size ? hcm->fifo_size : 8 << 10;
553 a->options[APP_OPTIONS_TX_FIFO_SIZE] =
554 hcm->fifo_size ? hcm->fifo_size : 32 << 10;
555 a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
556 a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = hcm->prealloc_fifos;
558 if (vnet_application_attach (a))
561 clib_warning ("failed to attach server");
565 hcm->app_index = a->app_index;
567 clib_memset (ck_pair, 0, sizeof (*ck_pair));
568 ck_pair->cert = (u8 *) test_srv_crt_rsa;
569 ck_pair->key = (u8 *) test_srv_key_rsa;
570 ck_pair->cert_len = test_srv_crt_rsa_len;
571 ck_pair->key_len = test_srv_key_rsa_len;
572 vnet_app_add_cert_key_pair (ck_pair);
573 hcm->ckpair_index = ck_pair->index;
579 hcs_transport_needs_crypto (transport_proto_t proto)
581 return proto == TRANSPORT_PROTO_TLS || proto == TRANSPORT_PROTO_DTLS ||
582 proto == TRANSPORT_PROTO_QUIC;
588 session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
589 hcs_main_t *hcm = &hcs_main;
590 vnet_listen_args_t _a, *a = &_a;
591 char *uri = "tcp://0.0.0.0/80";
595 clib_memset (a, 0, sizeof (*a));
596 a->app_index = hcm->app_index;
599 uri = (char *) hcm->uri;
601 if (parse_uri (uri, &sep))
604 need_crypto = hcs_transport_needs_crypto (sep.transport_proto);
606 sep.transport_proto = TRANSPORT_PROTO_HTTP;
607 clib_memcpy (&a->sep_ext, &sep, sizeof (sep));
611 session_endpoint_alloc_ext_cfg (&a->sep_ext,
612 TRANSPORT_ENDPT_EXT_CFG_CRYPTO);
613 a->sep_ext.ext_cfg->crypto.ckpair_index = hcm->ckpair_index;
616 rv = vnet_listen (a);
619 clib_mem_free (a->sep_ext.ext_cfg);
627 vnet_app_detach_args_t _a, *a = &_a;
628 hcs_main_t *hcm = &hcs_main;
629 a->app_index = hcm->app_index;
630 a->api_client_index = APP_INVALID_INDEX;
632 vnet_application_detach (a);
636 hcs_create (vlib_main_t *vm)
638 vlib_thread_main_t *vtm = vlib_get_thread_main ();
639 hcs_main_t *hcm = &hcs_main;
642 num_threads = 1 /* main thread */ + vtm->n_threads;
643 vec_validate (hcm->sessions, num_threads - 1);
647 clib_warning ("failed to attach server");
653 clib_warning ("failed to start listening");
660 static clib_error_t *
661 hcs_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
662 vlib_cli_command_t *cmd)
664 unformat_input_t _line_input, *line_input = &_line_input;
665 hcs_main_t *hcm = &hcs_main;
669 hcm->prealloc_fifos = 0;
670 hcm->private_segment_size = 0;
673 /* Get a line of input. */
674 if (!unformat_user (input, unformat_line_input, line_input))
677 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
679 if (unformat (line_input, "prealloc-fifos %d", &hcm->prealloc_fifos))
681 else if (unformat (line_input, "private-segment-size %U",
682 unformat_memory_size, &seg_size))
683 hcm->private_segment_size = seg_size;
684 else if (unformat (line_input, "fifo-size %d", &hcm->fifo_size))
685 hcm->fifo_size <<= 10;
686 else if (unformat (line_input, "uri %s", &hcm->uri))
690 unformat_free (line_input);
691 return clib_error_return (0, "unknown input `%U'",
692 format_unformat_error, line_input);
696 unformat_free (line_input);
700 if (hcm->app_index != (u32) ~0)
701 return clib_error_return (0, "test http server is already running");
703 vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
705 rv = hcs_create (vm);
711 return clib_error_return (0, "server_create returned %d", rv);
717 VLIB_CLI_COMMAND (hcs_create_command, static) = {
718 .path = "http cli server",
719 .short_help = "http cli server [uri <uri>] [fifo-size <nbytes>] "
720 "[private-segment-size <nMG>] [prealloc-fifos <n>]",
721 .function = hcs_create_command_fn,
724 static clib_error_t *
725 hcs_main_init (vlib_main_t *vm)
727 hcs_main_t *hcs = &hcs_main;
734 VLIB_INIT_FUNCTION (hcs_main_init);
737 * fd.io coding-style-patch-verification: ON
740 * eval: (c-set-style "gnu")