http: fix http server in response
[vpp.git] / src / plugins / http / http.c
1 /*
2  * Copyright (c) 2022 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 <http/http.h>
17 #include <vnet/session/session.h>
18 #include <http/http_timer.h>
19
20 static http_main_t http_main;
21
22 #define HTTP_FIFO_THRESH (16 << 10)
23 #define CONTENT_LEN_STR  "Content-Length: "
24
25 /* HTTP state machine result */
26 typedef enum http_sm_result_t_
27 {
28   HTTP_SM_STOP = 0,
29   HTTP_SM_CONTINUE = 1,
30   HTTP_SM_ERROR = -1,
31 } http_sm_result_t;
32
33 const char *http_status_code_str[] = {
34 #define _(c, s, str) str,
35   foreach_http_status_code
36 #undef _
37 };
38
39 const char *http_content_type_str[] = {
40 #define _(s, ext, str) str,
41   foreach_http_content_type
42 #undef _
43 };
44
45 const http_buffer_type_t msg_to_buf_type[] = {
46   [HTTP_MSG_DATA_INLINE] = HTTP_BUFFER_FIFO,
47   [HTTP_MSG_DATA_PTR] = HTTP_BUFFER_PTR,
48 };
49
50 u8 *
51 format_http_state (u8 *s, va_list *va)
52 {
53   http_state_t state = va_arg (*va, http_state_t);
54
55   switch (state)
56     {
57     case HTTP_STATE_IDLE:
58       return format (s, "idle");
59     case HTTP_STATE_WAIT_APP_METHOD:
60       return format (s, "wait app method");
61     case HTTP_STATE_WAIT_SERVER_REPLY:
62       return format (s, "wait server reply");
63     case HTTP_STATE_CLIENT_IO_MORE_DATA:
64       return format (s, "client io more data");
65     case HTTP_STATE_WAIT_CLIENT_METHOD:
66       return format (s, "wait client method");
67     case HTTP_STATE_WAIT_APP_REPLY:
68       return format (s, "wait app reply");
69     case HTTP_STATE_APP_IO_MORE_DATA:
70       return format (s, "app io more data");
71     default:
72       break;
73     }
74   return format (s, "unknown");
75 }
76
77 #define http_state_change(_hc, _state)                                        \
78   do                                                                          \
79     {                                                                         \
80       HTTP_DBG (1, "changing http state %U -> %U", format_http_state,         \
81                 (_hc)->http_state, format_http_state, _state);                \
82       (_hc)->http_state = _state;                                             \
83     }                                                                         \
84   while (0)
85
86 static inline http_worker_t *
87 http_worker_get (u32 thread_index)
88 {
89   return &http_main.wrk[thread_index];
90 }
91
92 static inline u32
93 http_conn_alloc_w_thread (u32 thread_index)
94 {
95   http_worker_t *wrk = http_worker_get (thread_index);
96   http_conn_t *hc;
97
98   pool_get_aligned_safe (wrk->conn_pool, hc, CLIB_CACHE_LINE_BYTES);
99   clib_memset (hc, 0, sizeof (*hc));
100   hc->c_thread_index = thread_index;
101   hc->h_hc_index = hc - wrk->conn_pool;
102   hc->h_pa_session_handle = SESSION_INVALID_HANDLE;
103   hc->h_tc_session_handle = SESSION_INVALID_HANDLE;
104   return hc->h_hc_index;
105 }
106
107 static inline http_conn_t *
108 http_conn_get_w_thread (u32 hc_index, u32 thread_index)
109 {
110   http_worker_t *wrk = http_worker_get (thread_index);
111   return pool_elt_at_index (wrk->conn_pool, hc_index);
112 }
113
114 void
115 http_conn_free (http_conn_t *hc)
116 {
117   http_worker_t *wrk = http_worker_get (hc->c_thread_index);
118   pool_put (wrk->conn_pool, hc);
119 }
120
121 static u32
122 http_listener_alloc (void)
123 {
124   http_main_t *hm = &http_main;
125   http_conn_t *lhc;
126
127   pool_get_zero (hm->listener_pool, lhc);
128   lhc->c_c_index = lhc - hm->listener_pool;
129   return lhc->c_c_index;
130 }
131
132 http_conn_t *
133 http_listener_get (u32 lhc_index)
134 {
135   return pool_elt_at_index (http_main.listener_pool, lhc_index);
136 }
137
138 void
139 http_listener_free (http_conn_t *lhc)
140 {
141   http_main_t *hm = &http_main;
142
143   vec_free (lhc->app_name);
144   if (CLIB_DEBUG)
145     memset (lhc, 0xfc, sizeof (*lhc));
146   pool_put (hm->listener_pool, lhc);
147 }
148
149 void
150 http_disconnect_transport (http_conn_t *hc)
151 {
152   vnet_disconnect_args_t a = {
153     .handle = hc->h_tc_session_handle,
154     .app_index = http_main.app_index,
155   };
156
157   hc->state = HTTP_CONN_STATE_CLOSED;
158
159   if (vnet_disconnect_session (&a))
160     clib_warning ("disconnect returned");
161 }
162
163 static void
164 http_conn_timeout_cb (void *hc_handlep)
165 {
166   http_conn_t *hc;
167   uword hs_handle;
168
169   hs_handle = pointer_to_uword (hc_handlep);
170   hc = http_conn_get_w_thread (hs_handle & 0x00FFFFFF, hs_handle >> 24);
171
172   HTTP_DBG (1, "terminate thread %d index %d hs %llx", hs_handle >> 24,
173             hs_handle & 0x00FFFFFF, hc);
174   if (!hc)
175     return;
176
177   hc->timer_handle = ~0;
178   session_transport_closing_notify (&hc->connection);
179   http_disconnect_transport (hc);
180 }
181
182 int
183 http_ts_accept_callback (session_t *ts)
184 {
185   session_t *ts_listener, *as, *asl;
186   app_worker_t *app_wrk;
187   http_conn_t *lhc, *hc;
188   u32 hc_index, thresh;
189   int rv;
190
191   ts_listener = listen_session_get_from_handle (ts->listener_handle);
192   lhc = http_listener_get (ts_listener->opaque);
193
194   hc_index = http_conn_alloc_w_thread (ts->thread_index);
195   hc = http_conn_get_w_thread (hc_index, ts->thread_index);
196   clib_memcpy_fast (hc, lhc, sizeof (*lhc));
197   hc->c_thread_index = ts->thread_index;
198   hc->h_hc_index = hc_index;
199
200   hc->h_tc_session_handle = session_handle (ts);
201   hc->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
202
203   hc->state = HTTP_CONN_STATE_ESTABLISHED;
204   http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD);
205
206   ts->session_state = SESSION_STATE_READY;
207   ts->opaque = hc_index;
208
209   /*
210    * Alloc session and initialize
211    */
212   as = session_alloc (hc->c_thread_index);
213   hc->c_s_index = as->session_index;
214
215   as->app_wrk_index = hc->h_pa_wrk_index;
216   as->connection_index = hc->c_c_index;
217   as->session_state = SESSION_STATE_ACCEPTING;
218
219   asl = listen_session_get_from_handle (lhc->h_pa_session_handle);
220   as->session_type = asl->session_type;
221   as->listener_handle = lhc->h_pa_session_handle;
222
223   /*
224    * Init session fifos and notify app
225    */
226   if ((rv = app_worker_init_accepted (as)))
227     {
228       HTTP_DBG (1, "failed to allocate fifos");
229       session_free (as);
230       return rv;
231     }
232
233   hc->h_pa_session_handle = session_handle (as);
234   hc->h_pa_wrk_index = as->app_wrk_index;
235   app_wrk = app_worker_get (as->app_wrk_index);
236
237   HTTP_DBG (1, "Accepted on listener %u new connection [%u]%x",
238             ts_listener->opaque, vlib_get_thread_index (), hc_index);
239
240   if ((rv = app_worker_accept_notify (app_wrk, as)))
241     {
242       HTTP_DBG (0, "app accept returned");
243       session_free (as);
244       return rv;
245     }
246
247   /* Avoid enqueuing small chunks of data on transport tx notifications. If
248    * the fifo is small (under 16K) we set the threshold to it's size, meaning
249    * a notification will be given when the fifo empties.
250    */
251   ts = session_get_from_handle (hc->h_tc_session_handle);
252   thresh = clib_min (svm_fifo_size (ts->tx_fifo), HTTP_FIFO_THRESH);
253   svm_fifo_set_deq_thresh (ts->tx_fifo, thresh);
254
255   http_conn_timer_start (hc);
256
257   return 0;
258 }
259
260 static int
261 http_ts_connected_callback (u32 http_app_index, u32 ho_hc_index, session_t *ts,
262                             session_error_t err)
263 {
264   u32 new_hc_index;
265   session_t *as;
266   http_conn_t *hc, *ho_hc;
267   app_worker_t *app_wrk;
268   int rv;
269
270   if (err)
271     {
272       clib_warning ("ERROR: %d", err);
273       return 0;
274     }
275
276   new_hc_index = http_conn_alloc_w_thread (ts->thread_index);
277   hc = http_conn_get_w_thread (new_hc_index, ts->thread_index);
278   ho_hc = http_conn_get_w_thread (ho_hc_index, 0);
279
280   ASSERT (ho_hc->state == HTTP_CONN_STATE_CONNECTING);
281
282   clib_memcpy_fast (hc, ho_hc, sizeof (*hc));
283
284   hc->c_thread_index = ts->thread_index;
285   hc->h_tc_session_handle = session_handle (ts);
286   hc->c_c_index = new_hc_index;
287   hc->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
288   hc->state = HTTP_CONN_STATE_ESTABLISHED;
289   http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD);
290
291   ts->session_state = SESSION_STATE_READY;
292   ts->opaque = new_hc_index;
293
294   /* allocate app session and initialize */
295
296   as = session_alloc (hc->c_thread_index);
297   hc->c_s_index = as->session_index;
298   as->connection_index = hc->c_c_index;
299   as->app_wrk_index = hc->h_pa_wrk_index;
300   as->session_state = SESSION_STATE_READY;
301   as->opaque = hc->h_pa_app_api_ctx;
302   as->session_type = session_type_from_proto_and_ip (
303     TRANSPORT_PROTO_HTTP, session_type_is_ip4 (ts->session_type));
304
305   HTTP_DBG (1, "half-open hc index %d,  hc index %d", ho_hc_index,
306             new_hc_index);
307
308   app_wrk = app_worker_get (hc->h_pa_wrk_index);
309   if (!app_wrk)
310     {
311       clib_warning ("no app worker");
312       return -1;
313     }
314
315   if ((rv = app_worker_init_connected (app_wrk, as)))
316     {
317       HTTP_DBG (1, "failed to allocate fifos");
318       session_free (as);
319       return rv;
320     }
321   app_worker_connect_notify (app_wrk, as, err, hc->h_pa_app_api_ctx);
322   hc->h_pa_session_handle = session_handle (as);
323   http_conn_timer_start (hc);
324
325   return 0;
326 }
327
328 static void
329 http_ts_disconnect_callback (session_t *ts)
330 {
331   http_conn_t *hc;
332
333   hc = http_conn_get_w_thread (ts->opaque, ts->thread_index);
334
335   if (hc->state < HTTP_CONN_STATE_TRANSPORT_CLOSED)
336     hc->state = HTTP_CONN_STATE_TRANSPORT_CLOSED;
337
338   /* Nothing more to rx, propagate to app */
339   if (!svm_fifo_max_dequeue_cons (ts->rx_fifo))
340     session_transport_closing_notify (&hc->connection);
341 }
342
343 static void
344 http_ts_reset_callback (session_t *ts)
345 {
346   http_conn_t *hc;
347
348   hc = http_conn_get_w_thread (ts->opaque, ts->thread_index);
349
350   hc->state = HTTP_CONN_STATE_CLOSED;
351   http_buffer_free (&hc->tx_buf);
352   http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD);
353   session_transport_reset_notify (&hc->connection);
354
355   http_disconnect_transport (hc);
356 }
357
358 /**
359  * http error boilerplate
360  */
361 static const char *http_error_template = "HTTP/1.1 %s\r\n"
362                                          "Date: %U GMT\r\n"
363                                          "Content-Type: text/html\r\n"
364                                          "Connection: close\r\n"
365                                          "Pragma: no-cache\r\n"
366                                          "Content-Length: 0\r\n\r\n";
367
368 static const char *http_redirect_template = "HTTP/1.1 %s\r\n";
369
370 /**
371  * http response boilerplate
372  */
373 static const char *http_response_template = "HTTP/1.1 %s\r\n"
374                                             "Date: %U GMT\r\n"
375                                             "Expires: %U GMT\r\n"
376                                             "Server: %s\r\n"
377                                             "Content-Type: %s\r\n"
378                                             "Content-Length: %lu\r\n\r\n";
379
380 static const char *http_request_template = "GET %s HTTP/1.1\r\n"
381                                            "User-Agent: VPP HTTP client\r\n"
382                                            "Accept: */*\r\n";
383
384 static u32
385 http_send_data (http_conn_t *hc, u8 *data, u32 length, u32 offset)
386 {
387   const u32 max_burst = 64 << 10;
388   session_t *ts;
389   u32 to_send;
390   int sent;
391
392   ts = session_get_from_handle (hc->h_tc_session_handle);
393
394   to_send = clib_min (length - offset, max_burst);
395   sent = svm_fifo_enqueue (ts->tx_fifo, to_send, data + offset);
396
397   if (sent <= 0)
398     return offset;
399
400   if (svm_fifo_set_event (ts->tx_fifo))
401     session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
402
403   return (offset + sent);
404 }
405
406 static void
407 http_send_error (http_conn_t *hc, http_status_code_t ec)
408 {
409   http_main_t *hm = &http_main;
410   u8 *data;
411   f64 now;
412
413   if (ec >= HTTP_N_STATUS)
414     ec = HTTP_STATUS_INTERNAL_ERROR;
415
416   now = clib_timebase_now (&hm->timebase);
417   data = format (0, http_error_template, http_status_code_str[ec],
418                  format_clib_timebase_time, now);
419   http_send_data (hc, data, vec_len (data), 0);
420   vec_free (data);
421 }
422
423 static int
424 http_read_message (http_conn_t *hc)
425 {
426   u32 max_deq, cursize;
427   session_t *ts;
428   int n_read;
429
430   ts = session_get_from_handle (hc->h_tc_session_handle);
431
432   cursize = vec_len (hc->rx_buf);
433   max_deq = svm_fifo_max_dequeue (ts->rx_fifo);
434   if (PREDICT_FALSE (max_deq == 0))
435     return -1;
436
437   vec_validate (hc->rx_buf, cursize + max_deq - 1);
438   n_read = svm_fifo_dequeue (ts->rx_fifo, max_deq, hc->rx_buf + cursize);
439   ASSERT (n_read == max_deq);
440
441   if (svm_fifo_is_empty (ts->rx_fifo))
442     svm_fifo_unset_event (ts->rx_fifo);
443
444   vec_set_len (hc->rx_buf, cursize + n_read);
445   return 0;
446 }
447
448 static int
449 v_find_index (u8 *vec, u32 offset, char *str)
450 {
451   int start_index = offset;
452   u32 slen = (u32) strnlen_s_inline (str, 16);
453   u32 vlen = vec_len (vec);
454
455   ASSERT (slen > 0);
456
457   if (vlen <= slen)
458     return -1;
459
460   for (; start_index < (vlen - slen); start_index++)
461     {
462       if (!memcmp (vec + start_index, str, slen))
463         return start_index;
464     }
465
466   return -1;
467 }
468
469 static int
470 http_parse_header (http_conn_t *hc, int *content_length)
471 {
472   unformat_input_t input;
473   int i, len;
474   u8 *line;
475
476   i = v_find_index (hc->rx_buf, hc->rx_buf_offset, CONTENT_LEN_STR);
477   if (i < 0)
478     {
479       clib_warning ("cannot find '%s' in the header!", CONTENT_LEN_STR);
480       return -1;
481     }
482
483   hc->rx_buf_offset = i;
484
485   i = v_find_index (hc->rx_buf, hc->rx_buf_offset, "\n");
486   if (i < 0)
487     {
488       clib_warning ("end of line missing; incomplete data");
489       return -1;
490     }
491
492   len = i - hc->rx_buf_offset;
493   line = vec_new (u8, len);
494   clib_memcpy (line, hc->rx_buf + hc->rx_buf_offset, len);
495
496   unformat_init_vector (&input, line);
497   if (!unformat (&input, CONTENT_LEN_STR "%d", content_length))
498     {
499       clib_warning ("failed to unformat content length!");
500       return -1;
501     }
502   unformat_free (&input);
503
504   /* skip rest of the header */
505   hc->rx_buf_offset += len;
506   i = v_find_index (hc->rx_buf, hc->rx_buf_offset, "<html>");
507   if (i < 0)
508     {
509       clib_warning ("<html> tag not found");
510       return -1;
511     }
512   hc->rx_buf_offset = i;
513
514   return 0;
515 }
516
517 static http_sm_result_t
518 http_state_wait_server_reply (http_conn_t *hc, transport_send_params_t *sp)
519 {
520   int i, rv, content_length;
521   http_msg_t msg = {};
522   app_worker_t *app_wrk;
523   session_t *as;
524   http_status_code_t ec;
525
526   rv = http_read_message (hc);
527
528   /* Nothing yet, wait for data or timer expire */
529   if (rv)
530     return HTTP_SM_STOP;
531
532   if (vec_len (hc->rx_buf) < 8)
533     {
534       ec = HTTP_STATUS_BAD_REQUEST;
535       goto error;
536     }
537
538   if ((i = v_find_index (hc->rx_buf, 0, "200 OK")) >= 0)
539     {
540       msg.type = HTTP_MSG_REPLY;
541       msg.content_type = HTTP_CONTENT_TEXT_HTML;
542       msg.code = HTTP_STATUS_OK;
543       msg.data.type = HTTP_MSG_DATA_INLINE;
544       msg.data.len = 0;
545
546       rv = http_parse_header (hc, &content_length);
547       if (rv)
548         {
549           clib_warning ("failed to parse http reply");
550           session_transport_closing_notify (&hc->connection);
551           http_disconnect_transport (hc);
552           return -1;
553         }
554       msg.data.len = content_length;
555       u32 dlen = vec_len (hc->rx_buf) - hc->rx_buf_offset;
556       as = session_get_from_handle (hc->h_pa_session_handle);
557       svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) },
558                                  { &hc->rx_buf[hc->rx_buf_offset], dlen } };
559
560       rv = svm_fifo_enqueue_segments (as->rx_fifo, segs, 2,
561                                       0 /* allow partial */);
562       if (rv < 0)
563         {
564           clib_warning ("error enqueue");
565           return HTTP_SM_ERROR;
566         }
567
568       hc->rx_buf_offset += dlen;
569       hc->to_recv = content_length - dlen;
570
571       if (hc->rx_buf_offset == vec_len (hc->rx_buf))
572         {
573           vec_reset_length (hc->rx_buf);
574           hc->rx_buf_offset = 0;
575         }
576
577       if (hc->to_recv == 0)
578         {
579           hc->rx_buf_offset = 0;
580           vec_reset_length (hc->rx_buf);
581           http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD);
582         }
583       else
584         {
585           http_state_change (hc, HTTP_STATE_CLIENT_IO_MORE_DATA);
586         }
587
588       app_wrk = app_worker_get_if_valid (as->app_wrk_index);
589       if (app_wrk)
590         app_worker_rx_notify (app_wrk, as);
591       return HTTP_SM_STOP;
592     }
593   else
594     {
595       HTTP_DBG (0, "Unknown http method %v", hc->rx_buf);
596       ec = HTTP_STATUS_METHOD_NOT_ALLOWED;
597       goto error;
598     }
599
600 error:
601
602   http_send_error (hc, ec);
603   session_transport_closing_notify (&hc->connection);
604   http_disconnect_transport (hc);
605
606   return HTTP_SM_ERROR;
607 }
608
609 static http_sm_result_t
610 http_state_wait_client_method (http_conn_t *hc, transport_send_params_t *sp)
611 {
612   http_status_code_t ec;
613   app_worker_t *app_wrk;
614   http_msg_t msg;
615   session_t *as;
616   int i, rv;
617   u32 len;
618   u8 *buf;
619
620   rv = http_read_message (hc);
621
622   /* Nothing yet, wait for data or timer expire */
623   if (rv)
624     return HTTP_SM_STOP;
625
626   if (vec_len (hc->rx_buf) < 8)
627     {
628       ec = HTTP_STATUS_BAD_REQUEST;
629       goto error;
630     }
631
632   if ((i = v_find_index (hc->rx_buf, 0, "GET ")) >= 0)
633     {
634       hc->method = HTTP_REQ_GET;
635       hc->rx_buf_offset = i + 5;
636
637       i = v_find_index (hc->rx_buf, hc->rx_buf_offset, "HTTP");
638       if (i < 0)
639         {
640           ec = HTTP_STATUS_BAD_REQUEST;
641           goto error;
642         }
643
644       HTTP_DBG (0, "GET method %v", hc->rx_buf);
645       len = i - hc->rx_buf_offset - 1;
646     }
647   else if ((i = v_find_index (hc->rx_buf, 0, "POST ")) >= 0)
648     {
649       hc->method = HTTP_REQ_POST;
650       hc->rx_buf_offset = i + 6;
651       len = vec_len (hc->rx_buf) - hc->rx_buf_offset - 1;
652       HTTP_DBG (0, "POST method %v", hc->rx_buf);
653     }
654   else
655     {
656       HTTP_DBG (0, "Unknown http method %v", hc->rx_buf);
657       ec = HTTP_STATUS_METHOD_NOT_ALLOWED;
658       goto error;
659     }
660
661   buf = &hc->rx_buf[hc->rx_buf_offset];
662
663   msg.type = HTTP_MSG_REQUEST;
664   msg.method_type = hc->method;
665   msg.content_type = HTTP_CONTENT_TEXT_HTML;
666   msg.data.type = HTTP_MSG_DATA_INLINE;
667   msg.data.len = len;
668
669   svm_fifo_seg_t segs[2] = { { (u8 *) &msg, sizeof (msg) }, { buf, len } };
670
671   as = session_get_from_handle (hc->h_pa_session_handle);
672   rv = svm_fifo_enqueue_segments (as->rx_fifo, segs, 2, 0 /* allow partial */);
673   if (rv < 0 || rv != sizeof (msg) + len)
674     {
675       clib_warning ("failed app enqueue");
676       /* This should not happen as we only handle 1 request per session,
677        * and fifo is allocated, but going forward we should consider
678        * rescheduling */
679       return HTTP_SM_ERROR;
680     }
681
682   vec_free (hc->rx_buf);
683   http_state_change (hc, HTTP_STATE_WAIT_APP_REPLY);
684
685   app_wrk = app_worker_get_if_valid (as->app_wrk_index);
686   if (app_wrk)
687     app_worker_rx_notify (app_wrk, as);
688
689   return HTTP_SM_STOP;
690
691 error:
692
693   http_send_error (hc, ec);
694   session_transport_closing_notify (&hc->connection);
695   http_disconnect_transport (hc);
696
697   return HTTP_SM_ERROR;
698 }
699
700 static http_sm_result_t
701 http_state_wait_app_reply (http_conn_t *hc, transport_send_params_t *sp)
702 {
703   http_main_t *hm = &http_main;
704   u8 *header;
705   u32 offset;
706   f64 now;
707   session_t *as;
708   http_status_code_t sc;
709   http_msg_t msg;
710   int rv;
711
712   as = session_get_from_handle (hc->h_pa_session_handle);
713
714   rv = svm_fifo_dequeue (as->tx_fifo, sizeof (msg), (u8 *) &msg);
715   ASSERT (rv == sizeof (msg));
716
717   if (msg.data.type > HTTP_MSG_DATA_PTR)
718     {
719       clib_warning ("no data");
720       sc = HTTP_STATUS_INTERNAL_ERROR;
721       goto error;
722     }
723
724   if (msg.type != HTTP_MSG_REPLY)
725     {
726       clib_warning ("unexpected message type %d", msg.type);
727       sc = HTTP_STATUS_INTERNAL_ERROR;
728       goto error;
729     }
730
731   http_buffer_init (&hc->tx_buf, msg_to_buf_type[msg.data.type], as->tx_fifo,
732                     msg.data.len);
733
734   /*
735    * Add headers. For now:
736    * - current time
737    * - expiration time
738    * - server name
739    * - content type
740    * - data length
741    */
742   now = clib_timebase_now (&hm->timebase);
743
744   switch (msg.code)
745     {
746     case HTTP_STATUS_OK:
747       header =
748         format (0, http_response_template, http_status_code_str[msg.code],
749                 /* Date */
750                 format_clib_timebase_time, now,
751                 /* Expires */
752                 format_clib_timebase_time, now + 600.0,
753                 /* Server */
754                 hc->app_name,
755                 /* Content type */
756                 http_content_type_str[msg.content_type],
757                 /* Length */
758                 msg.data.len);
759       break;
760     case HTTP_STATUS_MOVED:
761       header =
762         format (0, http_redirect_template, http_status_code_str[msg.code]);
763       /* Location: http(s)://new-place already queued up as data */
764       break;
765     default:
766       return HTTP_SM_ERROR;
767     }
768
769   offset = http_send_data (hc, header, vec_len (header), 0);
770   if (offset != vec_len (header))
771     {
772       clib_warning ("couldn't send response header!");
773       sc = HTTP_STATUS_INTERNAL_ERROR;
774       vec_free (header);
775       goto error;
776     }
777   vec_free (header);
778
779   /* Start sending the actual data */
780   http_state_change (hc, HTTP_STATE_APP_IO_MORE_DATA);
781
782   ASSERT (sp->max_burst_size >= offset);
783   sp->max_burst_size -= offset;
784   return HTTP_SM_CONTINUE;
785
786 error:
787   clib_warning ("unexpected msg type from app %u", msg.type);
788   http_send_error (hc, sc);
789   http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD);
790   session_transport_closing_notify (&hc->connection);
791   http_disconnect_transport (hc);
792   return HTTP_SM_STOP;
793 }
794
795 static http_sm_result_t
796 http_state_wait_app_method (http_conn_t *hc, transport_send_params_t *sp)
797 {
798   http_msg_t msg;
799   session_t *as;
800   u8 *buf = 0, *request;
801   u32 offset;
802   int rv;
803
804   as = session_get_from_handle (hc->h_pa_session_handle);
805
806   rv = svm_fifo_dequeue (as->tx_fifo, sizeof (msg), (u8 *) &msg);
807   ASSERT (rv == sizeof (msg));
808
809   if (msg.data.type > HTTP_MSG_DATA_PTR)
810     {
811       clib_warning ("no data");
812       goto error;
813     }
814
815   if (msg.type != HTTP_MSG_REQUEST)
816     {
817       clib_warning ("unexpected message type %d", msg.type);
818       goto error;
819     }
820
821   vec_validate (buf, msg.data.len - 1);
822   rv = svm_fifo_dequeue (as->tx_fifo, msg.data.len, buf);
823   ASSERT (rv == msg.data.len);
824
825   request = format (0, http_request_template, buf);
826   offset = http_send_data (hc, request, vec_len (request), 0);
827   if (offset != vec_len (request))
828     {
829       clib_warning ("sending request failed!");
830       goto error;
831     }
832
833   http_state_change (hc, HTTP_STATE_WAIT_SERVER_REPLY);
834
835   vec_free (buf);
836   vec_free (request);
837
838   return HTTP_SM_STOP;
839
840 error:
841   session_transport_closing_notify (&hc->connection);
842   http_disconnect_transport (hc);
843   return HTTP_SM_ERROR;
844 }
845
846 static http_sm_result_t
847 http_state_client_io_more_data (http_conn_t *hc, transport_send_params_t *sp)
848 {
849   session_t *as, *ts;
850   app_worker_t *app_wrk;
851   svm_fifo_seg_t _seg, *seg = &_seg;
852   u32 max_len, max_deq, max_enq, n_segs = 1;
853   int rv, len;
854
855   as = session_get_from_handle (hc->h_pa_session_handle);
856   ts = session_get_from_handle (hc->h_tc_session_handle);
857
858   max_deq = svm_fifo_max_dequeue (ts->rx_fifo);
859   if (max_deq == 0)
860     {
861       HTTP_DBG (1, "no data to deq");
862       return HTTP_SM_STOP;
863     }
864
865   max_enq = svm_fifo_max_enqueue (as->rx_fifo);
866   if (max_enq == 0)
867     {
868       HTTP_DBG (1, "app's rx fifo full");
869       svm_fifo_add_want_deq_ntf (as->rx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
870       return HTTP_SM_STOP;
871     }
872
873   max_len = clib_min (max_enq, max_deq);
874   len = svm_fifo_segments (ts->rx_fifo, 0, seg, &n_segs, max_len);
875   if (len < 0)
876     {
877       HTTP_DBG (1, "svm_fifo_segments() len %d", len);
878       return HTTP_SM_STOP;
879     }
880
881   rv = svm_fifo_enqueue_segments (as->rx_fifo, seg, 1, 0 /* allow partial */);
882   if (rv < 0)
883     {
884       clib_warning ("data enqueue failed, rv: %d", rv);
885       return HTTP_SM_ERROR;
886     }
887
888   svm_fifo_dequeue_drop (ts->rx_fifo, rv);
889   if (rv > hc->to_recv)
890     {
891       clib_warning ("http protocol error: received more data than expected");
892       session_transport_closing_notify (&hc->connection);
893       http_disconnect_transport (hc);
894       http_state_change (hc, HTTP_STATE_WAIT_APP_METHOD);
895       return HTTP_SM_ERROR;
896     }
897   hc->to_recv -= rv;
898   HTTP_DBG (1, "drained %d from ts; remains %d", rv, hc->to_recv);
899
900   app_wrk = app_worker_get_if_valid (as->app_wrk_index);
901   if (app_wrk)
902     app_worker_rx_notify (app_wrk, as);
903
904   if (svm_fifo_max_dequeue_cons (ts->rx_fifo))
905     session_enqueue_notify (ts);
906
907   return HTTP_SM_STOP;
908 }
909
910 static http_sm_result_t
911 http_state_app_io_more_data (http_conn_t *hc, transport_send_params_t *sp)
912 {
913   u32 max_send = 64 << 10, n_segs;
914   http_buffer_t *hb = &hc->tx_buf;
915   svm_fifo_seg_t *seg;
916   session_t *ts;
917   int sent = 0;
918
919   max_send = clib_min (max_send, sp->max_burst_size);
920   ts = session_get_from_handle (hc->h_tc_session_handle);
921   if ((seg = http_buffer_get_segs (hb, max_send, &n_segs)))
922     sent = svm_fifo_enqueue_segments (ts->tx_fifo, seg, n_segs,
923                                       1 /* allow partial */);
924
925   if (sent > 0)
926     {
927       /* Ask scheduler to notify app of deq event if needed */
928       sp->bytes_dequeued += http_buffer_drain (hb, sent);
929       sp->max_burst_size -= sent;
930     }
931
932   /* Not finished sending all data */
933   if (!http_buffer_is_drained (hb))
934     {
935       if (sent && svm_fifo_set_event (ts->tx_fifo))
936         session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX);
937
938       if (svm_fifo_max_enqueue (ts->tx_fifo) < HTTP_FIFO_THRESH)
939         {
940           /* Deschedule http session and wait for deq notification if
941            * underlying ts tx fifo almost full */
942           svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF);
943           transport_connection_deschedule (&hc->connection);
944           sp->flags |= TRANSPORT_SND_F_DESCHED;
945         }
946     }
947   else
948     {
949       if (sent && svm_fifo_set_event (ts->tx_fifo))
950         session_send_io_evt_to_thread (ts->tx_fifo, SESSION_IO_EVT_TX_FLUSH);
951
952       /* Finished transaction, back to HTTP_STATE_WAIT_METHOD */
953       http_state_change (hc, HTTP_STATE_WAIT_CLIENT_METHOD);
954       http_buffer_free (&hc->tx_buf);
955     }
956
957   return HTTP_SM_STOP;
958 }
959
960 typedef http_sm_result_t (*http_sm_handler) (http_conn_t *,
961                                              transport_send_params_t *sp);
962
963 static http_sm_handler state_funcs[HTTP_N_STATES] = {
964   0, /* idle state */
965   http_state_wait_app_method,
966   http_state_wait_client_method,
967   http_state_wait_server_reply,
968   http_state_wait_app_reply,
969   http_state_client_io_more_data,
970   http_state_app_io_more_data,
971 };
972
973 static void
974 http_req_run_state_machine (http_conn_t *hc, transport_send_params_t *sp)
975 {
976   http_sm_result_t res;
977
978   do
979     {
980       res = state_funcs[hc->http_state](hc, sp);
981       if (res == HTTP_SM_ERROR)
982         {
983           HTTP_DBG (1, "error in state machine %d", res);
984           return;
985         }
986     }
987   while (res == HTTP_SM_CONTINUE);
988
989   /* Reset the session expiration timer */
990   http_conn_timer_update (hc);
991 }
992
993 static int
994 http_ts_rx_callback (session_t *ts)
995 {
996   http_conn_t *hc;
997
998   hc = http_conn_get_w_thread (ts->opaque, ts->thread_index);
999   if (!hc)
1000     {
1001       clib_warning ("http connection not found (ts %d)", ts->opaque);
1002       return -1;
1003     }
1004
1005   if (hc->state == HTTP_CONN_STATE_CLOSED)
1006     {
1007       svm_fifo_dequeue_drop_all (ts->tx_fifo);
1008       return 0;
1009     }
1010
1011   http_req_run_state_machine (hc, 0);
1012
1013   if (hc->state == HTTP_CONN_STATE_TRANSPORT_CLOSED)
1014     {
1015       if (!svm_fifo_max_dequeue_cons (ts->rx_fifo))
1016         session_transport_closing_notify (&hc->connection);
1017     }
1018   return 0;
1019 }
1020
1021 int
1022 http_ts_builtin_tx_callback (session_t *ts)
1023 {
1024   http_conn_t *hc;
1025
1026   hc = http_conn_get_w_thread (ts->opaque, ts->thread_index);
1027   transport_connection_reschedule (&hc->connection);
1028
1029   return 0;
1030 }
1031
1032 static void
1033 http_ts_cleanup_callback (session_t *ts, session_cleanup_ntf_t ntf)
1034 {
1035   http_conn_t *hc;
1036
1037   if (ntf == SESSION_CLEANUP_TRANSPORT)
1038     return;
1039
1040   hc = http_conn_get_w_thread (ts->opaque, ts->thread_index);
1041   if (!hc)
1042     {
1043       clib_warning ("no http connection for %u", ts->session_index);
1044       return;
1045     }
1046
1047   vec_free (hc->rx_buf);
1048
1049   http_buffer_free (&hc->tx_buf);
1050   http_conn_timer_stop (hc);
1051
1052   session_transport_delete_notify (&hc->connection);
1053   http_conn_free (hc);
1054 }
1055
1056 int
1057 http_add_segment_callback (u32 client_index, u64 segment_handle)
1058 {
1059   /* No-op for builtin */
1060   return 0;
1061 }
1062
1063 int
1064 http_del_segment_callback (u32 client_index, u64 segment_handle)
1065 {
1066   return 0;
1067 }
1068
1069 static session_cb_vft_t http_app_cb_vft = {
1070   .session_accept_callback = http_ts_accept_callback,
1071   .session_disconnect_callback = http_ts_disconnect_callback,
1072   .session_connected_callback = http_ts_connected_callback,
1073   .session_reset_callback = http_ts_reset_callback,
1074   .session_cleanup_callback = http_ts_cleanup_callback,
1075   .add_segment_callback = http_add_segment_callback,
1076   .del_segment_callback = http_del_segment_callback,
1077   .builtin_app_rx_callback = http_ts_rx_callback,
1078   .builtin_app_tx_callback = http_ts_builtin_tx_callback,
1079 };
1080
1081 static clib_error_t *
1082 http_transport_enable (vlib_main_t *vm, u8 is_en)
1083 {
1084   vnet_app_detach_args_t _da, *da = &_da;
1085   vnet_app_attach_args_t _a, *a = &_a;
1086   u64 options[APP_OPTIONS_N_OPTIONS];
1087   http_main_t *hm = &http_main;
1088
1089   if (!is_en)
1090     {
1091       da->app_index = hm->app_index;
1092       da->api_client_index = APP_INVALID_INDEX;
1093       vnet_application_detach (da);
1094       return 0;
1095     }
1096
1097   vec_validate (hm->wrk, vlib_num_workers ());
1098
1099   clib_memset (a, 0, sizeof (*a));
1100   clib_memset (options, 0, sizeof (options));
1101
1102   a->session_cb_vft = &http_app_cb_vft;
1103   a->api_client_index = APP_INVALID_INDEX;
1104   a->options = options;
1105   a->name = format (0, "http");
1106   a->options[APP_OPTIONS_SEGMENT_SIZE] = hm->first_seg_size;
1107   a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = hm->add_seg_size;
1108   a->options[APP_OPTIONS_RX_FIFO_SIZE] = hm->fifo_size;
1109   a->options[APP_OPTIONS_TX_FIFO_SIZE] = hm->fifo_size;
1110   a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
1111   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
1112   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_IS_TRANSPORT_APP;
1113
1114   if (vnet_application_attach (a))
1115     return clib_error_return (0, "failed to attach http app");
1116
1117   hm->app_index = a->app_index;
1118   vec_free (a->name);
1119
1120   clib_timebase_init (&hm->timebase, 0 /* GMT */, CLIB_TIMEBASE_DAYLIGHT_NONE,
1121                       &vm->clib_time /* share the system clock */);
1122
1123   http_timers_init (vm, http_conn_timeout_cb);
1124
1125   return 0;
1126 }
1127
1128 static int
1129 http_transport_connect (transport_endpoint_cfg_t *tep)
1130 {
1131   vnet_connect_args_t _cargs, *cargs = &_cargs;
1132   http_main_t *hm = &http_main;
1133   session_endpoint_cfg_t *sep = (session_endpoint_cfg_t *) tep;
1134   application_t *app;
1135   http_conn_t *hc;
1136   int error;
1137   u32 hc_index;
1138   app_worker_t *app_wrk = app_worker_get (sep->app_wrk_index);
1139
1140   clib_memset (cargs, 0, sizeof (*cargs));
1141   clib_memcpy (&cargs->sep_ext, sep, sizeof (session_endpoint_cfg_t));
1142   cargs->sep.transport_proto = TRANSPORT_PROTO_TCP;
1143   cargs->app_index = hm->app_index;
1144   app = application_get (app_wrk->app_index);
1145   cargs->sep_ext.ns_index = app->ns_index;
1146
1147   hc_index = http_conn_alloc_w_thread (0 /* ts->thread_index */);
1148   hc = http_conn_get_w_thread (hc_index, 0);
1149   hc->h_pa_wrk_index = sep->app_wrk_index;
1150   hc->h_pa_app_api_ctx = sep->opaque;
1151   hc->state = HTTP_CONN_STATE_CONNECTING;
1152   cargs->api_context = hc_index;
1153
1154   HTTP_DBG (1, "hc ho_index %x", hc_index);
1155
1156   if ((error = vnet_connect (cargs)))
1157     return error;
1158
1159   return 0;
1160 }
1161
1162 static u32
1163 http_start_listen (u32 app_listener_index, transport_endpoint_cfg_t *tep)
1164 {
1165   vnet_listen_args_t _args = {}, *args = &_args;
1166   session_t *ts_listener, *app_listener;
1167   http_main_t *hm = &http_main;
1168   session_endpoint_cfg_t *sep;
1169   app_worker_t *app_wrk;
1170   transport_proto_t tp;
1171   app_listener_t *al;
1172   application_t *app;
1173   http_conn_t *lhc;
1174   u32 lhc_index;
1175
1176   sep = (session_endpoint_cfg_t *) tep;
1177
1178   app_wrk = app_worker_get (sep->app_wrk_index);
1179   app = application_get (app_wrk->app_index);
1180
1181   args->app_index = hm->app_index;
1182   args->sep_ext = *sep;
1183   args->sep_ext.ns_index = app->ns_index;
1184   tp = sep->ext_cfg ? TRANSPORT_PROTO_TLS : TRANSPORT_PROTO_TCP;
1185   args->sep_ext.transport_proto = tp;
1186
1187   if (vnet_listen (args))
1188     return SESSION_INVALID_INDEX;
1189
1190   lhc_index = http_listener_alloc ();
1191   lhc = http_listener_get (lhc_index);
1192
1193   /* Grab transport connection listener and link to http listener */
1194   lhc->h_tc_session_handle = args->handle;
1195   al = app_listener_get_w_handle (lhc->h_tc_session_handle);
1196   ts_listener = app_listener_get_session (al);
1197   ts_listener->opaque = lhc_index;
1198
1199   /* Grab application listener and link to http listener */
1200   app_listener = listen_session_get (app_listener_index);
1201   lhc->h_pa_wrk_index = sep->app_wrk_index;
1202   lhc->h_pa_session_handle = listen_session_get_handle (app_listener);
1203   lhc->c_s_index = app_listener_index;
1204   lhc->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
1205
1206   if (vec_len (app->name))
1207     lhc->app_name = vec_dup (app->name);
1208   else
1209     lhc->app_name = format (0, "VPP server app");
1210
1211   return lhc_index;
1212 }
1213
1214 static u32
1215 http_stop_listen (u32 listener_index)
1216 {
1217   http_conn_t *lhc;
1218   int rv;
1219
1220   lhc = http_listener_get (listener_index);
1221
1222   vnet_unlisten_args_t a = {
1223     .handle = lhc->h_tc_session_handle,
1224     .app_index = http_main.app_index,
1225     .wrk_map_index = 0 /* default wrk */
1226   };
1227
1228   if ((rv = vnet_unlisten (&a)))
1229     clib_warning ("unlisten returned %d", rv);
1230
1231   http_listener_free (lhc);
1232
1233   return 0;
1234 }
1235
1236 static void
1237 http_transport_close (u32 hc_index, u32 thread_index)
1238 {
1239   session_t *as;
1240   http_conn_t *hc;
1241
1242   HTTP_DBG (1, "App disconnecting %x", hc_index);
1243
1244   hc = http_conn_get_w_thread (hc_index, thread_index);
1245   if (hc->state == HTTP_CONN_STATE_CONNECTING)
1246     {
1247       hc->state = HTTP_CONN_STATE_APP_CLOSED;
1248       http_disconnect_transport (hc);
1249       return;
1250     }
1251
1252   as = session_get_from_handle (hc->h_pa_session_handle);
1253
1254   /* Nothing more to send, confirm close */
1255   if (!svm_fifo_max_dequeue_cons (as->tx_fifo))
1256     {
1257       session_transport_closed_notify (&hc->connection);
1258       http_disconnect_transport (hc);
1259     }
1260   else
1261     {
1262       /* Wait for all data to be written to ts */
1263       hc->state = HTTP_CONN_STATE_APP_CLOSED;
1264     }
1265 }
1266
1267 static transport_connection_t *
1268 http_transport_get_connection (u32 hc_index, u32 thread_index)
1269 {
1270   http_conn_t *hc = http_conn_get_w_thread (hc_index, thread_index);
1271   return &hc->connection;
1272 }
1273
1274 static transport_connection_t *
1275 http_transport_get_listener (u32 listener_index)
1276 {
1277   http_conn_t *lhc = http_listener_get (listener_index);
1278   return &lhc->connection;
1279 }
1280
1281 static int
1282 http_app_tx_callback (void *session, transport_send_params_t *sp)
1283 {
1284   session_t *as = (session_t *) session;
1285   u32 max_burst_sz, sent;
1286   http_conn_t *hc;
1287
1288   HTTP_DBG (1, "app session conn index %x", as->connection_index);
1289
1290   hc = http_conn_get_w_thread (as->connection_index, as->thread_index);
1291   if (!http_state_is_tx_valid (hc))
1292     {
1293       if (hc->state != HTTP_CONN_STATE_CLOSED)
1294         clib_warning ("app data req state '%U' session state %u",
1295                       format_http_state, hc->http_state, hc->state);
1296       svm_fifo_dequeue_drop_all (as->tx_fifo);
1297       return 0;
1298     }
1299
1300   max_burst_sz = sp->max_burst_size * TRANSPORT_PACER_MIN_MSS;
1301   sp->max_burst_size = max_burst_sz;
1302
1303   http_req_run_state_machine (hc, sp);
1304
1305   if (hc->state == HTTP_CONN_STATE_APP_CLOSED)
1306     {
1307       if (!svm_fifo_max_dequeue_cons (as->tx_fifo))
1308         http_disconnect_transport (hc);
1309     }
1310
1311   sent = max_burst_sz - sp->max_burst_size;
1312
1313   return sent > 0 ? clib_max (sent / TRANSPORT_PACER_MIN_MSS, 1) : 0;
1314 }
1315
1316 static void
1317 http_transport_get_endpoint (u32 hc_index, u32 thread_index,
1318                              transport_endpoint_t *tep, u8 is_lcl)
1319 {
1320   http_conn_t *hc = http_conn_get_w_thread (hc_index, thread_index);
1321   session_t *ts;
1322
1323   ts = session_get_from_handle (hc->h_tc_session_handle);
1324   session_get_endpoint (ts, tep, is_lcl);
1325 }
1326
1327 static u8 *
1328 format_http_connection (u8 *s, va_list *args)
1329 {
1330   http_conn_t *hc = va_arg (*args, http_conn_t *);
1331   session_t *ts;
1332
1333   ts = session_get_from_handle (hc->h_tc_session_handle);
1334   s = format (s, "[%d:%d][H] app_wrk %u ts %d:%d", hc->c_thread_index,
1335               hc->c_s_index, hc->h_pa_wrk_index, ts->thread_index,
1336               ts->session_index);
1337
1338   return s;
1339 }
1340
1341 static u8 *
1342 format_http_listener (u8 *s, va_list *args)
1343 {
1344   http_conn_t *lhc = va_arg (*args, http_conn_t *);
1345   app_listener_t *al;
1346   session_t *lts;
1347
1348   al = app_listener_get_w_handle (lhc->h_tc_session_handle);
1349   lts = app_listener_get_session (al);
1350   s = format (s, "[%d:%d][H] app_wrk %u ts %d:%d", lhc->c_thread_index,
1351               lhc->c_s_index, lhc->h_pa_wrk_index, lts->thread_index,
1352               lts->session_index);
1353
1354   return s;
1355 }
1356
1357 static u8 *
1358 format_http_conn_state (u8 *s, va_list *args)
1359 {
1360   http_conn_t *hc = va_arg (*args, http_conn_t *);
1361
1362   switch (hc->state)
1363     {
1364     case HTTP_CONN_STATE_LISTEN:
1365       s = format (s, "LISTEN");
1366       break;
1367     case HTTP_CONN_STATE_CONNECTING:
1368       s = format (s, "CONNECTING");
1369       break;
1370     case HTTP_CONN_STATE_ESTABLISHED:
1371       s = format (s, "ESTABLISHED");
1372       break;
1373     case HTTP_CONN_STATE_TRANSPORT_CLOSED:
1374       s = format (s, "TRANSPORT_CLOSED");
1375       break;
1376     case HTTP_CONN_STATE_APP_CLOSED:
1377       s = format (s, "APP_CLOSED");
1378       break;
1379     case HTTP_CONN_STATE_CLOSED:
1380       s = format (s, "CLOSED");
1381       break;
1382     }
1383
1384   return s;
1385 }
1386
1387 static u8 *
1388 format_http_transport_connection (u8 *s, va_list *args)
1389 {
1390   u32 tc_index = va_arg (*args, u32);
1391   u32 thread_index = va_arg (*args, u32);
1392   u32 verbose = va_arg (*args, u32);
1393   http_conn_t *hc;
1394
1395   hc = http_conn_get_w_thread (tc_index, thread_index);
1396
1397   s = format (s, "%-" SESSION_CLI_ID_LEN "U", format_http_connection, hc);
1398   if (verbose)
1399     {
1400       s =
1401         format (s, "%-" SESSION_CLI_STATE_LEN "U", format_http_conn_state, hc);
1402       if (verbose > 1)
1403         s = format (s, "\n");
1404     }
1405
1406   return s;
1407 }
1408
1409 static u8 *
1410 format_http_transport_listener (u8 *s, va_list *args)
1411 {
1412   u32 tc_index = va_arg (*args, u32);
1413   u32 __clib_unused thread_index = va_arg (*args, u32);
1414   u32 __clib_unused verbose = va_arg (*args, u32);
1415   http_conn_t *lhc = http_listener_get (tc_index);
1416
1417   s = format (s, "%-" SESSION_CLI_ID_LEN "U", format_http_listener, lhc);
1418   if (verbose)
1419     s =
1420       format (s, "%-" SESSION_CLI_STATE_LEN "U", format_http_conn_state, lhc);
1421   return s;
1422 }
1423
1424 static const transport_proto_vft_t http_proto = {
1425   .enable = http_transport_enable,
1426   .connect = http_transport_connect,
1427   .start_listen = http_start_listen,
1428   .stop_listen = http_stop_listen,
1429   .close = http_transport_close,
1430   .custom_tx = http_app_tx_callback,
1431   .get_connection = http_transport_get_connection,
1432   .get_listener = http_transport_get_listener,
1433   .get_transport_endpoint = http_transport_get_endpoint,
1434   .format_connection = format_http_transport_connection,
1435   .format_listener = format_http_transport_listener,
1436   .transport_options = {
1437     .name = "http",
1438     .short_name = "H",
1439     .tx_type = TRANSPORT_TX_INTERNAL,
1440     .service_type = TRANSPORT_SERVICE_APP,
1441   },
1442 };
1443
1444 static clib_error_t *
1445 http_transport_init (vlib_main_t *vm)
1446 {
1447   http_main_t *hm = &http_main;
1448
1449   transport_register_protocol (TRANSPORT_PROTO_HTTP, &http_proto,
1450                                FIB_PROTOCOL_IP4, ~0);
1451   transport_register_protocol (TRANSPORT_PROTO_HTTP, &http_proto,
1452                                FIB_PROTOCOL_IP6, ~0);
1453
1454   /* Default values, configurable via startup conf */
1455   hm->add_seg_size = 256 << 20;
1456   hm->first_seg_size = 32 << 20;
1457   hm->fifo_size = 512 << 10;
1458
1459   return 0;
1460 }
1461
1462 VLIB_INIT_FUNCTION (http_transport_init);
1463
1464 static clib_error_t *
1465 http_config_fn (vlib_main_t *vm, unformat_input_t *input)
1466 {
1467   http_main_t *hm = &http_main;
1468   uword mem_sz;
1469
1470   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1471     {
1472       if (unformat (input, "first-segment-size %U", unformat_memory_size,
1473                     &mem_sz))
1474         {
1475           hm->first_seg_size = clib_max (mem_sz, 1 << 20);
1476           if (hm->first_seg_size != mem_sz)
1477             clib_warning ("first seg size too small %u", mem_sz);
1478         }
1479       else if (unformat (input, "add-segment-size %U", unformat_memory_size,
1480                          &mem_sz))
1481         {
1482           hm->add_seg_size = clib_max (mem_sz, 1 << 20);
1483           if (hm->add_seg_size != mem_sz)
1484             clib_warning ("add seg size too small %u", mem_sz);
1485         }
1486       else if (unformat (input, "fifo-size %U", unformat_memory_size, &mem_sz))
1487         {
1488           hm->fifo_size = clib_clamp (mem_sz, 4 << 10, 2 << 30);
1489           if (hm->fifo_size != mem_sz)
1490             clib_warning ("invalid fifo size %lu", mem_sz);
1491         }
1492       else
1493         return clib_error_return (0, "unknown input `%U'",
1494                                   format_unformat_error, input);
1495     }
1496   return 0;
1497 }
1498
1499 VLIB_CONFIG_FUNCTION (http_config_fn, "http");
1500
1501 VLIB_PLUGIN_REGISTER () = {
1502   .version = VPP_BUILD_VER,
1503   .description = "Hypertext Transfer Protocol (HTTP)",
1504   .default_disabled = 0,
1505 };
1506
1507 /*
1508  * fd.io coding-style-patch-verification: ON
1509  *
1510  * Local Variables:
1511  * eval: (c-set-style "gnu")
1512  * End:
1513  */