cd36be7ccb60b3139a49b41e47006eb291d20114
[vpp.git] / src / plugins / http_static / static_server.c
1 /*
2  * Copyright (c) 2017-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 <http_static/http_static.h>
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21
22 /** @file static_server.c
23  *  Static http server, sufficient to serve .html / .css / .js content.
24  */
25 /*? %%clicmd:group_label Static HTTP Server %% ?*/
26
27 #define HSS_FIFO_THRESH (16 << 10)
28
29 hss_main_t hss_main;
30
31 static hss_session_t *
32 hss_session_alloc (u32 thread_index)
33 {
34   hss_main_t *hsm = &hss_main;
35   hss_session_t *hs;
36
37   pool_get_zero (hsm->sessions[thread_index], hs);
38   hs->session_index = hs - hsm->sessions[thread_index];
39   hs->thread_index = thread_index;
40   hs->cache_pool_index = ~0;
41   return hs;
42 }
43
44 static hss_session_t *
45 hss_session_get (u32 thread_index, u32 hs_index)
46 {
47   hss_main_t *hsm = &hss_main;
48   if (pool_is_free_index (hsm->sessions[thread_index], hs_index))
49     return 0;
50   return pool_elt_at_index (hsm->sessions[thread_index], hs_index);
51 }
52
53 static void
54 hss_session_free (hss_session_t *hs)
55 {
56   hss_main_t *hsm = &hss_main;
57
58   pool_put (hsm->sessions[hs->thread_index], hs);
59
60   if (CLIB_DEBUG)
61     {
62       u32 save_thread_index;
63       save_thread_index = hs->thread_index;
64       /* Poison the entry, preserve timer state and thread index */
65       memset (hs, 0xfa, sizeof (*hs));
66       hs->thread_index = save_thread_index;
67     }
68 }
69
70 /** \brief Disconnect a session
71  */
72 static void
73 hss_session_disconnect_transport (hss_session_t *hs)
74 {
75   vnet_disconnect_args_t _a = { 0 }, *a = &_a;
76   a->handle = hs->vpp_session_handle;
77   a->app_index = hss_main.app_index;
78   vnet_disconnect_session (a);
79 }
80
81 static void
82 start_send_data (hss_session_t *hs, http_status_code_t status)
83 {
84   http_msg_t msg;
85   session_t *ts;
86   int rv;
87
88   ts = session_get (hs->vpp_session_index, hs->thread_index);
89
90   msg.type = HTTP_MSG_REPLY;
91   msg.code = status;
92   msg.content_type = HTTP_CONTENT_TEXT_HTML;
93   msg.data.len = hs->data_len;
94
95   if (hs->data_len > hss_main.use_ptr_thresh)
96     {
97       msg.data.type = HTTP_MSG_DATA_PTR;
98       rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
99       ASSERT (rv == sizeof (msg));
100
101       uword data = pointer_to_uword (hs->data);
102       rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (data), (u8 *) &data);
103       ASSERT (rv == sizeof (sizeof (data)));
104
105       goto done;
106     }
107
108   msg.data.type = HTTP_MSG_DATA_INLINE;
109
110   rv = svm_fifo_enqueue (ts->tx_fifo, sizeof (msg), (u8 *) &msg);
111   ASSERT (rv == sizeof (msg));
112
113   if (!msg.data.len)
114     goto done;
115
116   rv = svm_fifo_enqueue (ts->tx_fifo, hs->data_len, hs->data);
117
118   if (rv != hs->data_len)
119     {
120       hs->data_offset = rv;
121       svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
122     }
123
124 done:
125
126   if (svm_fifo_set_event (ts->tx_fifo))
127     session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
128 }
129
130 __clib_export void
131 hss_session_send_data (hss_url_handler_args_t *args)
132 {
133   hss_session_t *hs;
134
135   hs = hss_session_get (args->sh.thread_index, args->sh.session_index);
136   if (!hs)
137     return;
138
139   if (hs->data && hs->free_data)
140     vec_free (hs->data);
141
142   hs->data = args->data;
143   hs->data_len = args->data_len;
144   hs->free_data = args->free_vec_data;
145   start_send_data (hs, args->sc);
146 }
147
148 static int
149 try_url_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
150                  u8 *request)
151 {
152   http_status_code_t sc = HTTP_STATUS_OK;
153   hss_url_handler_args_t args = {};
154   uword *p, *url_table;
155   int rv;
156
157   if (!hsm->enable_url_handlers || !request)
158     return -1;
159
160   /* Look for built-in GET / POST handlers */
161   url_table =
162     (rt == HTTP_REQ_GET) ? hsm->get_url_handlers : hsm->post_url_handlers;
163
164   p = hash_get_mem (url_table, request);
165   if (!p)
166     return -1;
167
168   hs->path = 0;
169   hs->data_offset = 0;
170   hs->cache_pool_index = ~0;
171
172   if (hsm->debug_level > 0)
173     clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST", request);
174
175   args.reqtype = rt;
176   args.request = request;
177   args.sh.thread_index = hs->thread_index;
178   args.sh.session_index = hs->session_index;
179
180   rv = ((hss_url_handler_fn) p[0]) (&args);
181
182   /* Wait for data from handler */
183   if (rv == HSS_URL_HANDLER_ASYNC)
184     return 0;
185
186   if (rv == HSS_URL_HANDLER_ERROR)
187     {
188       clib_warning ("builtin handler %llx hit on %s '%s' but failed!", p[0],
189                     (rt == HTTP_REQ_GET) ? "GET" : "POST", request);
190       sc = HTTP_STATUS_NOT_FOUND;
191     }
192
193   hs->data = args.data;
194   hs->data_len = args.data_len;
195   hs->free_data = args.free_vec_data;
196
197   start_send_data (hs, sc);
198
199   if (!hs->data)
200     hss_session_disconnect_transport (hs);
201
202   return 0;
203 }
204
205 static u8
206 file_path_is_valid (u8 *path)
207 {
208   struct stat _sb, *sb = &_sb;
209
210   if (stat ((char *) path, sb) < 0 /* can't stat the file */
211       || (sb->st_mode & S_IFMT) != S_IFREG /* not a regular file */)
212     return 0;
213
214   return 1;
215 }
216
217 static u32
218 try_index_file (hss_main_t *hsm, hss_session_t *hs, u8 *path)
219 {
220   u8 *port_str = 0, *redirect;
221   transport_endpoint_t endpt;
222   transport_proto_t proto;
223   int print_port = 0;
224   u16 local_port;
225   session_t *ts;
226   u32 plen;
227
228   /* Remove the trailing space */
229   vec_dec_len (path, 1);
230   plen = vec_len (path);
231
232   /* Append "index.html" */
233   if (path[plen - 1] != '/')
234     path = format (path, "/index.html%c", 0);
235   else
236     path = format (path, "index.html%c", 0);
237
238   if (hsm->debug_level > 0)
239     clib_warning ("trying to find index: %s", path);
240
241   if (!file_path_is_valid (path))
242     return HTTP_STATUS_NOT_FOUND;
243
244   /*
245    * We found an index.html file, build a redirect
246    */
247   vec_delete (path, vec_len (hsm->www_root) - 1, 0);
248
249   ts = session_get (hs->vpp_session_index, hs->thread_index);
250   session_get_endpoint (ts, &endpt, 1 /* is_local */);
251
252   local_port = clib_net_to_host_u16 (endpt.port);
253   proto = session_type_transport_proto (ts->session_type);
254
255   if ((proto == TRANSPORT_PROTO_TCP && local_port != 80) ||
256       (proto == TRANSPORT_PROTO_TLS && local_port != 443))
257     {
258       print_port = 1;
259       port_str = format (0, ":%u", (u32) local_port);
260     }
261
262   redirect =
263     format (0,
264             "HTTP/1.1 301 Moved Permanently\r\n"
265             "Location: http%s://%U%s%s\r\n\r\n",
266             proto == TRANSPORT_PROTO_TLS ? "s" : "", format_ip46_address,
267             &endpt.ip, endpt.is_ip4, print_port ? port_str : (u8 *) "", path);
268
269   if (hsm->debug_level > 0)
270     clib_warning ("redirect: %s", redirect);
271
272   vec_free (port_str);
273
274   hs->data = redirect;
275   hs->data_len = vec_len (redirect);
276   hs->free_data = 1;
277
278   return HTTP_STATUS_OK;
279 }
280
281 static int
282 try_file_handler (hss_main_t *hsm, hss_session_t *hs, http_req_method_t rt,
283                   u8 *request)
284 {
285   http_status_code_t sc = HTTP_STATUS_OK;
286   u8 *path;
287   u32 ce_index;
288
289   /* Feature not enabled */
290   if (!hsm->www_root)
291     return -1;
292
293   /*
294    * Construct the file to open
295    * Browsers are capable of sporadically including a leading '/'
296    */
297   if (!request)
298     path = format (0, "%s%c", hsm->www_root, 0);
299   else if (request[0] == '/')
300     path = format (0, "%s%s%c", hsm->www_root, request, 0);
301   else
302     path = format (0, "%s/%s%c", hsm->www_root, request, 0);
303
304   if (hsm->debug_level > 0)
305     clib_warning ("%s '%s'", (rt == HTTP_REQ_GET) ? "GET" : "POST", path);
306
307   if (hs->data && hs->free_data)
308     vec_free (hs->data);
309
310   hs->path = path;
311   hs->data_offset = 0;
312
313   ce_index =
314     hss_cache_lookup_and_attach (&hsm->cache, path, &hs->data, &hs->data_len);
315   if (ce_index == ~0)
316     {
317       if (!file_path_is_valid (path))
318         {
319           sc = try_index_file (hsm, hs, path);
320           goto done;
321         }
322       ce_index =
323         hss_cache_add_and_attach (&hsm->cache, path, &hs->data, &hs->data_len);
324       if (ce_index == ~0)
325         {
326           sc = HTTP_STATUS_INTERNAL_ERROR;
327           goto done;
328         }
329     }
330
331   hs->cache_pool_index = ce_index;
332
333 done:
334
335   start_send_data (hs, sc);
336   if (!hs->data)
337     hss_session_disconnect_transport (hs);
338
339   return 0;
340 }
341
342 static int
343 handle_request (hss_session_t *hs, http_req_method_t rt, u8 *request)
344 {
345   hss_main_t *hsm = &hss_main;
346
347   if (!try_url_handler (hsm, hs, rt, request))
348     return 0;
349
350   if (!try_file_handler (hsm, hs, rt, request))
351     return 0;
352
353   /* Handler did not find anything return 404 */
354   start_send_data (hs, HTTP_STATUS_NOT_FOUND);
355   hss_session_disconnect_transport (hs);
356
357   return 0;
358 }
359
360 static int
361 hss_ts_rx_callback (session_t *ts)
362 {
363   hss_session_t *hs;
364   u8 *request = 0;
365   http_msg_t msg;
366   int rv;
367
368   hs = hss_session_get (ts->thread_index, ts->opaque);
369
370   /* Read the http message header */
371   rv = svm_fifo_dequeue (ts->rx_fifo, sizeof (msg), (u8 *) &msg);
372   ASSERT (rv == sizeof (msg));
373
374   if (msg.type != HTTP_MSG_REQUEST ||
375       (msg.method_type != HTTP_REQ_GET && msg.method_type != HTTP_REQ_POST))
376     {
377       hs->data = 0;
378       start_send_data (hs, HTTP_STATUS_METHOD_NOT_ALLOWED);
379       return 0;
380     }
381
382   /* Read request */
383   if (msg.data.len)
384     {
385       vec_validate (request, msg.data.len - 1);
386       rv = svm_fifo_dequeue (ts->rx_fifo, msg.data.len, request);
387       ASSERT (rv == msg.data.len);
388     }
389
390   /* Find and send data */
391   handle_request (hs, msg.method_type, request);
392
393   vec_free (request);
394
395   return 0;
396 }
397
398 static int
399 hss_ts_tx_callback (session_t *ts)
400 {
401   hss_session_t *hs;
402   u32 to_send;
403   int rv;
404
405   hs = hss_session_get (ts->thread_index, ts->opaque);
406   if (!hs || !hs->data)
407     return 0;
408
409   to_send = hs->data_len - hs->data_offset;
410   rv = svm_fifo_enqueue (ts->tx_fifo, to_send, hs->data + hs->data_offset);
411
412   if (rv <= 0)
413     {
414       svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
415       return 0;
416     }
417
418   if (rv < to_send)
419     {
420       hs->data_offset += rv;
421       svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
422     }
423
424   if (svm_fifo_set_event (ts->tx_fifo))
425     session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
426
427   return 0;
428 }
429
430 /** \brief Session accept callback
431  */
432 static int
433 hss_ts_accept_callback (session_t *ts)
434 {
435   hss_session_t *hs;
436   u32 thresh;
437
438   hs = hss_session_alloc (ts->thread_index);
439
440   hs->vpp_session_index = ts->session_index;
441   hs->vpp_session_handle = session_handle (ts);
442
443   /* The application sets a threshold for it's fifo to get notified when
444    * additional data can be enqueued. We want to keep the TX fifo reasonably
445    * full, however avoid entering a state where the
446    * fifo is full all the time and small chunks of data are being enqueued
447    * each time. If the fifo is small (under 16K) we set
448    * the threshold to it's size, meaning a notification will be given when the
449    * fifo empties.
450    */
451   thresh = clib_min (svm_fifo_size (ts->tx_fifo), HSS_FIFO_THRESH);
452   svm_fifo_set_deq_thresh (ts->tx_fifo, thresh);
453
454   ts->opaque = hs->session_index;
455   ts->session_state = SESSION_STATE_READY;
456   return 0;
457 }
458
459 static void
460 hss_ts_disconnect_callback (session_t *ts)
461 {
462   hss_main_t *hsm = &hss_main;
463   vnet_disconnect_args_t _a = { 0 }, *a = &_a;
464
465   a->handle = session_handle (ts);
466   a->app_index = hsm->app_index;
467   vnet_disconnect_session (a);
468 }
469
470 static void
471 hss_ts_reset_callback (session_t *ts)
472 {
473   hss_main_t *hsm = &hss_main;
474   vnet_disconnect_args_t _a = { 0 }, *a = &_a;
475
476   a->handle = session_handle (ts);
477   a->app_index = hsm->app_index;
478   vnet_disconnect_session (a);
479 }
480
481 static int
482 hss_ts_connected_callback (u32 app_index, u32 api_context, session_t *ts,
483                            session_error_t err)
484 {
485   clib_warning ("called...");
486   return -1;
487 }
488
489 static int
490 hss_add_segment_callback (u32 client_index, u64 segment_handle)
491 {
492   return 0;
493 }
494
495 static void
496 hss_ts_cleanup (session_t *s, session_cleanup_ntf_t ntf)
497 {
498   hss_main_t *hsm = &hss_main;
499   hss_session_t *hs;
500
501   if (ntf == SESSION_CLEANUP_TRANSPORT)
502     return;
503
504   hs = hss_session_get (s->thread_index, s->opaque);
505   if (!hs)
506     return;
507
508   if (hs->cache_pool_index != ~0)
509     {
510       hss_cache_detach_entry (&hsm->cache, hs->cache_pool_index);
511       hs->cache_pool_index = ~0;
512     }
513
514   if (hs->free_data)
515     vec_free (hs->data);
516   hs->data = 0;
517   hs->data_offset = 0;
518   hs->free_data = 0;
519   vec_free (hs->path);
520
521   hss_session_free (hs);
522 }
523
524 static session_cb_vft_t hss_cb_vft = {
525   .session_accept_callback = hss_ts_accept_callback,
526   .session_disconnect_callback = hss_ts_disconnect_callback,
527   .session_connected_callback = hss_ts_connected_callback,
528   .add_segment_callback = hss_add_segment_callback,
529   .builtin_app_rx_callback = hss_ts_rx_callback,
530   .builtin_app_tx_callback = hss_ts_tx_callback,
531   .session_reset_callback = hss_ts_reset_callback,
532   .session_cleanup_callback = hss_ts_cleanup,
533 };
534
535 static int
536 hss_attach ()
537 {
538   vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair;
539   hss_main_t *hsm = &hss_main;
540   u64 options[APP_OPTIONS_N_OPTIONS];
541   vnet_app_attach_args_t _a, *a = &_a;
542   u32 segment_size = 128 << 20;
543
544   clib_memset (a, 0, sizeof (*a));
545   clib_memset (options, 0, sizeof (options));
546
547   if (hsm->private_segment_size)
548     segment_size = hsm->private_segment_size;
549
550   a->api_client_index = ~0;
551   a->name = format (0, "http_static_server");
552   a->session_cb_vft = &hss_cb_vft;
553   a->options = options;
554   a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
555   a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = segment_size;
556   a->options[APP_OPTIONS_RX_FIFO_SIZE] =
557     hsm->fifo_size ? hsm->fifo_size : 8 << 10;
558   a->options[APP_OPTIONS_TX_FIFO_SIZE] =
559     hsm->fifo_size ? hsm->fifo_size : 32 << 10;
560   a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
561   a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = hsm->prealloc_fifos;
562   a->options[APP_OPTIONS_TLS_ENGINE] = CRYPTO_ENGINE_OPENSSL;
563
564   if (vnet_application_attach (a))
565     {
566       vec_free (a->name);
567       clib_warning ("failed to attach server");
568       return -1;
569     }
570   vec_free (a->name);
571   hsm->app_index = a->app_index;
572
573   clib_memset (ck_pair, 0, sizeof (*ck_pair));
574   ck_pair->cert = (u8 *) test_srv_crt_rsa;
575   ck_pair->key = (u8 *) test_srv_key_rsa;
576   ck_pair->cert_len = test_srv_crt_rsa_len;
577   ck_pair->key_len = test_srv_key_rsa_len;
578   vnet_app_add_cert_key_pair (ck_pair);
579   hsm->ckpair_index = ck_pair->index;
580
581   return 0;
582 }
583
584 static int
585 hss_transport_needs_crypto (transport_proto_t proto)
586 {
587   return proto == TRANSPORT_PROTO_TLS || proto == TRANSPORT_PROTO_DTLS ||
588          proto == TRANSPORT_PROTO_QUIC;
589 }
590
591 static int
592 hss_listen (void)
593 {
594   hss_main_t *hsm = &hss_main;
595   session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
596   vnet_listen_args_t _a, *a = &_a;
597   char *uri = "tcp://0.0.0.0/80";
598   u8 need_crypto;
599   int rv;
600
601   clib_memset (a, 0, sizeof (*a));
602   a->app_index = hsm->app_index;
603
604   if (hsm->uri)
605     uri = (char *) hsm->uri;
606
607   if (parse_uri (uri, &sep))
608     return -1;
609
610   need_crypto = hss_transport_needs_crypto (sep.transport_proto);
611
612   sep.transport_proto = TRANSPORT_PROTO_HTTP;
613   clib_memcpy (&a->sep_ext, &sep, sizeof (sep));
614
615   if (need_crypto)
616     {
617       session_endpoint_alloc_ext_cfg (&a->sep_ext,
618                                       TRANSPORT_ENDPT_EXT_CFG_CRYPTO);
619       a->sep_ext.ext_cfg->crypto.ckpair_index = hsm->ckpair_index;
620     }
621
622   rv = vnet_listen (a);
623
624   if (need_crypto)
625     clib_mem_free (a->sep_ext.ext_cfg);
626
627   return rv;
628 }
629
630 static void
631 hss_url_handlers_init (hss_main_t *hsm)
632 {
633   if (!hsm->get_url_handlers)
634     {
635       hsm->get_url_handlers = hash_create_string (0, sizeof (uword));
636       hsm->post_url_handlers = hash_create_string (0, sizeof (uword));
637     }
638
639   hss_builtinurl_json_handlers_init ();
640 }
641
642 int
643 hss_create (vlib_main_t *vm)
644 {
645   vlib_thread_main_t *vtm = vlib_get_thread_main ();
646   hss_main_t *hsm = &hss_main;
647   u32 num_threads;
648
649   num_threads = 1 /* main thread */  + vtm->n_threads;
650   vec_validate (hsm->sessions, num_threads - 1);
651
652   if (hss_attach ())
653     {
654       clib_warning ("failed to attach server");
655       return -1;
656     }
657   if (hss_listen ())
658     {
659       clib_warning ("failed to start listening");
660       return -1;
661     }
662
663   if (hsm->www_root)
664     hss_cache_init (&hsm->cache, hsm->cache_size, hsm->debug_level);
665
666   if (hsm->enable_url_handlers)
667     hss_url_handlers_init (hsm);
668
669   return 0;
670 }
671
672 static clib_error_t *
673 hss_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
674                        vlib_cli_command_t *cmd)
675 {
676   unformat_input_t _line_input, *line_input = &_line_input;
677   hss_main_t *hsm = &hss_main;
678   clib_error_t *error = 0;
679   u64 seg_size;
680   int rv;
681
682   if (hsm->app_index != (u32) ~0)
683     return clib_error_return (0, "http server already running...");
684
685   hsm->prealloc_fifos = 0;
686   hsm->private_segment_size = 0;
687   hsm->fifo_size = 0;
688   hsm->cache_size = 10 << 20;
689
690   /* Get a line of input. */
691   if (!unformat_user (input, unformat_line_input, line_input))
692     goto no_input;
693
694   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
695     {
696       if (unformat (line_input, "www-root %s", &hsm->www_root))
697         ;
698       else
699         if (unformat (line_input, "prealloc-fifos %d", &hsm->prealloc_fifos))
700         ;
701       else if (unformat (line_input, "private-segment-size %U",
702                          unformat_memory_size, &seg_size))
703         hsm->private_segment_size = seg_size;
704       else if (unformat (line_input, "fifo-size %d", &hsm->fifo_size))
705         hsm->fifo_size <<= 10;
706       else if (unformat (line_input, "cache-size %U", unformat_memory_size,
707                          &hsm->cache_size))
708         ;
709       else if (unformat (line_input, "uri %s", &hsm->uri))
710         ;
711       else if (unformat (line_input, "debug %d", &hsm->debug_level))
712         ;
713       else if (unformat (line_input, "debug"))
714         hsm->debug_level = 1;
715       else if (unformat (line_input, "ptr-thresh %U", unformat_memory_size,
716                          &hsm->use_ptr_thresh))
717         ;
718       else if (unformat (line_input, "url-handlers"))
719         hsm->enable_url_handlers = 1;
720       else
721         {
722           error = clib_error_return (0, "unknown input `%U'",
723                                      format_unformat_error, line_input);
724           break;
725         }
726     }
727
728   unformat_free (line_input);
729
730 no_input:
731
732   if (error)
733     goto done;
734
735   if (hsm->www_root == 0 && !hsm->enable_url_handlers)
736     {
737       error = clib_error_return (0, "Must set www-root or url-handlers");
738       goto done;
739     }
740
741   if (hsm->cache_size < (128 << 10))
742     {
743       error = clib_error_return (0, "cache-size must be at least 128kb");
744       vec_free (hsm->www_root);
745       goto done;
746     }
747
748   vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
749
750   if ((rv = hss_create (vm)))
751     {
752       error = clib_error_return (0, "server_create returned %d", rv);
753       vec_free (hsm->www_root);
754     }
755
756 done:
757
758   return error;
759 }
760
761 /*?
762  * Enable the static http server
763  *
764  * @cliexpar
765  * This command enables the static http server. Only the www-root
766  * parameter is required
767  * @clistart
768  * http static server www-root /tmp/www uri tcp://0.0.0.0/80 cache-size 2m
769  * @cliend
770  * @cliexcmd{http static server www-root <path> [prealloc-fios <nn>]
771  *   [private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]}
772 ?*/
773 VLIB_CLI_COMMAND (hss_create_command, static) = {
774   .path = "http static server",
775   .short_help =
776     "http static server www-root <path> [prealloc-fifos <nn>]\n"
777     "[private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]\n"
778     "[ptr-thresh <nn>] [url-handlers] [debug [nn]]\n",
779   .function = hss_create_command_fn,
780 };
781
782 static u8 *
783 format_hss_session (u8 *s, va_list *args)
784 {
785   hss_session_t *hs = va_arg (*args, hss_session_t *);
786   int __clib_unused verbose = va_arg (*args, int);
787
788   s = format (s, "\n path %s, data length %u, data_offset %u",
789               hs->path ? hs->path : (u8 *) "[none]", hs->data_len,
790               hs->data_offset);
791   return s;
792 }
793
794 static clib_error_t *
795 hss_show_command_fn (vlib_main_t *vm, unformat_input_t *input,
796                      vlib_cli_command_t *cmd)
797 {
798   int verbose = 0, show_cache = 0, show_sessions = 0;
799   hss_main_t *hsm = &hss_main;
800
801   if (hsm->www_root == 0)
802     return clib_error_return (0, "Static server disabled");
803
804   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
805     {
806       if (unformat (input, "verbose %d", &verbose))
807         ;
808       else if (unformat (input, "verbose"))
809         verbose = 1;
810       else if (unformat (input, "cache"))
811         show_cache = 1;
812       else if (unformat (input, "sessions"))
813         show_sessions = 1;
814       else
815         break;
816     }
817
818   if ((show_cache + show_sessions) == 0)
819     return clib_error_return (0, "specify one or more of cache, sessions");
820
821   if (show_cache)
822     vlib_cli_output (vm, "%U", format_hss_cache, &hsm->cache, verbose);
823
824   if (show_sessions)
825     {
826       u32 *session_indices = 0;
827       hss_session_t *hs;
828       int i, j;
829
830
831       for (i = 0; i < vec_len (hsm->sessions); i++)
832         {
833           pool_foreach (hs, hsm->sessions[i])
834             vec_add1 (session_indices, hs - hsm->sessions[i]);
835
836           for (j = 0; j < vec_len (session_indices); j++)
837             {
838               vlib_cli_output (
839                 vm, "%U", format_hss_session,
840                 pool_elt_at_index (hsm->sessions[i], session_indices[j]),
841                 verbose);
842             }
843           vec_reset_length (session_indices);
844         }
845       vec_free (session_indices);
846     }
847   return 0;
848 }
849
850 /*?
851  * Display static http server cache statistics
852  *
853  * @cliexpar
854  * This command shows the contents of the static http server cache
855  * @clistart
856  * show http static server
857  * @cliend
858  * @cliexcmd{show http static server sessions cache [verbose [nn]]}
859 ?*/
860 VLIB_CLI_COMMAND (hss_show_command, static) = {
861   .path = "show http static server",
862   .short_help = "show http static server sessions cache [verbose [<nn>]]",
863   .function = hss_show_command_fn,
864 };
865
866 static clib_error_t *
867 hss_clear_cache_command_fn (vlib_main_t *vm, unformat_input_t *input,
868                             vlib_cli_command_t *cmd)
869 {
870   hss_main_t *hsm = &hss_main;
871   u32 busy_items = 0;
872
873   if (hsm->www_root == 0)
874     return clib_error_return (0, "Static server disabled");
875
876   busy_items = hss_cache_clear (&hsm->cache);
877
878   if (busy_items > 0)
879     vlib_cli_output (vm, "Note: %d busy items still in cache...", busy_items);
880   else
881     vlib_cli_output (vm, "Cache cleared...");
882   return 0;
883 }
884
885 /*?
886  * Clear the static http server cache, to force the server to
887  * reload content from backing files
888  *
889  * @cliexpar
890  * This command clear the static http server cache
891  * @clistart
892  * clear http static cache
893  * @cliend
894  * @cliexcmd{clear http static cache}
895 ?*/
896 VLIB_CLI_COMMAND (clear_hss_cache_command, static) = {
897   .path = "clear http static cache",
898   .short_help = "clear http static cache",
899   .function = hss_clear_cache_command_fn,
900 };
901
902 static clib_error_t *
903 hss_main_init (vlib_main_t *vm)
904 {
905   hss_main_t *hsm = &hss_main;
906
907   hsm->app_index = ~0;
908   hsm->vlib_main = vm;
909
910   return 0;
911 }
912
913 VLIB_INIT_FUNCTION (hss_main_init);
914
915 /*
916  * fd.io coding-style-patch-verification: ON
917  *
918  * Local Variables:
919  * eval: (c-set-style "gnu")
920  * End:
921  */