vcl session: switch to generic cert key apis
[vpp.git] / src / plugins / hs_apps / http_server.c
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>
17 #include <vnet/session/application.h>
18 #include <vnet/session/application_interface.h>
19 #include <vnet/session/session.h>
20 #include <vppinfra/tw_timer_2t_1w_2048sl.h>
21
22 typedef enum
23 {
24   EVENT_WAKEUP = 1,
25 } http_process_event_t;
26
27 typedef struct
28 {
29   u32 hs_index;
30   u32 thread_index;
31   u64 node_index;
32 } http_server_args;
33
34 typedef enum
35 {
36   HTTP_STATE_CLOSED,
37   HTTP_STATE_ESTABLISHED,
38   HTTP_STATE_OK_SENT,
39 } http_session_state_t;
40 typedef struct
41 {
42   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
43 #define _(type, name) type name;
44   foreach_app_session_field
45 #undef _
46   u32 thread_index;
47   u8 *rx_buf;
48   u32 vpp_session_index;
49   u64 vpp_session_handle;
50   u32 timer_handle;
51 } http_session_t;
52
53 typedef struct
54 {
55   http_session_t **sessions;
56   clib_rwlock_t sessions_lock;
57   u32 **session_to_http_session;
58
59   svm_msg_q_t **vpp_queue;
60
61   uword *handler_by_get_request;
62
63   u32 *free_http_cli_process_node_indices;
64
65   /* Sever's event queue */
66   svm_queue_t *vl_input_queue;
67
68   /* API client handle */
69   u32 my_client_index;
70
71   u32 app_index;
72
73   /* process node index for evnt scheduling */
74   u32 node_index;
75
76   /* Cert key pair for tls */
77   u32 ckpair_index;
78
79   tw_timer_wheel_2t_1w_2048sl_t tw;
80   clib_spinlock_t tw_lock;
81
82   u32 prealloc_fifos;
83   u32 private_segment_size;
84   u32 fifo_size;
85   u8 *uri;
86   u32 is_static;
87   vlib_main_t *vlib_main;
88 } http_server_main_t;
89
90 http_server_main_t http_server_main;
91
92 static void
93 http_server_sessions_reader_lock (void)
94 {
95   clib_rwlock_reader_lock (&http_server_main.sessions_lock);
96 }
97
98 static void
99 http_server_sessions_reader_unlock (void)
100 {
101   clib_rwlock_reader_unlock (&http_server_main.sessions_lock);
102 }
103
104 static void
105 http_server_sessions_writer_lock (void)
106 {
107   clib_rwlock_writer_lock (&http_server_main.sessions_lock);
108 }
109
110 static void
111 http_server_sessions_writer_unlock (void)
112 {
113   clib_rwlock_writer_unlock (&http_server_main.sessions_lock);
114 }
115
116 static http_session_t *
117 http_server_session_alloc (u32 thread_index)
118 {
119   http_server_main_t *hsm = &http_server_main;
120   http_session_t *hs;
121   pool_get (hsm->sessions[thread_index], hs);
122   memset (hs, 0, sizeof (*hs));
123   hs->session_index = hs - hsm->sessions[thread_index];
124   hs->thread_index = thread_index;
125   hs->timer_handle = ~0;
126   return hs;
127 }
128
129 static http_session_t *
130 http_server_session_get (u32 thread_index, u32 hs_index)
131 {
132   http_server_main_t *hsm = &http_server_main;
133   if (pool_is_free_index (hsm->sessions[thread_index], hs_index))
134     return 0;
135   return pool_elt_at_index (hsm->sessions[thread_index], hs_index);
136 }
137
138 static void
139 http_server_session_free (http_session_t * hs)
140 {
141   http_server_main_t *hsm = &http_server_main;
142   u32 thread = hs->thread_index;
143   if (CLIB_DEBUG)
144     memset (hs, 0xfa, sizeof (*hs));
145   pool_put (hsm->sessions[thread], hs);
146 }
147
148 static void
149 http_server_session_lookup_add (u32 thread_index, u32 s_index, u32 hs_index)
150 {
151   http_server_main_t *hsm = &http_server_main;
152   vec_validate (hsm->session_to_http_session[thread_index], s_index);
153   hsm->session_to_http_session[thread_index][s_index] = hs_index;
154 }
155
156 static void
157 http_server_session_lookup_del (u32 thread_index, u32 s_index)
158 {
159   http_server_main_t *hsm = &http_server_main;
160   hsm->session_to_http_session[thread_index][s_index] = ~0;
161 }
162
163 static http_session_t *
164 http_server_session_lookup (u32 thread_index, u32 s_index)
165 {
166   http_server_main_t *hsm = &http_server_main;
167   u32 hs_index;
168
169   if (s_index < vec_len (hsm->session_to_http_session[thread_index]))
170     {
171       hs_index = hsm->session_to_http_session[thread_index][s_index];
172       return http_server_session_get (thread_index, hs_index);
173     }
174   return 0;
175 }
176
177
178 static void
179 http_server_session_timer_start (http_session_t * hs)
180 {
181   u32 hs_handle;
182   hs_handle = hs->thread_index << 24 | hs->session_index;
183   clib_spinlock_lock (&http_server_main.tw_lock);
184   hs->timer_handle = tw_timer_start_2t_1w_2048sl (&http_server_main.tw,
185                                                   hs_handle, 0, 60);
186   clib_spinlock_unlock (&http_server_main.tw_lock);
187 }
188
189 static void
190 http_server_session_timer_stop (http_session_t * hs)
191 {
192   if (hs->timer_handle == ~0)
193     return;
194   clib_spinlock_lock (&http_server_main.tw_lock);
195   tw_timer_stop_2t_1w_2048sl (&http_server_main.tw, hs->timer_handle);
196   clib_spinlock_unlock (&http_server_main.tw_lock);
197 }
198
199 static void
200 http_server_session_disconnect (http_session_t * hs)
201 {
202   vnet_disconnect_args_t _a = { 0 }, *a = &_a;
203   a->handle = hs->vpp_session_handle;
204   a->app_index = http_server_main.app_index;
205   vnet_disconnect_session (a);
206 }
207
208 static void
209 http_process_free (http_server_args * args)
210 {
211   vlib_node_runtime_t *rt;
212   vlib_main_t *vm = &vlib_global_main;
213   http_server_main_t *hsm = &http_server_main;
214   vlib_node_t *n;
215   u32 node_index;
216   http_server_args **save_args;
217
218   node_index = args->node_index;
219   ASSERT (node_index != 0);
220
221   n = vlib_get_node (vm, node_index);
222   rt = vlib_node_get_runtime (vm, n->index);
223   save_args = vlib_node_get_runtime_data (vm, n->index);
224
225   /* Reset process session pointer */
226   clib_mem_free (*save_args);
227   *save_args = 0;
228
229   /* Turn off the process node */
230   vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
231
232   /* add node index to the freelist */
233   vec_add1 (hsm->free_http_cli_process_node_indices, node_index);
234 }
235
236 /* *INDENT-OFF* */
237 static const char *http_ok =
238     "HTTP/1.1 200 OK\r\n";
239
240 static const char *http_response =
241     "Content-Type: text/html\r\n"
242     "Expires: Mon, 11 Jan 1970 10:10:10 GMT\r\n"
243     "Connection: close \r\n"
244     "Pragma: no-cache\r\n"
245     "Content-Length: %d\r\n\r\n%v";
246
247 static const char *http_error_template =
248     "HTTP/1.1 %s\r\n"
249     "Content-Type: text/html\r\n"
250     "Expires: Mon, 11 Jan 1970 10:10:10 GMT\r\n"
251     "Connection: close\r\n"
252     "Pragma: no-cache\r\n"
253     "Content-Length: 0\r\n\r\n";
254
255 /* Header, including incantation to suppress favicon.ico requests */
256 static const char *html_header_template =
257     "<html><head><title>%v</title></head>"
258     "<link rel=\"icon\" href=\"data:,\">"
259     "<body><pre>";
260
261 static const char *html_footer =
262     "</pre></body></html>\r\n";
263
264 static const char *html_header_static =
265     "<html><head><title>static reply</title></head>"
266     "<link rel=\"icon\" href=\"data:,\">"
267     "<body><pre>hello</pre></body></html>\r\n";
268 /* *INDENT-ON* */
269
270 static u8 *static_http;
271 static u8 *static_ok;
272
273 static void
274 http_cli_output (uword arg, u8 * buffer, uword buffer_bytes)
275 {
276   u8 **output_vecp = (u8 **) arg;
277   u8 *output_vec;
278   u32 offset;
279
280   output_vec = *output_vecp;
281
282   offset = vec_len (output_vec);
283   vec_validate (output_vec, offset + buffer_bytes - 1);
284   clib_memcpy_fast (output_vec + offset, buffer, buffer_bytes);
285
286   *output_vecp = output_vec;
287 }
288
289 void
290 send_data (http_session_t * hs, u8 * data)
291 {
292   http_server_main_t *hsm = &http_server_main;
293   vnet_disconnect_args_t _a = { 0 }, *a = &_a;
294   vlib_main_t *vm = vlib_get_main ();
295   f64 last_sent_timer = vlib_time_now (vm);
296   u32 offset, bytes_to_send;
297   f64 delay = 10e-3;
298
299   bytes_to_send = vec_len (data);
300   offset = 0;
301
302   while (bytes_to_send > 0)
303     {
304       int actual_transfer;
305
306       actual_transfer = svm_fifo_enqueue
307         (hs->tx_fifo, bytes_to_send, data + offset);
308
309       /* Made any progress? */
310       if (actual_transfer <= 0)
311         {
312           http_server_sessions_reader_unlock ();
313           vlib_process_suspend (vm, delay);
314           http_server_sessions_reader_lock ();
315
316           /* 10s deadman timer */
317           if (vlib_time_now (vm) > last_sent_timer + 10.0)
318             {
319               a->handle = hs->vpp_session_handle;
320               a->app_index = hsm->app_index;
321               vnet_disconnect_session (a);
322               break;
323             }
324           /* Exponential backoff, within reason */
325           if (delay < 1.0)
326             delay = delay * 2.0;
327         }
328       else
329         {
330           last_sent_timer = vlib_time_now (vm);
331           offset += actual_transfer;
332           bytes_to_send -= actual_transfer;
333
334           if (svm_fifo_set_event (hs->tx_fifo))
335             session_send_io_evt_to_thread (hs->tx_fifo,
336                                            SESSION_IO_EVT_TX_FLUSH);
337           delay = 10e-3;
338         }
339     }
340 }
341
342 static void
343 send_error (http_session_t * hs, char *str)
344 {
345   u8 *data;
346
347   data = format (0, http_error_template, str);
348   send_data (hs, data);
349   vec_free (data);
350 }
351
352 static uword
353 http_cli_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
354                   vlib_frame_t * f)
355 {
356   u8 *request = 0, *reply = 0, *http = 0, *html = 0;
357   http_server_main_t *hsm = &http_server_main;
358   http_server_args **save_args;
359   http_server_args *args;
360   unformat_input_t input;
361   http_session_t *hs;
362   int i;
363
364   save_args = vlib_node_get_runtime_data (hsm->vlib_main, rt->node_index);
365   args = *save_args;
366
367   http_server_sessions_reader_lock ();
368
369   hs = http_server_session_get (args->thread_index, args->hs_index);
370   ASSERT (hs);
371
372   request = hs->rx_buf;
373   if (vec_len (request) < 7)
374     {
375       send_error (hs, "400 Bad Request");
376       goto out;
377     }
378
379   for (i = 0; i < vec_len (request) - 4; i++)
380     {
381       if (request[i] == 'G' &&
382           request[i + 1] == 'E' &&
383           request[i + 2] == 'T' && request[i + 3] == ' ')
384         goto found;
385     }
386 bad_request:
387   send_error (hs, "400 Bad Request");
388   goto out;
389
390 found:
391   /* Lose "GET " */
392   vec_delete (request, i + 5, 0);
393
394   /* Replace slashes with spaces, stop at the end of the path */
395   i = 0;
396   while (1)
397     {
398       if (request[i] == '/')
399         request[i] = ' ';
400       else if (request[i] == ' ')
401         {
402           /* vlib_cli_input is vector-based, no need for a NULL */
403           _vec_len (request) = i;
404           break;
405         }
406       i++;
407       /* Should never happen */
408       if (i == vec_len (request))
409         goto bad_request;
410     }
411
412   /* Generate the html header */
413   html = format (0, html_header_template, request /* title */ );
414
415   /* Run the command */
416   unformat_init_vector (&input, vec_dup (request));
417   vlib_cli_input (vm, &input, http_cli_output, (uword) & reply);
418   unformat_free (&input);
419   request = 0;
420
421   /* Generate the html page */
422   html = format (html, "%v", reply);
423   html = format (html, html_footer);
424   /* And the http reply */
425   http = format (0, http_ok);
426   http = format (http, http_response, vec_len (html), html);
427
428   /* Send it */
429   send_data (hs, http);
430
431 out:
432   /* Cleanup */
433   http_server_sessions_reader_unlock ();
434   vec_free (reply);
435   vec_free (html);
436   vec_free (http);
437
438   http_process_free (args);
439   return (0);
440 }
441
442 static void
443 alloc_http_process (http_server_args * args)
444 {
445   char *name;
446   vlib_node_t *n;
447   http_server_main_t *hsm = &http_server_main;
448   vlib_main_t *vm = hsm->vlib_main;
449   uword l = vec_len (hsm->free_http_cli_process_node_indices);
450   http_server_args **save_args;
451
452   if (vec_len (hsm->free_http_cli_process_node_indices) > 0)
453     {
454       n = vlib_get_node (vm, hsm->free_http_cli_process_node_indices[l - 1]);
455       vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
456       _vec_len (hsm->free_http_cli_process_node_indices) = l - 1;
457     }
458   else
459     {
460       static vlib_node_registration_t r = {
461         .function = http_cli_process,
462         .type = VLIB_NODE_TYPE_PROCESS,
463         .process_log2_n_stack_bytes = 16,
464         .runtime_data_bytes = sizeof (void *),
465       };
466
467       name = (char *) format (0, "http-cli-%d", l);
468       r.name = name;
469       vlib_register_node (vm, &r);
470       vec_free (name);
471
472       n = vlib_get_node (vm, r.index);
473     }
474
475   /* Save the node index in the args. It won't be zero. */
476   args->node_index = n->index;
477
478   /* Save the args (pointer) in the node runtime */
479   save_args = vlib_node_get_runtime_data (vm, n->index);
480   *save_args = clib_mem_alloc (sizeof (*args));
481   clib_memcpy_fast (*save_args, args, sizeof (*args));
482
483   vlib_start_process (vm, n->runtime_index);
484 }
485
486 static void
487 alloc_http_process_callback (void *cb_args)
488 {
489   alloc_http_process ((http_server_args *) cb_args);
490 }
491
492 static int
493 session_rx_request (http_session_t * hs)
494 {
495   u32 max_dequeue, cursize;
496   int n_read;
497
498   cursize = vec_len (hs->rx_buf);
499   max_dequeue = svm_fifo_max_dequeue_cons (hs->rx_fifo);
500   if (PREDICT_FALSE (max_dequeue == 0))
501     return -1;
502
503   vec_validate (hs->rx_buf, cursize + max_dequeue - 1);
504   n_read = app_recv_stream_raw (hs->rx_fifo, hs->rx_buf + cursize,
505                                 max_dequeue, 0, 0 /* peek */ );
506   ASSERT (n_read == max_dequeue);
507   if (svm_fifo_is_empty_cons (hs->rx_fifo))
508     svm_fifo_unset_event (hs->rx_fifo);
509
510   _vec_len (hs->rx_buf) = cursize + n_read;
511   return 0;
512 }
513
514 static int
515 http_server_rx_callback (session_t * s)
516 {
517   http_server_args args;
518   http_session_t *hs;
519   int rv;
520
521   http_server_sessions_reader_lock ();
522
523   hs = http_server_session_lookup (s->thread_index, s->session_index);
524   if (!hs || hs->session_state != HTTP_STATE_ESTABLISHED)
525     return -1;
526
527   rv = session_rx_request (hs);
528   if (rv)
529     return rv;
530
531   /* send the command to a new/recycled vlib process */
532   args.hs_index = hs->session_index;
533   args.thread_index = hs->thread_index;
534
535   http_server_sessions_reader_unlock ();
536
537   /* Send RPC request to main thread */
538   if (vlib_get_thread_index () != 0)
539     vlib_rpc_call_main_thread (alloc_http_process_callback, (u8 *) & args,
540                                sizeof (args));
541   else
542     alloc_http_process (&args);
543   return 0;
544 }
545
546 static int
547 http_server_rx_callback_static (session_t * s)
548 {
549   http_session_t *hs;
550   u32 request_len;
551   u8 *request = 0;
552   int i, rv;
553
554   hs = http_server_session_lookup (s->thread_index, s->session_index);
555   if (!hs || hs->session_state == HTTP_STATE_CLOSED)
556     return 0;
557
558   /* ok 200 was sent */
559   if (hs->session_state == HTTP_STATE_OK_SENT)
560     goto send_data;
561
562   rv = session_rx_request (hs);
563   if (rv)
564     goto wait_for_data;
565
566   request = hs->rx_buf;
567   request_len = vec_len (request);
568   if (vec_len (request) < 7)
569     {
570       send_error (hs, "400 Bad Request");
571       goto close_session;
572     }
573
574   for (i = 0; i < request_len - 4; i++)
575     {
576       if (request[i] == 'G' &&
577           request[i + 1] == 'E' &&
578           request[i + 2] == 'T' && request[i + 3] == ' ')
579         goto find_end;
580     }
581   send_error (hs, "400 Bad Request");
582   goto close_session;
583
584 find_end:
585
586   /* check for the end sequence: /r/n/r/n */
587   if (request[request_len - 1] != 0xa || request[request_len - 3] != 0xa
588       || request[request_len - 2] != 0xd || request[request_len - 4] != 0xd)
589     goto wait_for_data;
590
591   /* send 200 OK first */
592   send_data (hs, static_ok);
593   hs->session_state = HTTP_STATE_OK_SENT;
594   goto postpone;
595
596 send_data:
597   send_data (hs, static_http);
598
599 close_session:
600   http_server_session_disconnect (hs);
601   return 0;
602
603 postpone:
604   (void) svm_fifo_set_event (hs->rx_fifo);
605   session_send_io_evt_to_thread (hs->rx_fifo, SESSION_IO_EVT_BUILTIN_RX);
606   return 0;
607
608 wait_for_data:
609   return 0;
610 }
611
612 static int
613 http_server_session_accept_callback (session_t * s)
614 {
615   http_server_main_t *hsm = &http_server_main;
616   http_session_t *hs;
617
618   hsm->vpp_queue[s->thread_index] =
619     session_main_get_vpp_event_queue (s->thread_index);
620
621   if (!hsm->is_static)
622     http_server_sessions_writer_lock ();
623
624   hs = http_server_session_alloc (s->thread_index);
625   http_server_session_lookup_add (s->thread_index, s->session_index,
626                                   hs->session_index);
627   hs->rx_fifo = s->rx_fifo;
628   hs->tx_fifo = s->tx_fifo;
629   hs->vpp_session_index = s->session_index;
630   hs->vpp_session_handle = session_handle (s);
631   hs->session_state = HTTP_STATE_ESTABLISHED;
632   http_server_session_timer_start (hs);
633
634   if (!hsm->is_static)
635     http_server_sessions_writer_unlock ();
636
637   s->session_state = SESSION_STATE_READY;
638   return 0;
639 }
640
641 static void
642 http_server_session_disconnect_callback (session_t * s)
643 {
644   http_server_main_t *hsm = &http_server_main;
645   vnet_disconnect_args_t _a = { 0 }, *a = &_a;
646
647   a->handle = session_handle (s);
648   a->app_index = hsm->app_index;
649   vnet_disconnect_session (a);
650 }
651
652 static void
653 http_server_session_reset_callback (session_t * s)
654 {
655   http_server_main_t *hsm = &http_server_main;
656   vnet_disconnect_args_t _a = { 0 }, *a = &_a;
657
658   a->handle = session_handle (s);
659   a->app_index = hsm->app_index;
660   vnet_disconnect_session (a);
661 }
662
663 static int
664 http_server_session_connected_callback (u32 app_index, u32 api_context,
665                                         session_t * s, session_error_t err)
666 {
667   clib_warning ("called...");
668   return -1;
669 }
670
671 static int
672 http_server_add_segment_callback (u32 client_index, u64 segment_handle)
673 {
674   clib_warning ("called...");
675   return -1;
676 }
677
678 static void
679 http_server_cleanup_callback (session_t * s, session_cleanup_ntf_t ntf)
680 {
681   http_server_main_t *hsm = &http_server_main;
682   http_session_t *hs;
683
684   if (ntf == SESSION_CLEANUP_TRANSPORT)
685     return;
686
687   if (!hsm->is_static)
688     http_server_sessions_writer_lock ();
689
690   hs = http_server_session_lookup (s->thread_index, s->session_index);
691   if (!hs)
692     goto done;
693
694   http_server_session_lookup_del (hs->thread_index, hs->vpp_session_index);
695   vec_free (hs->rx_buf);
696   http_server_session_timer_stop (hs);
697   http_server_session_free (hs);
698
699 done:
700
701   if (!hsm->is_static)
702     http_server_sessions_writer_unlock ();
703 }
704
705 static session_cb_vft_t http_server_session_cb_vft = {
706   .session_accept_callback = http_server_session_accept_callback,
707   .session_disconnect_callback = http_server_session_disconnect_callback,
708   .session_connected_callback = http_server_session_connected_callback,
709   .add_segment_callback = http_server_add_segment_callback,
710   .builtin_app_rx_callback = http_server_rx_callback,
711   .session_reset_callback = http_server_session_reset_callback,
712   .session_cleanup_callback = http_server_cleanup_callback,
713 };
714
715 static int
716 http_server_attach ()
717 {
718   vnet_app_add_cert_key_pair_args_t _ck_pair, *ck_pair = &_ck_pair;
719   http_server_main_t *hsm = &http_server_main;
720   u64 options[APP_OPTIONS_N_OPTIONS];
721   vnet_app_attach_args_t _a, *a = &_a;
722   u32 segment_size = 128 << 20;
723
724   clib_memset (a, 0, sizeof (*a));
725   clib_memset (options, 0, sizeof (options));
726
727   if (hsm->private_segment_size)
728     segment_size = hsm->private_segment_size;
729
730   a->api_client_index = ~0;
731   a->name = format (0, "test_http_server");
732   a->session_cb_vft = &http_server_session_cb_vft;
733   a->options = options;
734   a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
735   a->options[APP_OPTIONS_RX_FIFO_SIZE] =
736     hsm->fifo_size ? hsm->fifo_size : 8 << 10;
737   a->options[APP_OPTIONS_TX_FIFO_SIZE] =
738     hsm->fifo_size ? hsm->fifo_size : 32 << 10;
739   a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
740   a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = hsm->prealloc_fifos;
741
742   if (vnet_application_attach (a))
743     {
744       vec_free (a->name);
745       clib_warning ("failed to attach server");
746       return -1;
747     }
748   vec_free (a->name);
749   hsm->app_index = a->app_index;
750
751   clib_memset (ck_pair, 0, sizeof (*ck_pair));
752   ck_pair->cert = (u8 *) test_srv_crt_rsa;
753   ck_pair->key = (u8 *) test_srv_key_rsa;
754   ck_pair->cert_len = test_srv_crt_rsa_len;
755   ck_pair->key_len = test_srv_key_rsa_len;
756   vnet_app_add_cert_key_pair (ck_pair);
757   hsm->ckpair_index = ck_pair->index;
758
759   return 0;
760 }
761
762 static int
763 http_server_listen ()
764 {
765   session_endpoint_cfg_t sep = SESSION_ENDPOINT_CFG_NULL;
766   http_server_main_t *hsm = &http_server_main;
767   vnet_listen_args_t _a, *a = &_a;
768   char *uri = "tcp://0.0.0.0/80";
769
770   clib_memset (a, 0, sizeof (*a));
771   a->app_index = hsm->app_index;
772
773   if (hsm->uri)
774     uri = (char *) hsm->uri;
775
776   if (parse_uri (uri, &sep))
777     return -1;
778
779   clib_memcpy (&a->sep_ext, &sep, sizeof (sep));
780   a->sep_ext.ckpair_index = hsm->ckpair_index;
781
782   return vnet_listen (a);
783 }
784
785 static void
786 http_server_session_close_cb (void *hs_handlep)
787 {
788   http_session_t *hs;
789   uword hs_handle;
790   hs_handle = pointer_to_uword (hs_handlep);
791   hs = http_server_session_get (hs_handle >> 24, hs_handle & 0x00FFFFFF);
792   if (!hs)
793     return;
794   hs->timer_handle = ~0;
795   http_server_session_disconnect (hs);
796 }
797
798 static void
799 http_expired_timers_dispatch (u32 * expired_timers)
800 {
801   u32 hs_handle;
802   int i;
803
804   for (i = 0; i < vec_len (expired_timers); i++)
805     {
806       /* Get session handle. The first bit is the timer id */
807       hs_handle = expired_timers[i] & 0x7FFFFFFF;
808       session_send_rpc_evt_to_thread (hs_handle >> 24,
809                                       http_server_session_close_cb,
810                                       uword_to_pointer (hs_handle, void *));
811     }
812 }
813
814 static uword
815 http_server_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
816                      vlib_frame_t * f)
817 {
818   http_server_main_t *hsm = &http_server_main;
819   f64 now, timeout = 1.0;
820   uword *event_data = 0;
821   uword __clib_unused event_type;
822
823   while (1)
824     {
825       vlib_process_wait_for_event_or_clock (vm, timeout);
826       now = vlib_time_now (vm);
827       event_type = vlib_process_get_events (vm, (uword **) & event_data);
828
829       /* expire timers */
830       clib_spinlock_lock (&http_server_main.tw_lock);
831       tw_timer_expire_timers_2t_1w_2048sl (&hsm->tw, now);
832       clib_spinlock_unlock (&http_server_main.tw_lock);
833
834       vec_reset_length (event_data);
835     }
836   return 0;
837 }
838
839 /* *INDENT-OFF* */
840 VLIB_REGISTER_NODE (http_server_process_node) =
841 {
842   .function = http_server_process,
843   .type = VLIB_NODE_TYPE_PROCESS,
844   .name = "http-server-process",
845   .state = VLIB_NODE_STATE_DISABLED,
846 };
847 /* *INDENT-ON* */
848
849 static int
850 http_server_create (vlib_main_t * vm)
851 {
852   vlib_thread_main_t *vtm = vlib_get_thread_main ();
853   http_server_main_t *hsm = &http_server_main;
854   u32 num_threads;
855   vlib_node_t *n;
856
857   num_threads = 1 /* main thread */  + vtm->n_threads;
858   vec_validate (hsm->vpp_queue, num_threads - 1);
859   vec_validate (hsm->sessions, num_threads - 1);
860   vec_validate (hsm->session_to_http_session, num_threads - 1);
861
862   clib_rwlock_init (&hsm->sessions_lock);
863   clib_spinlock_init (&hsm->tw_lock);
864
865   if (http_server_attach ())
866     {
867       clib_warning ("failed to attach server");
868       return -1;
869     }
870   if (http_server_listen ())
871     {
872       clib_warning ("failed to start listening");
873       return -1;
874     }
875
876   /* Init timer wheel and process */
877   tw_timer_wheel_init_2t_1w_2048sl (&hsm->tw, http_expired_timers_dispatch,
878                                     1 /* timer interval */ , ~0);
879   vlib_node_set_state (vm, http_server_process_node.index,
880                        VLIB_NODE_STATE_POLLING);
881   n = vlib_get_node (vm, http_server_process_node.index);
882   vlib_start_process (vm, n->runtime_index);
883
884   return 0;
885 }
886
887 static clib_error_t *
888 http_server_create_command_fn (vlib_main_t * vm,
889                                unformat_input_t * input,
890                                vlib_cli_command_t * cmd)
891 {
892   http_server_main_t *hsm = &http_server_main;
893   unformat_input_t _line_input, *line_input = &_line_input;
894   u64 seg_size;
895   u8 *html;
896   int rv;
897
898   hsm->prealloc_fifos = 0;
899   hsm->private_segment_size = 0;
900   hsm->fifo_size = 0;
901   hsm->is_static = 0;
902
903   /* Get a line of input. */
904   if (!unformat_user (input, unformat_line_input, line_input))
905     goto start_server;
906
907   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
908     {
909       if (unformat (line_input, "static"))
910         hsm->is_static = 1;
911       else
912         if (unformat (line_input, "prealloc-fifos %d", &hsm->prealloc_fifos))
913         ;
914       else if (unformat (line_input, "private-segment-size %U",
915                          unformat_memory_size, &seg_size))
916         {
917           if (seg_size >= 0x100000000ULL)
918             {
919               vlib_cli_output (vm, "private segment size %llu, too large",
920                                seg_size);
921               return 0;
922             }
923           hsm->private_segment_size = seg_size;
924         }
925       else if (unformat (line_input, "fifo-size %d", &hsm->fifo_size))
926         hsm->fifo_size <<= 10;
927       else if (unformat (line_input, "uri %s", &hsm->uri))
928         ;
929       else
930         return clib_error_return (0, "unknown input `%U'",
931                                   format_unformat_error, line_input);
932     }
933   unformat_free (line_input);
934
935 start_server:
936
937   if (hsm->my_client_index != (u32) ~ 0)
938     return clib_error_return (0, "test http server is already running");
939
940   vnet_session_enable_disable (vm, 1 /* turn on TCP, etc. */ );
941
942   if (hsm->is_static)
943     {
944       http_server_session_cb_vft.builtin_app_rx_callback =
945         http_server_rx_callback_static;
946       html = format (0, html_header_static);
947       static_http = format (0, http_response, vec_len (html), html);
948       static_ok = format (0, http_ok);
949     }
950   rv = http_server_create (vm);
951   switch (rv)
952     {
953     case 0:
954       break;
955     default:
956       return clib_error_return (0, "server_create returned %d", rv);
957     }
958   return 0;
959 }
960
961 /* *INDENT-OFF* */
962 VLIB_CLI_COMMAND (http_server_create_command, static) =
963 {
964   .path = "test http server",
965   .short_help = "test http server",
966   .function = http_server_create_command_fn,
967 };
968 /* *INDENT-ON* */
969
970 static clib_error_t *
971 http_server_main_init (vlib_main_t * vm)
972 {
973   http_server_main_t *hsm = &http_server_main;
974
975   hsm->my_client_index = ~0;
976   hsm->vlib_main = vm;
977   return 0;
978 }
979
980 VLIB_INIT_FUNCTION (http_server_main_init);
981
982 /*
983 * fd.io coding-style-patch-verification: ON
984 *
985 * Local Variables:
986 * eval: (c-set-style "gnu")
987 * End:
988 */