FD.io VPP  v21.06-1-gbb7418cf9
Vector Packet Processing
static_server.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017-2019 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/vnet.h>
19 #include <vnet/session/session.h>
20 #include <vppinfra/unix.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
25 
27 
28 /** @file static_server.c
29  * Static http server, sufficient to
30  * serve .html / .css / .js content.
31  */
32 /*? %%clicmd:group_label Static HTTP Server %% ?*/
33 
35 
36 /** \brief Format the called-from enum
37  */
38 
39 static u8 *
40 format_state_machine_called_from (u8 * s, va_list * args)
41 {
43  va_arg (*args, http_state_machine_called_from_t);
44  char *which = "bogus!";
45 
46  switch (cf)
47  {
48  case CALLED_FROM_RX:
49  which = "from rx";
50  break;
51  case CALLED_FROM_TX:
52  which = "from tx";
53  break;
54  case CALLED_FROM_TIMER:
55  which = "from timer";
56  break;
57 
58  default:
59  break;
60  }
61 
62  s = format (s, "%s", which);
63  return s;
64 }
65 
66 
67 /** \brief Acquire reader lock on the sessions pools
68  */
69 static void
71 {
72  clib_rwlock_reader_lock (&http_static_server_main.sessions_lock);
73 }
74 
75 /** \brief Drop reader lock on the sessions pools
76  */
77 static void
79 {
80  clib_rwlock_reader_unlock (&http_static_server_main.sessions_lock);
81 }
82 
83 /** \brief Acquire writer lock on the sessions pools
84  */
85 static void
87 {
88  clib_rwlock_writer_lock (&http_static_server_main.sessions_lock);
89 }
90 
91 /** \brief Drop writer lock on the sessions pools
92  */
93 static void
95 {
96  clib_rwlock_writer_unlock (&http_static_server_main.sessions_lock);
97 }
98 
99 /** \brief Start a session cleanup timer
100  */
101 static void
103 {
105  u32 hs_handle;
106 
107  /* The session layer may fire a callback at a later date... */
108  if (!pool_is_free (hsm->sessions[hs->thread_index], hs))
109  {
110  hs_handle = hs->thread_index << 24 | hs->session_index;
111  clib_spinlock_lock (&http_static_server_main.tw_lock);
112  hs->timer_handle = tw_timer_start_2t_1w_2048sl
113  (&http_static_server_main.tw, hs_handle, 0, 60);
114  clib_spinlock_unlock (&http_static_server_main.tw_lock);
115  }
116 }
117 
118 /** \brief stop a session cleanup timer
119  */
120 static void
122 {
123  if (hs->timer_handle == ~0)
124  return;
125  clib_spinlock_lock (&http_static_server_main.tw_lock);
126  tw_timer_stop_2t_1w_2048sl (&http_static_server_main.tw, hs->timer_handle);
127  clib_spinlock_unlock (&http_static_server_main.tw_lock);
128 }
129 
130 /** \brief Allocate an http session
131  */
132 static http_session_t *
134 {
136  http_session_t *hs;
137  pool_get_aligned_zero_numa (hsm->sessions[thread_index], hs,
138  0 /* not aligned */ ,
139  1 /* zero */ ,
140  os_get_numa_index ());
141  hs->session_index = hs - hsm->sessions[thread_index];
143  hs->timer_handle = ~0;
144  hs->cache_pool_index = ~0;
145  return hs;
146 }
147 
148 /** \brief Get an http session by index
149  */
150 static http_session_t *
152 {
154  if (pool_is_free_index (hsm->sessions[thread_index], hs_index))
155  return 0;
156  return pool_elt_at_index (hsm->sessions[thread_index], hs_index);
157 }
158 
159 /** \brief Free an http session
160  */
161 static void
163 {
165 
166  /* Make sure the timer is stopped... */
168  pool_put (hsm->sessions[hs->thread_index], hs);
169 
170  if (CLIB_DEBUG)
171  {
172  u32 save_thread_index;
173  save_thread_index = hs->thread_index;
174  /* Poison the entry, preserve timer state and thread index */
175  memset (hs, 0xfa, sizeof (*hs));
176  hs->timer_handle = ~0;
177  hs->thread_index = save_thread_index;
178  }
179 }
180 
181 /** \brief add a session to the vpp < -- > http session index map
182  */
183 static void
185  u32 hs_index)
186 {
188  vec_validate (hsm->session_to_http_session[thread_index], s_index);
189  hsm->session_to_http_session[thread_index][s_index] = hs_index;
190 }
191 
192 /** \brief Remove a session from the vpp < -- > http session index map
193  */
194 static void
196 {
198  hsm->session_to_http_session[thread_index][s_index] = ~0;
199 }
200 
201 /** \brief lookup a session in the vpp < -- > http session index map
202  */
203 
204 static http_session_t *
206 {
208  u32 hs_index;
209 
210  if (s_index < vec_len (hsm->session_to_http_session[thread_index]))
211  {
212  hs_index = hsm->session_to_http_session[thread_index][s_index];
213  return http_static_server_session_get (thread_index, hs_index);
214  }
215  return 0;
216 }
217 
218 /** \brief Detach cache entry from session
219  */
220 
221 static void
223 {
225  file_data_cache_t *ep;
226 
227  /*
228  * Decrement cache pool entry reference count
229  * Note that if e.g. a file lookup fails, the cache pool index
230  * won't be set
231  */
232  if (hs->cache_pool_index != ~0)
233  {
235  ep->inuse--;
236  if (hsm->debug_level > 1)
237  clib_warning ("index %d refcnt now %d", hs->cache_pool_index,
238  ep->inuse);
239  }
240  hs->cache_pool_index = ~0;
241  if (hs->free_data)
242  vec_free (hs->data);
243  hs->data = 0;
244  hs->data_offset = 0;
245  hs->free_data = 0;
246  vec_free (hs->path);
247 }
248 
249 /** \brief Disconnect a session
250  */
251 static void
253 {
254  vnet_disconnect_args_t _a = { 0 }, *a = &_a;
255  a->handle = hs->vpp_session_handle;
256  a->app_index = http_static_server_main.app_index;
258 }
259 
260 /* *INDENT-OFF* */
261 /** \brief http error boilerplate
262  */
263 static const char *http_error_template =
264  "HTTP/1.1 %s\r\n"
265  "Date: %U GMT\r\n"
266  "Content-Type: text/html\r\n"
267  "Connection: close\r\n"
268  "Pragma: no-cache\r\n"
269  "Content-Length: 0\r\n\r\n";
270 
271 /** \brief http response boilerplate
272  */
273 static const char *http_response_template =
274  "Date: %U GMT\r\n"
275  "Expires: %U GMT\r\n"
276  "Server: VPP Static\r\n"
277  "Content-Type: %s\r\n"
278  "Content-Length: %d\r\n\r\n";
279 
280 /* *INDENT-ON* */
281 
282 /** \brief send http data
283  @param hs - http session
284  @param data - the data vector to transmit
285  @param length - length of data
286  @param offset - transmit offset for this operation
287  @return offset for next transmit operation, may be unchanged w/ full fifo
288 */
289 
290 static u32
292 {
293  u32 bytes_to_send;
295 
296  bytes_to_send = length - offset;
297 
298  while (bytes_to_send > 0)
299  {
300  int actual_transfer;
301 
302  actual_transfer = svm_fifo_enqueue
303  (hs->tx_fifo, bytes_to_send, data + offset);
304 
305  /* Made any progress? */
306  if (actual_transfer <= 0)
307  {
308  if (hsm->debug_level > 0 && bytes_to_send > 0)
309  clib_warning ("WARNING: still %d bytes to send", bytes_to_send);
310  return offset;
311  }
312  else
313  {
314  offset += actual_transfer;
315  bytes_to_send -= actual_transfer;
316 
317  if (hsm->debug_level && bytes_to_send > 0)
318  clib_warning ("WARNING: still %d bytes to send", bytes_to_send);
319 
320  if (svm_fifo_set_event (hs->tx_fifo))
321  session_send_io_evt_to_thread (hs->tx_fifo,
323  return offset;
324  }
325  }
326  /* NOTREACHED */
327  return ~0;
328 }
329 
330 /** \brief Send an http error string
331  @param hs - the http session
332  @param str - the error string, e.g. "404 Not Found"
333 */
334 static void
335 send_error (http_session_t * hs, char *str)
336 {
338  u8 *data;
339  f64 now;
340 
341  now = clib_timebase_now (&hsm->timebase);
342  data = format (0, http_error_template, str, format_clib_timebase_time, now);
343  static_send_data (hs, data, vec_len (data), 0);
344  vec_free (data);
345 }
346 
347 /** \brief Retrieve data from the application layer
348  */
349 static int
351 {
352  u32 max_dequeue, cursize;
353  int n_read;
354 
355  cursize = vec_len (hs->rx_buf);
356  max_dequeue = svm_fifo_max_dequeue (hs->rx_fifo);
357  if (PREDICT_FALSE (max_dequeue == 0))
358  return -1;
359 
360  vec_validate (hs->rx_buf, cursize + max_dequeue - 1);
361  n_read = app_recv_stream_raw (hs->rx_fifo, hs->rx_buf + cursize,
362  max_dequeue, 0, 0 /* peek */ );
363  ASSERT (n_read == max_dequeue);
364  if (svm_fifo_is_empty (hs->rx_fifo))
365  svm_fifo_unset_event (hs->rx_fifo);
366 
367  _vec_len (hs->rx_buf) = cursize + n_read;
368  return 0;
369 }
370 
371 /** \brief Sanity-check the forward and reverse LRU lists
372  */
373 static inline void
375 {
376 #if CLIB_DEBUG > 0
377  f64 last_timestamp;
378  u32 index;
379  int i;
380  file_data_cache_t *ep;
381 
382  last_timestamp = 1e70;
383  for (i = 1, index = hsm->first_index; index != ~0;)
384  {
385  ep = pool_elt_at_index (hsm->cache_pool, index);
386  index = ep->next_index;
387  /* Timestamps should be smaller (older) as we walk the fwd list */
388  if (ep->last_used > last_timestamp)
389  {
390  clib_warning ("%d[%d]: last used %.6f, last_timestamp %.6f",
391  ep - hsm->cache_pool, i,
392  ep->last_used, last_timestamp);
393  }
394  last_timestamp = ep->last_used;
395  i++;
396  }
397 
398  last_timestamp = 0.0;
399  for (i = 1, index = hsm->last_index; index != ~0;)
400  {
401  ep = pool_elt_at_index (hsm->cache_pool, index);
402  index = ep->prev_index;
403  /* Timestamps should be larger (newer) as we walk the rev list */
404  if (ep->last_used < last_timestamp)
405  {
406  clib_warning ("%d[%d]: last used %.6f, last_timestamp %.6f",
407  ep - hsm->cache_pool, i,
408  ep->last_used, last_timestamp);
409  }
410  last_timestamp = ep->last_used;
411  i++;
412  }
413 #endif
414 }
415 
416 /** \brief Remove a data cache entry from the LRU lists
417  */
418 static inline void
420 {
421  file_data_cache_t *next_ep, *prev_ep;
422  u32 ep_index;
423 
424  lru_validate (hsm);
425 
426  ep_index = ep - hsm->cache_pool;
427 
428  /* Deal with list heads */
429  if (ep_index == hsm->first_index)
430  hsm->first_index = ep->next_index;
431  if (ep_index == hsm->last_index)
432  hsm->last_index = ep->prev_index;
433 
434  /* Fix next->prev */
435  if (ep->next_index != ~0)
436  {
437  next_ep = pool_elt_at_index (hsm->cache_pool, ep->next_index);
438  next_ep->prev_index = ep->prev_index;
439  }
440  /* Fix prev->next */
441  if (ep->prev_index != ~0)
442  {
443  prev_ep = pool_elt_at_index (hsm->cache_pool, ep->prev_index);
444  prev_ep->next_index = ep->next_index;
445  }
446  lru_validate (hsm);
447 }
448 
449 /** \brief Add an entry to the LRU lists, tag w/ supplied timestamp
450  */
451 
452 static inline void
454 {
455  file_data_cache_t *next_ep;
456  u32 ep_index;
457 
458  lru_validate (hsm);
459 
460  ep_index = ep - hsm->cache_pool;
461 
462  /*
463  * Re-add at the head of the forward LRU list,
464  * tail of the reverse LRU list
465  */
466  if (hsm->first_index != ~0)
467  {
468  next_ep = pool_elt_at_index (hsm->cache_pool, hsm->first_index);
469  next_ep->prev_index = ep_index;
470  }
471 
472  ep->prev_index = ~0;
473 
474  /* ep now the new head of the LRU forward list */
475  ep->next_index = hsm->first_index;
476  hsm->first_index = ep_index;
477 
478  /* single session case: also the tail of the reverse LRU list */
479  if (hsm->last_index == ~0)
480  hsm->last_index = ep_index;
481  ep->last_used = now;
482 
483  lru_validate (hsm);
484 }
485 
486 /** \brief Remove and re-add a cache entry from/to the LRU lists
487  */
488 
489 static inline void
491 {
492  lru_remove (hsm, ep);
493  lru_add (hsm, ep, now);
494 }
495 
496 /** \brief Session-layer (main) data rx callback.
497  Parse the http request, and reply to it.
498  Future extensions might include POST processing, active content, etc.
499 */
500 
501 /* svm_fifo_add_want_deq_ntf (tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL)
502 get shoulder-tap when transport dequeues something, set in
503 xmit routine. */
504 
505 /** \brief closed state - should never really get here
506  */
507 static int
510 {
511  clib_warning ("WARNING: http session %d, called from %U",
512  hs->session_index, format_state_machine_called_from, cf);
513  return -1;
514 }
515 
516 static void
518 {
520 }
521 
522 /** \brief Register a builtin GET or POST handler
523  */
525  (void *fp, char *url, int request_type)
526 {
528  uword *p, *builtin_table;
529 
530  builtin_table = (request_type == HTTP_BUILTIN_METHOD_GET)
531  ? hsm->get_url_handlers : hsm->post_url_handlers;
532 
533  p = hash_get_mem (builtin_table, url);
534 
535  if (p)
536  {
537  clib_warning ("WARNING: attempt to replace handler for %s '%s' ignored",
538  (request_type == HTTP_BUILTIN_METHOD_GET) ?
539  "GET" : "POST", url);
540  return;
541  }
542 
543  hash_set_mem (builtin_table, url, (uword) fp);
544 
545  /*
546  * Need to update the hash table pointer in http_static_server_main
547  * in case we just expanded it...
548  */
549  if (request_type == HTTP_BUILTIN_METHOD_GET)
550  hsm->get_url_handlers = builtin_table;
551  else
552  hsm->post_url_handlers = builtin_table;
553 }
554 
555 static int
556 v_find_index (u8 * vec, char *str)
557 {
558  int start_index;
559  u32 slen = (u32) strnlen_s_inline (str, 8);
560  u32 vlen = vec_len (vec);
561 
562  ASSERT (slen > 0);
563 
564  if (vlen <= slen)
565  return -1;
566 
567  for (start_index = 0; start_index < (vlen - slen); start_index++)
568  {
569  if (!memcmp (vec, str, slen))
570  return start_index;
571  }
572 
573  return -1;
574 }
575 
576 /** \brief established state - waiting for GET, POST, etc.
577  */
578 static int
581 {
583  u8 *request = 0;
584  u8 *path;
585  int i, rv;
586  struct stat _sb, *sb = &_sb;
588  u8 request_type = HTTP_BUILTIN_METHOD_GET;
589  u8 save_byte = 0;
590  uword *p, *builtin_table;
591 
592  /* Read data from the sessison layer */
593  rv = session_rx_request (hs);
594 
595  /* No data? Odd, but stay in this state and await further instructions */
596  if (rv)
597  return 0;
598 
599  /* Process the client request */
600  request = hs->rx_buf;
601  if (vec_len (request) < 8)
602  {
603  send_error (hs, "400 Bad Request");
604  close_session (hs);
605  return -1;
606  }
607 
608  if ((i = v_find_index (request, "GET ")) >= 0)
609  goto find_end;
610  else if ((i = v_find_index (request, "POST ")) >= 0)
611  {
612  request_type = HTTP_BUILTIN_METHOD_POST;
613  goto find_end;
614  }
615 
616  if (hsm->debug_level > 1)
617  clib_warning ("Unknown http method");
618 
619  send_error (hs, "405 Method Not Allowed");
620  close_session (hs);
621  return -1;
622 
623 find_end:
624 
625  /* Lose "GET " or "POST " */
626  vec_delete (request, i + 5 + request_type, 0);
627 
628  /* Temporarily drop in a NULL byte for lookup purposes */
629  for (i = 0; i < vec_len (request); i++)
630  {
631  if (request[i] == ' ' || request[i] == '?')
632  {
633  save_byte = request[i];
634  request[i] = 0;
635  break;
636  }
637  }
638 
639  /*
640  * Now we can construct the file to open
641  * Browsers are capable of sporadically including a leading '/'
642  */
643  if (request[0] == '/')
644  path = format (0, "%s%s%c", hsm->www_root, request, 0);
645  else
646  path = format (0, "%s/%s%c", hsm->www_root, request, 0);
647 
648  if (hsm->debug_level > 0)
649  clib_warning ("%s '%s'", (request_type) == HTTP_BUILTIN_METHOD_GET ?
650  "GET" : "POST", path);
651 
652  /* Look for built-in GET / POST handlers */
653  builtin_table = (request_type == HTTP_BUILTIN_METHOD_GET) ?
655 
656  p = hash_get_mem (builtin_table, request);
657 
658  if (save_byte != 0)
659  request[i] = save_byte;
660 
661  if (p)
662  {
663  int rv;
664  int (*fp) (http_builtin_method_type_t, u8 *, http_session_t *);
665  fp = (void *) p[0];
666  hs->path = path;
667  rv = (*fp) (request_type, request, hs);
668  if (rv)
669  {
670  clib_warning ("builtin handler %llx hit on %s '%s' but failed!",
671  p[0], (request_type == HTTP_BUILTIN_METHOD_GET) ?
672  "GET" : "POST", request);
673  send_error (hs, "404 Not Found");
674  close_session (hs);
675  return -1;
676  }
677  vec_reset_length (hs->rx_buf);
678  goto send_ok;
679  }
680  vec_reset_length (hs->rx_buf);
681  /* poison request, it's not valid anymore */
682  request = 0;
683  /* The static server itself doesn't do POSTs */
684  if (request_type == HTTP_BUILTIN_METHOD_POST)
685  {
686  send_error (hs, "404 Not Found");
687  close_session (hs);
688  return -1;
689  }
690 
691  /* Try to find the file. 2x special cases to find index.html */
692  if (stat ((char *) path, sb) < 0 /* cant even stat the file */
693  || sb->st_size < 20 /* file too small */
694  || (sb->st_mode & S_IFMT) != S_IFREG /* not a regular file */ )
695  {
696  u32 save_length = vec_len (path) - 1;
697  /* Try appending "index.html"... */
698  _vec_len (path) -= 1;
699  path = format (path, "index.html%c", 0);
700  if (stat ((char *) path, sb) < 0 /* cant even stat the file */
701  || sb->st_size < 20 /* file too small */
702  || (sb->st_mode & S_IFMT) != S_IFREG /* not a regular file */ )
703  {
704  _vec_len (path) = save_length;
705  path = format (path, "/index.html%c", 0);
706 
707  /* Send a redirect, otherwise the browser will confuse itself */
708  if (stat ((char *) path, sb) < 0 /* cant even stat the file */
709  || sb->st_size < 20 /* file too small */
710  || (sb->st_mode & S_IFMT) != S_IFREG /* not a regular file */ )
711  {
712  vec_free (path);
713  send_error (hs, "404 Not Found");
714  close_session (hs);
715  return -1;
716  }
717  else
718  {
721  u16 local_port;
722  int print_port = 0;
723  u8 *port_str = 0;
724 
725  /*
726  * To make this bit work correctly, we need to know our local
727  * IP address, etc. and send it in the redirect...
728  */
729  u8 *redirect;
730 
731  vec_delete (path, vec_len (hsm->www_root) - 1, 0);
732 
733  session_get_endpoint (s, &endpoint, 1 /* is_local */ );
734 
735  local_port = clib_net_to_host_u16 (endpoint.port);
736 
738 
739  if ((proto == TRANSPORT_PROTO_TCP && local_port != 80)
740  || (proto == TRANSPORT_PROTO_TLS && local_port != 443))
741  {
742  print_port = 1;
743  port_str = format (0, ":%u", (u32) local_port);
744  }
745 
746  redirect = format (0, "HTTP/1.1 301 Moved Permanently\r\n"
747  "Location: http%s://%U%s%s\r\n\r\n",
748  proto == TRANSPORT_PROTO_TLS ? "s" : "",
749  format_ip46_address, &endpoint.ip,
750  endpoint.is_ip4,
751  print_port ? port_str : (u8 *) "", path);
752  if (hsm->debug_level > 0)
753  clib_warning ("redirect: %s", redirect);
754 
755  vec_free (port_str);
756 
757  static_send_data (hs, redirect, vec_len (redirect), 0);
758  hs->session_state = HTTP_STATE_CLOSED;
759  hs->path = 0;
760  vec_free (redirect);
761  vec_free (path);
762  close_session (hs);
763  return -1;
764  }
765  }
766  }
767 
768  /* find or read the file if we haven't done so yet. */
769  if (hs->data == 0)
770  {
771  BVT (clib_bihash_kv) kv;
772  file_data_cache_t *dp;
773 
774  hs->path = path;
775 
776  /* First, try the cache */
777  kv.key = (u64) hs->path;
778  if (BV (clib_bihash_search) (&hsm->name_to_data, &kv, &kv) == 0)
779  {
780  if (hsm->debug_level > 1)
781  clib_warning ("lookup '%s' returned %lld", kv.key, kv.value);
782 
783  /* found the data.. */
784  dp = pool_elt_at_index (hsm->cache_pool, kv.value);
785  hs->data = dp->data;
786  /* Update the cache entry, mark it in-use */
787  lru_update (hsm, dp, vlib_time_now (vlib_get_main ()));
788  hs->cache_pool_index = dp - hsm->cache_pool;
789  dp->inuse++;
790  if (hsm->debug_level > 1)
791  clib_warning ("index %d refcnt now %d", hs->cache_pool_index,
792  dp->inuse);
793  }
794  else
795  {
796  if (hsm->debug_level > 1)
797  clib_warning ("lookup '%s' failed", kv.key, kv.value);
798  /* Need to recycle one (or more cache) entries? */
799  if (hsm->cache_size > hsm->cache_limit)
800  {
801  int free_index = hsm->last_index;
802 
803  while (free_index != ~0)
804  {
805  /* pick the LRU */
806  dp = pool_elt_at_index (hsm->cache_pool, free_index);
807  free_index = dp->prev_index;
808  /* Which could be in use... */
809  if (dp->inuse)
810  {
811  if (hsm->debug_level > 1)
812  clib_warning ("index %d in use refcnt %d",
813  dp - hsm->cache_pool, dp->inuse);
814 
815  }
816  kv.key = (u64) (dp->filename);
817  kv.value = ~0ULL;
818  if (BV (clib_bihash_add_del) (&hsm->name_to_data, &kv,
819  0 /* is_add */ ) < 0)
820  {
821  clib_warning ("LRU delete '%s' FAILED!", dp->filename);
822  }
823  else if (hsm->debug_level > 1)
824  clib_warning ("LRU delete '%s' ok", dp->filename);
825 
826  lru_remove (hsm, dp);
827  hsm->cache_size -= vec_len (dp->data);
828  hsm->cache_evictions++;
829  vec_free (dp->filename);
830  vec_free (dp->data);
831  if (hsm->debug_level > 1)
832  clib_warning ("pool put index %d", dp - hsm->cache_pool);
833  pool_put (hsm->cache_pool, dp);
834  if (hsm->cache_size < hsm->cache_limit)
835  break;
836  }
837  }
838 
839  /* Read the file */
840  error = clib_file_contents ((char *) (hs->path), &hs->data);
841  if (error)
842  {
843  clib_warning ("Error reading '%s'", hs->path);
844  clib_error_report (error);
845  vec_free (hs->path);
846  close_session (hs);
847  return -1;
848  }
849  /* Create a cache entry for it */
850  pool_get (hsm->cache_pool, dp);
851  memset (dp, 0, sizeof (*dp));
852  dp->filename = vec_dup (hs->path);
853  dp->data = hs->data;
854  hs->cache_pool_index = dp - hsm->cache_pool;
855  dp->inuse++;
856  if (hsm->debug_level > 1)
857  clib_warning ("index %d refcnt now %d", hs->cache_pool_index,
858  dp->inuse);
859  lru_add (hsm, dp, vlib_time_now (vlib_get_main ()));
860  kv.key = (u64) vec_dup (hs->path);
861  kv.value = dp - hsm->cache_pool;
862  /* Add to the lookup table */
863  if (hsm->debug_level > 1)
864  clib_warning ("add '%s' value %lld", kv.key, kv.value);
865 
866  if (BV (clib_bihash_add_del) (&hsm->name_to_data, &kv,
867  1 /* is_add */ ) < 0)
868  {
869  clib_warning ("BUG: add failed!");
870  }
871  hsm->cache_size += vec_len (dp->data);
872  }
873  hs->data_offset = 0;
874  }
875  /* send 200 OK first */
876 send_ok:
877  static_send_data (hs, (u8 *) "HTTP/1.1 200 OK\r\n", 17, 0);
878  hs->session_state = HTTP_STATE_OK_SENT;
879  return 1;
880 }
881 
882 static int
885 {
886 
887  /* Start sending data */
888  hs->data_offset = static_send_data (hs, hs->data, vec_len (hs->data),
889  hs->data_offset);
890 
891  /* Did we finish? */
892  if (hs->data_offset < vec_len (hs->data))
893  {
894  /* No: ask for a shoulder-tap when the tx fifo has space */
895  svm_fifo_add_want_deq_ntf (hs->tx_fifo,
897  hs->session_state = HTTP_STATE_SEND_MORE_DATA;
898  return 0;
899  }
900  /* Finished with this transaction, back to HTTP_STATE_ESTABLISHED */
901 
902  /* Let go of the file cache entry */
904  hs->session_state = HTTP_STATE_ESTABLISHED;
905  return 0;
906 }
907 
908 static int
911 {
913  char *suffix;
914  char *http_type;
915  u8 *http_response;
916  f64 now;
917  u32 offset;
918 
919  /* What kind of dog food are we serving? */
920  suffix = (char *) (hs->path + vec_len (hs->path) - 1);
921  while ((u8 *) suffix >= hs->path && *suffix != '.')
922  suffix--;
923  suffix++;
924  http_type = "text/html";
925  if (!clib_strcmp (suffix, "css"))
926  http_type = "text/css";
927  else if (!clib_strcmp (suffix, "js"))
928  http_type = "text/javascript";
929  else if (!clib_strcmp (suffix, "json"))
930  http_type = "application/json";
931 
932  if (hs->data == 0)
933  {
934  clib_warning ("BUG: hs->data not set for session %d",
935  hs->session_index);
936  close_session (hs);
937  return 0;
938  }
939 
940  /*
941  * Send an http response, which needs the current time,
942  * the expiration time, and the data length
943  */
944  now = clib_timebase_now (&hsm->timebase);
945  http_response = format (0, http_response_template,
946  /* Date */
948  /* Expires */
949  format_clib_timebase_time, now + 600.0,
950  http_type, vec_len (hs->data));
951  offset = static_send_data (hs, http_response, vec_len (http_response), 0);
952  if (offset != vec_len (http_response))
953  {
954  clib_warning ("BUG: couldn't send response header!");
955  close_session (hs);
956  return 0;
957  }
958  vec_free (http_response);
959 
960  /* Send data from the beginning... */
961  hs->data_offset = 0;
962  hs->session_state = HTTP_STATE_SEND_MORE_DATA;
963  return 1;
964 }
965 
966 static void *state_funcs[HTTP_STATE_N_STATES] = {
967  state_closed,
968  /* Waiting for GET, POST, etc. */
970  /* Sent OK */
972  /* Send more data */
974 };
975 
976 static inline int
979 {
980  http_session_t *hs;
982  int rv;
983 
984  /* Acquire a reader lock on the session table */
987 
988  if (!hs)
989  {
990  clib_warning ("No http session for thread %d session_index %d",
991  s->thread_index, s->session_index);
993  return 0;
994  }
995 
996  /* Execute state machine for this session */
997  do
998  {
999  fp = state_funcs[hs->session_state];
1000  rv = (*fp) (s, hs, cf);
1001  if (rv < 0)
1002  goto session_closed;
1003  }
1004  while (rv);
1005 
1006  /* Reset the session expiration timer */
1009 
1010 session_closed:
1012  return 0;
1013 }
1014 
1015 static int
1017 {
1019 }
1020 
1021 static int
1023 {
1025 }
1026 
1027 
1028 /** \brief Session accept callback
1029  */
1030 
1031 static int
1033 {
1035  http_session_t *hs;
1036 
1037  hsm->vpp_queue[s->thread_index] =
1039 
1041 
1044  hs->session_index);
1045  hs->rx_fifo = s->rx_fifo;
1046  hs->tx_fifo = s->tx_fifo;
1049  hs->session_state = HTTP_STATE_ESTABLISHED;
1051 
1053 
1054  s->session_state = SESSION_STATE_READY;
1055  return 0;
1056 }
1057 
1058 /** \brief Session disconnect callback
1059  */
1060 
1061 static void
1063 {
1065  vnet_disconnect_args_t _a = { 0 }, *a = &_a;
1066 
1067  a->handle = session_handle (s);
1068  a->app_index = hsm->app_index;
1070 }
1071 
1072 /** \brief Session reset callback
1073  */
1074 
1075 static void
1077 {
1079  vnet_disconnect_args_t _a = { 0 }, *a = &_a;
1080 
1081  a->handle = session_handle (s);
1082  a->app_index = hsm->app_index;
1084 }
1085 
1086 static int
1088  session_t * s,
1089  session_error_t err)
1090 {
1091  clib_warning ("called...");
1092  return -1;
1093 }
1094 
1095 static int
1096 http_static_server_add_segment_callback (u32 client_index, u64 segment_handle)
1097 {
1098  return 0;
1099 }
1100 
1101 static void
1103 {
1104  http_session_t *hs;
1105 
1106  if (ntf == SESSION_CLEANUP_TRANSPORT)
1107  return;
1108 
1110 
1112  if (!hs)
1113  goto done;
1114 
1117  hs->vpp_session_index);
1118  vec_free (hs->rx_buf);
1120 
1121 done:
1123 }
1124 
1125 /** \brief Session-layer virtual function table
1126  */
1127 static session_cb_vft_t http_static_server_session_cb_vft = {
1129  .session_disconnect_callback =
1131  .session_connected_callback = http_static_server_session_connected_callback,
1132  .add_segment_callback = http_static_server_add_segment_callback,
1133  .builtin_app_rx_callback = http_static_server_rx_callback,
1134  .builtin_app_tx_callback = http_static_server_tx_callback,
1135  .session_reset_callback = http_static_server_session_reset_callback,
1136  .session_cleanup_callback = http_static_session_cleanup,
1137 };
1138 
1139 static int
1141 {
1142  vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair;
1145  vnet_app_attach_args_t _a, *a = &_a;
1146  u32 segment_size = 128 << 20;
1147 
1148  clib_memset (a, 0, sizeof (*a));
1149  clib_memset (options, 0, sizeof (options));
1150 
1151  if (hsm->private_segment_size)
1152  segment_size = hsm->private_segment_size;
1153 
1154  a->api_client_index = ~0;
1155  a->name = format (0, "test_http_static_server");
1156  a->session_cb_vft = &http_static_server_session_cb_vft;
1157  a->options = options;
1158  a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
1159  a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = segment_size;
1160  a->options[APP_OPTIONS_RX_FIFO_SIZE] =
1161  hsm->fifo_size ? hsm->fifo_size : 8 << 10;
1162  a->options[APP_OPTIONS_TX_FIFO_SIZE] =
1163  hsm->fifo_size ? hsm->fifo_size : 32 << 10;
1164  a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
1167 
1168  if (vnet_application_attach (a))
1169  {
1170  vec_free (a->name);
1171  clib_warning ("failed to attach server");
1172  return -1;
1173  }
1174  vec_free (a->name);
1175  hsm->app_index = a->app_index;
1176 
1177  clib_memset (ck_pair, 0, sizeof (*ck_pair));
1178  ck_pair->cert = (u8 *) test_srv_crt_rsa;
1179  ck_pair->key = (u8 *) test_srv_key_rsa;
1180  ck_pair->cert_len = test_srv_crt_rsa_len;
1181  ck_pair->key_len = test_srv_key_rsa_len;
1182  vnet_app_add_cert_key_pair (ck_pair);
1183  hsm->ckpair_index = ck_pair->index;
1184 
1185  return 0;
1186 }
1187 
1188 static int
1190 {
1191  return proto == TRANSPORT_PROTO_TLS || proto == TRANSPORT_PROTO_DTLS ||
1192  proto == TRANSPORT_PROTO_QUIC;
1193 }
1194 
1195 static int
1197 {
1200  vnet_listen_args_t _a, *a = &_a;
1201  char *uri = "tcp://0.0.0.0/80";
1202  int rv;
1203 
1204  clib_memset (a, 0, sizeof (*a));
1205  a->app_index = hsm->app_index;
1206 
1207  if (hsm->uri)
1208  uri = (char *) hsm->uri;
1209 
1210  if (parse_uri (uri, &sep))
1211  return -1;
1212 
1213  clib_memcpy (&a->sep_ext, &sep, sizeof (sep));
1214  if (http_static_transport_needs_crypto (a->sep_ext.transport_proto))
1215  {
1216  session_endpoint_alloc_ext_cfg (&a->sep_ext,
1218  a->sep_ext.ext_cfg->crypto.ckpair_index = hsm->ckpair_index;
1219  }
1220 
1221  rv = vnet_listen (a);
1222  if (a->sep_ext.ext_cfg)
1223  clib_mem_free (a->sep_ext.ext_cfg);
1224  return rv;
1225 }
1226 
1227 static void
1229 {
1231  http_session_t *hs;
1232  uword hs_handle;
1233  hs_handle = pointer_to_uword (hs_handlep);
1234  hs =
1235  http_static_server_session_get (hs_handle >> 24, hs_handle & 0x00FFFFFF);
1236 
1237  if (hsm->debug_level > 1)
1238  clib_warning ("terminate thread %d index %d hs %llx",
1239  hs_handle >> 24, hs_handle & 0x00FFFFFF, hs);
1240  if (!hs)
1241  return;
1242  hs->timer_handle = ~0;
1244 }
1245 
1246 /** \brief Expired session timer-wheel callback
1247  */
1248 static void
1250 {
1251  u32 hs_handle;
1252  int i;
1253 
1254  for (i = 0; i < vec_len (expired_timers); i++)
1255  {
1256  /* Get session handle. The first bit is the timer id */
1257  hs_handle = expired_timers[i] & 0x7FFFFFFF;
1258  session_send_rpc_evt_to_thread (hs_handle >> 24,
1260  uword_to_pointer (hs_handle, void *));
1261  }
1262 }
1263 
1264 /** \brief Timer-wheel expiration process
1265  */
1266 static uword
1268  vlib_frame_t * f)
1269 {
1271  f64 now, timeout = 1.0;
1272  uword *event_data = 0;
1273  uword __clib_unused event_type;
1274 
1275  while (1)
1276  {
1278  now = vlib_time_now (vm);
1279  event_type = vlib_process_get_events (vm, (uword **) & event_data);
1280 
1281  /* expire timers */
1282  clib_spinlock_lock (&http_static_server_main.tw_lock);
1283  tw_timer_expire_timers_2t_1w_2048sl (&hsm->tw, now);
1284  clib_spinlock_unlock (&http_static_server_main.tw_lock);
1285 
1286  vec_reset_length (event_data);
1287  }
1288  return 0;
1289 }
1290 
1291 /* *INDENT-OFF* */
1292 VLIB_REGISTER_NODE (http_static_server_process_node) =
1293 {
1294  .function = http_static_server_process,
1295  .type = VLIB_NODE_TYPE_PROCESS,
1296  .name = "static-http-server-process",
1297  .state = VLIB_NODE_STATE_DISABLED,
1298 };
1299 /* *INDENT-ON* */
1300 
1301 static int
1303 {
1306  u32 num_threads;
1307  vlib_node_t *n;
1308 
1309  num_threads = 1 /* main thread */ + vtm->n_threads;
1310  vec_validate (hsm->vpp_queue, num_threads - 1);
1311  vec_validate (hsm->sessions, num_threads - 1);
1312  vec_validate (hsm->session_to_http_session, num_threads - 1);
1313 
1315  clib_spinlock_init (&hsm->tw_lock);
1316 
1318  {
1319  clib_warning ("failed to attach server");
1320  return -1;
1321  }
1323  {
1324  clib_warning ("failed to start listening");
1325  return -1;
1326  }
1327 
1328  /* Init path-to-cache hash table */
1329  BV (clib_bihash_init) (&hsm->name_to_data, "http cache", 128, 32 << 20);
1330 
1331  hsm->get_url_handlers = hash_create_string (0, sizeof (uword));
1332  hsm->post_url_handlers = hash_create_string (0, sizeof (uword));
1333 
1334  /* Init timer wheel and process */
1335  tw_timer_wheel_init_2t_1w_2048sl (&hsm->tw, http_expired_timers_dispatch,
1336  1.0 /* timer interval */ , ~0);
1337  vlib_node_set_state (vm, http_static_server_process_node.index,
1338  VLIB_NODE_STATE_POLLING);
1339  n = vlib_get_node (vm, http_static_server_process_node.index);
1341 
1342  return 0;
1343 }
1344 
1345 /** \brief API helper function for vl_api_http_static_enable_t messages
1346  */
1347 int
1348 http_static_server_enable_api (u32 fifo_size, u32 cache_limit,
1349  u32 prealloc_fifos,
1350  u32 private_segment_size,
1351  u8 * www_root, u8 * uri)
1352 {
1354  int rv;
1355 
1356  hsm->fifo_size = fifo_size;
1357  hsm->cache_limit = cache_limit;
1358  hsm->prealloc_fifos = prealloc_fifos;
1359  hsm->private_segment_size = private_segment_size;
1360  hsm->www_root = format (0, "%s%c", www_root, 0);
1361  hsm->uri = format (0, "%s%c", uri, 0);
1362 
1363  if (vec_len (hsm->www_root) < 2)
1364  return VNET_API_ERROR_INVALID_VALUE;
1365 
1366  if (hsm->my_client_index != ~0)
1367  return VNET_API_ERROR_APP_ALREADY_ATTACHED;
1368 
1369  vnet_session_enable_disable (hsm->vlib_main, 1 /* turn on TCP, etc. */ );
1370 
1372  switch (rv)
1373  {
1374  case 0:
1375  break;
1376  default:
1377  vec_free (hsm->www_root);
1378  vec_free (hsm->uri);
1379  return VNET_API_ERROR_INIT_FAILED;
1380  }
1381  return 0;
1382 }
1383 
1384 static clib_error_t *
1386  unformat_input_t * input,
1387  vlib_cli_command_t * cmd)
1388 {
1390  unformat_input_t _line_input, *line_input = &_line_input;
1391  u64 seg_size;
1392  u8 *www_root = 0;
1393  int rv;
1394 
1395  hsm->prealloc_fifos = 0;
1396  hsm->private_segment_size = 0;
1397  hsm->fifo_size = 0;
1398  /* 10mb cache limit, before LRU occurs */
1399  hsm->cache_limit = 10 << 20;
1400 
1401  /* Get a line of input. */
1402  if (!unformat_user (input, unformat_line_input, line_input))
1403  goto no_wwwroot;
1404 
1405  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1406  {
1407  if (unformat (line_input, "www-root %s", &www_root))
1408  ;
1409  else
1410  if (unformat (line_input, "prealloc-fifos %d", &hsm->prealloc_fifos))
1411  ;
1412  else if (unformat (line_input, "private-segment-size %U",
1413  unformat_memory_size, &seg_size))
1414  {
1415  if (seg_size >= 0x100000000ULL)
1416  {
1417  vlib_cli_output (vm, "private segment size %llu, too large",
1418  seg_size);
1419  return 0;
1420  }
1421  hsm->private_segment_size = seg_size;
1422  }
1423  else if (unformat (line_input, "fifo-size %d", &hsm->fifo_size))
1424  hsm->fifo_size <<= 10;
1425  else if (unformat (line_input, "cache-size %U", unformat_memory_size,
1426  &hsm->cache_limit))
1427  {
1428  if (hsm->cache_limit < (128 << 10))
1429  {
1430  return clib_error_return (0,
1431  "cache-size must be at least 128kb");
1432  }
1433  }
1434 
1435  else if (unformat (line_input, "uri %s", &hsm->uri))
1436  ;
1437  else if (unformat (line_input, "debug %d", &hsm->debug_level))
1438  ;
1439  else if (unformat (line_input, "debug"))
1440  hsm->debug_level = 1;
1441  else
1442  return clib_error_return (0, "unknown input `%U'",
1443  format_unformat_error, line_input);
1444  }
1445  unformat_free (line_input);
1446 
1447  if (www_root == 0)
1448  {
1449  no_wwwroot:
1450  return clib_error_return (0, "Must specify www-root <path>");
1451  }
1452 
1453  if (hsm->my_client_index != (u32) ~ 0)
1454  {
1455  vec_free (www_root);
1456  return clib_error_return (0, "http server already running...");
1457  }
1458 
1459  hsm->www_root = www_root;
1460 
1461  vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
1462 
1463  rv = http_static_server_create (vm);
1464  switch (rv)
1465  {
1466  case 0:
1467  break;
1468  default:
1469  vec_free (hsm->www_root);
1470  return clib_error_return (0, "server_create returned %d", rv);
1471  }
1472  return 0;
1473 }
1474 
1475 /*?
1476  * Enable the static http server
1477  *
1478  * @cliexpar
1479  * This command enables the static http server. Only the www-root
1480  * parameter is required
1481  * @clistart
1482  * http static server www-root /tmp/www uri tcp://0.0.0.0/80 cache-size 2m
1483  * @cliend
1484  * @cliexcmd{http static server www-root <path> [prealloc-fios <nn>]
1485  * [private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]}
1486 ?*/
1487 /* *INDENT-OFF* */
1488 VLIB_CLI_COMMAND (http_static_server_create_command, static) =
1489 {
1490  .path = "http static server",
1491  .short_help = "http static server www-root <path> [prealloc-fifos <nn>]\n"
1492  "[private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]\n"
1493  "[debug [nn]]\n",
1495 };
1496 /* *INDENT-ON* */
1497 
1498 /** \brief format a file cache entry
1499  */
1500 u8 *
1501 format_hsm_cache_entry (u8 * s, va_list * args)
1502 {
1503  file_data_cache_t *ep = va_arg (*args, file_data_cache_t *);
1504  f64 now = va_arg (*args, f64);
1505 
1506  /* Header */
1507  if (ep == 0)
1508  {
1509  s = format (s, "%40s%12s%20s", "File", "Size", "Age");
1510  return s;
1511  }
1512  s = format (s, "%40s%12lld%20.2f", ep->filename, vec_len (ep->data),
1513  now - ep->last_used);
1514  return s;
1515 }
1516 
1517 u8 *
1518 format_http_session_state (u8 * s, va_list * args)
1519 {
1521  char *state_string = "bogus!";
1522 
1523  switch (state)
1524  {
1525  case HTTP_STATE_CLOSED:
1526  state_string = "closed";
1527  break;
1529  state_string = "established";
1530  break;
1531  case HTTP_STATE_OK_SENT:
1532  state_string = "ok sent";
1533  break;
1535  state_string = "send more data";
1536  break;
1537  default:
1538  break;
1539  }
1540 
1541  return format (s, "%s", state_string);
1542 }
1543 
1544 u8 *
1545 format_http_session (u8 * s, va_list * args)
1546 {
1547  http_session_t *hs = va_arg (*args, http_session_t *);
1548  int verbose = va_arg (*args, int);
1549 
1550  s = format (s, "[%d]: state %U", hs->session_index,
1551  format_http_session_state, hs->session_state);
1552  if (verbose > 0)
1553  {
1554  s = format (s, "\n path %s, data length %u, data_offset %u",
1555  hs->path ? hs->path : (u8 *) "[none]",
1556  vec_len (hs->data), hs->data_offset);
1557  }
1558  return s;
1559 }
1560 
1561 static clib_error_t *
1563  unformat_input_t * input,
1564  vlib_cli_command_t * cmd)
1565 {
1567  file_data_cache_t *ep, **entries = 0;
1568  int verbose = 0;
1569  int show_cache = 0;
1570  int show_sessions = 0;
1571  u32 index;
1572  f64 now;
1573 
1574  if (hsm->www_root == 0)
1575  return clib_error_return (0, "Static server disabled");
1576 
1577  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1578  {
1579  if (unformat (input, "verbose %d", &verbose))
1580  ;
1581  else if (unformat (input, "verbose"))
1582  verbose = 1;
1583  else if (unformat (input, "cache"))
1584  show_cache = 1;
1585  else if (unformat (input, "sessions"))
1586  show_sessions = 1;
1587  else
1588  break;
1589  }
1590 
1591  if ((show_cache + show_sessions) == 0)
1592  return clib_error_return (0, "specify one or more of cache, sessions");
1593 
1594  if (show_cache)
1595  {
1596  if (verbose == 0)
1597  {
1599  (vm, "www_root %s, cache size %lld bytes, limit %lld bytes, "
1600  "evictions %lld",
1601  hsm->www_root, hsm->cache_size, hsm->cache_limit,
1602  hsm->cache_evictions);
1603  return 0;
1604  }
1605 
1606  now = vlib_time_now (vm);
1607 
1608  vlib_cli_output (vm, "%U", format_hsm_cache_entry, 0 /* header */ ,
1609  now);
1610 
1611  for (index = hsm->first_index; index != ~0;)
1612  {
1613  ep = pool_elt_at_index (hsm->cache_pool, index);
1614  index = ep->next_index;
1615  vlib_cli_output (vm, "%U", format_hsm_cache_entry, ep, now);
1616  }
1617 
1618  vlib_cli_output (vm, "%40s%12lld", "Total Size", hsm->cache_size);
1619 
1620  vec_free (entries);
1621  }
1622 
1623  if (show_sessions)
1624  {
1625  u32 *session_indices = 0;
1626  http_session_t *hs;
1627  int i, j;
1628 
1630 
1631  for (i = 0; i < vec_len (hsm->sessions); i++)
1632  {
1633  /* *INDENT-OFF* */
1634  pool_foreach (hs, hsm->sessions[i])
1635  {
1636  vec_add1 (session_indices, hs - hsm->sessions[i]);
1637  }
1638  /* *INDENT-ON* */
1639 
1640  for (j = 0; j < vec_len (session_indices); j++)
1641  {
1644  (hsm->sessions[i], session_indices[j]),
1645  verbose);
1646  }
1647  vec_reset_length (session_indices);
1648  }
1650  vec_free (session_indices);
1651  }
1652  return 0;
1653 }
1654 
1655 /*?
1656  * Display static http server cache statistics
1657  *
1658  * @cliexpar
1659  * This command shows the contents of the static http server cache
1660  * @clistart
1661  * show http static server
1662  * @cliend
1663  * @cliexcmd{show http static server sessions cache [verbose [nn]]}
1664 ?*/
1665 /* *INDENT-OFF* */
1666 VLIB_CLI_COMMAND (http_show_static_server_command, static) =
1667 {
1668  .path = "show http static server",
1669  .short_help = "show http static server sessions cache [verbose [<nn>]]",
1671 };
1672 /* *INDENT-ON* */
1673 
1674 static clib_error_t *
1676  unformat_input_t * input,
1677  vlib_cli_command_t * cmd)
1678 {
1680  file_data_cache_t *dp;
1681  u32 free_index;
1682  u32 busy_items = 0;
1683  BVT (clib_bihash_kv) kv;
1684 
1685  if (hsm->www_root == 0)
1686  return clib_error_return (0, "Static server disabled");
1687 
1689 
1690  /* Walk the LRU list to find active entries */
1691  free_index = hsm->last_index;
1692  while (free_index != ~0)
1693  {
1694  dp = pool_elt_at_index (hsm->cache_pool, free_index);
1695  free_index = dp->prev_index;
1696  /* Which could be in use... */
1697  if (dp->inuse)
1698  {
1699  busy_items++;
1700  free_index = dp->next_index;
1701  continue;
1702  }
1703  kv.key = (u64) (dp->filename);
1704  kv.value = ~0ULL;
1705  if (BV (clib_bihash_add_del) (&hsm->name_to_data, &kv,
1706  0 /* is_add */ ) < 0)
1707  {
1708  clib_warning ("BUG: cache clear delete '%s' FAILED!", dp->filename);
1709  }
1710 
1711  lru_remove (hsm, dp);
1712  hsm->cache_size -= vec_len (dp->data);
1713  hsm->cache_evictions++;
1714  vec_free (dp->filename);
1715  vec_free (dp->data);
1716  if (hsm->debug_level > 1)
1717  clib_warning ("pool put index %d", dp - hsm->cache_pool);
1718  pool_put (hsm->cache_pool, dp);
1719  free_index = hsm->last_index;
1720  }
1722  if (busy_items > 0)
1723  vlib_cli_output (vm, "Note: %d busy items still in cache...", busy_items);
1724  else
1725  vlib_cli_output (vm, "Cache cleared...");
1726  return 0;
1727 }
1728 
1729 /*?
1730  * Clear the static http server cache, to force the server to
1731  * reload content from backing files
1732  *
1733  * @cliexpar
1734  * This command clear the static http server cache
1735  * @clistart
1736  * clear http static cache
1737  * @cliend
1738  * @cliexcmd{clear http static cache}
1739 ?*/
1740 /* *INDENT-OFF* */
1741 VLIB_CLI_COMMAND (clear_http_static_cache_command, static) =
1742 {
1743  .path = "clear http static cache",
1744  .short_help = "clear http static cache",
1746 };
1747 /* *INDENT-ON* */
1748 
1749 static clib_error_t *
1751 {
1753 
1754  hsm->my_client_index = ~0;
1755  hsm->vlib_main = vm;
1756  hsm->first_index = hsm->last_index = ~0;
1757 
1758  clib_timebase_init (&hsm->timebase, 0 /* GMT */ ,
1760  &vm->clib_time /* share the system clock */ );
1761 
1762  return 0;
1763 }
1764 
1766 
1767 /*
1768  * fd.io coding-style-patch-verification: ON
1769  *
1770  * Local Variables:
1771  * eval: (c-set-style "gnu")
1772  * End:
1773  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:524
static void clib_rwlock_reader_lock(clib_rwlock_t *p)
Definition: lock.h:169
static int app_recv_stream_raw(svm_fifo_t *f, u8 *buf, u32 len, u8 clear_evt, u8 peek)
u8 * filename
Name of the file.
Definition: http_static.h:119
u8 * format_http_session(u8 *s, va_list *args)
static void lru_remove(http_static_server_main_t *hsm, file_data_cache_t *ep)
Remove a data cache entry from the LRU lists.
vnet_interface_output_runtime_t * rt
static_always_inline void clib_spinlock_unlock(clib_spinlock_t *p)
Definition: lock.h:121
static http_session_t * http_static_server_session_lookup(u32 thread_index, u32 s_index)
lookup a session in the vpp < – > http session index map
static_always_inline void clib_spinlock_lock(clib_spinlock_t *p)
Definition: lock.h:82
session_type_t session_type
Type built from transport and network protocol types.
int vnet_app_add_cert_key_pair(vnet_app_add_cert_key_pair_args_t *a)
Definition: application.c:2017
static void lru_add(http_static_server_main_t *hsm, file_data_cache_t *ep, f64 now)
Add an entry to the LRU lists, tag w/ supplied timestamp.
static f64 vlib_process_wait_for_event_or_clock(vlib_main_t *vm, f64 dt)
Suspend a cooperative multi-tasking thread Waits for an event, or for the indicated number of seconds...
Definition: node_funcs.h:755
a
Definition: bitmap.h:544
static void clib_rwlock_writer_lock(clib_rwlock_t *p)
Definition: lock.h:192
svm_fifo_t * tx_fifo
Static http server definitions.
#define pool_foreach(VAR, POOL)
Iterate through pool.
Definition: pool.h:534
static const char * http_response
Definition: http_server.c:240
u32 thread_index
u32 session_index
Index in thread pool where session was allocated.
static clib_error_t * http_clear_static_cache_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
unsigned long u64
Definition: types.h:89
static svm_msg_q_t * session_main_get_vpp_event_queue(u32 thread_index)
Definition: session.h:716
u32 timer_handle
Timeout timer handle.
Definition: http_server.c:50
clib_memset(h->entries, 0, sizeof(h->entries[0]) *entries)
static void http_static_server_sessions_writer_unlock(void)
Drop writer lock on the sessions pools.
Definition: static_server.c:94
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:325
u32 fifo_size
Size of the allocated rx, tx fifos, roughly 8K or so.
Definition: http_static.h:199
svm_fifo_t * rx_fifo
Pointers to rx/tx buffers.
static uword http_static_server_process(vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
Timer-wheel expiration process.
u32 data_offset
Current data send offset.
Definition: http_static.h:104
Main data structure.
Definition: http_static.h:134
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:607
u32 private_segment_size
Private segment size, usually 0.
Definition: http_static.h:197
u32 vpp_session_index
vpp session index, handle
Definition: http_server.c:48
void session_send_rpc_evt_to_thread(u32 thread_index, void *fp, void *rpc_args)
Definition: session.c:116
uword unformat_user(unformat_input_t *input, unformat_function_t *func,...)
Definition: unformat.c:989
#define hash_set_mem(h, key, value)
Definition: hash.h:275
int debug_level
Enable debug messages.
Definition: http_static.h:144
clib_timebase_t timebase
Time base, so we can generate browser cache control http spew.
Definition: http_static.h:192
clib_time_t clib_time
Definition: main.h:106
static void http_static_server_session_disconnect(http_session_t *hs)
Disconnect a session.
#define pool_is_free(P, E)
Use free bitmap to query whether given element is free.
Definition: pool.h:294
vl_api_fib_path_t path
Definition: mfib_types.api:44
u8 * path
Fully-resolved file path.
Definition: http_static.h:100
static int http_static_transport_needs_crypto(transport_proto_t proto)
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:255
static int v_find_index(u8 *vec, char *str)
static void http_static_session_cleanup(session_t *s, session_cleanup_ntf_t ntf)
u32 prealloc_fifos
Number of preallocated fifos, usually 0.
Definition: http_static.h:195
static void http_static_server_detach_cache_entry(http_session_t *hs)
Detach cache entry from session.
unsigned char u8
Definition: types.h:56
static int http_static_server_session_connected_callback(u32 app_index, u32 api_context, session_t *s, session_error_t err)
int http_static_server_enable_api(u32 fifo_size, u32 cache_limit, u32 prealloc_fifos, u32 private_segment_size, u8 *www_root, u8 *uri)
API helper function for vl_api_http_static_enable_t messages.
u8 data[128]
Definition: ipsec_types.api:92
struct _vnet_bind_args_t vnet_listen_args_t
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
double f64
Definition: types.h:142
static session_handle_t session_handle(session_t *s)
static void lru_validate(http_static_server_main_t *hsm)
Sanity-check the forward and reverse LRU lists.
unsigned int u32
Definition: types.h:88
void session_get_endpoint(session_t *s, transport_endpoint_t *tep, u8 is_lcl)
Definition: session.c:1756
#define clib_memcpy(d, s, n)
Definition: string.h:197
static void http_static_server_session_timer_stop(http_session_t *hs)
stop a session cleanup timer
svm_msg_q_t ** vpp_queue
vpp message/event queue
Definition: http_static.h:147
vlib_frame_t * f
static int svm_fifo_is_empty(svm_fifo_t *f)
Check if fifo is empty.
Definition: svm_fifo.h:579
if(node->flags &VLIB_NODE_FLAG_TRACE) vnet_interface_output_trace(vm
u32 ckpair_index
Cert and key pair for tls.
Definition: http_static.h:185
http_session_state_t
Definition: http_server.c:34
static_always_inline uword os_get_numa_index(void)
Definition: os.h:75
int clib_bihash_add_del(clib_bihash *h, clib_bihash_kv *add_v, int is_add)
Add or delete a (key,value) pair from a bi-hash table.
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:172
static int state_send_more_data(session_t *s, http_session_t *hs, http_state_machine_called_from_t cf)
static uword vlib_process_get_events(vlib_main_t *vm, uword **data_vector)
Return the first event type which has occurred and a vector of per-event data of that type...
Definition: node_funcs.h:583
struct _vnet_disconnect_args_t vnet_disconnect_args_t
int which
Definition: cJSON.h:234
static u32 svm_fifo_max_dequeue(svm_fifo_t *f)
Fifo max bytes to dequeue.
Definition: svm_fifo.h:516
static int http_static_server_attach()
static const u32 test_srv_key_rsa_len
Definition: tls_test.h:77
static f64 clib_timebase_now(clib_timebase_t *tb)
Definition: time_range.h:88
description fragment has unexpected format
Definition: map.api:433
foreach_app_session_field u32 thread_index
rx thread index
Definition: http_server.c:46
#define clib_error_return(e, args...)
Definition: error.h:99
u8 * format_http_session_state(u8 *s, va_list *args)
u64 cache_size
Current cache size.
Definition: http_static.h:159
Number of states.
Definition: http_static.h:64
static int session_rx_request(http_session_t *hs)
Retrieve data from the application layer.
int __clib_unused rv
Definition: application.c:491
int session_send_io_evt_to_thread(svm_fifo_t *f, session_evt_type_t evt_type)
Definition: session.c:84
format_function_t format_clib_timebase_time
Definition: time_range.h:70
#define hash_create_string(elts, value_bytes)
Definition: hash.h:690
static void http_static_server_session_close_cb(void *hs_handlep)
__clib_export clib_error_t * clib_file_contents(char *file, u8 **result)
Definition: unix-misc.c:112
struct _vnet_app_attach_args_t vnet_app_attach_args_t
unformat_function_t unformat_line_input
Definition: format.h:275
static void clib_spinlock_init(clib_spinlock_t *p)
Definition: lock.h:65
u8 * format_hsm_cache_entry(u8 *s, va_list *args)
format a file cache entry
int free_data
Need to free data in detach_cache_entry.
Definition: http_static.h:106
struct _session_endpoint_cfg session_endpoint_cfg_t
u8 * data
Contents of the file, as a u8 * vector.
Definition: http_static.h:121
static void lru_update(http_static_server_main_t *hsm, file_data_cache_t *ep, f64 now)
Remove and re-add a cache entry from/to the LRU lists.
file_data_cache_t * cache_pool
Unified file data cache pool.
Definition: http_static.h:150
Definition: cJSON.c:88
static int http_static_server_rx_tx_callback(session_t *s, http_state_machine_called_from_t cf)
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:553
static void clib_rwlock_init(clib_rwlock_t *p)
Definition: lock.h:152
static int http_static_server_tx_callback(session_t *s)
static u8 * format_state_machine_called_from(u8 *s, va_list *args)
Format the called-from enum.
Definition: static_server.c:40
u8 * data
File data, a vector.
Definition: http_static.h:102
u64 vpp_session_handle
Definition: http_server.c:49
static void clib_rwlock_reader_unlock(clib_rwlock_t *p)
Definition: lock.h:184
vl_api_ip_proto_t proto
Definition: acl_types.api:51
static int http_static_server_session_accept_callback(session_t *s)
Session accept callback.
struct _unformat_input_t unformat_input_t
unsigned short u16
Definition: types.h:57
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:305
#define vec_dup(V)
Return copy of vector (no header, no alignment)
Definition: vec.h:444
int svm_fifo_enqueue(svm_fifo_t *f, u32 len, const u8 *src)
Enqueue data to fifo.
Definition: svm_fifo.c:847
#define PREDICT_FALSE(x)
Definition: clib.h:124
http_state_machine_called_from_t
Definition: http_static.h:67
static void svm_fifo_unset_event(svm_fifo_t *f)
Unset fifo event flag.
Definition: svm_fifo.h:803
static void http_expired_timers_dispatch(u32 *expired_timers)
Expired session timer-wheel callback.
vlib_main_t * vm
X-connect all packets from the HOST to the PHY.
Definition: nat44_ei.c:3047
Application session.
Definition: http_server.c:40
#define pool_get_aligned_zero_numa(P, E, A, Z, S)
Definition: pool.h:236
uword * get_url_handlers
Hash tables for built-in GET and POST handlers.
Definition: http_static.h:155
static const char test_srv_crt_rsa[]
Definition: tls_test.h:23
void clib_bihash_init(clib_bihash *h, char *name, u32 nbuckets, uword memory_size)
initialize a bounded index extensible hash table
clib_error_t * vnet_session_enable_disable(vlib_main_t *vm, u8 is_en)
Definition: session.c:1927
static void session_endpoint_alloc_ext_cfg(session_endpoint_cfg_t *sep_ext, transport_endpt_ext_cfg_type_t type)
static int http_static_server_rx_callback(session_t *s)
format_function_t format_ip46_address
Definition: ip46_address.h:50
int vnet_application_attach(vnet_app_attach_args_t *a)
Attach application to vpp.
Definition: application.c:1114
static void http_static_server_session_lookup_del(u32 thread_index, u32 s_index)
Remove a session from the vpp < – > http session index map.
BVT(clib_bihash)
The table of adjacencies indexed by the rewrite string.
Definition: l2_fib.c:1069
static u8 svm_fifo_set_event(svm_fifo_t *f)
Set fifo event flag.
Definition: svm_fifo.h:790
static void clib_rwlock_writer_unlock(clib_rwlock_t *p)
Definition: lock.h:206
f64 last_used
Last time the cache entry was used.
Definition: http_static.h:123
vl_api_address_t endpoint
Definition: wireguard.api:103
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:169
__clib_export void clib_timebase_init(clib_timebase_t *tb, i32 timezone_offset_in_hours, clib_timebase_daylight_time_t daylight_type, clib_time_t *clib_time)
Definition: time_range.c:19
#define UNFORMAT_END_OF_INPUT
Definition: format.h:137
u32 runtime_index
Definition: node.h:273
sll srl srl sll sra u16x4 i
Definition: vector_sse42.h:261
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:395
u32 index
Definition: flow_types.api:221
static clib_error_t * http_static_server_create_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
#define clib_warning(format, args...)
Definition: error.h:59
static void http_static_server_session_reset_callback(session_t *s)
Session reset callback.
u32 ** session_to_http_session
vpp session to http session index map
Definition: http_static.h:141
http_session_t ** sessions
Per thread vector of session pools.
Definition: http_static.h:137
static int state_established(session_t *s, http_session_t *hs, http_state_machine_called_from_t cf)
established state - waiting for GET, POST, etc.
static void http_static_server_sessions_reader_unlock(void)
Drop reader lock on the sessions pools.
Definition: static_server.c:78
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:302
static u32 static_send_data(http_session_t *hs, u8 *data, u32 length, u32 offset)
send http data
static const char * http_error_template
http error boilerplate
static transport_proto_t session_type_transport_proto(session_type_t st)
clib_rwlock_t sessions_lock
Session pool reader writer lock.
Definition: http_static.h:139
static session_cb_vft_t http_static_server_session_cb_vft
Session-layer virtual function table.
static const char test_srv_key_rsa[]
Definition: tls_test.h:49
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:163
u8 * rx_buf
rx buffer
Definition: http_server.c:47
u32 cache_pool_index
File cache pool index.
Definition: http_static.h:109
#define uword_to_pointer(u, type)
Definition: types.h:136
static int state_closed(session_t *s, http_session_t *hs, http_state_machine_called_from_t cf)
Session-layer (main) data rx callback.
#define ASSERT(truth)
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:716
#define vec_delete(V, N, M)
Delete N elements starting at element M.
Definition: vec.h:876
static void http_static_server_session_disconnect_callback(session_t *s)
Session disconnect callback.
static int http_static_server_create(vlib_main_t *vm)
static int http_static_server_listen()
int vnet_listen(vnet_listen_args_t *a)
Definition: application.c:1266
static void clib_mem_free(void *p)
Definition: mem.h:311
enum _transport_proto transport_proto_t
u32 app_index
Application index.
Definition: http_static.h:179
#define clib_error_report(e)
Definition: error.h:113
struct _vnet_app_add_cert_key_pair_args_ vnet_app_add_cert_key_pair_args_t
static void svm_fifo_add_want_deq_ntf(svm_fifo_t *f, u8 ntf_type)
Set specific want notification flag.
Definition: svm_fifo.h:817
u32 my_client_index
API client handle.
Definition: http_static.h:176
static void vlib_node_set_state(vlib_main_t *vm, u32 node_index, vlib_node_state_t new_state)
Set node dispatch state.
Definition: node_funcs.h:175
char const int length
Definition: cJSON.h:163
Session has sent an HTML response.
Definition: http_static.h:62
__clib_export void http_static_server_register_builtin_handler(void *fp, char *url, int request_type)
Register a builtin GET or POST handler.
static uword pointer_to_uword(const void *p)
Definition: types.h:131
u32 first_index
Cache LRU listheads.
Definition: http_static.h:166
Notify on transition from full.
Definition: svm_fifo.h:36
http_builtin_method_type_t
Definition: http_static.h:74
static http_session_t * http_static_server_session_alloc(u32 thread_index)
Allocate an http session.
static vlib_main_t * vlib_get_main(void)
Definition: global_funcs.h:38
u32 entries
u8 thread_index
Index of the thread that allocated the session.
tw_timer_wheel_2t_1w_2048sl_t tw
Session cleanup timer wheel.
Definition: http_static.h:188
template key/value backing page structure
Definition: bihash_doc.h:44
int parse_uri(char *uri, session_endpoint_cfg_t *sep)
static void http_static_server_session_lookup_add(u32 thread_index, u32 s_index, u32 hs_index)
add a session to the vpp < – > http session index map
u32 next_index
Cache LRU links.
Definition: http_static.h:125
static void http_static_server_sessions_writer_lock(void)
Acquire writer lock on the sessions pools.
Definition: static_server.c:86
static const char * http_response_template
http response boilerplate
static clib_error_t * http_static_server_main_init(vlib_main_t *vm)
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
static clib_error_t * http_show_static_server_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
volatile u8 session_state
State in session layer state machine.
static void close_session(http_session_t *hs)
static http_session_t * http_static_server_session_get(u32 thread_index, u32 hs_index)
Get an http session by index.
u64 uword
Definition: types.h:112
static void http_static_server_session_free(http_session_t *hs)
Free an http session.
static void send_error(http_session_t *hs, char *str)
Send an http error string.
static void unformat_free(unformat_input_t *i)
Definition: format.h:155
int inuse
Reference count, so we don&#39;t recycle while referenced.
Definition: http_static.h:128
static int state_sent_ok(session_t *s, http_session_t *hs, http_state_machine_called_from_t cf)
int vnet_disconnect_session(vnet_disconnect_args_t *a)
Definition: application.c:1411
vhost_user_req_t request
Definition: vhost_user.h:123
#define SESSION_ENDPOINT_CFG_NULL
Definition: session_types.h:77
u8 * uri
The bind URI, defaults to tcp://0.0.0.0/80.
Definition: http_static.h:201
session_cleanup_ntf_t
#define hash_get_mem(h, key)
Definition: hash.h:269
unformat_function_t unformat_memory_size
Definition: format.h:288
struct clib_bihash_value offset
template key/value backing page structure
static struct option options[]
Definition: main.c:52
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:91
static vlib_thread_main_t * vlib_get_thread_main()
Definition: global_funcs.h:56
vl_api_dhcp_client_state_t state
Definition: dhcp.api:201
f64 now
static vlib_node_t * vlib_get_node(vlib_main_t *vm, u32 i)
Get vlib node by index.
Definition: node_funcs.h:86
enum session_error_ session_error_t
#define clib_strcmp(s1, s2)
Definition: string.h:823
u64 cache_evictions
Number of cache evictions.
Definition: http_static.h:163
int(* session_accept_callback)(session_t *new_session)
Notify server of newly accepted session.
clib_spinlock_t tw_lock
Definition: http_static.h:189
static size_t strnlen_s_inline(const char *s, size_t maxsize)
Definition: string.h:787
static int http_static_server_add_segment_callback(u32 client_index, u64 segment_handle)
In-memory file data cache entry.
Definition: http_static.h:116
void vlib_start_process(vlib_main_t *vm, uword process_index)
Definition: main.c:1416
u8 * www_root
root path to be served
Definition: http_static.h:170
u64 cache_limit
Max cache size in bytes.
Definition: http_static.h:161
http_static_server_main_t http_static_server_main
Definition: static_server.c:34
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:978
static void http_static_server_sessions_reader_lock(void)
Acquire reader lock on the sessions pools.
Definition: static_server.c:70
static const u32 test_srv_crt_rsa_len
Definition: tls_test.h:47
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:163
static void http_static_server_session_timer_start(http_session_t *hs)
Start a session cleanup timer.