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