FD.io VPP  v17.10-9-gd594711
Vector Packet Processing
builtin_http_server.c
Go to the documentation of this file.
1 /*
2 * Copyright (c) 2015-2017 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 
20 typedef enum
21 {
24 
25 typedef struct
26 {
29  u8 *data;
31 
32 typedef struct
33 {
37 
39 
41 
42  /* Sever's event queue */
44 
45  /* API client handle */
47 
49 
50  /* process node index for evnt scheduling */
54 
56 
57 static void
59 {
63  vlib_node_t *n;
64  u32 node_index;
65  builtin_http_server_args **save_args;
66 
67  node_index = args->node_index;
68  ASSERT (node_index != 0);
69 
70  n = vlib_get_node (vm, node_index);
71  rt = vlib_node_get_runtime (vm, n->index);
72  save_args = vlib_node_get_runtime_data (vm, n->index);
73 
74  /* Reset process session pointer */
75  clib_mem_free (*save_args);
76  *save_args = 0;
77 
78  /* Turn off the process node */
79  vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
80 
81  /* add node index to the freelist */
83 }
84 
85 static const char
86  *http_response = "HTTP/1.1 200 OK\r\n"
87  "Content-Type: text/html\r\n"
88  "Expires: Mon, 11 Jan 1970 10:10:10 GMT\r\n"
89  "Connection: close\r\n"
90  "Pragma: no-cache\r\n" "Content-Length: %d\r\n\r\n%s";
91 
92 static const char
93  *http_error_template = "HTTP/1.1 %s\r\n"
94  "Content-Type: text/html\r\n"
95  "Expires: Mon, 11 Jan 1970 10:10:10 GMT\r\n"
96  "Connection: close\r\n" "Pragma: no-cache\r\n" "Content-Length: 0\r\n\r\n";
97 
98 /* Header, including incantation to suppress favicon.ico requests */
99 static const char
100  *html_header_template = "<html><head><title>%v</title>"
101  "</head><link rel=\"icon\" href=\"data:,\"><body><pre>";
102 
103 static const char *html_footer = "</pre></body></html>\r\n";
104 
105 static void
106 http_cli_output (uword arg, u8 * buffer, uword buffer_bytes)
107 {
108  u8 **output_vecp = (u8 **) arg;
109  u8 *output_vec;
110  u32 offset;
111 
112  output_vec = *output_vecp;
113 
114  offset = vec_len (output_vec);
115  vec_validate (output_vec, offset + buffer_bytes - 1);
116  clib_memcpy (output_vec + offset, buffer, buffer_bytes);
117 
118  *output_vecp = output_vec;
119 }
120 
121 void
123 {
124  session_fifo_event_t evt;
125  u32 offset, bytes_to_send;
126  f64 delay = 10e-3;
128  vlib_main_t *vm = hsm->vlib_main;
129  f64 last_sent_timer = vlib_time_now (vm);
130  stream_session_t *s;
131 
133  ASSERT (s);
134  bytes_to_send = vec_len (data);
135  offset = 0;
136 
137  while (bytes_to_send > 0)
138  {
139  int actual_transfer;
140 
141  actual_transfer = svm_fifo_enqueue_nowait
142  (s->server_tx_fifo, bytes_to_send, data + offset);
143 
144  /* Made any progress? */
145  if (actual_transfer <= 0)
146  {
147  vlib_process_suspend (vm, delay);
148  /* 10s deadman timer */
149  if (vlib_time_now (vm) > last_sent_timer + 10.0)
150  {
151  /* $$$$ FC: reset transport session here? */
152  break;
153  }
154  /* Exponential backoff, within reason */
155  if (delay < 1.0)
156  delay = delay * 2.0;
157  }
158  else
159  {
160  last_sent_timer = vlib_time_now (vm);
161  offset += actual_transfer;
162  bytes_to_send -= actual_transfer;
163 
164  if (svm_fifo_set_event (s->server_tx_fifo))
165  {
166  /* Fabricate TX event, send to vpp */
167  evt.fifo = s->server_tx_fifo;
168  evt.event_type = FIFO_EVENT_APP_TX;
169  evt.event_id = 0;
170 
171  unix_shared_memory_queue_add (hsm->vpp_queue[s->thread_index],
172  (u8 *) & evt,
173  0 /* do wait for mutex */ );
174  }
175  delay = 10e-3;
176  }
177  }
178 }
179 
180 static void
182 {
183  u8 *data;
184 
185  data = format (0, http_error_template, str);
186  send_data (args, data);
187  vec_free (data);
188 }
189 
190 static uword
193 {
195  u8 *request = 0, *reply = 0;
196  builtin_http_server_args **save_args;
198  unformat_input_t input;
199  int i;
200  u8 *http = 0, *html = 0;
201 
202  save_args = vlib_node_get_runtime_data (hsm->vlib_main, rt->node_index);
203  args = *save_args;
204 
205  request = (u8 *) (void *) (args->data);
206  if (vec_len (request) < 7)
207  {
208  send_error (args, "400 Bad Request");
209  goto out;
210  }
211 
212  for (i = 0; i < vec_len (request) - 4; i++)
213  {
214  if (request[i] == 'G' &&
215  request[i + 1] == 'E' &&
216  request[i + 2] == 'T' && request[i + 3] == ' ')
217  goto found;
218  }
219 bad_request:
220  send_error (args, "400 Bad Request");
221  goto out;
222 
223 found:
224  /* Lose "GET " */
225  vec_delete (request, i + 5, 0);
226 
227  /* Replace slashes with spaces, stop at the end of the path */
228  i = 0;
229  while (1)
230  {
231  if (request[i] == '/')
232  request[i] = ' ';
233  else if (request[i] == ' ')
234  {
235  /* vlib_cli_input is vector-based, no need for a NULL */
236  _vec_len (request) = i;
237  break;
238  }
239  i++;
240  /* Should never happen */
241  if (i == vec_len (request))
242  goto bad_request;
243  }
244 
245  /* Generate the html header */
246  html = format (0, html_header_template, request /* title */ );
247 
248  /* Run the command */
249  unformat_init_vector (&input, request);
250  vlib_cli_input (vm, &input, http_cli_output, (uword) & reply);
251  unformat_free (&input);
252  request = 0;
253 
254  /* Generate the html page */
255  html = format (html, "%v", reply);
256  html = format (html, html_footer);
257  /* And the http reply */
258  http = format (0, http_response, vec_len (html), html);
259 
260  /* Send it */
261  send_data (args, http);
262 
263 out:
264  /* Cleanup */
265  vec_free (request);
266  vec_free (reply);
267  vec_free (html);
268  vec_free (http);
269 
270  free_http_process (args);
271  return (0);
272 }
273 
274 static void
276 {
277  char *name;
278  vlib_node_t *n;
280  vlib_main_t *vm = hsm->vlib_main;
282  builtin_http_server_args **save_args;
283 
285  {
287  vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
288  _vec_len (hsm->free_http_cli_process_node_indices) = l - 1;
289  }
290  else
291  {
292  static vlib_node_registration_t r = {
293  .function = http_cli_process,
294  .type = VLIB_NODE_TYPE_PROCESS,
295  .process_log2_n_stack_bytes = 16,
296  .runtime_data_bytes = sizeof (void *),
297  };
298 
299  name = (char *) format (0, "http-cli-%d", l);
300  r.name = name;
301  vlib_register_node (vm, &r);
302  vec_free (name);
303 
304  n = vlib_get_node (vm, r.index);
305  }
306 
307  /* Save the node index in the args. It won't be zero. */
308  args->node_index = n->index;
309 
310  /* Save the args (pointer) in the node runtime */
311  save_args = vlib_node_get_runtime_data (vm, n->index);
312  *save_args = args;
313 
315 }
316 
317 static void
319 {
321 }
322 
323 static int
325 {
326  u32 max_dequeue;
327  int actual_transfer;
329  svm_fifo_t *rx_fifo;
331 
332  rx_fifo = s->server_rx_fifo;
333  max_dequeue = svm_fifo_max_dequeue (rx_fifo);
334  svm_fifo_unset_event (rx_fifo);
335  if (PREDICT_FALSE (max_dequeue == 0))
336  return 0;
337 
338  vec_validate (hsm->rx_buf, max_dequeue - 1);
339  _vec_len (hsm->rx_buf) = max_dequeue;
340 
341  actual_transfer = svm_fifo_dequeue_nowait (rx_fifo, max_dequeue,
342  hsm->rx_buf);
343  ASSERT (actual_transfer > 0);
344  _vec_len (hsm->rx_buf) = actual_transfer;
345 
346  /* send the command to a new/recycled vlib process */
347  args = clib_mem_alloc (sizeof (*args));
348  args->data = vec_dup (hsm->rx_buf);
350 
351  /* Send an RPC request via the thread-0 input node */
352  if (vlib_get_thread_index () != 0)
353  {
354  session_fifo_event_t evt;
355  evt.rpc_args.fp = alloc_http_process_callback;
356  evt.rpc_args.arg = args;
357  evt.event_type = FIFO_EVENT_RPC;
359  (session_manager_get_vpp_event_queue (0 /* main thread */ ),
360  (u8 *) & evt, 0 /* do wait for mutex */ );
361  }
362  else
363  alloc_http_process (args);
364  return 0;
365 }
366 
367 static int
369 {
371 
372  bsm->vpp_queue[s->thread_index] =
373  session_manager_get_vpp_event_queue (s->thread_index);
374  s->session_state = SESSION_STATE_READY;
375  bsm->byte_index = 0;
376  return 0;
377 }
378 
379 static void
381 {
383  vnet_disconnect_args_t _a, *a = &_a;
384 
385  a->handle = stream_session_handle (s);
386  a->app_index = bsm->app_index;
388 }
389 
390 static void
392 {
393  clib_warning ("called.. ");
394 
396 }
397 
398 static int
400  stream_session_t * s, u8 is_fail)
401 {
402  clib_warning ("called...");
403  return -1;
404 }
405 
406 static int
408  const u8 * seg_name, u32 seg_size)
409 {
410  clib_warning ("called...");
411  return -1;
412 }
413 
414 static int
415 builtin_redirect_connect_callback (u32 client_index, void *mp)
416 {
417  clib_warning ("called...");
418  return -1;
419 }
420 
422  .session_accept_callback = builtin_session_accept_callback,
423  .session_disconnect_callback = builtin_session_disconnect_callback,
424  .session_connected_callback = builtin_session_connected_callback,
425  .add_segment_callback = builtin_add_segment_callback,
426  .redirect_connect_callback = builtin_redirect_connect_callback,
427  .builtin_server_rx_callback = http_server_rx_callback,
428  .session_reset_callback = builtin_session_reset_callback
429 };
430 
431 /* Abuse VPP's input queue */
432 static int
434 {
436  api_main_t *am = &api_main;
438 
439  shmem_hdr = am->shmem_hdr;
440  hsm->vl_input_queue = shmem_hdr->vl_input_queue;
441  hsm->my_client_index =
442  vl_api_memclnt_create_internal ("tcp_test_client", hsm->vl_input_queue);
443  return 0;
444 }
445 
446 static int
448 {
450  u8 segment_name[128];
452  vnet_app_attach_args_t _a, *a = &_a;
453 
454  memset (a, 0, sizeof (*a));
455  memset (options, 0, sizeof (options));
456 
457  a->api_client_index = hsm->my_client_index;
458  a->session_cb_vft = &builtin_session_cb_vft;
459  a->options = options;
460  a->options[SESSION_OPTIONS_SEGMENT_SIZE] = 128 << 20;
461  a->options[SESSION_OPTIONS_RX_FIFO_SIZE] = 8 << 10;
462  a->options[SESSION_OPTIONS_TX_FIFO_SIZE] = 32 << 10;
463  a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_BUILTIN_APP;
464  a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = 16;
465  a->segment_name = segment_name;
466  a->segment_name_length = ARRAY_LEN (segment_name);
467 
468  if (vnet_application_attach (a))
469  {
470  clib_warning ("failed to attach server");
471  return -1;
472  }
473  hsm->app_index = a->app_index;
474  return 0;
475 }
476 
477 static int
479 {
481  vnet_bind_args_t _a, *a = &_a;
482  memset (a, 0, sizeof (*a));
483  a->app_index = hsm->app_index;
484  a->uri = "tcp://0.0.0.0/80";
485  return vnet_bind_uri (a);
486 }
487 
488 static int
490 {
492  u32 num_threads;
494 
495  ASSERT (hsm->my_client_index == (u32) ~ 0);
496  if (create_api_loopback (vm))
497  return -1;
498 
499  num_threads = 1 /* main thread */ + vtm->n_threads;
500  vec_validate (http_server_main.vpp_queue, num_threads - 1);
501 
502  if (server_attach ())
503  {
504  clib_warning ("failed to attach server");
505  return -1;
506  }
507  if (server_listen ())
508  {
509  clib_warning ("failed to start listening");
510  return -1;
511  }
512  return 0;
513 }
514 
515 static clib_error_t *
517  unformat_input_t * input, vlib_cli_command_t * cmd)
518 {
520  int rv;
521 
522  if (hsm->my_client_index != (u32) ~ 0)
523  return clib_error_return (0, "test http server is already running");
524 
525  vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
526  rv = server_create (vm);
527  switch (rv)
528  {
529  case 0:
530  break;
531  default:
532  return clib_error_return (0, "server_create returned %d", rv);
533  }
534  return 0;
535 }
536 
537 /* *INDENT-OFF* */
538 VLIB_CLI_COMMAND (server_create_command, static) =
539 {
540  .path = "test http server",
541  .short_help = "test http server",
542  .function = server_create_command_fn,
543 };
544 /* *INDENT-ON* */
545 
546 static clib_error_t *
548 {
550  hsm->my_client_index = ~0;
551  hsm->vlib_main = vm;
552 
553  return 0;
554 }
555 
557 
558 /*
559 * fd.io coding-style-patch-verification: ON
560 *
561 * Local Variables:
562 * eval: (c-set-style "gnu")
563 * End:
564 */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:432
static void http_cli_output(uword arg, u8 *buffer, uword buffer_bytes)
vlib_main_t vlib_global_main
Definition: main.c:1650
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:337
a
Definition: bitmap.h:516
void vlib_cli_input(vlib_main_t *vm, unformat_input_t *input, vlib_cli_output_function_t *function, uword function_arg)
Definition: cli.c:643
int vnet_bind_uri(vnet_bind_args_t *a)
unix_shared_memory_queue_t * vl_input_queue
Definition: api_common.h:68
static const char * http_error_template
static void alloc_http_process_callback(void *cb_args)
u32 index
Definition: node.h:237
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:221
static int http_server_rx_callback(stream_session_t *s)
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:518
struct _vlib_node_registration vlib_node_registration_t
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
static void free_http_process(builtin_http_server_args *args)
static int builtin_add_segment_callback(u32 client_index, const u8 *seg_name, u32 seg_size)
static const char * http_response
static int server_attach()
struct _svm_fifo svm_fifo_t
static uword vlib_process_suspend(vlib_main_t *vm, f64 dt)
Suspend a vlib cooperative multi-tasking thread for a period of time.
Definition: node_funcs.h:448
unix_shared_memory_queue_t ** vpp_queue
static clib_error_t * server_create_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
static void alloc_http_process(builtin_http_server_args *args)
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
struct _vnet_disconnect_args_t vnet_disconnect_args_t
static u32 svm_fifo_max_dequeue(svm_fifo_t *f)
Definition: svm_fifo.h:100
static int builtin_session_connected_callback(u32 app_index, u32 api_context, stream_session_t *s, u8 is_fail)
struct _stream_session_cb_vft session_cb_vft_t
#define clib_error_return(e, args...)
Definition: error.h:99
unsigned long u64
Definition: types.h:89
struct vl_shmem_hdr_ * shmem_hdr
Binary API shared-memory segment header pointer.
Definition: api_common.h:241
static int server_listen()
void stream_session_cleanup(stream_session_t *s)
Cleanup transport and session state.
Definition: session.c:776
struct _stream_session_t stream_session_t
static const char * html_header_template
struct _vnet_app_attach_args_t vnet_app_attach_args_t
http_server_main_t http_server_main
vl_shmem_hdr_t * shmem_hdr
int unix_shared_memory_queue_add(unix_shared_memory_queue_t *q, u8 *elem, int nowait)
struct _unformat_input_t unformat_input_t
#define vec_dup(V)
Return copy of vector (no header, no alignment)
Definition: vec.h:370
static void * vlib_node_get_runtime_data(vlib_main_t *vm, u32 node_index)
Get node runtime private data by node index.
Definition: node_funcs.h:110
#define PREDICT_FALSE(x)
Definition: clib.h:97
static void svm_fifo_unset_event(svm_fifo_t *f)
Unsets fifo event flag.
Definition: svm_fifo.h:133
u32 node_index
Node index.
Definition: node.h:437
clib_error_t * vnet_session_enable_disable(vlib_main_t *vm, u8 is_en)
Definition: session.c:928
API main structure, used by both vpp and binary API clients.
Definition: api_common.h:182
void unformat_init_vector(unformat_input_t *input, u8 *vector_string)
Definition: unformat.c:1031
static u8 svm_fifo_set_event(svm_fifo_t *f)
Sets fifo event flag.
Definition: svm_fifo.h:123
static unix_shared_memory_queue_t * session_manager_get_vpp_event_queue(u32 thread_index)
Definition: session.h:345
api_main_t api_main
Definition: api_shared.c:35
u32 runtime_index
Definition: node.h:240
static_always_inline uword vlib_get_thread_index(void)
Definition: threads.h:221
vlib_main_t * vm
Definition: buffer.c:283
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:336
#define clib_warning(format, args...)
Definition: error.h:59
static vlib_node_runtime_t * vlib_node_get_runtime(vlib_main_t *vm, u32 node_index)
Get node runtime by node index.
Definition: node_funcs.h:89
#define clib_memcpy(a, b, c)
Definition: string.h:69
u32 vl_api_memclnt_create_internal(char *, unix_shared_memory_queue_t *)
Definition: memory_vlib.c:152
int vnet_disconnect_session(vnet_disconnect_args_t *a)
#define ARRAY_LEN(x)
Definition: clib.h:59
int svm_fifo_enqueue_nowait(svm_fifo_t *f, u32 max_bytes, u8 *copy_from_here)
Definition: svm_fifo.c:533
static int builtin_session_accept_callback(stream_session_t *s)
static int builtin_redirect_connect_callback(u32 client_index, void *mp)
unix_shared_memory_queue_t * vl_input_queue
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:154
int vnet_application_attach(vnet_app_attach_args_t *a)
Attaches application.
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
#define vec_delete(V, N, M)
Delete N elements starting at element M.
Definition: vec.h:781
u32 vlib_register_node(vlib_main_t *vm, vlib_node_registration_t *r)
Definition: node.c:498
static void clib_mem_free(void *p)
Definition: mem.h:179
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:147
static void * clib_mem_alloc(uword size)
Definition: mem.h:112
u64 uword
Definition: types.h:112
static u64 stream_session_handle(stream_session_t *s)
Definition: session.h:237
static clib_error_t * builtin_http_server_main_init(vlib_main_t *vm)
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
double f64
Definition: types.h:142
unsigned char u8
Definition: types.h:56
static void unformat_free(unformat_input_t *i)
Definition: format.h:161
void send_data(builtin_http_server_args *args, u8 *data)
static int create_api_loopback(vlib_main_t *vm)
static int server_create(vlib_main_t *vm)
static uword http_cli_process(vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
struct clib_bihash_value offset
template key/value backing page structure
static const char * html_footer
vhost_user_req_t request
Definition: vhost-user.h:76
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
http_process_event_t
void vlib_start_process(vlib_main_t *vm, uword process_index)
Definition: main.c:1367
int svm_fifo_dequeue_nowait(svm_fifo_t *f, u32 max_bytes, u8 *copy_here)
Definition: svm_fifo.c:690
struct _vnet_bind_args_t vnet_bind_args_t
static void builtin_session_disconnect_callback(stream_session_t *s)
static stream_session_t * stream_session_get_from_handle(u64 handle)
Definition: session.h:262
static session_cb_vft_t builtin_session_cb_vft
static void builtin_session_reset_callback(stream_session_t *s)
u32 * free_http_cli_process_node_indices
struct _unix_shared_memory_queue unix_shared_memory_queue_t
static void send_error(builtin_http_server_args *args, char *str)