Line data Source code
1 : /*
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:
6 : *
7 : * http://www.apache.org/licenses/LICENSE-2.0
8 : *
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.
14 : */
15 :
16 : #include <vnet/session/application.h>
17 : #include <vnet/session/application_interface.h>
18 : #include <vnet/session/session.h>
19 : #include <http/http.h>
20 :
21 : typedef struct
22 : {
23 : CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
24 : u32 session_index;
25 : u32 thread_index;
26 : u64 data_len;
27 : u64 data_offset;
28 : u32 vpp_session_index;
29 : union
30 : {
31 : /** threshold after which connection is closed */
32 : f64 close_threshold;
33 : /** rate at which accepted sessions are marked for random close */
34 : u32 close_rate;
35 : };
36 : u8 *uri;
37 : } hts_session_t;
38 :
39 : typedef struct hts_listen_cfg_
40 : {
41 : u8 *uri;
42 : u32 vrf;
43 : f64 rnd_close;
44 : u8 is_del;
45 : } hts_listen_cfg_t;
46 :
47 : typedef struct hs_main_
48 : {
49 : hts_session_t **sessions;
50 : u32 app_index;
51 :
52 : u32 ckpair_index;
53 : u8 *test_data;
54 :
55 : /** Hash table of listener uris to handles */
56 : uword *uri_to_handle;
57 :
58 : /*
59 : * Configs
60 : */
61 : u8 *uri;
62 : u32 fifo_size;
63 : u64 segment_size;
64 : u8 debug_level;
65 : u8 no_zc;
66 : u8 *default_uri;
67 : u32 seed;
68 : } hts_main_t;
69 :
70 : static hts_main_t hts_main;
71 :
72 : static hts_session_t *
73 0 : hts_session_alloc (u32 thread_index)
74 : {
75 0 : hts_main_t *htm = &hts_main;
76 : hts_session_t *hs;
77 :
78 0 : pool_get_zero (htm->sessions[thread_index], hs);
79 0 : hs->session_index = hs - htm->sessions[thread_index];
80 0 : hs->thread_index = thread_index;
81 :
82 0 : return hs;
83 : }
84 :
85 : static hts_session_t *
86 0 : hts_session_get (u32 thread_index, u32 hts_index)
87 : {
88 0 : hts_main_t *htm = &hts_main;
89 :
90 0 : if (pool_is_free_index (htm->sessions[thread_index], hts_index))
91 0 : return 0;
92 :
93 0 : return pool_elt_at_index (htm->sessions[thread_index], hts_index);
94 : }
95 :
96 : static void
97 0 : hts_session_free (hts_session_t *hs)
98 : {
99 0 : hts_main_t *htm = &hts_main;
100 0 : u32 thread = hs->thread_index;
101 :
102 0 : if (htm->debug_level > 0)
103 0 : clib_warning ("Freeing session %u", hs->session_index);
104 :
105 : if (CLIB_DEBUG)
106 0 : clib_memset (hs, 0xfa, sizeof (*hs));
107 :
108 0 : pool_put (htm->sessions[thread], hs);
109 0 : }
110 :
111 : static void
112 0 : hts_disconnect_transport (hts_session_t *hs)
113 : {
114 0 : vnet_disconnect_args_t _a = { 0 }, *a = &_a;
115 0 : hts_main_t *htm = &hts_main;
116 : session_t *ts;
117 :
118 0 : if (htm->debug_level > 0)
119 0 : clib_warning ("Actively closing session %u", hs->session_index);
120 :
121 0 : ts = session_get (hs->vpp_session_index, hs->thread_index);
122 0 : a->handle = session_handle (ts);
123 0 : a->app_index = htm->app_index;
124 0 : vnet_disconnect_session (a);
125 0 : }
126 :
127 : static void
128 0 : hts_session_tx_zc (hts_session_t *hs, session_t *ts)
129 : {
130 : u32 to_send, space;
131 : u64 max_send;
132 : int rv;
133 :
134 0 : rv = svm_fifo_fill_chunk_list (ts->tx_fifo);
135 0 : if (rv < 0)
136 : {
137 0 : svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
138 0 : return;
139 : }
140 :
141 0 : max_send = hs->data_len - hs->data_offset;
142 0 : space = svm_fifo_max_enqueue (ts->tx_fifo);
143 0 : ASSERT (space != 0);
144 0 : to_send = clib_min (space, max_send);
145 :
146 0 : svm_fifo_enqueue_nocopy (ts->tx_fifo, to_send);
147 :
148 0 : hs->data_offset += to_send;
149 :
150 0 : if (to_send < max_send)
151 0 : svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
152 :
153 0 : if (svm_fifo_set_event (ts->tx_fifo))
154 0 : session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
155 : }
156 :
157 : static void
158 0 : hts_session_tx_no_zc (hts_session_t *hs, session_t *ts)
159 : {
160 : u32 n_segs, buf_offset, buf_left;
161 0 : u64 max_send = 32 << 10, left;
162 0 : hts_main_t *htm = &hts_main;
163 : svm_fifo_seg_t seg[2];
164 : int sent;
165 :
166 0 : left = hs->data_len - hs->data_offset;
167 0 : max_send = clib_min (left, max_send);
168 0 : buf_offset = hs->data_offset % vec_len (htm->test_data);
169 0 : buf_left = vec_len (htm->test_data) - buf_offset;
170 :
171 0 : if (buf_left < max_send)
172 : {
173 0 : seg[0].data = htm->test_data + buf_offset;
174 0 : seg[0].len = buf_left;
175 0 : seg[1].data = htm->test_data;
176 0 : seg[1].len = max_send - buf_left;
177 0 : n_segs = 2;
178 : }
179 : else
180 : {
181 0 : seg[0].data = htm->test_data + buf_offset;
182 0 : seg[0].len = max_send;
183 0 : n_segs = 1;
184 : }
185 :
186 0 : sent = svm_fifo_enqueue_segments (ts->tx_fifo, seg, n_segs,
187 : 1 /* allow partial */);
188 :
189 0 : if (sent <= 0)
190 : {
191 0 : svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
192 0 : return;
193 : }
194 :
195 0 : hs->data_offset += sent;
196 :
197 0 : if (sent < left)
198 0 : svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
199 :
200 0 : if (svm_fifo_set_event (ts->tx_fifo))
201 0 : session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
202 : }
203 :
204 : static inline void
205 0 : hts_session_tx (hts_session_t *hs, session_t *ts)
206 : {
207 0 : hts_main_t *htm = &hts_main;
208 :
209 0 : if (!htm->no_zc)
210 0 : hts_session_tx_zc (hs, ts);
211 : else
212 0 : hts_session_tx_no_zc (hs, ts);
213 :
214 0 : if (hs->close_threshold > 0)
215 : {
216 0 : if ((f64) hs->data_offset / hs->data_len > hs->close_threshold)
217 0 : hts_disconnect_transport (hs);
218 : }
219 0 : }
220 :
221 : static void
222 0 : hts_start_send_data (hts_session_t *hs, http_status_code_t status)
223 : {
224 : http_msg_t msg;
225 : session_t *ts;
226 : int rv;
227 :
228 0 : msg.type = HTTP_MSG_REPLY;
229 0 : msg.code = status;
230 0 : msg.content_type = HTTP_CONTENT_APP_OCTET_STREAM;
231 0 : msg.data.type = HTTP_MSG_DATA_INLINE;
232 0 : msg.data.len = hs->data_len;
233 :
234 0 : ts = session_get (hs->vpp_session_index, hs->thread_index);
235 0 : rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
236 0 : ASSERT (rv == sizeof (msg));
237 :
238 0 : if (!msg.data.len)
239 : {
240 0 : if (svm_fifo_set_event (ts->tx_fifo))
241 0 : session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
242 0 : return;
243 : }
244 :
245 0 : hts_session_tx (hs, ts);
246 : }
247 :
248 : static int
249 0 : try_test_file (hts_session_t *hs, u8 *request)
250 : {
251 0 : char *test_str = "test_file";
252 0 : hts_main_t *htm = &hts_main;
253 : unformat_input_t input;
254 : uword file_size;
255 0 : int rc = 0;
256 :
257 0 : if (memcmp (request, test_str, clib_strnlen (test_str, 9)))
258 0 : return -1;
259 :
260 0 : unformat_init_vector (&input, vec_dup (request));
261 0 : if (!unformat (&input, "test_file_%U", unformat_memory_size, &file_size))
262 : {
263 0 : rc = -1;
264 0 : goto done;
265 : }
266 :
267 0 : if (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
268 : {
269 0 : rc = -1;
270 0 : goto done;
271 : }
272 :
273 0 : if (htm->debug_level)
274 0 : clib_warning ("Requested file size %U", format_memory_size, file_size);
275 :
276 0 : hs->data_len = file_size;
277 0 : hs->data_offset = 0;
278 :
279 0 : if (hs->close_threshold > 0)
280 : {
281 : /* Disconnect if the header is already enough to fill the quota */
282 0 : if ((f64) 30 / hs->data_len > hs->close_threshold)
283 : {
284 0 : hts_disconnect_transport (hs);
285 0 : goto done;
286 : }
287 : }
288 :
289 0 : hts_start_send_data (hs, HTTP_STATUS_OK);
290 :
291 0 : done:
292 0 : unformat_free (&input);
293 :
294 0 : return rc;
295 : }
296 :
297 : static int
298 0 : hts_ts_rx_callback (session_t *ts)
299 : {
300 : hts_session_t *hs;
301 0 : u8 *request = 0;
302 : http_msg_t msg;
303 : int rv;
304 :
305 0 : hs = hts_session_get (ts->thread_index, ts->opaque);
306 :
307 : /* Read the http message header */
308 0 : rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
309 0 : ASSERT (rv == sizeof (msg));
310 :
311 0 : if (msg.type != HTTP_MSG_REQUEST || msg.method_type != HTTP_REQ_GET)
312 : {
313 0 : hts_start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED);
314 0 : goto done;
315 : }
316 :
317 0 : if (!msg.data.len)
318 : {
319 0 : hts_start_send_data (hs, HTTP_STATUS_BAD_REQUEST);
320 0 : goto done;
321 : }
322 :
323 0 : vec_validate (request, msg.data.len - 1);
324 0 : rv = svm_fifo_dequeue (ts->rx_fifo, msg.data.len, request);
325 :
326 0 : if (try_test_file (hs, request))
327 0 : hts_start_send_data (hs, HTTP_STATUS_NOT_FOUND);
328 :
329 0 : done:
330 :
331 0 : return 0;
332 : }
333 :
334 : static int
335 0 : hs_ts_tx_callback (session_t *ts)
336 : {
337 : hts_session_t *hs;
338 :
339 0 : hs = hts_session_get (ts->thread_index, ts->opaque);
340 0 : if (!hs)
341 0 : return 0;
342 :
343 0 : hts_session_tx (hs, ts);
344 :
345 0 : return 0;
346 : }
347 :
348 : static int
349 0 : hts_ts_accept_callback (session_t *ts)
350 : {
351 0 : hts_main_t *htm = &hts_main;
352 : hts_session_t *hs, *lhs;
353 : session_t *ls;
354 :
355 0 : hs = hts_session_alloc (ts->thread_index);
356 0 : hs->vpp_session_index = ts->session_index;
357 :
358 0 : ts->opaque = hs->session_index;
359 0 : ts->session_state = SESSION_STATE_READY;
360 :
361 : /* Check if listener configured for random closes */
362 0 : ls = listen_session_get_from_handle (ts->listener_handle);
363 0 : lhs = hts_session_get (0, ls->opaque);
364 :
365 0 : if (lhs->close_rate)
366 : {
367 : /* overload listener's data_offset as session counter */
368 0 : u32 cnt = __atomic_add_fetch (&lhs->data_offset, 1, __ATOMIC_RELEASE);
369 0 : if ((cnt % lhs->close_rate) == 0)
370 0 : hs->close_threshold = random_f64 (&htm->seed);
371 : }
372 :
373 0 : if (htm->debug_level > 0)
374 0 : clib_warning ("Accepted session %u close threshold %.2f", ts->opaque,
375 : hs->close_threshold);
376 :
377 0 : return 0;
378 : }
379 :
380 : static int
381 0 : hts_ts_connected_callback (u32 app_index, u32 api_context, session_t *s,
382 : session_error_t err)
383 : {
384 0 : clib_warning ("called...");
385 0 : return -1;
386 : }
387 :
388 : static void
389 0 : hts_ts_disconnect_callback (session_t *ts)
390 : {
391 0 : hts_main_t *htm = &hts_main;
392 0 : vnet_disconnect_args_t _a = { 0 }, *a = &_a;
393 :
394 0 : if (htm->debug_level > 0)
395 0 : clib_warning ("Transport closing session %u", ts->opaque);
396 :
397 0 : a->handle = session_handle (ts);
398 0 : a->app_index = htm->app_index;
399 0 : vnet_disconnect_session (a);
400 0 : }
401 :
402 : static void
403 0 : hts_ts_reset_callback (session_t *ts)
404 : {
405 0 : hts_main_t *htm = &hts_main;
406 0 : vnet_disconnect_args_t _a = { 0 }, *a = &_a;
407 :
408 0 : if (htm->debug_level > 0)
409 0 : clib_warning ("Transport reset session %u", ts->opaque);
410 :
411 0 : a->handle = session_handle (ts);
412 0 : a->app_index = htm->app_index;
413 0 : vnet_disconnect_session (a);
414 0 : }
415 :
416 : static void
417 0 : hts_ts_cleanup_callback (session_t *s, session_cleanup_ntf_t ntf)
418 : {
419 : hts_session_t *hs;
420 :
421 0 : if (ntf == SESSION_CLEANUP_TRANSPORT)
422 0 : return;
423 :
424 0 : hs = hts_session_get (s->thread_index, s->opaque);
425 0 : if (!hs)
426 0 : return;
427 :
428 0 : hts_session_free (hs);
429 : }
430 :
431 : static int
432 0 : hts_add_segment_callback (u32 client_index, u64 segment_handle)
433 : {
434 0 : return 0;
435 : }
436 :
437 : static int
438 0 : hts_del_segment_callback (u32 client_index, u64 segment_handle)
439 : {
440 0 : return 0;
441 : }
442 :
443 : static session_cb_vft_t hs_session_cb_vft = {
444 : .session_accept_callback = hts_ts_accept_callback,
445 : .session_disconnect_callback = hts_ts_disconnect_callback,
446 : .session_connected_callback = hts_ts_connected_callback,
447 : .add_segment_callback = hts_add_segment_callback,
448 : .del_segment_callback = hts_del_segment_callback,
449 : .builtin_app_rx_callback = hts_ts_rx_callback,
450 : .builtin_app_tx_callback = hs_ts_tx_callback,
451 : .session_reset_callback = hts_ts_reset_callback,
452 : .session_cleanup_callback = hts_ts_cleanup_callback,
453 : };
454 :
455 : static int
456 0 : hts_attach (hts_main_t *hm)
457 : {
458 0 : vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair;
459 : u64 options[APP_OPTIONS_N_OPTIONS];
460 0 : vnet_app_attach_args_t _a, *a = &_a;
461 :
462 0 : clib_memset (a, 0, sizeof (*a));
463 0 : clib_memset (options, 0, sizeof (options));
464 :
465 0 : a->api_client_index = ~0;
466 0 : a->name = format (0, "http_tps");
467 0 : a->session_cb_vft = &hs_session_cb_vft;
468 0 : a->options = options;
469 0 : a->options[APP_OPTIONS_SEGMENT_SIZE] = hm->segment_size;
470 0 : a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = hm->segment_size;
471 0 : a->options[APP_OPTIONS_RX_FIFO_SIZE] = hm->fifo_size;
472 0 : a->options[APP_OPTIONS_TX_FIFO_SIZE] = hm->fifo_size;
473 0 : a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
474 :
475 0 : if (vnet_application_attach (a))
476 : {
477 0 : vec_free (a->name);
478 0 : clib_warning ("failed to attach server");
479 0 : return -1;
480 : }
481 0 : vec_free (a->name);
482 0 : hm->app_index = a->app_index;
483 :
484 0 : clib_memset (ck_pair, 0, sizeof (*ck_pair));
485 0 : ck_pair->cert = (u8 *) test_srv_crt_rsa;
486 0 : ck_pair->key = (u8 *) test_srv_key_rsa;
487 0 : ck_pair->cert_len = test_srv_crt_rsa_len;
488 0 : ck_pair->key_len = test_srv_key_rsa_len;
489 0 : vnet_app_add_cert_key_pair (ck_pair);
490 0 : hm->ckpair_index = ck_pair->index;
491 :
492 0 : return 0;
493 : }
494 :
495 : static int
496 0 : hts_transport_needs_crypto (transport_proto_t proto)
497 : {
498 0 : return proto == TRANSPORT_PROTO_TLS || proto == TRANSPORT_PROTO_DTLS ||
499 : proto == TRANSPORT_PROTO_QUIC;
500 : }
501 :
502 : static int
503 0 : hts_start_listen (hts_main_t *htm, session_endpoint_cfg_t *sep, u8 *uri,
504 : f64 rnd_close)
505 : {
506 0 : vnet_listen_args_t _a, *a = &_a;
507 : u8 need_crypto;
508 : hts_session_t *hls;
509 : session_t *ls;
510 0 : u32 thread_index = 0;
511 : int rv;
512 :
513 0 : clib_memset (a, 0, sizeof (*a));
514 0 : a->app_index = htm->app_index;
515 :
516 0 : need_crypto = hts_transport_needs_crypto (sep->transport_proto);
517 :
518 0 : sep->transport_proto = TRANSPORT_PROTO_HTTP;
519 0 : clib_memcpy (&a->sep_ext, sep, sizeof (*sep));
520 :
521 0 : if (need_crypto)
522 : {
523 0 : session_endpoint_alloc_ext_cfg (&a->sep_ext,
524 : TRANSPORT_ENDPT_EXT_CFG_CRYPTO);
525 0 : a->sep_ext.ext_cfg->crypto.ckpair_index = htm->ckpair_index;
526 : }
527 :
528 0 : rv = vnet_listen (a);
529 :
530 0 : if (need_crypto)
531 0 : clib_mem_free (a->sep_ext.ext_cfg);
532 :
533 0 : if (rv)
534 0 : return rv;
535 :
536 0 : hls = hts_session_alloc (thread_index);
537 0 : hls->uri = vec_dup (uri);
538 0 : hls->close_rate = (f64) 1 / rnd_close;
539 0 : ls = listen_session_get_from_handle (a->handle);
540 0 : hls->vpp_session_index = ls->session_index;
541 0 : hash_set_mem (htm->uri_to_handle, hls->uri, hls->session_index);
542 :
543 : /* opaque holds index of hls, which is used in `hts_ts_accept_callback`
544 : * to get back the pointer to hls */
545 0 : ls->opaque = hls - htm->sessions[thread_index];
546 :
547 0 : return 0;
548 : }
549 :
550 : static int
551 0 : hts_stop_listen (hts_main_t *htm, u32 hls_index)
552 : {
553 : hts_session_t *hls;
554 : session_t *ls;
555 :
556 0 : hls = hts_session_get (0, hls_index);
557 0 : ls = listen_session_get (hls->vpp_session_index);
558 :
559 0 : vnet_unlisten_args_t ua = {
560 0 : .handle = listen_session_get_handle (ls),
561 0 : .app_index = htm->app_index,
562 : .wrk_map_index = 0 /* default wrk */
563 : };
564 :
565 0 : hash_unset_mem (htm->uri_to_handle, hls->uri);
566 :
567 0 : if (vnet_unlisten (&ua))
568 0 : return -1;
569 :
570 0 : vec_free (hls->uri);
571 0 : hts_session_free (hls);
572 :
573 0 : return 0;
574 : }
575 :
576 : static clib_error_t *
577 0 : hts_listen (hts_main_t *htm, hts_listen_cfg_t *lcfg)
578 : {
579 0 : session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
580 0 : clib_error_t *error = 0;
581 : u8 *uri, *uri_key;
582 : uword *p;
583 : int rv;
584 :
585 0 : uri = lcfg->uri ? lcfg->uri : htm->default_uri;
586 0 : uri_key = format (0, "vrf%u-%s", lcfg->vrf, uri);
587 0 : p = hash_get_mem (htm->uri_to_handle, uri_key);
588 :
589 0 : if (lcfg->is_del)
590 : {
591 0 : if (!p)
592 0 : error = clib_error_return (0, "not listening on %v", uri);
593 0 : else if (hts_stop_listen (htm, p[0]))
594 0 : error = clib_error_return (0, "failed to unlisten");
595 0 : goto done;
596 : }
597 :
598 0 : if (p)
599 : {
600 0 : error = clib_error_return (0, "already listening %v", uri);
601 0 : goto done;
602 : }
603 :
604 0 : if (parse_uri ((char *) uri, &sep))
605 : {
606 0 : error = clib_error_return (0, "failed to parse uri %v", uri);
607 0 : goto done;
608 : }
609 :
610 0 : if (lcfg->vrf)
611 : {
612 : fib_protocol_t fp;
613 : u32 fib_index;
614 :
615 0 : fp = sep.is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
616 0 : fib_index = fib_table_find (fp, lcfg->vrf);
617 0 : if (fib_index == ~0)
618 : {
619 0 : error = clib_error_return (0, "no such vrf %u", lcfg->vrf);
620 0 : goto done;
621 : }
622 0 : sep.fib_index = fib_index;
623 : }
624 :
625 0 : if ((rv = hts_start_listen (htm, &sep, uri_key, lcfg->rnd_close)))
626 : {
627 0 : error = clib_error_return (0, "failed to listen on %v: %U", uri,
628 : format_session_error, rv);
629 : }
630 :
631 0 : done:
632 :
633 0 : vec_free (uri_key);
634 0 : return error;
635 : }
636 :
637 : static int
638 0 : hts_create (vlib_main_t *vm)
639 : {
640 0 : vlib_thread_main_t *vtm = vlib_get_thread_main ();
641 0 : hts_main_t *htm = &hts_main;
642 : u32 num_threads;
643 :
644 0 : num_threads = 1 /* main thread */ + vtm->n_threads;
645 0 : vec_validate (htm->sessions, num_threads - 1);
646 :
647 0 : if (htm->no_zc)
648 0 : vec_validate (htm->test_data, (64 << 10) - 1);
649 :
650 0 : if (hts_attach (htm))
651 : {
652 0 : clib_warning ("failed to attach server");
653 0 : return -1;
654 : }
655 :
656 0 : htm->default_uri = format (0, "tcp://0.0.0.0/80%c", 0);
657 0 : htm->uri_to_handle = hash_create_vec (0, sizeof (u8), sizeof (uword));
658 :
659 0 : return 0;
660 : }
661 :
662 : static clib_error_t *
663 0 : hts_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
664 : vlib_cli_command_t *cmd)
665 : {
666 0 : unformat_input_t _line_input, *line_input = &_line_input;
667 0 : hts_main_t *htm = &hts_main;
668 0 : hts_listen_cfg_t lcfg = {};
669 0 : clib_error_t *error = 0;
670 : u64 mem_size;
671 :
672 : /* Get a line of input. */
673 0 : if (!unformat_user (input, unformat_line_input, line_input))
674 0 : goto start_server;
675 :
676 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
677 : {
678 0 : if (unformat (line_input, "private-segment-size %U",
679 : unformat_memory_size, &mem_size))
680 0 : htm->segment_size = mem_size;
681 0 : else if (unformat (line_input, "fifo-size %U", unformat_memory_size,
682 : &mem_size))
683 0 : htm->fifo_size = mem_size;
684 0 : else if (unformat (line_input, "no-zc"))
685 0 : htm->no_zc = 1;
686 0 : else if (unformat (line_input, "debug"))
687 0 : htm->debug_level = 1;
688 0 : else if (unformat (line_input, "vrf %u", &lcfg.vrf))
689 : ;
690 0 : else if (unformat (line_input, "uri %s", &lcfg.uri))
691 : ;
692 0 : else if (unformat (line_input, "rnd-close %f", &lcfg.rnd_close))
693 : {
694 0 : if (lcfg.rnd_close > 1.0)
695 : {
696 0 : error = clib_error_return (0, "invalid rnd close value %f",
697 : lcfg.rnd_close);
698 0 : break;
699 : }
700 : }
701 0 : else if (unformat (line_input, "del"))
702 0 : lcfg.is_del = 1;
703 : else
704 : {
705 0 : error = clib_error_return (0, "unknown input `%U'",
706 : format_unformat_error, line_input);
707 0 : break;
708 : }
709 : }
710 :
711 0 : unformat_free (line_input);
712 :
713 0 : if (error)
714 0 : goto done;
715 :
716 0 : start_server:
717 :
718 0 : if (htm->app_index == (u32) ~0)
719 : {
720 0 : vnet_session_enable_disable (vm, 1 /* is_enable */);
721 :
722 0 : if (hts_create (vm))
723 : {
724 0 : error = clib_error_return (0, "http tps create failed");
725 0 : goto done;
726 : }
727 : }
728 :
729 0 : error = hts_listen (htm, &lcfg);
730 :
731 0 : done:
732 :
733 0 : vec_free (lcfg.uri);
734 0 : return error;
735 : }
736 :
737 203447 : VLIB_CLI_COMMAND (http_tps_command, static) = {
738 : .path = "http tps",
739 : .short_help = "http tps [uri <uri>] [fifo-size <nbytes>] "
740 : "[segment-size <nMG>] [prealloc-fifos <n>] [debug] [no-zc] "
741 : "[del]",
742 : .function = hts_create_command_fn,
743 : };
744 :
745 : static clib_error_t *
746 0 : hts_show_command_fn (vlib_main_t *vm, unformat_input_t *input,
747 : vlib_cli_command_t *cmd)
748 : {
749 0 : unformat_input_t _line_input, *line_input = &_line_input;
750 0 : hts_main_t *htm = &hts_main;
751 0 : clib_error_t *error = 0;
752 0 : u8 do_listeners = 0;
753 : hts_session_t **sessions;
754 0 : u32 n_listeners = 0, n_sessions = 0;
755 :
756 0 : if (!unformat_user (input, unformat_line_input, line_input))
757 0 : goto no_input;
758 :
759 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
760 : {
761 0 : if (unformat (line_input, "listeners"))
762 0 : do_listeners = 1;
763 : else
764 : {
765 0 : error = clib_error_return (0, "unknown input `%U'",
766 : format_unformat_error, line_input);
767 0 : break;
768 : }
769 : }
770 :
771 0 : if (error)
772 0 : return error;
773 :
774 0 : no_input:
775 :
776 0 : if (htm->app_index == ~0)
777 : {
778 0 : vlib_cli_output (vm, "http tps not enabled");
779 0 : goto done;
780 : }
781 :
782 0 : if (do_listeners)
783 : {
784 : uword handle;
785 0 : u8 *s = 0, *uri;
786 :
787 : /* clang-format off */
788 0 : hash_foreach (uri, handle, htm->uri_to_handle, ({
789 : s = format (s, "%-30v%lx\n", uri, handle);
790 : }));
791 : /* clang-format on */
792 :
793 0 : if (s)
794 : {
795 0 : vlib_cli_output (vm, "%-29s%s", "URI", "Index");
796 0 : vlib_cli_output (vm, "%v", s);
797 0 : vec_free (s);
798 : }
799 0 : goto done;
800 : }
801 :
802 0 : n_listeners = hash_elts (htm->uri_to_handle);
803 0 : vec_foreach (sessions, htm->sessions)
804 0 : n_sessions += pool_elts (*sessions);
805 :
806 0 : vlib_cli_output (vm, " app index: %u\n listeners: %u\n sesions: %u",
807 : htm->app_index, n_listeners, n_sessions - n_listeners);
808 :
809 0 : done:
810 0 : return 0;
811 : }
812 :
813 203447 : VLIB_CLI_COMMAND (show_http_tps_command, static) = {
814 : .path = "show http tps",
815 : .short_help = "http tps [listeners]",
816 : .function = hts_show_command_fn,
817 : };
818 :
819 : static clib_error_t *
820 559 : hs_main_init (vlib_main_t *vm)
821 : {
822 559 : hts_main_t *htm = &hts_main;
823 :
824 559 : htm->app_index = ~0;
825 559 : htm->segment_size = 128 << 20;
826 559 : htm->fifo_size = 64 << 10;
827 :
828 559 : return 0;
829 : }
830 :
831 2799 : VLIB_INIT_FUNCTION (hs_main_init);
832 :
833 : /*
834 : * fd.io coding-style-patch-verification: ON
835 : *
836 : * Local Variables:
837 : * eval: (c-set-style "gnu")
838 : * End:
839 : */
|