LCOV - code coverage report
Current view: top level - plugins/http_static - static_server.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 12 465 2.6 %
Date: 2023-10-26 01:39:38 Functions: 9 39 23.1 %

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

Generated by: LCOV version 1.14