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