tcp: improve builtin http server
[vpp.git] / src / vnet / session / application.c
1 /*
2  * Copyright (c) 2017 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
20 /**
21  * Pool from which we allocate all applications
22  */
23 static application_t *app_pool;
24
25 /**
26  * Hash table of apps by api client index
27  */
28 static uword *app_by_api_client_index;
29
30 /**
31  * Default application event queue size
32  */
33 static u32 default_app_evt_queue_size = 128;
34
35 int
36 application_api_queue_is_full (application_t * app)
37 {
38   unix_shared_memory_queue_t *q;
39
40   /* builtin servers are always OK */
41   if (app->api_client_index == ~0)
42     return 0;
43
44   q = vl_api_client_index_to_input_queue (app->api_client_index);
45   if (!q)
46     return 1;
47
48   if (q->cursize == q->maxsize)
49     return 1;
50   return 0;
51 }
52
53 static void
54 application_table_add (application_t * app)
55 {
56   hash_set (app_by_api_client_index, app->api_client_index, app->index);
57 }
58
59 static void
60 application_table_del (application_t * app)
61 {
62   hash_unset (app_by_api_client_index, app->api_client_index);
63 }
64
65 application_t *
66 application_lookup (u32 api_client_index)
67 {
68   uword *p;
69   p = hash_get (app_by_api_client_index, api_client_index);
70   if (p)
71     return application_get (p[0]);
72
73   return 0;
74 }
75
76 application_t *
77 application_new ()
78 {
79   application_t *app;
80   pool_get (app_pool, app);
81   memset (app, 0, sizeof (*app));
82   app->index = application_get_index (app);
83   app->connects_seg_manager = ~0;
84   app->first_segment_manager = ~0;
85   if (CLIB_DEBUG > 1)
86     clib_warning ("[%d] New app (%d)", getpid (), app->index);
87   return app;
88 }
89
90 void
91 application_del (application_t * app)
92 {
93   segment_manager_t *sm;
94   u64 handle;
95   u32 index, *handles = 0;
96   int i;
97   vnet_unbind_args_t _a, *a = &_a;
98
99   /*
100    * The app event queue allocated in first segment is cleared with
101    * the segment manager. No need to explicitly free it.
102    */
103   if (CLIB_DEBUG > 1)
104     clib_warning ("[%d] Delete app (%d)", getpid (), app->index);
105
106   /*
107    * Cleanup segment managers
108    */
109   if ((app->connects_seg_manager != (u32) ~ 0) &&
110       (app->connects_seg_manager != app->first_segment_manager))
111     {
112       sm = segment_manager_get (app->connects_seg_manager);
113       segment_manager_del (sm);
114     }
115
116   /* *INDENT-OFF* */
117   hash_foreach (handle, index, app->listeners_table,
118   ({
119     vec_add1 (handles, handle);
120   }));
121   /* *INDENT-ON* */
122
123   /* Actual listener cleanup */
124   for (i = 0; i < vec_len (handles); i++)
125     {
126       a->app_index = app->index;
127       a->handle = handles[i];
128       /* seg manager is removed when unbind completes */
129       vnet_unbind (a);
130     }
131
132   if (app->first_segment_manager != ~0)
133     {
134       sm = segment_manager_get (app->first_segment_manager);
135       segment_manager_first_segment_maybe_del (sm);
136     }
137
138   application_table_del (app);
139   pool_put (app_pool, app);
140 }
141
142 static void
143 application_verify_cb_fns (session_cb_vft_t * cb_fns)
144 {
145   if (cb_fns->session_accept_callback == 0)
146     clib_warning ("No accept callback function provided");
147   if (cb_fns->session_connected_callback == 0)
148     clib_warning ("No session connected callback function provided");
149   if (cb_fns->session_disconnect_callback == 0)
150     clib_warning ("No session disconnect callback function provided");
151   if (cb_fns->session_reset_callback == 0)
152     clib_warning ("No session reset callback function provided");
153 }
154
155 int
156 application_init (application_t * app, u32 api_client_index, u64 * options,
157                   session_cb_vft_t * cb_fns)
158 {
159   segment_manager_t *sm;
160   segment_manager_properties_t *props;
161   u32 app_evt_queue_size, first_seg_size;
162   int rv;
163
164   app_evt_queue_size = options[APP_EVT_QUEUE_SIZE] > 0 ?
165     options[APP_EVT_QUEUE_SIZE] : default_app_evt_queue_size;
166
167   /* Setup segment manager */
168   sm = segment_manager_new ();
169   sm->app_index = app->index;
170   props = &app->sm_properties;
171   props->add_segment_size = options[SESSION_OPTIONS_ADD_SEGMENT_SIZE];
172   props->rx_fifo_size = options[SESSION_OPTIONS_RX_FIFO_SIZE];
173   props->tx_fifo_size = options[SESSION_OPTIONS_TX_FIFO_SIZE];
174   props->add_segment = props->add_segment_size != 0;
175   props->preallocated_fifo_pairs = options[APP_OPTIONS_PREALLOC_FIFO_PAIRS];
176   props->use_private_segment = options[APP_OPTIONS_FLAGS]
177     & APP_OPTIONS_FLAGS_BUILTIN_APP;
178   props->private_segment_count = options[APP_OPTIONS_PRIVATE_SEGMENT_COUNT];
179   props->private_segment_size = options[APP_OPTIONS_PRIVATE_SEGMENT_SIZE];
180
181   first_seg_size = options[SESSION_OPTIONS_SEGMENT_SIZE];
182   if ((rv = segment_manager_init (sm, props, first_seg_size)))
183     return rv;
184
185   app->first_segment_manager = segment_manager_index (sm);
186   app->api_client_index = api_client_index;
187   app->flags = options[APP_OPTIONS_FLAGS];
188   app->cb_fns = *cb_fns;
189
190   /* Allocate app event queue in the first shared-memory segment */
191   app->event_queue = segment_manager_alloc_queue (sm, app_evt_queue_size);
192
193   /* Check that the obvious things are properly set up */
194   application_verify_cb_fns (cb_fns);
195
196   /* Add app to lookup by api_client_index table */
197   application_table_add (app);
198
199   return 0;
200 }
201
202 application_t *
203 application_get (u32 index)
204 {
205   return pool_elt_at_index (app_pool, index);
206 }
207
208 application_t *
209 application_get_if_valid (u32 index)
210 {
211   if (pool_is_free_index (app_pool, index))
212     return 0;
213
214   return pool_elt_at_index (app_pool, index);
215 }
216
217 u32
218 application_get_index (application_t * app)
219 {
220   return app - app_pool;
221 }
222
223 static segment_manager_t *
224 application_alloc_segment_manager (application_t * app)
225 {
226   segment_manager_t *sm = 0;
227
228   if (app->first_segment_manager != (u32) ~ 0
229       && app->first_segment_manager_in_use == 0)
230     {
231       sm = segment_manager_get (app->first_segment_manager);
232       app->first_segment_manager_in_use = 1;
233       return sm;
234     }
235
236   sm = segment_manager_new ();
237   sm->properties = &app->sm_properties;
238
239   return sm;
240 }
241
242 /**
243  * Start listening local transport endpoint for requested transport.
244  *
245  * Creates a 'dummy' stream session with state LISTENING to be used in session
246  * lookups, prior to establishing connection. Requests transport to build
247  * it's own specific listening connection.
248  */
249 int
250 application_start_listen (application_t * srv, session_type_t session_type,
251                           transport_endpoint_t * tep, u64 * res)
252 {
253   segment_manager_t *sm;
254   stream_session_t *s;
255   u64 handle;
256
257   s = listen_session_new (session_type);
258   s->app_index = srv->index;
259
260   if (stream_session_listen (s, tep))
261     goto err;
262
263   /* Allocate segment manager. All sessions derived out of a listen session
264    * have fifos allocated by the same segment manager. */
265   sm = application_alloc_segment_manager (srv);
266   if (sm == 0)
267     goto err;
268
269   /* Add to app's listener table. Useful to find all child listeners
270    * when app goes down, although, just for unbinding this is not needed */
271   handle = listen_session_get_handle (s);
272   hash_set (srv->listeners_table, handle, segment_manager_index (sm));
273
274   *res = handle;
275   return 0;
276
277 err:
278   listen_session_del (s);
279   return -1;
280 }
281
282 /**
283  * Stop listening on session associated to handle
284  */
285 int
286 application_stop_listen (application_t * srv, u64 handle)
287 {
288   stream_session_t *listener;
289   uword *indexp;
290   segment_manager_t *sm;
291
292   if (srv && hash_get (srv->listeners_table, handle) == 0)
293     {
294       clib_warning ("app doesn't own handle %llu!", handle);
295       return -1;
296     }
297
298   listener = listen_session_get_from_handle (handle);
299   stream_session_stop_listen (listener);
300
301   indexp = hash_get (srv->listeners_table, handle);
302   ASSERT (indexp);
303
304   sm = segment_manager_get (*indexp);
305   segment_manager_del (sm);
306   if (srv->first_segment_manager == *indexp)
307     {
308       srv->first_segment_manager_in_use = 0;
309       srv->first_segment_manager = ~0;
310     }
311   hash_unset (srv->listeners_table, handle);
312   listen_session_del (listener);
313
314   return 0;
315 }
316
317 int
318 application_open_session (application_t * app, session_type_t sst,
319                           transport_endpoint_t * tep, u32 api_context)
320 {
321   segment_manager_t *sm;
322   transport_connection_t *tc = 0;
323   int rv;
324
325   /* Make sure we have a segment manager for connects */
326   if (app->connects_seg_manager == (u32) ~ 0)
327     {
328       sm = application_alloc_segment_manager (app);
329       if (sm == 0)
330         return -1;
331       app->connects_seg_manager = segment_manager_index (sm);
332     }
333
334   if ((rv = stream_session_open (app->index, sst, tep, &tc)))
335     return rv;
336
337   /* Store api_context for when the reply comes. Not the nicest thing
338    * but better than allocating a separate half-open pool. */
339   tc->s_index = api_context;
340
341   return 0;
342 }
343
344 segment_manager_t *
345 application_get_connect_segment_manager (application_t * app)
346 {
347   ASSERT (app->connects_seg_manager != (u32) ~ 0);
348   return segment_manager_get (app->connects_seg_manager);
349 }
350
351 segment_manager_t *
352 application_get_listen_segment_manager (application_t * app,
353                                         stream_session_t * s)
354 {
355   uword *smp;
356   smp = hash_get (app->listeners_table, listen_session_get_handle (s));
357   ASSERT (smp != 0);
358   return segment_manager_get (*smp);
359 }
360
361 static u8 *
362 app_get_name_from_reg_index (application_t * app)
363 {
364   u8 *app_name;
365
366   vl_api_registration_t *regp;
367   regp = vl_api_client_index_to_registration (app->api_client_index);
368   if (!regp)
369     app_name = format (0, "builtin-%d%c", app->index, 0);
370   else
371     app_name = format (0, "%s%c", regp->name, 0);
372
373   return app_name;
374 }
375
376 int
377 application_is_proxy (application_t * app)
378 {
379   return !(app->flags & APP_OPTIONS_FLAGS_IS_PROXY);
380 }
381
382 u8 *
383 format_application_listener (u8 * s, va_list * args)
384 {
385   application_t *app = va_arg (*args, application_t *);
386   u64 handle = va_arg (*args, u64);
387   u32 index = va_arg (*args, u32);
388   int verbose = va_arg (*args, int);
389   stream_session_t *listener;
390   u8 *app_name, *str;
391
392   if (app == 0)
393     {
394       if (verbose)
395         s = format (s, "%-40s%-20s%-15s%-15s%-10s", "Connection", "App",
396                     "API Client", "ListenerID", "SegManager");
397       else
398         s = format (s, "%-40s%-20s", "Connection", "App");
399
400       return s;
401     }
402
403   app_name = app_get_name_from_reg_index (app);
404   listener = listen_session_get_from_handle (handle);
405   str = format (0, "%U", format_stream_session, listener, verbose);
406
407   if (verbose)
408     {
409       s = format (s, "%-40s%-20s%-15u%-15u%-10u", str, app_name,
410                   app->api_client_index, handle, index);
411     }
412   else
413     s = format (s, "%-40s%-20s", str, app_name);
414
415   vec_free (app_name);
416   return s;
417 }
418
419 void
420 application_format_connects (application_t * app, int verbose)
421 {
422   vlib_main_t *vm = vlib_get_main ();
423   segment_manager_t *sm;
424   u8 *app_name, *s = 0;
425   int j;
426
427   /* Header */
428   if (app == 0)
429     {
430       if (verbose)
431         vlib_cli_output (vm, "%-40s%-20s%-15s%-10s", "Connection", "App",
432                          "API Client", "SegManager");
433       else
434         vlib_cli_output (vm, "%-40s%-20s", "Connection", "App");
435       return;
436     }
437
438   /* make sure */
439   if (app->connects_seg_manager == (u32) ~ 0)
440     return;
441
442   app_name = app_get_name_from_reg_index (app);
443
444   /* Across all fifo segments */
445   sm = segment_manager_get (app->connects_seg_manager);
446   for (j = 0; j < vec_len (sm->segment_indices); j++)
447     {
448       svm_fifo_segment_private_t *fifo_segment;
449       svm_fifo_t *fifo;
450       u8 *str;
451
452       fifo_segment = svm_fifo_get_segment (sm->segment_indices[j]);
453       fifo = svm_fifo_segment_get_fifo_list (fifo_segment);
454       while (fifo)
455         {
456           u32 session_index, thread_index;
457           stream_session_t *session;
458
459           session_index = fifo->master_session_index;
460           thread_index = fifo->master_thread_index;
461
462           session = stream_session_get (session_index, thread_index);
463           str = format (0, "%U", format_stream_session, session, verbose);
464
465           if (verbose)
466             s = format (s, "%-40s%-20s%-15u%-10u", str, app_name,
467                         app->api_client_index, app->connects_seg_manager);
468           else
469             s = format (s, "%-40s%-20s", str, app_name);
470
471           vlib_cli_output (vm, "%v", s);
472           vec_reset_length (s);
473           vec_free (str);
474
475           fifo = fifo->next;
476         }
477       vec_free (s);
478     }
479
480   vec_free (app_name);
481 }
482
483 u8 *
484 format_application (u8 * s, va_list * args)
485 {
486   application_t *app = va_arg (*args, application_t *);
487   CLIB_UNUSED (int verbose) = va_arg (*args, int);
488   u8 *app_name;
489
490   if (app == 0)
491     {
492       if (verbose)
493         s = format (s, "%-10s%-20s%-15s%-15s%-15s%-15s", "Index", "Name",
494                     "API Client", "Add seg size", "Rx fifo size",
495                     "Tx fifo size");
496       else
497         s = format (s, "%-10s%-20s%-20s", "Index", "Name", "API Client");
498       return s;
499     }
500
501   app_name = app_get_name_from_reg_index (app);
502   if (verbose)
503     s = format (s, "%-10d%-20s%-15d%-15d%-15d%-15d", app->index, app_name,
504                 app->api_client_index, app->sm_properties.add_segment_size,
505                 app->sm_properties.rx_fifo_size,
506                 app->sm_properties.tx_fifo_size);
507   else
508     s = format (s, "%-10d%-20s%-20d", app->index, app_name,
509                 app->api_client_index);
510   return s;
511 }
512
513 static clib_error_t *
514 show_app_command_fn (vlib_main_t * vm, unformat_input_t * input,
515                      vlib_cli_command_t * cmd)
516 {
517   application_t *app;
518   int do_server = 0;
519   int do_client = 0;
520   int verbose = 0;
521
522   if (!session_manager_is_enabled ())
523     {
524       clib_error_return (0, "session layer is not enabled");
525     }
526
527   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
528     {
529       if (unformat (input, "server"))
530         do_server = 1;
531       else if (unformat (input, "client"))
532         do_client = 1;
533       else if (unformat (input, "verbose"))
534         verbose = 1;
535       else
536         break;
537     }
538
539   if (do_server)
540     {
541       u64 handle;
542       u32 index;
543       if (pool_elts (app_pool))
544         {
545           vlib_cli_output (vm, "%U", format_application_listener,
546                            0 /* header */ , 0, 0,
547                            verbose);
548           /* *INDENT-OFF* */
549           pool_foreach (app, app_pool,
550           ({
551             /* App's listener sessions */
552             if (hash_elts (app->listeners_table) == 0)
553               continue;
554             hash_foreach (handle, index, app->listeners_table,
555             ({
556               vlib_cli_output (vm, "%U", format_application_listener, app,
557                                        handle, index, verbose);
558             }));
559           }));
560           /* *INDENT-ON* */
561         }
562       else
563         vlib_cli_output (vm, "No active server bindings");
564     }
565
566   if (do_client)
567     {
568       if (pool_elts (app_pool))
569         {
570           application_format_connects (0, verbose);
571
572           /* *INDENT-OFF* */
573           pool_foreach (app, app_pool,
574           ({
575             if (app->connects_seg_manager == (u32)~0)
576               continue;
577             application_format_connects (app, verbose);
578           }));
579           /* *INDENT-ON* */
580         }
581       else
582         vlib_cli_output (vm, "No active client bindings");
583     }
584
585   /* Print app related info */
586   if (!do_server && !do_client)
587     {
588       vlib_cli_output (vm, "%U", format_application, 0, verbose);
589       pool_foreach (app, app_pool, (
590                                      {
591                                      vlib_cli_output (vm, "%U",
592                                                       format_application, app,
593                                                       verbose);
594                                      }
595                     ));
596     }
597
598   return 0;
599 }
600
601 /* *INDENT-OFF* */
602 VLIB_CLI_COMMAND (show_app_command, static) =
603 {
604   .path = "show app",
605   .short_help = "show app [server|client] [verbose]",
606   .function = show_app_command_fn,
607 };
608 /* *INDENT-ON* */
609
610 /*
611  * fd.io coding-style-patch-verification: ON
612  *
613  * Local Variables:
614  * eval: (c-set-style "gnu")
615  * End:
616  */