FD.io VPP  v19.08-27-gf4dcae4
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>
21 #include <vppinfra/unix.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
26 #include <vppinfra/bihash_vec8_8.h>
27 
29 
30 /** @file
31  Simple Static http server, sufficient to
32  serve .html / .css / .js content.
33 */
34 /*? %%clicmd:group_label Static HTTP Server %% ?*/
35 
36 /** \brief Session States
37  */
38 
39 typedef enum
40 {
41  /** Session is closed */
43  /** Session is established */
45  /** Session has sent an OK response */
47  /** Session has sent an HTML response */
49  /** Number of states */
52 
53 typedef enum
54 {
59 
60 
61 /** \brief Application session
62  */
63 typedef struct
64 {
65  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
66  /** Base class instance variables */
67 #define _(type, name) type name;
69 #undef _
70  /** rx thread index */
71  u32 thread_index;
72  /** rx buffer */
73  u8 *rx_buf;
74  /** vpp session index, handle */
75  u32 vpp_session_index;
76  u64 vpp_session_handle;
77  /** Timeout timer handle */
78  u32 timer_handle;
79  /** Fully-resolved file path */
80  u8 *path;
81  /** File data, a vector */
82  u8 *data;
83  /** Current data send offset */
85  /** File cache pool index */
87  /** state machine called from... */
88  state_machine_called_from_t called_from;
90 
91 /** \brief In-memory file data cache entry
92  */
93 typedef struct
94 {
95  /** Name of the file */
97  /** Contents of the file, as a u8 * vector */
98  u8 *data;
99  /** Last time the cache entry was used */
101  /** Cache LRU links */
104  /** Reference count, so we don't recycle while referenced */
105  int inuse;
107 
108 /** \brief Main data structure
109  */
110 
111 typedef struct
112 {
113  /** Per thread vector of session pools */
115  /** Session pool reader writer lock */
117  /** vpp session to http session index map */
119 
120  /** Enable debug messages */
122 
123  /** vpp message/event queue */
125 
126  /** Unified file data cache pool */
128  /** Hash table which maps file name to file data */
129  BVT (clib_bihash) name_to_data;
130 
131  /** Current cache size */
132  u64 cache_size;
133  /** Max cache size in bytes */
134  u64 cache_limit;
135  /** Number of cache evictions */
136  u64 cache_evictions;
137 
138  /** Cache LRU listheads */
139  u32 first_index;
140  u32 last_index;
141 
142  /** root path to be served */
143  u8 *www_root;
144 
145  /** Server's event queue */
146  svm_queue_t *vl_input_queue;
147 
148  /** API client handle */
149  u32 my_client_index;
150 
151  /** Application index */
152  u32 app_index;
153 
154  /** Process node index for event scheduling */
155  u32 node_index;
156 
157  /** Session cleanup timer wheel */
158  tw_timer_wheel_2t_1w_2048sl_t tw;
159  clib_spinlock_t tw_lock;
160 
161  /** Time base, so we can generate browser cache control http spew */
162  clib_timebase_t timebase;
163 
164  /** Number of preallocated fifos, usually 0 */
165  u32 prealloc_fifos;
166  /** Private segment size, usually 0 */
167  u32 private_segment_size;
168  /** Size of the allocated rx, tx fifos, roughly 8K or so */
169  u32 fifo_size;
170  /** The bind URI, defaults to tcp://0.0.0.0/80 */
171  u8 *uri;
174 
176 
177 /** \brief Format the called-from enum
178  */
179 
180 static u8 *
182 {
183  state_machine_called_from_t cf =
184  va_arg (*args, state_machine_called_from_t);
185  char *which = "bogus!";
186 
187  switch (cf)
188  {
189  case CALLED_FROM_RX:
190  which = "from rx";
191  break;
192  case CALLED_FROM_TX:
193  which = "from tx";
194  break;
195  case CALLED_FROM_TIMER:
196  which = "from timer";
197  break;
198 
199  default:
200  break;
201  }
202 
203  s = format (s, "%s", which);
204  return s;
205 }
206 
207 
208 /** \brief Acquire reader lock on the sessions pools
209  */
210 static void
212 {
214 }
215 
216 /** \brief Drop reader lock on the sessions pools
217  */
218 static void
220 {
222 }
223 
224 /** \brief Acquire writer lock on the sessions pools
225  */
226 static void
228 {
230 }
231 
232 /** \brief Drop writer lock on the sessions pools
233  */
234 static void
236 {
238 }
239 
240 /** \brief Start a session cleanup timer
241  */
242 static void
244 {
246  u32 hs_handle;
247 
248  /* The session layer may fire a callback at a later date... */
249  if (!pool_is_free (hsm->sessions[hs->thread_index], hs))
250  {
251  hs_handle = hs->thread_index << 24 | hs->session_index;
253  hs->timer_handle = tw_timer_start_2t_1w_2048sl
254  (&http_static_server_main.tw, hs_handle, 0, 60);
256  }
257 }
258 
259 /** \brief stop a session cleanup timer
260  */
261 static void
263 {
264  if (hs->timer_handle == ~0)
265  return;
267  tw_timer_stop_2t_1w_2048sl (&http_static_server_main.tw, hs->timer_handle);
269 }
270 
271 /** \brief Allocate an http session
272  */
273 static http_session_t *
275 {
277  http_session_t *hs;
278  pool_get (hsm->sessions[thread_index], hs);
279  memset (hs, 0, sizeof (*hs));
280  hs->session_index = hs - hsm->sessions[thread_index];
281  hs->thread_index = thread_index;
282  hs->timer_handle = ~0;
283  hs->cache_pool_index = ~0;
284  return hs;
285 }
286 
287 /** \brief Get an http session by index
288  */
289 static http_session_t *
290 http_static_server_session_get (u32 thread_index, u32 hs_index)
291 {
293  if (pool_is_free_index (hsm->sessions[thread_index], hs_index))
294  return 0;
295  return pool_elt_at_index (hsm->sessions[thread_index], hs_index);
296 }
297 
298 /** \brief Free an http session
299  */
300 static void
302 {
304 
305  /* Make sure the timer is stopped... */
307  pool_put (hsm->sessions[hs->thread_index], hs);
308 
309  if (CLIB_DEBUG)
310  {
311  u32 save_thread_index;
312  save_thread_index = hs->thread_index;
313  /* Poison the entry, preserve timer state and thread index */
314  memset (hs, 0xfa, sizeof (*hs));
315  hs->timer_handle = ~0;
316  hs->thread_index = save_thread_index;
317  }
318 }
319 
320 /** \brief add a session to the vpp < -- > http session index map
321  */
322 static void
324  u32 hs_index)
325 {
327  vec_validate (hsm->session_to_http_session[thread_index], s_index);
328  hsm->session_to_http_session[thread_index][s_index] = hs_index;
329 }
330 
331 /** \brief Remove a session from the vpp < -- > http session index map
332  */
333 static void
335 {
337  hsm->session_to_http_session[thread_index][s_index] = ~0;
338 }
339 
340 /** \brief lookup a session in the vpp < -- > http session index map
341  */
342 
343 static http_session_t *
345 {
347  u32 hs_index;
348 
349  if (s_index < vec_len (hsm->session_to_http_session[thread_index]))
350  {
351  hs_index = hsm->session_to_http_session[thread_index][s_index];
352  return http_static_server_session_get (thread_index, hs_index);
353  }
354  return 0;
355 }
356 
357 /** \brief Detach cache entry from session
358  */
359 
360 static void
362 {
364  file_data_cache_t *ep;
365 
366  /*
367  * Decrement cache pool entry reference count
368  * Note that if e.g. a file lookup fails, the cache pool index
369  * won't be set
370  */
371  if (hs->cache_pool_index != ~0)
372  {
374  ep->inuse--;
375  if (hsm->debug_level > 1)
376  clib_warning ("index %d refcnt now %d", hs->cache_pool_index,
377  ep->inuse);
378  }
379  hs->cache_pool_index = ~0;
380  hs->data = 0;
381  hs->data_offset = 0;
382  vec_free (hs->path);
383 }
384 
385 /** \brief clean up a session
386  */
387 
388 static void
390 {
391  if (!hs)
392  return;
393 
395 
397  hs->vpp_session_index);
398  vec_free (hs->rx_buf);
400 }
401 
402 /** \brief Disconnect a session
403  */
404 
405 static void
407 {
408  vnet_disconnect_args_t _a = { 0 }, *a = &_a;
409  a->handle = hs->vpp_session_handle;
410  a->app_index = http_static_server_main.app_index;
412 }
413 
414 /* *INDENT-OFF* */
415 /** \brief http error boilerplate
416  */
417 static const char *http_error_template =
418  "HTTP/1.1 %s\r\n"
419  "Date: %U GMT\r\n"
420  "Content-Type: text/html\r\n"
421  "Connection: close\r\n"
422  "Pragma: no-cache\r\n"
423  "Content-Length: 0\r\n\r\n";
424 
425 /** \brief http response boilerplate
426  */
427 static const char *http_response_template =
428  "Date: %U GMT\r\n"
429  "Expires: %U GMT\r\n"
430  "Server: VPP Static\r\n"
431  "Content-Type: text/%s\r\n"
432  "Content-Length: %d\r\n\r\n";
433 
434 /* *INDENT-ON* */
435 
436 /** \brief send http data
437  @param hs - http session
438  @param data - the data vector to transmit
439  @param offset - transmit offset for this operation
440  @return offset for next transmit operation, may be unchanged w/ full fifo
441 */
442 
443 static u32
445 {
446  u32 bytes_to_send;
448 
449  bytes_to_send = length - offset;
450 
451  while (bytes_to_send > 0)
452  {
453  int actual_transfer;
454 
455  actual_transfer = svm_fifo_enqueue
456  (hs->tx_fifo, bytes_to_send, data + offset);
457 
458  /* Made any progress? */
459  if (actual_transfer <= 0)
460  {
461  if (hsm->debug_level > 0 && bytes_to_send > 0)
462  clib_warning ("WARNING: still %d bytes to send", bytes_to_send);
463  return offset;
464  }
465  else
466  {
467  offset += actual_transfer;
468  bytes_to_send -= actual_transfer;
469 
470  if (hsm->debug_level && bytes_to_send > 0)
471  clib_warning ("WARNING: still %d bytes to send", bytes_to_send);
472 
473  if (svm_fifo_set_event (hs->tx_fifo))
474  session_send_io_evt_to_thread (hs->tx_fifo,
476  return offset;
477  }
478  }
479  /* NOTREACHED */
480  return ~0;
481 }
482 
483 /** \brief Send an http error string
484  @param hs - the http session
485  @param str - the error string, e.g. "404 Not Found"
486 */
487 static void
488 send_error (http_session_t * hs, char *str)
489 {
491  u8 *data;
492  f64 now;
493 
494  now = clib_timebase_now (&hsm->timebase);
495  data = format (0, http_error_template, str, format_clib_timebase_time, now);
496  static_send_data (hs, data, vec_len (data), 0);
497  vec_free (data);
498 }
499 
500 /** \brief Retrieve data from the application layer
501  */
502 static int
504 {
505  u32 max_dequeue, cursize;
506  int n_read;
507 
508  cursize = vec_len (hs->rx_buf);
509  max_dequeue = svm_fifo_max_dequeue (hs->rx_fifo);
510  if (PREDICT_FALSE (max_dequeue == 0))
511  return -1;
512 
513  vec_validate (hs->rx_buf, cursize + max_dequeue - 1);
514  n_read = app_recv_stream_raw (hs->rx_fifo, hs->rx_buf + cursize,
515  max_dequeue, 0, 0 /* peek */ );
516  ASSERT (n_read == max_dequeue);
517  if (svm_fifo_is_empty (hs->rx_fifo))
518  svm_fifo_unset_event (hs->rx_fifo);
519 
520  _vec_len (hs->rx_buf) = cursize + n_read;
521  return 0;
522 }
523 
524 /** \brief Sanity-check the forward and reverse LRU lists
525  */
526 static inline void
528 {
529 #if CLIB_DEBUG > 0
530  f64 last_timestamp;
531  u32 index;
532  int i;
533  file_data_cache_t *ep;
534 
535  last_timestamp = 1e70;
536  for (i = 1, index = hsm->first_index; index != ~0;)
537  {
538  ep = pool_elt_at_index (hsm->cache_pool, index);
539  index = ep->next_index;
540  /* Timestamps should be smaller (older) as we walk the fwd list */
541  if (ep->last_used > last_timestamp)
542  {
543  clib_warning ("%d[%d]: last used %.6f, last_timestamp %.6f",
544  ep - hsm->cache_pool, i,
545  ep->last_used, last_timestamp);
546  }
547  last_timestamp = ep->last_used;
548  i++;
549  }
550 
551  last_timestamp = 0.0;
552  for (i = 1, index = hsm->last_index; index != ~0;)
553  {
554  ep = pool_elt_at_index (hsm->cache_pool, index);
555  index = ep->prev_index;
556  /* Timestamps should be larger (newer) as we walk the rev list */
557  if (ep->last_used < last_timestamp)
558  {
559  clib_warning ("%d[%d]: last used %.6f, last_timestamp %.6f",
560  ep - hsm->cache_pool, i,
561  ep->last_used, last_timestamp);
562  }
563  last_timestamp = ep->last_used;
564  i++;
565  }
566 #endif
567 }
568 
569 /** \brief Remove a data cache entry from the LRU lists
570  */
571 static inline void
573 {
574  file_data_cache_t *next_ep, *prev_ep;
575  u32 ep_index;
576 
577  lru_validate (hsm);
578 
579  ep_index = ep - hsm->cache_pool;
580 
581  /* Deal with list heads */
582  if (ep_index == hsm->first_index)
583  hsm->first_index = ep->next_index;
584  if (ep_index == hsm->last_index)
585  hsm->last_index = ep->prev_index;
586 
587  /* Fix next->prev */
588  if (ep->next_index != ~0)
589  {
590  next_ep = pool_elt_at_index (hsm->cache_pool, ep->next_index);
591  next_ep->prev_index = ep->prev_index;
592  }
593  /* Fix prev->next */
594  if (ep->prev_index != ~0)
595  {
596  prev_ep = pool_elt_at_index (hsm->cache_pool, ep->prev_index);
597  prev_ep->next_index = ep->next_index;
598  }
599  lru_validate (hsm);
600 }
601 
602 /** \brief Add an entry to the LRU lists, tag w/ supplied timestamp
603  */
604 
605 static inline void
607 {
608  file_data_cache_t *next_ep;
609  u32 ep_index;
610 
611  lru_validate (hsm);
612 
613  ep_index = ep - hsm->cache_pool;
614 
615  /*
616  * Re-add at the head of the forward LRU list,
617  * tail of the reverse LRU list
618  */
619  if (hsm->first_index != ~0)
620  {
621  next_ep = pool_elt_at_index (hsm->cache_pool, hsm->first_index);
622  next_ep->prev_index = ep_index;
623  }
624 
625  ep->prev_index = ~0;
626 
627  /* ep now the new head of the LRU forward list */
628  ep->next_index = hsm->first_index;
629  hsm->first_index = ep_index;
630 
631  /* single session case: also the tail of the reverse LRU list */
632  if (hsm->last_index == ~0)
633  hsm->last_index = ep_index;
634  ep->last_used = now;
635 
636  lru_validate (hsm);
637 }
638 
639 /** \brief Remove and re-add a cache entry from/to the LRU lists
640  */
641 
642 static inline void
644 {
645  lru_remove (hsm, ep);
646  lru_add (hsm, ep, now);
647 }
648 
649 /** \brief Session-layer (main) data rx callback.
650  Parse the http request, and reply to it.
651  Future extensions might include POST processing, active content, etc.
652 */
653 
654 /* svm_fifo_add_want_deq_ntf (tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL)
655 get shoulder-tap when transport dequeues something, set in
656 xmit routine. */
657 
658 /** \brief closed state - should never really get here
659  */
660 static int
662  state_machine_called_from_t cf)
663 {
664  clib_warning ("WARNING: http session %d, called from %U",
665  hs->session_index, format_state_machine_called_from, cf);
666  return -1;
667 }
668 
669 static void
671 {
674 }
675 
676 /** \brief established state - waiting for GET, POST, etc.
677  */
678 static int
680  state_machine_called_from_t cf)
681 {
683  u32 request_len;
684  u8 *request = 0;
685  u8 *path;
686  int i, rv;
687  struct stat _sb, *sb = &_sb;
688  clib_error_t *error;
689 
690  /* Read data from the sessison layer */
691  rv = session_rx_request (hs);
692 
693  /* No data? Odd, but stay in this state and await further instructions */
694  if (rv)
695  return 0;
696 
697  /* Process the client request */
698  request = hs->rx_buf;
699  request_len = vec_len (request);
700  if (vec_len (request) < 7)
701  {
702  send_error (hs, "400 Bad Request");
703  close_session (hs);
704  return -1;
705  }
706 
707  /* We only handle GET requests at the moment */
708  for (i = 0; i < request_len - 4; i++)
709  {
710  if (request[i] == 'G' &&
711  request[i + 1] == 'E' &&
712  request[i + 2] == 'T' && request[i + 3] == ' ')
713  goto find_end;
714  }
715  if (hsm->debug_level > 1)
716  clib_warning ("Unknown http method");
717 
718  send_error (hs, "405 Method Not Allowed");
719  close_session (hs);
720  return -1;
721 
722 find_end:
723 
724  /* Lose "GET " */
725  vec_delete (request, i + 5, 0);
726 
727  /* Lose stuff to the right of the path */
728  for (i = 0; i < vec_len (request); i++)
729  {
730  if (request[i] == ' ' || request[i] == '?')
731  {
732  request[i] = 0;
733  break;
734  }
735  }
736 
737  /*
738  * Now we can construct the file to open
739  * Browsers are capable of sporadically including a leading '/'
740  */
741  if (request[0] == '/')
742  path = format (0, "%s%s%c", hsm->www_root, request, 0);
743  else
744  path = format (0, "%s/%s%c", hsm->www_root, request, 0);
745 
746  if (hsm->debug_level > 0)
747  clib_warning ("GET '%s'", path);
748 
749  /* Try to find the file. 2x special cases to find index.html */
750  if (stat ((char *) path, sb) < 0 /* cant even stat the file */
751  || sb->st_size < 20 /* file too small */
752  || (sb->st_mode & S_IFMT) != S_IFREG /* not a regular file */ )
753  {
754  u32 save_length = vec_len (path) - 1;
755  /* Try appending "index.html"... */
756  _vec_len (path) -= 1;
757  path = format (path, "index.html%c", 0);
758  if (stat ((char *) path, sb) < 0 /* cant even stat the file */
759  || sb->st_size < 20 /* file too small */
760  || (sb->st_mode & S_IFMT) != S_IFREG /* not a regular file */ )
761  {
762  _vec_len (path) = save_length;
763  path = format (path, "/index.html%c", 0);
764 
765  /* Send a redirect, otherwise the browser will confuse itself */
766  if (stat ((char *) path, sb) < 0 /* cant even stat the file */
767  || sb->st_size < 20 /* file too small */
768  || (sb->st_mode & S_IFMT) != S_IFREG /* not a regular file */ )
769  {
770  vec_free (path);
771  send_error (hs, "404 Not Found");
772  close_session (hs);
773  return -1;
774  }
775  else
776  {
777  transport_endpoint_t endpoint;
779  u16 local_port;
780  int print_port = 0;
781  u8 *port_str = 0;
782 
783  /*
784  * To make this bit work correctly, we need to know our local
785  * IP address, etc. and send it in the redirect...
786  */
787  u8 *redirect;
788 
789  vec_delete (path, vec_len (hsm->www_root) - 1, 0);
790 
791  session_get_endpoint (s, &endpoint, 1 /* is_local */ );
792 
793  local_port = clib_net_to_host_u16 (endpoint.port);
794 
796 
797  if ((proto == TRANSPORT_PROTO_TCP && local_port != 80)
798  || (proto == TRANSPORT_PROTO_TLS && local_port != 443))
799  {
800  print_port = 1;
801  port_str = format (0, ":%u", (u32) local_port);
802  }
803 
804  redirect = format (0, "HTTP/1.1 301 Moved Permanently\r\n"
805  "Location: http%s://%U%s%s\r\n\r\n",
806  proto == TRANSPORT_PROTO_TLS ? "s" : "",
807  format_ip46_address, &endpoint.ip,
808  endpoint.is_ip4,
809  print_port ? port_str : (u8 *) "", path);
810  if (hsm->debug_level > 0)
811  clib_warning ("redirect: %s", redirect);
812 
813  vec_free (port_str);
814 
815  static_send_data (hs, redirect, vec_len (redirect), 0);
816  hs->session_state = HTTP_STATE_CLOSED;
817  hs->path = 0;
818  vec_free (redirect);
819  vec_free (path);
820  close_session (hs);
821  return -1;
822  }
823  }
824  }
825 
826  /* find or read the file if we haven't done so yet. */
827  if (hs->data == 0)
828  {
829  BVT (clib_bihash_kv) kv;
830  file_data_cache_t *dp;
831 
832  hs->path = path;
833 
834  /* First, try the cache */
835  kv.key = (u64) hs->path;
836  if (BV (clib_bihash_search) (&hsm->name_to_data, &kv, &kv) == 0)
837  {
838  if (hsm->debug_level > 1)
839  clib_warning ("lookup '%s' returned %lld", kv.key, kv.value);
840 
841  /* found the data.. */
842  dp = pool_elt_at_index (hsm->cache_pool, kv.value);
843  hs->data = dp->data;
844  /* Update the cache entry, mark it in-use */
845  lru_update (hsm, dp, vlib_time_now (hsm->vlib_main));
846  hs->cache_pool_index = dp - hsm->cache_pool;
847  dp->inuse++;
848  if (hsm->debug_level > 1)
849  clib_warning ("index %d refcnt now %d", hs->cache_pool_index,
850  dp->inuse);
851  }
852  else
853  {
854  if (hsm->debug_level > 1)
855  clib_warning ("lookup '%s' failed", kv.key, kv.value);
856  /* Need to recycle one (or more cache) entries? */
857  if (hsm->cache_size > hsm->cache_limit)
858  {
859  int free_index = hsm->last_index;
860 
861  while (free_index != ~0)
862  {
863  /* pick the LRU */
864  dp = pool_elt_at_index (hsm->cache_pool, free_index);
865  free_index = dp->prev_index;
866  /* Which could be in use... */
867  if (dp->inuse)
868  {
869  if (hsm->debug_level > 1)
870  clib_warning ("index %d in use refcnt %d",
871  dp - hsm->cache_pool, dp->inuse);
872 
873  }
874  kv.key = (u64) (dp->filename);
875  kv.value = ~0ULL;
876  if (BV (clib_bihash_add_del) (&hsm->name_to_data, &kv,
877  0 /* is_add */ ) < 0)
878  {
879  clib_warning ("LRU delete '%s' FAILED!", dp->filename);
880  }
881  else if (hsm->debug_level > 1)
882  clib_warning ("LRU delete '%s' ok", dp->filename);
883 
884  lru_remove (hsm, dp);
885  hsm->cache_size -= vec_len (dp->data);
886  hsm->cache_evictions++;
887  vec_free (dp->filename);
888  vec_free (dp->data);
889  if (hsm->debug_level > 1)
890  clib_warning ("pool put index %d", dp - hsm->cache_pool);
891  pool_put (hsm->cache_pool, dp);
892  if (hsm->cache_size < hsm->cache_limit)
893  break;
894  }
895  }
896 
897  /* Read the file */
898  error = clib_file_contents ((char *) (hs->path), &hs->data);
899  if (error)
900  {
901  clib_warning ("Error reading '%s'", hs->path);
902  clib_error_report (error);
903  vec_free (hs->path);
904  close_session (hs);
905  return -1;
906  }
907  /* Create a cache entry for it */
908  pool_get (hsm->cache_pool, dp);
909  memset (dp, 0, sizeof (*dp));
910  dp->filename = vec_dup (hs->path);
911  dp->data = hs->data;
912  hs->cache_pool_index = dp - hsm->cache_pool;
913  dp->inuse++;
914  if (hsm->debug_level > 1)
915  clib_warning ("index %d refcnt now %d", hs->cache_pool_index,
916  dp->inuse);
917  lru_add (hsm, dp, vlib_time_now (hsm->vlib_main));
918  kv.key = (u64) vec_dup (hs->path);
919  kv.value = dp - hsm->cache_pool;
920  /* Add to the lookup table */
921  if (hsm->debug_level > 1)
922  clib_warning ("add '%s' value %lld", kv.key, kv.value);
923 
924  if (BV (clib_bihash_add_del) (&hsm->name_to_data, &kv,
925  1 /* is_add */ ) < 0)
926  {
927  clib_warning ("BUG: add failed!");
928  }
929  hsm->cache_size += vec_len (dp->data);
930  }
931  hs->data_offset = 0;
932  }
933  /* send 200 OK first */
934  static_send_data (hs, (u8 *) "HTTP/1.1 200 OK\r\n", 17, 0);
935  hs->session_state = HTTP_STATE_OK_SENT;
936  return 1;
937 }
938 
939 static int
941  state_machine_called_from_t cf)
942 {
943 
944  /* Start sending data */
945  hs->data_offset = static_send_data (hs, hs->data, vec_len (hs->data),
946  hs->data_offset);
947 
948  /* Did we finish? */
949  if (hs->data_offset < vec_len (hs->data))
950  {
951  /* No: ask for a shoulder-tap when the tx fifo has space */
952  svm_fifo_add_want_deq_ntf (hs->tx_fifo,
954  hs->session_state = HTTP_STATE_SEND_MORE_DATA;
955  return 0;
956  }
957  /* Finished with this transaction, back to HTTP_STATE_ESTABLISHED */
958 
959  /* Let go of the file cache entry */
961  hs->session_state = HTTP_STATE_ESTABLISHED;
962  return 0;
963 }
964 
965 static int
967  state_machine_called_from_t cf)
968 {
970  char *suffix;
971  char *http_type;
972  u8 *http_response;
973  f64 now;
974  u32 offset;
975 
976  /* What kind of dog food are we serving? */
977  suffix = (char *) (hs->path + vec_len (hs->path) - 1);
978  while (*suffix != '.')
979  suffix--;
980  suffix++;
981  http_type = "html";
982  if (!clib_strcmp (suffix, "css"))
983  http_type = "css";
984  else if (!clib_strcmp (suffix, "js"))
985  http_type = "javascript";
986 
987 
988  if (hs->data == 0)
989  {
990  clib_warning ("BUG: hs->data not set for session %d",
991  hs->session_index);
992  close_session (hs);
993  return 0;
994  }
995 
996  /*
997  * Send an http response, which needs the current time,
998  * the expiration time, and the data length
999  */
1000  now = clib_timebase_now (&hsm->timebase);
1001  http_response = format (0, http_response_template,
1002  /* Date */
1004  /* Expires */
1005  format_clib_timebase_time, now + 600.0,
1006  http_type, vec_len (hs->data));
1007  offset = static_send_data (hs, http_response, vec_len (http_response), 0);
1008  if (offset != vec_len (http_response))
1009  {
1010  clib_warning ("BUG: couldn't send response header!");
1011  close_session (hs);
1012  return 0;
1013  }
1014  vec_free (http_response);
1015 
1016  /* Send data from the beginning... */
1017  hs->data_offset = 0;
1018  hs->session_state = HTTP_STATE_SEND_MORE_DATA;
1019  return 1;
1020 }
1021 
1022 static void *state_funcs[HTTP_STATE_N_STATES] = {
1023  state_closed,
1024  /* Waiting for GET, POST, etc. */
1026  /* Sent OK */
1027  state_sent_ok,
1028  /* Send more data */
1030 };
1031 
1032 static inline int
1034  state_machine_called_from_t cf)
1035 {
1036  http_session_t *hs;
1038  int rv;
1039 
1040  /* Acquire a reader lock on the session table */
1043 
1044  if (!hs)
1045  {
1046  clib_warning ("No http session for thread %d session_index %d",
1047  s->thread_index, s->session_index);
1049  return 0;
1050  }
1051 
1052  /* Execute state machine for this session */
1053  do
1054  {
1055  fp = state_funcs[hs->session_state];
1056  rv = (*fp) (s, hs, cf);
1057  if (rv < 0)
1058  goto session_closed;
1059  }
1060  while (rv);
1061 
1062  /* Reset the session expiration timer */
1065 
1066 session_closed:
1068  return 0;
1069 }
1070 
1071 static int
1073 {
1075 }
1076 
1077 static int
1079 {
1081 }
1082 
1083 
1084 /** \brief Session accept callback
1085  */
1086 
1087 static int
1089 {
1091  http_session_t *hs;
1092 
1093  hsm->vpp_queue[s->thread_index] =
1095 
1097 
1100  hs->session_index);
1101  hs->rx_fifo = s->rx_fifo;
1102  hs->tx_fifo = s->tx_fifo;
1105  hs->session_state = HTTP_STATE_ESTABLISHED;
1107 
1109 
1111  return 0;
1112 }
1113 
1114 /** \brief Session disconnect callback
1115  */
1116 
1117 static void
1119 {
1121  vnet_disconnect_args_t _a = { 0 }, *a = &_a;
1122  http_session_t *hs;
1123 
1125 
1128 
1130 
1131  a->handle = session_handle (s);
1132  a->app_index = hsm->app_index;
1134 }
1135 
1136 /** \brief Session reset callback
1137  */
1138 
1139 static void
1141 {
1143  vnet_disconnect_args_t _a = { 0 }, *a = &_a;
1144  http_session_t *hs;
1145 
1147 
1150 
1152 
1153  a->handle = session_handle (s);
1154  a->app_index = hsm->app_index;
1156 }
1157 
1158 static int
1160  session_t * s, u8 is_fail)
1161 {
1162  clib_warning ("called...");
1163  return -1;
1164 }
1165 
1166 static int
1167 http_static_server_add_segment_callback (u32 client_index, u64 segment_handle)
1168 {
1169  clib_warning ("called...");
1170  return -1;
1171 }
1172 
1173 /** \brief Session-layer virtual function table
1174  */
1175 static session_cb_vft_t http_static_server_session_cb_vft = {
1176  .session_accept_callback = http_static_server_session_accept_callback,
1177  .session_disconnect_callback =
1179  .session_connected_callback = http_static_server_session_connected_callback,
1180  .add_segment_callback = http_static_server_add_segment_callback,
1181  .builtin_app_rx_callback = http_static_server_rx_callback,
1182  .builtin_app_tx_callback = http_static_server_tx_callback,
1183  .session_reset_callback = http_static_server_session_reset_callback
1184 };
1185 
1186 static int
1188 {
1189  vnet_app_add_tls_cert_args_t _a_cert, *a_cert = &_a_cert;
1190  vnet_app_add_tls_key_args_t _a_key, *a_key = &_a_key;
1192  u64 options[APP_OPTIONS_N_OPTIONS];
1193  vnet_app_attach_args_t _a, *a = &_a;
1194  u32 segment_size = 128 << 20;
1195 
1196  clib_memset (a, 0, sizeof (*a));
1197  clib_memset (options, 0, sizeof (options));
1198 
1199  if (hsm->private_segment_size)
1200  segment_size = hsm->private_segment_size;
1201 
1202  a->api_client_index = ~0;
1203  a->name = format (0, "test_http_static_server");
1204  a->session_cb_vft = &http_static_server_session_cb_vft;
1205  a->options = options;
1206  a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
1207  a->options[APP_OPTIONS_RX_FIFO_SIZE] =
1208  hsm->fifo_size ? hsm->fifo_size : 8 << 10;
1209  a->options[APP_OPTIONS_TX_FIFO_SIZE] =
1210  hsm->fifo_size ? hsm->fifo_size : 32 << 10;
1211  a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
1212  a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = hsm->prealloc_fifos;
1214 
1215  if (vnet_application_attach (a))
1216  {
1217  vec_free (a->name);
1218  clib_warning ("failed to attach server");
1219  return -1;
1220  }
1221  vec_free (a->name);
1222  hsm->app_index = a->app_index;
1223 
1224  clib_memset (a_cert, 0, sizeof (*a_cert));
1225  a_cert->app_index = a->app_index;
1226  vec_validate (a_cert->cert, test_srv_crt_rsa_len);
1228  vnet_app_add_tls_cert (a_cert);
1229 
1230  clib_memset (a_key, 0, sizeof (*a_key));
1231  a_key->app_index = a->app_index;
1232  vec_validate (a_key->key, test_srv_key_rsa_len);
1234  vnet_app_add_tls_key (a_key);
1235 
1236  return 0;
1237 }
1238 
1239 static int
1241 {
1243  vnet_listen_args_t _a, *a = &_a;
1244  clib_memset (a, 0, sizeof (*a));
1245  a->app_index = hsm->app_index;
1246  a->uri = "tcp://0.0.0.0/80";
1247  if (hsm->uri)
1248  a->uri = (char *) hsm->uri;
1249  return vnet_bind_uri (a);
1250 }
1251 
1252 static void
1254 {
1256  http_session_t *hs;
1257  uword hs_handle;
1258  hs_handle = pointer_to_uword (hs_handlep);
1259  hs =
1260  http_static_server_session_get (hs_handle >> 24, hs_handle & 0x00FFFFFF);
1261 
1262  if (hsm->debug_level > 1)
1263  clib_warning ("terminate thread %d index %d hs %llx",
1264  hs_handle >> 24, hs_handle & 0x00FFFFFF, hs);
1265  if (!hs)
1266  return;
1267  hs->timer_handle = ~0;
1270 }
1271 
1272 /** \brief Expired session timer-wheel callback
1273  */
1274 static void
1276 {
1277  u32 hs_handle;
1278  int i;
1279 
1280  for (i = 0; i < vec_len (expired_timers); i++)
1281  {
1282  /* Get session handle. The first bit is the timer id */
1283  hs_handle = expired_timers[i] & 0x7FFFFFFF;
1284  session_send_rpc_evt_to_thread (hs_handle >> 24,
1286  uword_to_pointer (hs_handle, void *));
1287  }
1288 }
1289 
1290 /** \brief Timer-wheel expiration process
1291  */
1292 static uword
1294  vlib_frame_t * f)
1295 {
1297  f64 now, timeout = 1.0;
1298  uword *event_data = 0;
1299  uword __clib_unused event_type;
1300 
1301  while (1)
1302  {
1304  now = vlib_time_now (vm);
1305  event_type = vlib_process_get_events (vm, (uword **) & event_data);
1306 
1307  /* expire timers */
1309  tw_timer_expire_timers_2t_1w_2048sl (&hsm->tw, now);
1311 
1312  vec_reset_length (event_data);
1313  }
1314  return 0;
1315 }
1316 
1317 /* *INDENT-OFF* */
1318 VLIB_REGISTER_NODE (http_static_server_process_node) =
1319 {
1320  .function = http_static_server_process,
1321  .type = VLIB_NODE_TYPE_PROCESS,
1322  .name = "static-http-server-process",
1323  .state = VLIB_NODE_STATE_DISABLED,
1324 };
1325 /* *INDENT-ON* */
1326 
1327 static int
1329 {
1332  u32 num_threads;
1333  vlib_node_t *n;
1334 
1335  num_threads = 1 /* main thread */ + vtm->n_threads;
1336  vec_validate (hsm->vpp_queue, num_threads - 1);
1337  vec_validate (hsm->sessions, num_threads - 1);
1338  vec_validate (hsm->session_to_http_session, num_threads - 1);
1339 
1341  clib_spinlock_init (&hsm->tw_lock);
1342 
1344  {
1345  clib_warning ("failed to attach server");
1346  return -1;
1347  }
1349  {
1350  clib_warning ("failed to start listening");
1351  return -1;
1352  }
1353 
1354  /* Init path-to-cache hash table */
1355  BV (clib_bihash_init) (&hsm->name_to_data, "http cache", 128, 32 << 20);
1356 
1357  /* Init timer wheel and process */
1358  tw_timer_wheel_init_2t_1w_2048sl (&hsm->tw, http_expired_timers_dispatch,
1359  1.0 /* timer interval */ , ~0);
1360  vlib_node_set_state (vm, http_static_server_process_node.index,
1361  VLIB_NODE_STATE_POLLING);
1362  n = vlib_get_node (vm, http_static_server_process_node.index);
1364 
1365  return 0;
1366 }
1367 
1368 /** \brief API helper function for vl_api_http_static_enable_t messages
1369  */
1370 int
1371 http_static_server_enable_api (u32 fifo_size, u32 cache_limit,
1372  u32 prealloc_fifos,
1373  u32 private_segment_size,
1374  u8 * www_root, u8 * uri)
1375 {
1377  int rv;
1378 
1379  hsm->fifo_size = fifo_size;
1380  hsm->cache_limit = cache_limit;
1381  hsm->prealloc_fifos = prealloc_fifos;
1382  hsm->private_segment_size = private_segment_size;
1383  hsm->www_root = format (0, "%s%c", www_root, 0);
1384  hsm->uri = format (0, "%s%c", uri, 0);
1385 
1386  if (vec_len (hsm->www_root) < 2)
1387  return VNET_API_ERROR_INVALID_VALUE;
1388 
1389  if (hsm->my_client_index != ~0)
1390  return VNET_API_ERROR_APP_ALREADY_ATTACHED;
1391 
1392  vnet_session_enable_disable (hsm->vlib_main, 1 /* turn on TCP, etc. */ );
1393 
1394  rv = http_static_server_create (hsm->vlib_main);
1395  switch (rv)
1396  {
1397  case 0:
1398  break;
1399  default:
1400  vec_free (hsm->www_root);
1401  vec_free (hsm->uri);
1402  return VNET_API_ERROR_INIT_FAILED;
1403  }
1404  return 0;
1405 }
1406 
1407 static clib_error_t *
1409  unformat_input_t * input,
1410  vlib_cli_command_t * cmd)
1411 {
1413  unformat_input_t _line_input, *line_input = &_line_input;
1414  u64 seg_size;
1415  u8 *www_root = 0;
1416  int rv;
1417 
1418  hsm->prealloc_fifos = 0;
1419  hsm->private_segment_size = 0;
1420  hsm->fifo_size = 0;
1421  /* 10mb cache limit, before LRU occurs */
1422  hsm->cache_limit = 10 << 20;
1423 
1424  /* Get a line of input. */
1425  if (!unformat_user (input, unformat_line_input, line_input))
1426  goto no_wwwroot;
1427 
1428  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1429  {
1430  if (unformat (line_input, "www-root %s", &www_root))
1431  ;
1432  else
1433  if (unformat (line_input, "prealloc-fifos %d", &hsm->prealloc_fifos))
1434  ;
1435  else if (unformat (line_input, "private-segment-size %U",
1436  unformat_memory_size, &seg_size))
1437  {
1438  if (seg_size >= 0x100000000ULL)
1439  {
1440  vlib_cli_output (vm, "private segment size %llu, too large",
1441  seg_size);
1442  return 0;
1443  }
1444  hsm->private_segment_size = seg_size;
1445  }
1446  else if (unformat (line_input, "fifo-size %d", &hsm->fifo_size))
1447  hsm->fifo_size <<= 10;
1448  else if (unformat (line_input, "cache-size %U", unformat_memory_size,
1449  &hsm->cache_limit))
1450  {
1451  if (hsm->cache_limit < (128 << 10))
1452  {
1453  return clib_error_return (0,
1454  "cache-size must be at least 128kb");
1455  }
1456  }
1457 
1458  else if (unformat (line_input, "uri %s", &hsm->uri))
1459  ;
1460  else if (unformat (line_input, "debug %d", &hsm->debug_level))
1461  ;
1462  else if (unformat (line_input, "debug"))
1463  hsm->debug_level = 1;
1464  else
1465  return clib_error_return (0, "unknown input `%U'",
1466  format_unformat_error, line_input);
1467  }
1468  unformat_free (line_input);
1469 
1470  if (www_root == 0)
1471  {
1472  no_wwwroot:
1473  return clib_error_return (0, "Must specify www-root <path>");
1474  }
1475 
1476  if (hsm->my_client_index != (u32) ~ 0)
1477  {
1478  vec_free (www_root);
1479  return clib_error_return (0, "http server already running...");
1480  }
1481 
1482  hsm->www_root = www_root;
1483 
1484  vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
1485 
1486  rv = http_static_server_create (vm);
1487  switch (rv)
1488  {
1489  case 0:
1490  break;
1491  default:
1492  vec_free (hsm->www_root);
1493  return clib_error_return (0, "server_create returned %d", rv);
1494  }
1495  return 0;
1496 }
1497 
1498 /*?
1499  * Enable the static http server
1500  *
1501  * @cliexpar
1502  * This command enables the static http server. Only the www-root
1503  * parameter is required
1504  * @clistart
1505  * http static server www-root /tmp/www uri tcp://0.0.0.0/80 cache-size 2m
1506  * @cliend
1507  * @cliexcmd{http static server www-root <path> [prealloc-fios <nn>]
1508  * [private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]}
1509 ?*/
1510 /* *INDENT-OFF* */
1511 VLIB_CLI_COMMAND (http_static_server_create_command, static) =
1512 {
1513  .path = "http static server",
1514  .short_help = "http static server www-root <path> [prealloc-fifos <nn>]\n"
1515  "[private-segment-size <nnMG>] [fifo-size <nbytes>] [uri <uri>]\n"
1516  "[debug [nn]]\n",
1518 };
1519 /* *INDENT-ON* */
1520 
1521 /** \brief format a file cache entry
1522  */
1523 u8 *
1524 format_hsm_cache_entry (u8 * s, va_list * args)
1525 {
1526  file_data_cache_t *ep = va_arg (*args, file_data_cache_t *);
1527  f64 now = va_arg (*args, f64);
1528 
1529  /* Header */
1530  if (ep == 0)
1531  {
1532  s = format (s, "%40s%12s%20s", "File", "Size", "Age");
1533  return s;
1534  }
1535  s = format (s, "%40s%12lld%20.2f", ep->filename, vec_len (ep->data),
1536  now - ep->last_used);
1537  return s;
1538 }
1539 
1540 u8 *
1541 format_http_session_state (u8 * s, va_list * args)
1542 {
1543  http_session_state_t state = va_arg (*args, http_session_state_t);
1544  char *state_string = "bogus!";
1545 
1546  switch (state)
1547  {
1548  case HTTP_STATE_CLOSED:
1549  state_string = "closed";
1550  break;
1552  state_string = "established";
1553  break;
1554  case HTTP_STATE_OK_SENT:
1555  state_string = "ok sent";
1556  break;
1558  state_string = "send more data";
1559  break;
1560  default:
1561  break;
1562  }
1563 
1564  return format (s, "%s", state_string);
1565 }
1566 
1567 u8 *
1568 format_http_session (u8 * s, va_list * args)
1569 {
1570  http_session_t *hs = va_arg (*args, http_session_t *);
1571  int verbose = va_arg (*args, int);
1572 
1573  s = format (s, "[%d]: state %U", hs->session_index,
1574  format_http_session_state, hs->session_state);
1575  if (verbose > 0)
1576  {
1577  s = format (s, "\n path %s, data length %u, data_offset %u",
1578  hs->path ? hs->path : (u8 *) "[none]",
1579  vec_len (hs->data), hs->data_offset);
1580  }
1581  return s;
1582 }
1583 
1584 static clib_error_t *
1586  unformat_input_t * input,
1587  vlib_cli_command_t * cmd)
1588 {
1590  file_data_cache_t *ep, **entries = 0;
1591  int verbose = 0;
1592  int show_cache = 0;
1593  int show_sessions = 0;
1594  u32 index;
1595  f64 now;
1596 
1597  if (hsm->www_root == 0)
1598  return clib_error_return (0, "Static server disabled");
1599 
1600  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1601  {
1602  if (unformat (input, "verbose %d", &verbose))
1603  ;
1604  else if (unformat (input, "verbose"))
1605  verbose = 1;
1606  else if (unformat (input, "cache"))
1607  show_cache = 1;
1608  else if (unformat (input, "sessions"))
1609  show_sessions = 1;
1610  else
1611  break;
1612  }
1613 
1614  if ((show_cache + show_sessions) == 0)
1615  return clib_error_return (0, "specify one or more of cache, sessions");
1616 
1617  if (show_cache)
1618  {
1619  if (verbose == 0)
1620  {
1622  (vm, "www_root %s, cache size %lld bytes, limit %lld bytes, "
1623  "evictions %lld",
1624  hsm->www_root, hsm->cache_size, hsm->cache_limit,
1625  hsm->cache_evictions);
1626  return 0;
1627  }
1628 
1629  now = vlib_time_now (vm);
1630 
1631  vlib_cli_output (vm, "%U", format_hsm_cache_entry, 0 /* header */ ,
1632  now);
1633 
1634  for (index = hsm->first_index; index != ~0;)
1635  {
1636  ep = pool_elt_at_index (hsm->cache_pool, index);
1637  index = ep->next_index;
1638  vlib_cli_output (vm, "%U", format_hsm_cache_entry, ep, now);
1639  }
1640 
1641  vlib_cli_output (vm, "%40s%12lld", "Total Size", hsm->cache_size);
1642 
1643  vec_free (entries);
1644  }
1645 
1646  if (show_sessions)
1647  {
1648  u32 *session_indices = 0;
1649  http_session_t *hs;
1650  int i, j;
1651 
1653 
1654  for (i = 0; i < vec_len (hsm->sessions); i++)
1655  {
1656  /* *INDENT-OFF* */
1657  pool_foreach (hs, hsm->sessions[i],
1658  ({
1659  vec_add1 (session_indices, hs - hsm->sessions[i]);
1660  }));
1661  /* *INDENT-ON* */
1662 
1663  for (j = 0; j < vec_len (session_indices); j++)
1664  {
1667  (hsm->sessions[i], session_indices[j]),
1668  verbose);
1669  }
1670  vec_reset_length (session_indices);
1671  }
1673  vec_free (session_indices);
1674  }
1675  return 0;
1676 }
1677 
1678 /*?
1679  * Display static http server cache statistics
1680  *
1681  * @cliexpar
1682  * This command shows the contents of the static http server cache
1683  * @clistart
1684  * show http static server
1685  * @cliend
1686  * @cliexcmd{show http static server sessions cache [verbose [nn]]}
1687 ?*/
1688 /* *INDENT-OFF* */
1689 VLIB_CLI_COMMAND (http_show_static_server_command, static) =
1690 {
1691  .path = "show http static server",
1692  .short_help = "show http static server sessions cache [verbose [<nn>]]",
1694 };
1695 /* *INDENT-ON* */
1696 
1697 static clib_error_t *
1699 {
1701 
1702  hsm->my_client_index = ~0;
1703  hsm->vlib_main = vm;
1704  hsm->first_index = hsm->last_index = ~0;
1705 
1706  clib_timebase_init (&hsm->timebase, 0 /* GMT */ ,
1708 
1709  return 0;
1710 }
1711 
1713 
1714 /*
1715 * fd.io coding-style-patch-verification: ON
1716 *
1717 * Local Variables:
1718 * eval: (c-set-style "gnu")
1719 * End:
1720 */
static int state_sent_ok(session_t *s, http_session_t *hs, state_machine_called_from_t cf)
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:439
#define foreach_app_session_field
flag for dgram mode
static void clib_rwlock_reader_lock(clib_rwlock_t *p)
Definition: lock.h:148
Session has sent an OK response.
Definition: static_server.c:46
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: static_server.c:96
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.
#define CLIB_CACHE_LINE_ALIGN_MARK(mark)
Definition: cache.h:60
static_always_inline void clib_spinlock_unlock(clib_spinlock_t *p)
Definition: lock.h:100
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:78
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:171
static void http_static_server_session_cleanup(http_session_t *hs)
clean up a session
svm_fifo_t * tx_fifo
static const char * http_response
Definition: http_server.c:247
u32 session_index
Index in thread pool where session was allocated.
unsigned long u64
Definition: types.h:89
static svm_msg_q_t * session_main_get_vpp_event_queue(u32 thread_index)
Definition: session.h:554
u32 timer_handle
Timeout timer handle.
Definition: http_server.c:50
#define clib_memcpy_fast(a, b, c)
Definition: string.h:81
static void http_static_server_sessions_writer_unlock(void)
Drop writer lock on the sessions pools.
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:258
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: static_server.c:84
Main data structure.
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
int i
uword unformat_user(unformat_input_t *input, unformat_function_t *func,...)
Definition: unformat.c:989
format_function_t format_ip46_address
Definition: format.h:61
int debug_level
Enable debug messages.
clib_memset(h->entries, 0, sizeof(h->entries[0])*entries)
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
u8 data[128]
Definition: ipsec.api:249
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:275
struct _vnet_application_add_tls_cert_args_t vnet_app_add_tls_cert_args_t
u8 * path
Fully-resolved file path.
Definition: static_server.c:80
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:236
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:1300
unsigned char u8
Definition: types.h:56
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:1409
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
static int svm_fifo_is_empty(svm_fifo_t *f)
Check if fifo is empty.
Definition: svm_fifo.h:581
#define pool_foreach(VAR, POOL, BODY)
Iterate through pool.
Definition: pool.h:493
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 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:518
static BVT(clib_bihash)
Definition: adj_nbr.c:28
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:92
clib_error_t * clib_file_contents(char *file, u8 **result)
Definition: unix-misc.c:111
static int http_static_server_rx_tx_callback(session_t *s, state_machine_called_from_t cf)
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)
vhost_vring_state_t state
Definition: vhost_user.h:146
static int http_static_server_session_connected_callback(u32 app_index, u32 api_context, session_t *s, u8 is_fail)
unsigned int u32
Definition: types.h:88
static int session_rx_request(http_session_t *hs)
Retrieve data from the application layer.
state_machine_called_from_t called_from
state machine called from...
Definition: static_server.c:88
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:74
static int state_send_more_data(session_t *s, http_session_t *hs, state_machine_called_from_t cf)
struct _vnet_app_attach_args_t vnet_app_attach_args_t
Session is closed.
Definition: static_server.c:42
unformat_function_t unformat_line_input
Definition: format.h:283
static void clib_spinlock_init(clib_spinlock_t *p)
Definition: lock.h:61
u8 * format_hsm_cache_entry(u8 *s, va_list *args)
format a file cache entry
Session is established.
Definition: static_server.c:44
u8 * data
Contents of the file, as a u8 * vector.
Definition: static_server.c:98
clib_error_t * vnet_app_add_tls_key(vnet_app_add_tls_key_args_t *a)
Definition: application.c:1312
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.
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:514
static void clib_rwlock_init(clib_rwlock_t *p)
Definition: lock.h:131
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.
u8 * data
File data, a vector.
Definition: static_server.c:82
u64 vpp_session_handle
Definition: http_server.c:49
static void clib_rwlock_reader_unlock(clib_rwlock_t *p)
Definition: lock.h:163
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:286
#define vec_dup(V)
Return copy of vector (no header, no alignment)
Definition: vec.h:375
int svm_fifo_enqueue(svm_fifo_t *f, u32 len, const u8 *src)
Enqueue data to fifo.
Definition: svm_fifo.c:807
#define PREDICT_FALSE(x)
Definition: clib.h:111
static void svm_fifo_unset_event(svm_fifo_t *f)
Unset fifo event flag.
Definition: svm_fifo.h:752
static void http_expired_timers_dispatch(u32 *expired_timers)
Expired session timer-wheel callback.
Application session.
Definition: http_server.c:40
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:1543
static int http_static_server_rx_callback(session_t *s)
static int state_closed(session_t *s, http_session_t *hs, state_machine_called_from_t cf)
Session-layer (main) data rx callback.
int vnet_application_attach(vnet_app_attach_args_t *a)
Attach application to vpp.
Definition: application.c:809
static void http_static_server_session_lookup_del(u32 thread_index, u32 s_index)
Remove a session from the vpp < – > http session index map.
static u8 svm_fifo_set_event(svm_fifo_t *f)
Set fifo event flag.
Definition: svm_fifo.h:739
static void clib_rwlock_writer_unlock(clib_rwlock_t *p)
Definition: lock.h:185
f64 last_used
Last time the cache entry was used.
#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
vlib_main_t * vm
Definition: buffer.c:312
Session has sent an HTML response.
Definition: static_server.c:48
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:341
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
struct _stream_session_cb_vft session_cb_vft_t
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
http_session_t ** sessions
Per thread vector of session pools.
static void http_static_server_sessions_reader_unlock(void)
Drop reader lock on the sessions pools.
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:283
static u32 static_send_data(http_session_t *hs, u8 *data, u32 length, u32 offset)
send http data
static transport_proto_t session_type_transport_proto(session_type_t st)
clib_rwlock_t sessions_lock
Session pool reader writer lock.
void clib_timebase_init(clib_timebase_t *tb, i32 timezone_offset_in_hours, clib_timebase_daylight_time_t daylight_type)
Definition: time_range.c:19
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:155
u8 * rx_buf
rx buffer
Definition: http_server.c:47
u32 cache_pool_index
File cache pool index.
Definition: static_server.c:86
#define uword_to_pointer(u, type)
Definition: types.h:136
#define ASSERT(truth)
#define vec_delete(V, N, M)
Delete N elements starting at element M.
Definition: vec.h:784
static void http_static_server_session_disconnect_callback(session_t *s)
Session disconnect callback.
static int http_static_server_create(vlib_main_t *vm)
static int http_static_server_listen()
struct _vnet_application_add_tls_key_args_t vnet_app_add_tls_key_args_t
http_session_state_t
Session States.
Definition: static_server.c:39
enum _transport_proto transport_proto_t
#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:766
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
static uword pointer_to_uword(const void *p)
Definition: types.h:131
Notify on transition from full.
Definition: svm_fifo.h:47
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.
int vlib_main(vlib_main_t *volatile vm, unformat_input_t *input)
Definition: main.c:2011
static int state_established(session_t *s, http_session_t *hs, state_machine_called_from_t cf)
established state - waiting for GET, POST, etc.
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
state_machine_called_from_t
Definition: static_server.c:53
u32 next_index
Cache LRU links.
static void http_static_server_sessions_writer_lock(void)
Acquire writer lock on the sessions pools.
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.
int vnet_disconnect_session(vnet_disconnect_args_t *a)
Definition: application.c:1064
vhost_user_req_t request
Definition: vhost_user.h:140
struct _svm_queue svm_queue_t
unformat_function_t unformat_memory_size
Definition: format.h:296
struct clib_bihash_value offset
template key/value backing page structure
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
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)
#define clib_strcmp(s1, s2)
Definition: string.h:826
Number of states.
Definition: static_server.c:50
static int http_static_server_add_segment_callback(u32 client_index, u64 segment_handle)
In-memory file data cache entry.
Definition: static_server.c:93
void vlib_start_process(vlib_main_t *vm, uword process_index)
Definition: main.c:1583
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:768
http_static_server_main_t http_static_server_main
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.
static const u32 test_srv_crt_rsa_len
Definition: tls_test.h:47
vl_api_fib_path_nh_proto_t proto
Definition: fib_types.api:125
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.