FD.io VPP  v20.09-64-g4f7b92f0a
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];
142  hs->thread_index = 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 *
151 http_static_server_session_get (u32 thread_index, u32 hs_index)
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;
587  clib_error_t *error;
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  clib_warning ("called...");
1099  return -1;
1100 }
1101 
1102 static void
1104 {
1105  http_session_t *hs;
1106 
1107  if (ntf == SESSION_CLEANUP_TRANSPORT)
1108  return;
1109 
1111 
1113  if (!hs)
1114  goto done;
1115 
1118  hs->vpp_session_index);
1119  vec_free (hs->rx_buf);
1121 
1122 done:
1124 }
1125 
1126 /** \brief Session-layer virtual function table
1127  */
1128 static session_cb_vft_t http_static_server_session_cb_vft = {
1130  .session_disconnect_callback =
1132  .session_connected_callback = http_static_server_session_connected_callback,
1133  .add_segment_callback = http_static_server_add_segment_callback,
1134  .builtin_app_rx_callback = http_static_server_rx_callback,
1135  .builtin_app_tx_callback = http_static_server_tx_callback,
1136  .session_reset_callback = http_static_server_session_reset_callback,
1137  .session_cleanup_callback = http_static_session_cleanup,
1138 };
1139 
1140 static int
1142 {
1143  vnet_app_add_tls_cert_args_t _a_cert, *a_cert = &_a_cert;
1144  vnet_app_add_tls_key_args_t _a_key, *a_key = &_a_key;
1147  vnet_app_attach_args_t _a, *a = &_a;
1148  u32 segment_size = 128 << 20;
1149 
1150  clib_memset (a, 0, sizeof (*a));
1151  clib_memset (options, 0, sizeof (options));
1152 
1153  if (hsm->private_segment_size)
1154  segment_size = hsm->private_segment_size;
1155 
1156  a->api_client_index = ~0;
1157  a->name = format (0, "test_http_static_server");
1158  a->session_cb_vft = &http_static_server_session_cb_vft;
1159  a->options = options;
1160  a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
1161  a->options[APP_OPTIONS_RX_FIFO_SIZE] =
1162  hsm->fifo_size ? hsm->fifo_size : 8 << 10;
1163  a->options[APP_OPTIONS_TX_FIFO_SIZE] =
1164  hsm->fifo_size ? hsm->fifo_size : 32 << 10;
1165  a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
1168 
1169  if (vnet_application_attach (a))
1170  {
1171  vec_free (a->name);
1172  clib_warning ("failed to attach server");
1173  return -1;
1174  }
1175  vec_free (a->name);
1176  hsm->app_index = a->app_index;
1177 
1178  clib_memset (a_cert, 0, sizeof (*a_cert));
1179  a_cert->app_index = a->app_index;
1180  vec_validate (a_cert->cert, test_srv_crt_rsa_len);
1182  vnet_app_add_tls_cert (a_cert);
1183 
1184  clib_memset (a_key, 0, sizeof (*a_key));
1185  a_key->app_index = a->app_index;
1186  vec_validate (a_key->key, test_srv_key_rsa_len);
1188  vnet_app_add_tls_key (a_key);
1189 
1190  return 0;
1191 }
1192 
1193 static int
1195 {
1197  vnet_listen_args_t _a, *a = &_a;
1198  clib_memset (a, 0, sizeof (*a));
1199  a->app_index = hsm->app_index;
1200  a->uri = "tcp://0.0.0.0/80";
1201  if (hsm->uri)
1202  a->uri = (char *) hsm->uri;
1203  return vnet_bind_uri (a);
1204 }
1205 
1206 static void
1208 {
1210  http_session_t *hs;
1211  uword hs_handle;
1212  hs_handle = pointer_to_uword (hs_handlep);
1213  hs =
1214  http_static_server_session_get (hs_handle >> 24, hs_handle & 0x00FFFFFF);
1215 
1216  if (hsm->debug_level > 1)
1217  clib_warning ("terminate thread %d index %d hs %llx",
1218  hs_handle >> 24, hs_handle & 0x00FFFFFF, hs);
1219  if (!hs)
1220  return;
1221  hs->timer_handle = ~0;
1223 }
1224 
1225 /** \brief Expired session timer-wheel callback
1226  */
1227 static void
1229 {
1230  u32 hs_handle;
1231  int i;
1232 
1233  for (i = 0; i < vec_len (expired_timers); i++)
1234  {
1235  /* Get session handle. The first bit is the timer id */
1236  hs_handle = expired_timers[i] & 0x7FFFFFFF;
1237  session_send_rpc_evt_to_thread (hs_handle >> 24,
1239  uword_to_pointer (hs_handle, void *));
1240  }
1241 }
1242 
1243 /** \brief Timer-wheel expiration process
1244  */
1245 static uword
1247  vlib_frame_t * f)
1248 {
1250  f64 now, timeout = 1.0;
1251  uword *event_data = 0;
1252  uword __clib_unused event_type;
1253 
1254  while (1)
1255  {
1257  now = vlib_time_now (vm);
1258  event_type = vlib_process_get_events (vm, (uword **) & event_data);
1259 
1260  /* expire timers */
1261  clib_spinlock_lock (&http_static_server_main.tw_lock);
1262  tw_timer_expire_timers_2t_1w_2048sl (&hsm->tw, now);
1263  clib_spinlock_unlock (&http_static_server_main.tw_lock);
1264 
1265  vec_reset_length (event_data);
1266  }
1267  return 0;
1268 }
1269 
1270 /* *INDENT-OFF* */
1271 VLIB_REGISTER_NODE (http_static_server_process_node) =
1272 {
1273  .function = http_static_server_process,
1274  .type = VLIB_NODE_TYPE_PROCESS,
1275  .name = "static-http-server-process",
1276  .state = VLIB_NODE_STATE_DISABLED,
1277 };
1278 /* *INDENT-ON* */
1279 
1280 static int
1282 {
1285  u32 num_threads;
1286  vlib_node_t *n;
1287 
1288  num_threads = 1 /* main thread */ + vtm->n_threads;
1289  vec_validate (hsm->vpp_queue, num_threads - 1);
1290  vec_validate (hsm->sessions, num_threads - 1);
1291  vec_validate (hsm->session_to_http_session, num_threads - 1);
1292 
1294  clib_spinlock_init (&hsm->tw_lock);
1295 
1297  {
1298  clib_warning ("failed to attach server");
1299  return -1;
1300  }
1302  {
1303  clib_warning ("failed to start listening");
1304  return -1;
1305  }
1306 
1307  /* Init path-to-cache hash table */
1308  BV (clib_bihash_init) (&hsm->name_to_data, "http cache", 128, 32 << 20);
1309 
1310  hsm->get_url_handlers = hash_create_string (0, sizeof (uword));
1311  hsm->post_url_handlers = hash_create_string (0, sizeof (uword));
1312 
1313  /* Init timer wheel and process */
1314  tw_timer_wheel_init_2t_1w_2048sl (&hsm->tw, http_expired_timers_dispatch,
1315  1.0 /* timer interval */ , ~0);
1316  vlib_node_set_state (vm, http_static_server_process_node.index,
1317  VLIB_NODE_STATE_POLLING);
1318  n = vlib_get_node (vm, http_static_server_process_node.index);
1320 
1321  return 0;
1322 }
1323 
1324 /** \brief API helper function for vl_api_http_static_enable_t messages
1325  */
1326 int
1327 http_static_server_enable_api (u32 fifo_size, u32 cache_limit,
1328  u32 prealloc_fifos,
1329  u32 private_segment_size,
1330  u8 * www_root, u8 * uri)
1331 {
1333  int rv;
1334 
1335  hsm->fifo_size = fifo_size;
1336  hsm->cache_limit = cache_limit;
1337  hsm->prealloc_fifos = prealloc_fifos;
1338  hsm->private_segment_size = private_segment_size;
1339  hsm->www_root = format (0, "%s%c", www_root, 0);
1340  hsm->uri = format (0, "%s%c", uri, 0);
1341 
1342  if (vec_len (hsm->www_root) < 2)
1343  return VNET_API_ERROR_INVALID_VALUE;
1344 
1345  if (hsm->my_client_index != ~0)
1346  return VNET_API_ERROR_APP_ALREADY_ATTACHED;
1347 
1348  vnet_session_enable_disable (hsm->vlib_main, 1 /* turn on TCP, etc. */ );
1349 
1351  switch (rv)
1352  {
1353  case 0:
1354  break;
1355  default:
1356  vec_free (hsm->www_root);
1357  vec_free (hsm->uri);
1358  return VNET_API_ERROR_INIT_FAILED;
1359  }
1360  return 0;
1361 }
1362 
1363 static clib_error_t *
1365  unformat_input_t * input,
1366  vlib_cli_command_t * cmd)
1367 {
1369  unformat_input_t _line_input, *line_input = &_line_input;
1370  u64 seg_size;
1371  u8 *www_root = 0;
1372  int rv;
1373 
1374  hsm->prealloc_fifos = 0;
1375  hsm->private_segment_size = 0;
1376  hsm->fifo_size = 0;
1377  /* 10mb cache limit, before LRU occurs */
1378  hsm->cache_limit = 10 << 20;
1379 
1380  /* Get a line of input. */
1381  if (!unformat_user (input, unformat_line_input, line_input))
1382  goto no_wwwroot;
1383 
1384  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1385  {
1386  if (unformat (line_input, "www-root %s", &www_root))
1387  ;
1388  else
1389  if (unformat (line_input, "prealloc-fifos %d", &hsm->prealloc_fifos))
1390  ;
1391  else if (unformat (line_input, "private-segment-size %U",
1392  unformat_memory_size, &seg_size))
1393  {
1394  if (seg_size >= 0x100000000ULL)
1395  {
1396  vlib_cli_output (vm, "private segment size %llu, too large",
1397  seg_size);
1398  return 0;
1399  }
1400  hsm->private_segment_size = seg_size;
1401  }
1402  else if (unformat (line_input, "fifo-size %d", &hsm->fifo_size))
1403  hsm->fifo_size <<= 10;
1404  else if (unformat (line_input, "cache-size %U", unformat_memory_size,
1405  &hsm->cache_limit))
1406  {
1407  if (hsm->cache_limit < (128 << 10))
1408  {
1409  return clib_error_return (0,
1410  "cache-size must be at least 128kb");
1411  }
1412  }
1413 
1414  else if (unformat (line_input, "uri %s", &hsm->uri))
1415  ;
1416  else if (unformat (line_input, "debug %d", &hsm->debug_level))
1417  ;
1418  else if (unformat (line_input, "debug"))
1419  hsm->debug_level = 1;
1420  else
1421  return clib_error_return (0, "unknown input `%U'",
1422  format_unformat_error, line_input);
1423  }
1424  unformat_free (line_input);
1425 
1426  if (www_root == 0)
1427  {
1428  no_wwwroot:
1429  return clib_error_return (0, "Must specify www-root <path>");
1430  }
1431 
1432  if (hsm->my_client_index != (u32) ~ 0)
1433  {
1434  vec_free (www_root);
1435  return clib_error_return (0, "http server already running...");
1436  }
1437 
1438  hsm->www_root = www_root;
1439 
1440  vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
1441 
1442  rv = http_static_server_create (vm);
1443  switch (rv)
1444  {
1445  case 0:
1446  break;
1447  default:
1448  vec_free (hsm->www_root);
1449  return clib_error_return (0, "server_create returned %d", rv);
1450  }
1451  return 0;
1452 }
1453 
1454 /*?
1455  * Enable the static http server
1456  *
1457  * @cliexpar
1458  * This command enables the static http server. Only the www-root
1459  * parameter is required
1460  * @clistart
1461  * http static server www-root /tmp/www uri tcp://0.0.0.0/80 cache-size 2m
1462  * @cliend
1463  * @cliexcmd{http static server www-root <path> [prealloc-fios <nn>]
1464  * [private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]}
1465 ?*/
1466 /* *INDENT-OFF* */
1467 VLIB_CLI_COMMAND (http_static_server_create_command, static) =
1468 {
1469  .path = "http static server",
1470  .short_help = "http static server www-root <path> [prealloc-fifos <nn>]\n"
1471  "[private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]\n"
1472  "[debug [nn]]\n",
1474 };
1475 /* *INDENT-ON* */
1476 
1477 /** \brief format a file cache entry
1478  */
1479 u8 *
1480 format_hsm_cache_entry (u8 * s, va_list * args)
1481 {
1482  file_data_cache_t *ep = va_arg (*args, file_data_cache_t *);
1483  f64 now = va_arg (*args, f64);
1484 
1485  /* Header */
1486  if (ep == 0)
1487  {
1488  s = format (s, "%40s%12s%20s", "File", "Size", "Age");
1489  return s;
1490  }
1491  s = format (s, "%40s%12lld%20.2f", ep->filename, vec_len (ep->data),
1492  now - ep->last_used);
1493  return s;
1494 }
1495 
1496 u8 *
1497 format_http_session_state (u8 * s, va_list * args)
1498 {
1500  char *state_string = "bogus!";
1501 
1502  switch (state)
1503  {
1504  case HTTP_STATE_CLOSED:
1505  state_string = "closed";
1506  break;
1508  state_string = "established";
1509  break;
1510  case HTTP_STATE_OK_SENT:
1511  state_string = "ok sent";
1512  break;
1514  state_string = "send more data";
1515  break;
1516  default:
1517  break;
1518  }
1519 
1520  return format (s, "%s", state_string);
1521 }
1522 
1523 u8 *
1524 format_http_session (u8 * s, va_list * args)
1525 {
1526  http_session_t *hs = va_arg (*args, http_session_t *);
1527  int verbose = va_arg (*args, int);
1528 
1529  s = format (s, "[%d]: state %U", hs->session_index,
1530  format_http_session_state, hs->session_state);
1531  if (verbose > 0)
1532  {
1533  s = format (s, "\n path %s, data length %u, data_offset %u",
1534  hs->path ? hs->path : (u8 *) "[none]",
1535  vec_len (hs->data), hs->data_offset);
1536  }
1537  return s;
1538 }
1539 
1540 static clib_error_t *
1542  unformat_input_t * input,
1543  vlib_cli_command_t * cmd)
1544 {
1546  file_data_cache_t *ep, **entries = 0;
1547  int verbose = 0;
1548  int show_cache = 0;
1549  int show_sessions = 0;
1550  u32 index;
1551  f64 now;
1552 
1553  if (hsm->www_root == 0)
1554  return clib_error_return (0, "Static server disabled");
1555 
1556  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1557  {
1558  if (unformat (input, "verbose %d", &verbose))
1559  ;
1560  else if (unformat (input, "verbose"))
1561  verbose = 1;
1562  else if (unformat (input, "cache"))
1563  show_cache = 1;
1564  else if (unformat (input, "sessions"))
1565  show_sessions = 1;
1566  else
1567  break;
1568  }
1569 
1570  if ((show_cache + show_sessions) == 0)
1571  return clib_error_return (0, "specify one or more of cache, sessions");
1572 
1573  if (show_cache)
1574  {
1575  if (verbose == 0)
1576  {
1578  (vm, "www_root %s, cache size %lld bytes, limit %lld bytes, "
1579  "evictions %lld",
1580  hsm->www_root, hsm->cache_size, hsm->cache_limit,
1581  hsm->cache_evictions);
1582  return 0;
1583  }
1584 
1585  now = vlib_time_now (vm);
1586 
1587  vlib_cli_output (vm, "%U", format_hsm_cache_entry, 0 /* header */ ,
1588  now);
1589 
1590  for (index = hsm->first_index; index != ~0;)
1591  {
1592  ep = pool_elt_at_index (hsm->cache_pool, index);
1593  index = ep->next_index;
1594  vlib_cli_output (vm, "%U", format_hsm_cache_entry, ep, now);
1595  }
1596 
1597  vlib_cli_output (vm, "%40s%12lld", "Total Size", hsm->cache_size);
1598 
1599  vec_free (entries);
1600  }
1601 
1602  if (show_sessions)
1603  {
1604  u32 *session_indices = 0;
1605  http_session_t *hs;
1606  int i, j;
1607 
1609 
1610  for (i = 0; i < vec_len (hsm->sessions); i++)
1611  {
1612  /* *INDENT-OFF* */
1613  pool_foreach (hs, hsm->sessions[i],
1614  ({
1615  vec_add1 (session_indices, hs - hsm->sessions[i]);
1616  }));
1617  /* *INDENT-ON* */
1618 
1619  for (j = 0; j < vec_len (session_indices); j++)
1620  {
1623  (hsm->sessions[i], session_indices[j]),
1624  verbose);
1625  }
1626  vec_reset_length (session_indices);
1627  }
1629  vec_free (session_indices);
1630  }
1631  return 0;
1632 }
1633 
1634 /*?
1635  * Display static http server cache statistics
1636  *
1637  * @cliexpar
1638  * This command shows the contents of the static http server cache
1639  * @clistart
1640  * show http static server
1641  * @cliend
1642  * @cliexcmd{show http static server sessions cache [verbose [nn]]}
1643 ?*/
1644 /* *INDENT-OFF* */
1645 VLIB_CLI_COMMAND (http_show_static_server_command, static) =
1646 {
1647  .path = "show http static server",
1648  .short_help = "show http static server sessions cache [verbose [<nn>]]",
1650 };
1651 /* *INDENT-ON* */
1652 
1653 static clib_error_t *
1655  unformat_input_t * input,
1656  vlib_cli_command_t * cmd)
1657 {
1659  file_data_cache_t *dp;
1660  u32 free_index;
1661  u32 busy_items = 0;
1662  BVT (clib_bihash_kv) kv;
1663 
1664  if (hsm->www_root == 0)
1665  return clib_error_return (0, "Static server disabled");
1666 
1668 
1669  /* Walk the LRU list to find active entries */
1670  free_index = hsm->last_index;
1671  while (free_index != ~0)
1672  {
1673  dp = pool_elt_at_index (hsm->cache_pool, free_index);
1674  free_index = dp->prev_index;
1675  /* Which could be in use... */
1676  if (dp->inuse)
1677  {
1678  busy_items++;
1679  free_index = dp->next_index;
1680  continue;
1681  }
1682  kv.key = (u64) (dp->filename);
1683  kv.value = ~0ULL;
1684  if (BV (clib_bihash_add_del) (&hsm->name_to_data, &kv,
1685  0 /* is_add */ ) < 0)
1686  {
1687  clib_warning ("BUG: cache clear delete '%s' FAILED!", dp->filename);
1688  }
1689 
1690  lru_remove (hsm, dp);
1691  hsm->cache_size -= vec_len (dp->data);
1692  hsm->cache_evictions++;
1693  vec_free (dp->filename);
1694  vec_free (dp->data);
1695  if (hsm->debug_level > 1)
1696  clib_warning ("pool put index %d", dp - hsm->cache_pool);
1697  pool_put (hsm->cache_pool, dp);
1698  free_index = hsm->last_index;
1699  }
1701  if (busy_items > 0)
1702  vlib_cli_output (vm, "Note: %d busy items still in cache...", busy_items);
1703  else
1704  vlib_cli_output (vm, "Cache cleared...");
1705  return 0;
1706 }
1707 
1708 /*?
1709  * Clear the static http server cache, to force the server to
1710  * reload content from backing files
1711  *
1712  * @cliexpar
1713  * This command clear the static http server cache
1714  * @clistart
1715  * clear http static cache
1716  * @cliend
1717  * @cliexcmd{clear http static cache}
1718 ?*/
1719 /* *INDENT-OFF* */
1720 VLIB_CLI_COMMAND (clear_http_static_cache_command, static) =
1721 {
1722  .path = "clear http static cache",
1723  .short_help = "clear http static cache",
1725 };
1726 /* *INDENT-ON* */
1727 
1728 static clib_error_t *
1730 {
1732 
1733  hsm->my_client_index = ~0;
1734  hsm->vlib_main = vm;
1735  hsm->first_index = hsm->last_index = ~0;
1736 
1737  clib_timebase_init (&hsm->timebase, 0 /* GMT */ ,
1739  &vm->clib_time /* share the system clock */ );
1740 
1741  return 0;
1742 }
1743 
1745 
1746 /*
1747  * fd.io coding-style-patch-verification: ON
1748  *
1749  * Local Variables:
1750  * eval: (c-set-style "gnu")
1751  * End:
1752  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:509
static void clib_rwlock_reader_lock(clib_rwlock_t *p)
Definition: lock.h:167
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.
static_always_inline void clib_spinlock_unlock(clib_spinlock_t *p)
Definition: lock.h:119
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:80
session_type_t session_type
Type built from transport and network protocol types.
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:751
a
Definition: bitmap.h:538
static void clib_rwlock_writer_lock(clib_rwlock_t *p)
Definition: lock.h:190
svm_fifo_t * tx_fifo
Static http server definitions.
static const char * http_response
Definition: http_server.c:236
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:633
u32 timer_handle
Timeout timer handle.
Definition: http_server.c:50
#define clib_memcpy_fast(a, b, c)
Definition: string.h:81
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:333
u32 fifo_size
Size of the allocated rx, tx fifos, roughly 8K or so.
Definition: http_static.h:196
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
u32 private_segment_size
Private segment size, usually 0.
Definition: http_static.h:194
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:110
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:189
vlib_main_t * vm
Definition: in2out_ed.c:1582
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
clib_time_t clib_time
Definition: main.h:124
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:291
vl_api_fib_path_t path
Definition: mfib_types.api:34
struct _vnet_application_add_tls_cert_args_t vnet_app_add_tls_cert_args_t
void http_static_server_register_builtin_handler(void *fp, char *url, int request_type)
Register a builtin GET or POST handler.
u8 * path
Fully-resolved file path.
Definition: http_static.h:100
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:252
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:192
static void http_static_server_detach_cache_entry(http_session_t *hs)
Detach cache entry from session.
clib_error_t * vnet_app_add_tls_cert(vnet_app_add_tls_cert_args_t *a)
Definition: application.c:1325
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:89
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.
void session_get_endpoint(session_t *s, transport_endpoint_t *tep, u8 is_lcl)
Definition: session.c:1631
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
static int svm_fifo_is_empty(svm_fifo_t *f)
Check if fifo is empty.
Definition: svm_fifo.h:496
http_session_state_t
Definition: http_server.c:34
static_always_inline uword os_get_numa_index(void)
Definition: os.h:75
#define pool_foreach(VAR, POOL, BODY)
Iterate through pool.
Definition: pool.h:513
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:173
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:579
struct _vnet_disconnect_args_t vnet_disconnect_args_t
static u32 svm_fifo_max_dequeue(svm_fifo_t *f)
Fifo max bytes to dequeue.
Definition: svm_fifo.h:433
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
clib_error_t * clib_file_contents(char *file, u8 **result)
Definition: unix-misc.c:112
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
unsigned int u32
Definition: types.h:88
static int session_rx_request(http_session_t *hs)
Retrieve data from the application layer.
int session_send_io_evt_to_thread(svm_fifo_t *f, session_evt_type_t evt_type)
Definition: session.c:79
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)
struct _vnet_app_attach_args_t vnet_app_attach_args_t
unformat_function_t unformat_line_input
Definition: format.h:283
static void clib_spinlock_init(clib_spinlock_t *p)
Definition: lock.h:63
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
u8 * data
Contents of the file, as a u8 * vector.
Definition: http_static.h:121
clib_error_t * vnet_app_add_tls_key(vnet_app_add_tls_key_args_t *a)
Definition: application.c:1335
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
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:534
static void clib_rwlock_init(clib_rwlock_t *p)
Definition: lock.h:150
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:182
vl_api_ip_proto_t proto
Definition: acl_types.api:50
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:302
#define vec_dup(V)
Return copy of vector (no header, no alignment)
Definition: vec.h:429
int svm_fifo_enqueue(svm_fifo_t *f, u32 len, const u8 *src)
Enqueue data to fifo.
Definition: svm_fifo.c:838
#define PREDICT_FALSE(x)
Definition: clib.h:120
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:714
static void http_expired_timers_dispatch(u32 *expired_timers)
Expired session timer-wheel callback.
Application session.
Definition: http_server.c:40
#define pool_get_aligned_zero_numa(P, E, A, Z, S)
Definition: pool.h:233
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:1768
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:827
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)
Definition: l2_fib.c:972
static u8 svm_fifo_set_event(svm_fifo_t *f)
Set fifo event flag.
Definition: svm_fifo.h:701
static void clib_rwlock_writer_unlock(clib_rwlock_t *p)
Definition: lock.h:204
f64 last_used
Last time the cache entry was used.
Definition: http_static.h:123
vl_api_address_t endpoint
Definition: wireguard.api:101
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:169
#define UNFORMAT_END_OF_INPUT
Definition: format.h:145
u32 runtime_index
Definition: node.h:282
sll srl srl sll sra u16x4 i
Definition: vector_sse42.h:317
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:380
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:299
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:158
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:696
#define vec_delete(V, N, M)
Delete N elements starting at element M.
Definition: vec.h:854
static void http_static_server_session_disconnect_callback(session_t *s)
Session disconnect callback.
static int http_static_server_create(vlib_main_t *vm)
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
static int http_static_server_listen()
struct _vnet_application_add_tls_key_args_t vnet_app_add_tls_key_args_t
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
static void svm_fifo_add_want_deq_ntf(svm_fifo_t *f, u8 ntf_type)
Set specific want notification flag.
Definition: svm_fifo.h:728
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:174
Session has sent an HTML response.
Definition: http_static.h:62
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:23
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:185
template key/value backing page structure
Definition: bihash_doc.h:44
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.
int vnet_bind_uri(vnet_listen_args_t *a)
static void unformat_free(unformat_input_t *i)
Definition: format.h:163
int inuse
Reference count, so we don&#39;t recycle while referenced.
Definition: http_static.h:128
u32 index
Definition: flow_types.api:221
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:1087
vhost_user_req_t request
Definition: vhost_user.h:104
u8 * uri
The bind URI, defaults to tcp://0.0.0.0/80.
Definition: http_static.h:198
session_cleanup_ntf_t
#define hash_get_mem(h, key)
Definition: hash.h:269
unformat_function_t unformat_memory_size
Definition: format.h:296
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:32
vl_api_dhcp_client_state_t state
Definition: dhcp.api:201
static vlib_node_t * vlib_get_node(vlib_main_t *vm, u32 i)
Get vlib node by index.
Definition: node_funcs.h:85
enum session_error_ session_error_t
#define clib_strcmp(s1, s2)
Definition: string.h:809
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:186
static size_t strnlen_s_inline(const char *s, size_t maxsize)
Definition: string.h:773
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:1586
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:171
static void http_static_server_session_timer_start(http_session_t *hs)
Start a session cleanup timer.