FD.io VPP  v20.05-21-gb1500e9ff
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 clean up a session
250  */
251 
252 static void
254 {
255  if (!hs)
256  return;
257 
259 
261  hs->vpp_session_index);
262  vec_free (hs->rx_buf);
264 }
265 
266 /** \brief Disconnect a session
267  */
268 
269 static void
271 {
272  vnet_disconnect_args_t _a = { 0 }, *a = &_a;
273  a->handle = hs->vpp_session_handle;
274  a->app_index = http_static_server_main.app_index;
276 }
277 
278 /* *INDENT-OFF* */
279 /** \brief http error boilerplate
280  */
281 static const char *http_error_template =
282  "HTTP/1.1 %s\r\n"
283  "Date: %U GMT\r\n"
284  "Content-Type: text/html\r\n"
285  "Connection: close\r\n"
286  "Pragma: no-cache\r\n"
287  "Content-Length: 0\r\n\r\n";
288 
289 /** \brief http response boilerplate
290  */
291 static const char *http_response_template =
292  "Date: %U GMT\r\n"
293  "Expires: %U GMT\r\n"
294  "Server: VPP Static\r\n"
295  "Content-Type: %s\r\n"
296  "Content-Length: %d\r\n\r\n";
297 
298 /* *INDENT-ON* */
299 
300 /** \brief send http data
301  @param hs - http session
302  @param data - the data vector to transmit
303  @param length - length of data
304  @param offset - transmit offset for this operation
305  @return offset for next transmit operation, may be unchanged w/ full fifo
306 */
307 
308 static u32
310 {
311  u32 bytes_to_send;
313 
314  bytes_to_send = length - offset;
315 
316  while (bytes_to_send > 0)
317  {
318  int actual_transfer;
319 
320  actual_transfer = svm_fifo_enqueue
321  (hs->tx_fifo, bytes_to_send, data + offset);
322 
323  /* Made any progress? */
324  if (actual_transfer <= 0)
325  {
326  if (hsm->debug_level > 0 && bytes_to_send > 0)
327  clib_warning ("WARNING: still %d bytes to send", bytes_to_send);
328  return offset;
329  }
330  else
331  {
332  offset += actual_transfer;
333  bytes_to_send -= actual_transfer;
334 
335  if (hsm->debug_level && bytes_to_send > 0)
336  clib_warning ("WARNING: still %d bytes to send", bytes_to_send);
337 
338  if (svm_fifo_set_event (hs->tx_fifo))
339  session_send_io_evt_to_thread (hs->tx_fifo,
341  return offset;
342  }
343  }
344  /* NOTREACHED */
345  return ~0;
346 }
347 
348 /** \brief Send an http error string
349  @param hs - the http session
350  @param str - the error string, e.g. "404 Not Found"
351 */
352 static void
353 send_error (http_session_t * hs, char *str)
354 {
356  u8 *data;
357  f64 now;
358 
359  now = clib_timebase_now (&hsm->timebase);
360  data = format (0, http_error_template, str, format_clib_timebase_time, now);
361  static_send_data (hs, data, vec_len (data), 0);
362  vec_free (data);
363 }
364 
365 /** \brief Retrieve data from the application layer
366  */
367 static int
369 {
370  u32 max_dequeue, cursize;
371  int n_read;
372 
373  cursize = vec_len (hs->rx_buf);
374  max_dequeue = svm_fifo_max_dequeue (hs->rx_fifo);
375  if (PREDICT_FALSE (max_dequeue == 0))
376  return -1;
377 
378  vec_validate (hs->rx_buf, cursize + max_dequeue - 1);
379  n_read = app_recv_stream_raw (hs->rx_fifo, hs->rx_buf + cursize,
380  max_dequeue, 0, 0 /* peek */ );
381  ASSERT (n_read == max_dequeue);
382  if (svm_fifo_is_empty (hs->rx_fifo))
383  svm_fifo_unset_event (hs->rx_fifo);
384 
385  _vec_len (hs->rx_buf) = cursize + n_read;
386  return 0;
387 }
388 
389 /** \brief Sanity-check the forward and reverse LRU lists
390  */
391 static inline void
393 {
394 #if CLIB_DEBUG > 0
395  f64 last_timestamp;
396  u32 index;
397  int i;
398  file_data_cache_t *ep;
399 
400  last_timestamp = 1e70;
401  for (i = 1, index = hsm->first_index; index != ~0;)
402  {
403  ep = pool_elt_at_index (hsm->cache_pool, index);
404  index = ep->next_index;
405  /* Timestamps should be smaller (older) as we walk the fwd list */
406  if (ep->last_used > last_timestamp)
407  {
408  clib_warning ("%d[%d]: last used %.6f, last_timestamp %.6f",
409  ep - hsm->cache_pool, i,
410  ep->last_used, last_timestamp);
411  }
412  last_timestamp = ep->last_used;
413  i++;
414  }
415 
416  last_timestamp = 0.0;
417  for (i = 1, index = hsm->last_index; index != ~0;)
418  {
419  ep = pool_elt_at_index (hsm->cache_pool, index);
420  index = ep->prev_index;
421  /* Timestamps should be larger (newer) as we walk the rev list */
422  if (ep->last_used < last_timestamp)
423  {
424  clib_warning ("%d[%d]: last used %.6f, last_timestamp %.6f",
425  ep - hsm->cache_pool, i,
426  ep->last_used, last_timestamp);
427  }
428  last_timestamp = ep->last_used;
429  i++;
430  }
431 #endif
432 }
433 
434 /** \brief Remove a data cache entry from the LRU lists
435  */
436 static inline void
438 {
439  file_data_cache_t *next_ep, *prev_ep;
440  u32 ep_index;
441 
442  lru_validate (hsm);
443 
444  ep_index = ep - hsm->cache_pool;
445 
446  /* Deal with list heads */
447  if (ep_index == hsm->first_index)
448  hsm->first_index = ep->next_index;
449  if (ep_index == hsm->last_index)
450  hsm->last_index = ep->prev_index;
451 
452  /* Fix next->prev */
453  if (ep->next_index != ~0)
454  {
455  next_ep = pool_elt_at_index (hsm->cache_pool, ep->next_index);
456  next_ep->prev_index = ep->prev_index;
457  }
458  /* Fix prev->next */
459  if (ep->prev_index != ~0)
460  {
461  prev_ep = pool_elt_at_index (hsm->cache_pool, ep->prev_index);
462  prev_ep->next_index = ep->next_index;
463  }
464  lru_validate (hsm);
465 }
466 
467 /** \brief Add an entry to the LRU lists, tag w/ supplied timestamp
468  */
469 
470 static inline void
472 {
473  file_data_cache_t *next_ep;
474  u32 ep_index;
475 
476  lru_validate (hsm);
477 
478  ep_index = ep - hsm->cache_pool;
479 
480  /*
481  * Re-add at the head of the forward LRU list,
482  * tail of the reverse LRU list
483  */
484  if (hsm->first_index != ~0)
485  {
486  next_ep = pool_elt_at_index (hsm->cache_pool, hsm->first_index);
487  next_ep->prev_index = ep_index;
488  }
489 
490  ep->prev_index = ~0;
491 
492  /* ep now the new head of the LRU forward list */
493  ep->next_index = hsm->first_index;
494  hsm->first_index = ep_index;
495 
496  /* single session case: also the tail of the reverse LRU list */
497  if (hsm->last_index == ~0)
498  hsm->last_index = ep_index;
499  ep->last_used = now;
500 
501  lru_validate (hsm);
502 }
503 
504 /** \brief Remove and re-add a cache entry from/to the LRU lists
505  */
506 
507 static inline void
509 {
510  lru_remove (hsm, ep);
511  lru_add (hsm, ep, now);
512 }
513 
514 /** \brief Session-layer (main) data rx callback.
515  Parse the http request, and reply to it.
516  Future extensions might include POST processing, active content, etc.
517 */
518 
519 /* svm_fifo_add_want_deq_ntf (tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL)
520 get shoulder-tap when transport dequeues something, set in
521 xmit routine. */
522 
523 /** \brief closed state - should never really get here
524  */
525 static int
528 {
529  clib_warning ("WARNING: http session %d, called from %U",
530  hs->session_index, format_state_machine_called_from, cf);
531  return -1;
532 }
533 
534 static void
536 {
539 }
540 
541 /** \brief Register a builtin GET or POST handler
542  */
544  (void *fp, char *url, int request_type)
545 {
547  uword *p, *builtin_table;
548 
549  builtin_table = (request_type == HTTP_BUILTIN_METHOD_GET)
550  ? hsm->get_url_handlers : hsm->post_url_handlers;
551 
552  p = hash_get_mem (builtin_table, url);
553 
554  if (p)
555  {
556  clib_warning ("WARNING: attempt to replace handler for %s '%s' ignored",
557  (request_type == HTTP_BUILTIN_METHOD_GET) ?
558  "GET" : "POST", url);
559  return;
560  }
561 
562  hash_set_mem (builtin_table, url, (uword) fp);
563 
564  /*
565  * Need to update the hash table pointer in http_static_server_main
566  * in case we just expanded it...
567  */
568  if (request_type == HTTP_BUILTIN_METHOD_GET)
569  hsm->get_url_handlers = builtin_table;
570  else
571  hsm->post_url_handlers = builtin_table;
572 }
573 
574 static int
575 v_find_index (u8 * vec, char *str)
576 {
577  int start_index;
578  u32 slen = (u32) strnlen_s_inline (str, 8);
579  u32 vlen = vec_len (vec);
580 
581  ASSERT (slen > 0);
582 
583  if (vlen <= slen)
584  return -1;
585 
586  for (start_index = 0; start_index < (vlen - slen); start_index++)
587  {
588  if (!memcmp (vec, str, slen))
589  return start_index;
590  }
591 
592  return -1;
593 }
594 
595 /** \brief established state - waiting for GET, POST, etc.
596  */
597 static int
600 {
602  u8 *request = 0;
603  u8 *path;
604  int i, rv;
605  struct stat _sb, *sb = &_sb;
606  clib_error_t *error;
607  u8 request_type = HTTP_BUILTIN_METHOD_GET;
608  u8 save_byte = 0;
609  uword *p, *builtin_table;
610 
611  /* Read data from the sessison layer */
612  rv = session_rx_request (hs);
613 
614  /* No data? Odd, but stay in this state and await further instructions */
615  if (rv)
616  return 0;
617 
618  /* Process the client request */
619  request = hs->rx_buf;
620  if (vec_len (request) < 8)
621  {
622  send_error (hs, "400 Bad Request");
623  close_session (hs);
624  return -1;
625  }
626 
627  if ((i = v_find_index (request, "GET ")) >= 0)
628  goto find_end;
629  else if ((i = v_find_index (request, "POST ")) >= 0)
630  {
631  request_type = HTTP_BUILTIN_METHOD_POST;
632  goto find_end;
633  }
634 
635  if (hsm->debug_level > 1)
636  clib_warning ("Unknown http method");
637 
638  send_error (hs, "405 Method Not Allowed");
639  close_session (hs);
640  return -1;
641 
642 find_end:
643 
644  /* Lose "GET " or "POST " */
645  vec_delete (request, i + 5 + request_type, 0);
646 
647  /* Temporarily drop in a NULL byte for lookup purposes */
648  for (i = 0; i < vec_len (request); i++)
649  {
650  if (request[i] == ' ' || request[i] == '?')
651  {
652  save_byte = request[i];
653  request[i] = 0;
654  break;
655  }
656  }
657 
658  /*
659  * Now we can construct the file to open
660  * Browsers are capable of sporadically including a leading '/'
661  */
662  if (request[0] == '/')
663  path = format (0, "%s%s%c", hsm->www_root, request, 0);
664  else
665  path = format (0, "%s/%s%c", hsm->www_root, request, 0);
666 
667  if (hsm->debug_level > 0)
668  clib_warning ("%s '%s'", (request_type) == HTTP_BUILTIN_METHOD_GET ?
669  "GET" : "POST", path);
670 
671  /* Look for built-in GET / POST handlers */
672  builtin_table = (request_type == HTTP_BUILTIN_METHOD_GET) ?
674 
675  p = hash_get_mem (builtin_table, request);
676 
677  if (save_byte != 0)
678  request[i] = save_byte;
679 
680  if (p)
681  {
682  int rv;
683  int (*fp) (http_builtin_method_type_t, u8 *, http_session_t *);
684  fp = (void *) p[0];
685  hs->path = path;
686  rv = (*fp) (request_type, request, hs);
687  if (rv)
688  {
689  clib_warning ("builtin handler %llx hit on %s '%s' but failed!",
690  p[0], (request_type == HTTP_BUILTIN_METHOD_GET) ?
691  "GET" : "POST", request);
692  send_error (hs, "404 Not Found");
693  close_session (hs);
694  return -1;
695  }
696  vec_reset_length (hs->rx_buf);
697  goto send_ok;
698  }
699  vec_reset_length (hs->rx_buf);
700  /* poison request, it's not valid anymore */
701  request = 0;
702  /* The static server itself doesn't do POSTs */
703  if (request_type == HTTP_BUILTIN_METHOD_POST)
704  {
705  send_error (hs, "404 Not Found");
706  close_session (hs);
707  return -1;
708  }
709 
710  /* Try to find the file. 2x special cases to find index.html */
711  if (stat ((char *) path, sb) < 0 /* cant even stat the file */
712  || sb->st_size < 20 /* file too small */
713  || (sb->st_mode & S_IFMT) != S_IFREG /* not a regular file */ )
714  {
715  u32 save_length = vec_len (path) - 1;
716  /* Try appending "index.html"... */
717  _vec_len (path) -= 1;
718  path = format (path, "index.html%c", 0);
719  if (stat ((char *) path, sb) < 0 /* cant even stat the file */
720  || sb->st_size < 20 /* file too small */
721  || (sb->st_mode & S_IFMT) != S_IFREG /* not a regular file */ )
722  {
723  _vec_len (path) = save_length;
724  path = format (path, "/index.html%c", 0);
725 
726  /* Send a redirect, otherwise the browser will confuse itself */
727  if (stat ((char *) path, sb) < 0 /* cant even stat the file */
728  || sb->st_size < 20 /* file too small */
729  || (sb->st_mode & S_IFMT) != S_IFREG /* not a regular file */ )
730  {
731  vec_free (path);
732  send_error (hs, "404 Not Found");
733  close_session (hs);
734  return -1;
735  }
736  else
737  {
738  transport_endpoint_t endpoint;
740  u16 local_port;
741  int print_port = 0;
742  u8 *port_str = 0;
743 
744  /*
745  * To make this bit work correctly, we need to know our local
746  * IP address, etc. and send it in the redirect...
747  */
748  u8 *redirect;
749 
750  vec_delete (path, vec_len (hsm->www_root) - 1, 0);
751 
752  session_get_endpoint (s, &endpoint, 1 /* is_local */ );
753 
754  local_port = clib_net_to_host_u16 (endpoint.port);
755 
757 
758  if ((proto == TRANSPORT_PROTO_TCP && local_port != 80)
759  || (proto == TRANSPORT_PROTO_TLS && local_port != 443))
760  {
761  print_port = 1;
762  port_str = format (0, ":%u", (u32) local_port);
763  }
764 
765  redirect = format (0, "HTTP/1.1 301 Moved Permanently\r\n"
766  "Location: http%s://%U%s%s\r\n\r\n",
767  proto == TRANSPORT_PROTO_TLS ? "s" : "",
768  format_ip46_address, &endpoint.ip,
769  endpoint.is_ip4,
770  print_port ? port_str : (u8 *) "", path);
771  if (hsm->debug_level > 0)
772  clib_warning ("redirect: %s", redirect);
773 
774  vec_free (port_str);
775 
776  static_send_data (hs, redirect, vec_len (redirect), 0);
777  hs->session_state = HTTP_STATE_CLOSED;
778  hs->path = 0;
779  vec_free (redirect);
780  vec_free (path);
781  close_session (hs);
782  return -1;
783  }
784  }
785  }
786 
787  /* find or read the file if we haven't done so yet. */
788  if (hs->data == 0)
789  {
790  BVT (clib_bihash_kv) kv;
791  file_data_cache_t *dp;
792 
793  hs->path = path;
794 
795  /* First, try the cache */
796  kv.key = (u64) hs->path;
797  if (BV (clib_bihash_search) (&hsm->name_to_data, &kv, &kv) == 0)
798  {
799  if (hsm->debug_level > 1)
800  clib_warning ("lookup '%s' returned %lld", kv.key, kv.value);
801 
802  /* found the data.. */
803  dp = pool_elt_at_index (hsm->cache_pool, kv.value);
804  hs->data = dp->data;
805  /* Update the cache entry, mark it in-use */
806  lru_update (hsm, dp, vlib_time_now (hsm->vlib_main));
807  hs->cache_pool_index = dp - hsm->cache_pool;
808  dp->inuse++;
809  if (hsm->debug_level > 1)
810  clib_warning ("index %d refcnt now %d", hs->cache_pool_index,
811  dp->inuse);
812  }
813  else
814  {
815  if (hsm->debug_level > 1)
816  clib_warning ("lookup '%s' failed", kv.key, kv.value);
817  /* Need to recycle one (or more cache) entries? */
818  if (hsm->cache_size > hsm->cache_limit)
819  {
820  int free_index = hsm->last_index;
821 
822  while (free_index != ~0)
823  {
824  /* pick the LRU */
825  dp = pool_elt_at_index (hsm->cache_pool, free_index);
826  free_index = dp->prev_index;
827  /* Which could be in use... */
828  if (dp->inuse)
829  {
830  if (hsm->debug_level > 1)
831  clib_warning ("index %d in use refcnt %d",
832  dp - hsm->cache_pool, dp->inuse);
833 
834  }
835  kv.key = (u64) (dp->filename);
836  kv.value = ~0ULL;
837  if (BV (clib_bihash_add_del) (&hsm->name_to_data, &kv,
838  0 /* is_add */ ) < 0)
839  {
840  clib_warning ("LRU delete '%s' FAILED!", dp->filename);
841  }
842  else if (hsm->debug_level > 1)
843  clib_warning ("LRU delete '%s' ok", dp->filename);
844 
845  lru_remove (hsm, dp);
846  hsm->cache_size -= vec_len (dp->data);
847  hsm->cache_evictions++;
848  vec_free (dp->filename);
849  vec_free (dp->data);
850  if (hsm->debug_level > 1)
851  clib_warning ("pool put index %d", dp - hsm->cache_pool);
852  pool_put (hsm->cache_pool, dp);
853  if (hsm->cache_size < hsm->cache_limit)
854  break;
855  }
856  }
857 
858  /* Read the file */
859  error = clib_file_contents ((char *) (hs->path), &hs->data);
860  if (error)
861  {
862  clib_warning ("Error reading '%s'", hs->path);
863  clib_error_report (error);
864  vec_free (hs->path);
865  close_session (hs);
866  return -1;
867  }
868  /* Create a cache entry for it */
869  pool_get (hsm->cache_pool, dp);
870  memset (dp, 0, sizeof (*dp));
871  dp->filename = vec_dup (hs->path);
872  dp->data = hs->data;
873  hs->cache_pool_index = dp - hsm->cache_pool;
874  dp->inuse++;
875  if (hsm->debug_level > 1)
876  clib_warning ("index %d refcnt now %d", hs->cache_pool_index,
877  dp->inuse);
878  lru_add (hsm, dp, vlib_time_now (hsm->vlib_main));
879  kv.key = (u64) vec_dup (hs->path);
880  kv.value = dp - hsm->cache_pool;
881  /* Add to the lookup table */
882  if (hsm->debug_level > 1)
883  clib_warning ("add '%s' value %lld", kv.key, kv.value);
884 
885  if (BV (clib_bihash_add_del) (&hsm->name_to_data, &kv,
886  1 /* is_add */ ) < 0)
887  {
888  clib_warning ("BUG: add failed!");
889  }
890  hsm->cache_size += vec_len (dp->data);
891  }
892  hs->data_offset = 0;
893  }
894  /* send 200 OK first */
895 send_ok:
896  static_send_data (hs, (u8 *) "HTTP/1.1 200 OK\r\n", 17, 0);
897  hs->session_state = HTTP_STATE_OK_SENT;
898  return 1;
899 }
900 
901 static int
904 {
905 
906  /* Start sending data */
907  hs->data_offset = static_send_data (hs, hs->data, vec_len (hs->data),
908  hs->data_offset);
909 
910  /* Did we finish? */
911  if (hs->data_offset < vec_len (hs->data))
912  {
913  /* No: ask for a shoulder-tap when the tx fifo has space */
914  svm_fifo_add_want_deq_ntf (hs->tx_fifo,
916  hs->session_state = HTTP_STATE_SEND_MORE_DATA;
917  return 0;
918  }
919  /* Finished with this transaction, back to HTTP_STATE_ESTABLISHED */
920 
921  /* Let go of the file cache entry */
923  hs->session_state = HTTP_STATE_ESTABLISHED;
924  return 0;
925 }
926 
927 static int
930 {
932  char *suffix;
933  char *http_type;
934  u8 *http_response;
935  f64 now;
936  u32 offset;
937 
938  /* What kind of dog food are we serving? */
939  suffix = (char *) (hs->path + vec_len (hs->path) - 1);
940  while (*suffix != '.')
941  suffix--;
942  suffix++;
943  http_type = "text/html";
944  if (!clib_strcmp (suffix, "css"))
945  http_type = "text/css";
946  else if (!clib_strcmp (suffix, "js"))
947  http_type = "text/javascript";
948  else if (!clib_strcmp (suffix, "json"))
949  http_type = "application/json";
950 
951  if (hs->data == 0)
952  {
953  clib_warning ("BUG: hs->data not set for session %d",
954  hs->session_index);
955  close_session (hs);
956  return 0;
957  }
958 
959  /*
960  * Send an http response, which needs the current time,
961  * the expiration time, and the data length
962  */
963  now = clib_timebase_now (&hsm->timebase);
964  http_response = format (0, http_response_template,
965  /* Date */
967  /* Expires */
968  format_clib_timebase_time, now + 600.0,
969  http_type, vec_len (hs->data));
970  offset = static_send_data (hs, http_response, vec_len (http_response), 0);
971  if (offset != vec_len (http_response))
972  {
973  clib_warning ("BUG: couldn't send response header!");
974  close_session (hs);
975  return 0;
976  }
977  vec_free (http_response);
978 
979  /* Send data from the beginning... */
980  hs->data_offset = 0;
981  hs->session_state = HTTP_STATE_SEND_MORE_DATA;
982  return 1;
983 }
984 
985 static void *state_funcs[HTTP_STATE_N_STATES] = {
986  state_closed,
987  /* Waiting for GET, POST, etc. */
989  /* Sent OK */
991  /* Send more data */
993 };
994 
995 static inline int
998 {
999  http_session_t *hs;
1001  int rv;
1002 
1003  /* Acquire a reader lock on the session table */
1006 
1007  if (!hs)
1008  {
1009  clib_warning ("No http session for thread %d session_index %d",
1010  s->thread_index, s->session_index);
1012  return 0;
1013  }
1014 
1015  /* Execute state machine for this session */
1016  do
1017  {
1018  fp = state_funcs[hs->session_state];
1019  rv = (*fp) (s, hs, cf);
1020  if (rv < 0)
1021  goto session_closed;
1022  }
1023  while (rv);
1024 
1025  /* Reset the session expiration timer */
1028 
1029 session_closed:
1031  return 0;
1032 }
1033 
1034 static int
1036 {
1038 }
1039 
1040 static int
1042 {
1044 }
1045 
1046 
1047 /** \brief Session accept callback
1048  */
1049 
1050 static int
1052 {
1054  http_session_t *hs;
1055 
1056  hsm->vpp_queue[s->thread_index] =
1058 
1060 
1063  hs->session_index);
1064  hs->rx_fifo = s->rx_fifo;
1065  hs->tx_fifo = s->tx_fifo;
1068  hs->session_state = HTTP_STATE_ESTABLISHED;
1070 
1072 
1073  s->session_state = SESSION_STATE_READY;
1074  return 0;
1075 }
1076 
1077 /** \brief Session disconnect callback
1078  */
1079 
1080 static void
1082 {
1084  vnet_disconnect_args_t _a = { 0 }, *a = &_a;
1085  http_session_t *hs;
1086 
1088 
1091 
1093 
1094  a->handle = session_handle (s);
1095  a->app_index = hsm->app_index;
1097 }
1098 
1099 /** \brief Session reset callback
1100  */
1101 
1102 static void
1104 {
1106  vnet_disconnect_args_t _a = { 0 }, *a = &_a;
1107  http_session_t *hs;
1108 
1110 
1113 
1115 
1116  a->handle = session_handle (s);
1117  a->app_index = hsm->app_index;
1119 }
1120 
1121 static int
1123  session_t * s,
1124  session_error_t err)
1125 {
1126  clib_warning ("called...");
1127  return -1;
1128 }
1129 
1130 static int
1131 http_static_server_add_segment_callback (u32 client_index, u64 segment_handle)
1132 {
1133  clib_warning ("called...");
1134  return -1;
1135 }
1136 
1137 /** \brief Session-layer virtual function table
1138  */
1139 static session_cb_vft_t http_static_server_session_cb_vft = {
1141  .session_disconnect_callback =
1143  .session_connected_callback = http_static_server_session_connected_callback,
1144  .add_segment_callback = http_static_server_add_segment_callback,
1145  .builtin_app_rx_callback = http_static_server_rx_callback,
1146  .builtin_app_tx_callback = http_static_server_tx_callback,
1147  .session_reset_callback = http_static_server_session_reset_callback
1148 };
1149 
1150 static int
1152 {
1153  vnet_app_add_tls_cert_args_t _a_cert, *a_cert = &_a_cert;
1154  vnet_app_add_tls_key_args_t _a_key, *a_key = &_a_key;
1157  vnet_app_attach_args_t _a, *a = &_a;
1158  u32 segment_size = 128 << 20;
1159 
1160  clib_memset (a, 0, sizeof (*a));
1161  clib_memset (options, 0, sizeof (options));
1162 
1163  if (hsm->private_segment_size)
1164  segment_size = hsm->private_segment_size;
1165 
1166  a->api_client_index = ~0;
1167  a->name = format (0, "test_http_static_server");
1168  a->session_cb_vft = &http_static_server_session_cb_vft;
1169  a->options = options;
1170  a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
1171  a->options[APP_OPTIONS_RX_FIFO_SIZE] =
1172  hsm->fifo_size ? hsm->fifo_size : 8 << 10;
1173  a->options[APP_OPTIONS_TX_FIFO_SIZE] =
1174  hsm->fifo_size ? hsm->fifo_size : 32 << 10;
1175  a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
1178 
1179  if (vnet_application_attach (a))
1180  {
1181  vec_free (a->name);
1182  clib_warning ("failed to attach server");
1183  return -1;
1184  }
1185  vec_free (a->name);
1186  hsm->app_index = a->app_index;
1187 
1188  clib_memset (a_cert, 0, sizeof (*a_cert));
1189  a_cert->app_index = a->app_index;
1190  vec_validate (a_cert->cert, test_srv_crt_rsa_len);
1192  vnet_app_add_tls_cert (a_cert);
1193 
1194  clib_memset (a_key, 0, sizeof (*a_key));
1195  a_key->app_index = a->app_index;
1196  vec_validate (a_key->key, test_srv_key_rsa_len);
1198  vnet_app_add_tls_key (a_key);
1199 
1200  return 0;
1201 }
1202 
1203 static int
1205 {
1207  vnet_listen_args_t _a, *a = &_a;
1208  clib_memset (a, 0, sizeof (*a));
1209  a->app_index = hsm->app_index;
1210  a->uri = "tcp://0.0.0.0/80";
1211  if (hsm->uri)
1212  a->uri = (char *) hsm->uri;
1213  return vnet_bind_uri (a);
1214 }
1215 
1216 static void
1218 {
1220  http_session_t *hs;
1221  uword hs_handle;
1222  hs_handle = pointer_to_uword (hs_handlep);
1223  hs =
1224  http_static_server_session_get (hs_handle >> 24, hs_handle & 0x00FFFFFF);
1225 
1226  if (hsm->debug_level > 1)
1227  clib_warning ("terminate thread %d index %d hs %llx",
1228  hs_handle >> 24, hs_handle & 0x00FFFFFF, hs);
1229  if (!hs)
1230  return;
1231  hs->timer_handle = ~0;
1234 }
1235 
1236 /** \brief Expired session timer-wheel callback
1237  */
1238 static void
1240 {
1241  u32 hs_handle;
1242  int i;
1243 
1244  for (i = 0; i < vec_len (expired_timers); i++)
1245  {
1246  /* Get session handle. The first bit is the timer id */
1247  hs_handle = expired_timers[i] & 0x7FFFFFFF;
1248  session_send_rpc_evt_to_thread (hs_handle >> 24,
1250  uword_to_pointer (hs_handle, void *));
1251  }
1252 }
1253 
1254 /** \brief Timer-wheel expiration process
1255  */
1256 static uword
1258  vlib_frame_t * f)
1259 {
1261  f64 now, timeout = 1.0;
1262  uword *event_data = 0;
1263  uword __clib_unused event_type;
1264 
1265  while (1)
1266  {
1268  now = vlib_time_now (vm);
1269  event_type = vlib_process_get_events (vm, (uword **) & event_data);
1270 
1271  /* expire timers */
1272  clib_spinlock_lock (&http_static_server_main.tw_lock);
1273  tw_timer_expire_timers_2t_1w_2048sl (&hsm->tw, now);
1274  clib_spinlock_unlock (&http_static_server_main.tw_lock);
1275 
1276  vec_reset_length (event_data);
1277  }
1278  return 0;
1279 }
1280 
1281 /* *INDENT-OFF* */
1282 VLIB_REGISTER_NODE (http_static_server_process_node) =
1283 {
1284  .function = http_static_server_process,
1285  .type = VLIB_NODE_TYPE_PROCESS,
1286  .name = "static-http-server-process",
1287  .state = VLIB_NODE_STATE_DISABLED,
1288 };
1289 /* *INDENT-ON* */
1290 
1291 static int
1293 {
1296  u32 num_threads;
1297  vlib_node_t *n;
1298 
1299  num_threads = 1 /* main thread */ + vtm->n_threads;
1300  vec_validate (hsm->vpp_queue, num_threads - 1);
1301  vec_validate (hsm->sessions, num_threads - 1);
1302  vec_validate (hsm->session_to_http_session, num_threads - 1);
1303 
1305  clib_spinlock_init (&hsm->tw_lock);
1306 
1308  {
1309  clib_warning ("failed to attach server");
1310  return -1;
1311  }
1313  {
1314  clib_warning ("failed to start listening");
1315  return -1;
1316  }
1317 
1318  /* Init path-to-cache hash table */
1319  BV (clib_bihash_init) (&hsm->name_to_data, "http cache", 128, 32 << 20);
1320 
1321  hsm->get_url_handlers = hash_create_string (0, sizeof (uword));
1322  hsm->post_url_handlers = hash_create_string (0, sizeof (uword));
1323 
1324  /* Init timer wheel and process */
1325  tw_timer_wheel_init_2t_1w_2048sl (&hsm->tw, http_expired_timers_dispatch,
1326  1.0 /* timer interval */ , ~0);
1327  vlib_node_set_state (vm, http_static_server_process_node.index,
1328  VLIB_NODE_STATE_POLLING);
1329  n = vlib_get_node (vm, http_static_server_process_node.index);
1331 
1332  return 0;
1333 }
1334 
1335 /** \brief API helper function for vl_api_http_static_enable_t messages
1336  */
1337 int
1338 http_static_server_enable_api (u32 fifo_size, u32 cache_limit,
1339  u32 prealloc_fifos,
1340  u32 private_segment_size,
1341  u8 * www_root, u8 * uri)
1342 {
1344  int rv;
1345 
1346  hsm->fifo_size = fifo_size;
1347  hsm->cache_limit = cache_limit;
1348  hsm->prealloc_fifos = prealloc_fifos;
1349  hsm->private_segment_size = private_segment_size;
1350  hsm->www_root = format (0, "%s%c", www_root, 0);
1351  hsm->uri = format (0, "%s%c", uri, 0);
1352 
1353  if (vec_len (hsm->www_root) < 2)
1354  return VNET_API_ERROR_INVALID_VALUE;
1355 
1356  if (hsm->my_client_index != ~0)
1357  return VNET_API_ERROR_APP_ALREADY_ATTACHED;
1358 
1359  vnet_session_enable_disable (hsm->vlib_main, 1 /* turn on TCP, etc. */ );
1360 
1362  switch (rv)
1363  {
1364  case 0:
1365  break;
1366  default:
1367  vec_free (hsm->www_root);
1368  vec_free (hsm->uri);
1369  return VNET_API_ERROR_INIT_FAILED;
1370  }
1371  return 0;
1372 }
1373 
1374 static clib_error_t *
1376  unformat_input_t * input,
1377  vlib_cli_command_t * cmd)
1378 {
1380  unformat_input_t _line_input, *line_input = &_line_input;
1381  u64 seg_size;
1382  u8 *www_root = 0;
1383  int rv;
1384 
1385  hsm->prealloc_fifos = 0;
1386  hsm->private_segment_size = 0;
1387  hsm->fifo_size = 0;
1388  /* 10mb cache limit, before LRU occurs */
1389  hsm->cache_limit = 10 << 20;
1390 
1391  /* Get a line of input. */
1392  if (!unformat_user (input, unformat_line_input, line_input))
1393  goto no_wwwroot;
1394 
1395  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1396  {
1397  if (unformat (line_input, "www-root %s", &www_root))
1398  ;
1399  else
1400  if (unformat (line_input, "prealloc-fifos %d", &hsm->prealloc_fifos))
1401  ;
1402  else if (unformat (line_input, "private-segment-size %U",
1403  unformat_memory_size, &seg_size))
1404  {
1405  if (seg_size >= 0x100000000ULL)
1406  {
1407  vlib_cli_output (vm, "private segment size %llu, too large",
1408  seg_size);
1409  return 0;
1410  }
1411  hsm->private_segment_size = seg_size;
1412  }
1413  else if (unformat (line_input, "fifo-size %d", &hsm->fifo_size))
1414  hsm->fifo_size <<= 10;
1415  else if (unformat (line_input, "cache-size %U", unformat_memory_size,
1416  &hsm->cache_limit))
1417  {
1418  if (hsm->cache_limit < (128 << 10))
1419  {
1420  return clib_error_return (0,
1421  "cache-size must be at least 128kb");
1422  }
1423  }
1424 
1425  else if (unformat (line_input, "uri %s", &hsm->uri))
1426  ;
1427  else if (unformat (line_input, "debug %d", &hsm->debug_level))
1428  ;
1429  else if (unformat (line_input, "debug"))
1430  hsm->debug_level = 1;
1431  else
1432  return clib_error_return (0, "unknown input `%U'",
1433  format_unformat_error, line_input);
1434  }
1435  unformat_free (line_input);
1436 
1437  if (www_root == 0)
1438  {
1439  no_wwwroot:
1440  return clib_error_return (0, "Must specify www-root <path>");
1441  }
1442 
1443  if (hsm->my_client_index != (u32) ~ 0)
1444  {
1445  vec_free (www_root);
1446  return clib_error_return (0, "http server already running...");
1447  }
1448 
1449  hsm->www_root = www_root;
1450 
1451  vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
1452 
1453  rv = http_static_server_create (vm);
1454  switch (rv)
1455  {
1456  case 0:
1457  break;
1458  default:
1459  vec_free (hsm->www_root);
1460  return clib_error_return (0, "server_create returned %d", rv);
1461  }
1462  return 0;
1463 }
1464 
1465 /*?
1466  * Enable the static http server
1467  *
1468  * @cliexpar
1469  * This command enables the static http server. Only the www-root
1470  * parameter is required
1471  * @clistart
1472  * http static server www-root /tmp/www uri tcp://0.0.0.0/80 cache-size 2m
1473  * @cliend
1474  * @cliexcmd{http static server www-root <path> [prealloc-fios <nn>]
1475  * [private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]}
1476 ?*/
1477 /* *INDENT-OFF* */
1478 VLIB_CLI_COMMAND (http_static_server_create_command, static) =
1479 {
1480  .path = "http static server",
1481  .short_help = "http static server www-root <path> [prealloc-fifos <nn>]\n"
1482  "[private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]\n"
1483  "[debug [nn]]\n",
1485 };
1486 /* *INDENT-ON* */
1487 
1488 /** \brief format a file cache entry
1489  */
1490 u8 *
1491 format_hsm_cache_entry (u8 * s, va_list * args)
1492 {
1493  file_data_cache_t *ep = va_arg (*args, file_data_cache_t *);
1494  f64 now = va_arg (*args, f64);
1495 
1496  /* Header */
1497  if (ep == 0)
1498  {
1499  s = format (s, "%40s%12s%20s", "File", "Size", "Age");
1500  return s;
1501  }
1502  s = format (s, "%40s%12lld%20.2f", ep->filename, vec_len (ep->data),
1503  now - ep->last_used);
1504  return s;
1505 }
1506 
1507 u8 *
1508 format_http_session_state (u8 * s, va_list * args)
1509 {
1511  char *state_string = "bogus!";
1512 
1513  switch (state)
1514  {
1515  case HTTP_STATE_CLOSED:
1516  state_string = "closed";
1517  break;
1519  state_string = "established";
1520  break;
1521  case HTTP_STATE_OK_SENT:
1522  state_string = "ok sent";
1523  break;
1525  state_string = "send more data";
1526  break;
1527  default:
1528  break;
1529  }
1530 
1531  return format (s, "%s", state_string);
1532 }
1533 
1534 u8 *
1535 format_http_session (u8 * s, va_list * args)
1536 {
1537  http_session_t *hs = va_arg (*args, http_session_t *);
1538  int verbose = va_arg (*args, int);
1539 
1540  s = format (s, "[%d]: state %U", hs->session_index,
1541  format_http_session_state, hs->session_state);
1542  if (verbose > 0)
1543  {
1544  s = format (s, "\n path %s, data length %u, data_offset %u",
1545  hs->path ? hs->path : (u8 *) "[none]",
1546  vec_len (hs->data), hs->data_offset);
1547  }
1548  return s;
1549 }
1550 
1551 static clib_error_t *
1553  unformat_input_t * input,
1554  vlib_cli_command_t * cmd)
1555 {
1557  file_data_cache_t *ep, **entries = 0;
1558  int verbose = 0;
1559  int show_cache = 0;
1560  int show_sessions = 0;
1561  u32 index;
1562  f64 now;
1563 
1564  if (hsm->www_root == 0)
1565  return clib_error_return (0, "Static server disabled");
1566 
1567  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1568  {
1569  if (unformat (input, "verbose %d", &verbose))
1570  ;
1571  else if (unformat (input, "verbose"))
1572  verbose = 1;
1573  else if (unformat (input, "cache"))
1574  show_cache = 1;
1575  else if (unformat (input, "sessions"))
1576  show_sessions = 1;
1577  else
1578  break;
1579  }
1580 
1581  if ((show_cache + show_sessions) == 0)
1582  return clib_error_return (0, "specify one or more of cache, sessions");
1583 
1584  if (show_cache)
1585  {
1586  if (verbose == 0)
1587  {
1589  (vm, "www_root %s, cache size %lld bytes, limit %lld bytes, "
1590  "evictions %lld",
1591  hsm->www_root, hsm->cache_size, hsm->cache_limit,
1592  hsm->cache_evictions);
1593  return 0;
1594  }
1595 
1596  now = vlib_time_now (vm);
1597 
1598  vlib_cli_output (vm, "%U", format_hsm_cache_entry, 0 /* header */ ,
1599  now);
1600 
1601  for (index = hsm->first_index; index != ~0;)
1602  {
1603  ep = pool_elt_at_index (hsm->cache_pool, index);
1604  index = ep->next_index;
1605  vlib_cli_output (vm, "%U", format_hsm_cache_entry, ep, now);
1606  }
1607 
1608  vlib_cli_output (vm, "%40s%12lld", "Total Size", hsm->cache_size);
1609 
1610  vec_free (entries);
1611  }
1612 
1613  if (show_sessions)
1614  {
1615  u32 *session_indices = 0;
1616  http_session_t *hs;
1617  int i, j;
1618 
1620 
1621  for (i = 0; i < vec_len (hsm->sessions); i++)
1622  {
1623  /* *INDENT-OFF* */
1624  pool_foreach (hs, hsm->sessions[i],
1625  ({
1626  vec_add1 (session_indices, hs - hsm->sessions[i]);
1627  }));
1628  /* *INDENT-ON* */
1629 
1630  for (j = 0; j < vec_len (session_indices); j++)
1631  {
1634  (hsm->sessions[i], session_indices[j]),
1635  verbose);
1636  }
1637  vec_reset_length (session_indices);
1638  }
1640  vec_free (session_indices);
1641  }
1642  return 0;
1643 }
1644 
1645 /*?
1646  * Display static http server cache statistics
1647  *
1648  * @cliexpar
1649  * This command shows the contents of the static http server cache
1650  * @clistart
1651  * show http static server
1652  * @cliend
1653  * @cliexcmd{show http static server sessions cache [verbose [nn]]}
1654 ?*/
1655 /* *INDENT-OFF* */
1656 VLIB_CLI_COMMAND (http_show_static_server_command, static) =
1657 {
1658  .path = "show http static server",
1659  .short_help = "show http static server sessions cache [verbose [<nn>]]",
1661 };
1662 /* *INDENT-ON* */
1663 
1664 static clib_error_t *
1666  unformat_input_t * input,
1667  vlib_cli_command_t * cmd)
1668 {
1670  file_data_cache_t *dp;
1671  u32 free_index;
1672  u32 busy_items = 0;
1673  BVT (clib_bihash_kv) kv;
1674 
1675  if (hsm->www_root == 0)
1676  return clib_error_return (0, "Static server disabled");
1677 
1679 
1680  /* Walk the LRU list to find active entries */
1681  free_index = hsm->last_index;
1682  while (free_index != ~0)
1683  {
1684  dp = pool_elt_at_index (hsm->cache_pool, free_index);
1685  free_index = dp->prev_index;
1686  /* Which could be in use... */
1687  if (dp->inuse)
1688  {
1689  busy_items++;
1690  free_index = dp->next_index;
1691  continue;
1692  }
1693  kv.key = (u64) (dp->filename);
1694  kv.value = ~0ULL;
1695  if (BV (clib_bihash_add_del) (&hsm->name_to_data, &kv,
1696  0 /* is_add */ ) < 0)
1697  {
1698  clib_warning ("BUG: cache clear delete '%s' FAILED!", dp->filename);
1699  }
1700 
1701  lru_remove (hsm, dp);
1702  hsm->cache_size -= vec_len (dp->data);
1703  hsm->cache_evictions++;
1704  vec_free (dp->filename);
1705  vec_free (dp->data);
1706  if (hsm->debug_level > 1)
1707  clib_warning ("pool put index %d", dp - hsm->cache_pool);
1708  pool_put (hsm->cache_pool, dp);
1709  free_index = hsm->last_index;
1710  }
1712  if (busy_items > 0)
1713  vlib_cli_output (vm, "Note: %d busy items still in cache...", busy_items);
1714  else
1715  vlib_cli_output (vm, "Cache cleared...");
1716  return 0;
1717 }
1718 
1719 /*?
1720  * Clear the static http server cache, to force the server to
1721  * reload content from backing files
1722  *
1723  * @cliexpar
1724  * This command clear the static http server cache
1725  * @clistart
1726  * clear http static cache
1727  * @cliend
1728  * @cliexcmd{clear http static cache}
1729 ?*/
1730 /* *INDENT-OFF* */
1731 VLIB_CLI_COMMAND (clear_http_static_cache_command, static) =
1732 {
1733  .path = "clear http static cache",
1734  .short_help = "clear http static cache",
1736 };
1737 /* *INDENT-ON* */
1738 
1739 static clib_error_t *
1741 {
1743 
1744  hsm->my_client_index = ~0;
1745  hsm->vlib_main = vm;
1746  hsm->first_index = hsm->last_index = ~0;
1747 
1748  clib_timebase_init (&hsm->timebase, 0 /* GMT */ ,
1750  &vm->clib_time /* share the system clock */ );
1751 
1752  return 0;
1753 }
1754 
1756 
1757 /*
1758  * fd.io coding-style-patch-verification: ON
1759  *
1760  * Local Variables:
1761  * eval: (c-set-style "gnu")
1762  * End:
1763  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:507
static void clib_rwlock_reader_lock(clib_rwlock_t *p)
Definition: lock.h:150
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:102
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:673
a
Definition: bitmap.h:538
static void clib_rwlock_writer_lock(clib_rwlock_t *p)
Definition: lock.h:173
static void http_static_server_session_cleanup(http_session_t *hs)
clean up a session
svm_fifo_t * tx_fifo
Static http server definitions.
static const char * http_response
Definition: http_server.c:247
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:291
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
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
clib_time_t clib_time
Definition: main.h:87
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)
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.
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:1628
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:494
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:516
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:431
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
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:133
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:165
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:427
int svm_fifo_enqueue(svm_fifo_t *f, u32 len, const u8 *src)
Enqueue data to fifo.
Definition: svm_fifo.c:836
#define PREDICT_FALSE(x)
Definition: clib.h:118
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:712
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
vlib_main_t * vm
Definition: in2out_ed.c:1599
clib_error_t * vnet_session_enable_disable(vlib_main_t *vm, u8 is_en)
Definition: session.c:1765
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:699
static void clib_rwlock_writer_unlock(clib_rwlock_t *p)
Definition: lock.h:187
f64 last_used
Last time the cache entry was used.
Definition: http_static.h:123
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:169
#define UNFORMAT_END_OF_INPUT
Definition: format.h:145
u32 runtime_index
Definition: node.h:285
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:152
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:689
#define vec_delete(V, N, M)
Delete N elements starting at element M.
Definition: vec.h:852
static void http_static_server_session_disconnect_callback(session_t *s)
Session disconnect callback.
u8 data[128]
Definition: ipsec_types.api:89
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:726
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:148
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.
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
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:247
u8 * uri
The bind URI, defaults to tcp://0.0.0.0/80.
Definition: http_static.h:198
#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:59
static void http_static_server_session_cleanup_cb(void *hs_handlep)
enum session_error_ session_error_t
#define clib_strcmp(s1, s2)
Definition: string.h:826
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:790
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:1617
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.