hs-test: cache docker build in local filesystem
[vpp.git] / src / plugins / http / http.h
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 #ifndef SRC_PLUGINS_HTTP_HTTP_H_
17 #define SRC_PLUGINS_HTTP_HTTP_H_
18
19 #include <ctype.h>
20
21 #include <vnet/plugin/plugin.h>
22 #include <vpp/app/version.h>
23
24 #include <vppinfra/time_range.h>
25
26 #include <vnet/session/application_interface.h>
27 #include <vnet/session/application.h>
28 #include <http/http_buffer.h>
29
30 #define HTTP_DEBUG 0
31
32 #if HTTP_DEBUG
33 #define HTTP_DBG(_lvl, _fmt, _args...)                                        \
34   if (_lvl <= HTTP_DEBUG)                                                     \
35   clib_warning (_fmt, ##_args)
36 #else
37 #define HTTP_DBG(_lvl, _fmt, _args...)
38 #endif
39
40 typedef struct http_conn_id_
41 {
42   union
43   {
44     session_handle_t app_session_handle;
45     u32 parent_app_api_ctx;
46   };
47   session_handle_t tc_session_handle;
48   u32 parent_app_wrk_index;
49 } http_conn_id_t;
50
51 STATIC_ASSERT (sizeof (http_conn_id_t) <= TRANSPORT_CONN_ID_LEN,
52                "ctx id must be less than TRANSPORT_CONN_ID_LEN");
53
54 typedef enum http_conn_state_
55 {
56   HTTP_CONN_STATE_LISTEN,
57   HTTP_CONN_STATE_CONNECTING,
58   HTTP_CONN_STATE_ESTABLISHED,
59   HTTP_CONN_STATE_TRANSPORT_CLOSED,
60   HTTP_CONN_STATE_APP_CLOSED,
61   HTTP_CONN_STATE_CLOSED
62 } http_conn_state_t;
63
64 typedef enum http_state_
65 {
66   HTTP_STATE_IDLE = 0,
67   HTTP_STATE_WAIT_APP_METHOD,
68   HTTP_STATE_WAIT_CLIENT_METHOD,
69   HTTP_STATE_WAIT_SERVER_REPLY,
70   HTTP_STATE_WAIT_APP_REPLY,
71   HTTP_STATE_CLIENT_IO_MORE_DATA,
72   HTTP_STATE_APP_IO_MORE_DATA,
73   HTTP_N_STATES,
74 } http_state_t;
75
76 typedef enum http_req_method_
77 {
78   HTTP_REQ_GET = 0,
79   HTTP_REQ_POST,
80 } http_req_method_t;
81
82 typedef enum http_msg_type_
83 {
84   HTTP_MSG_REQUEST,
85   HTTP_MSG_REPLY
86 } http_msg_type_t;
87
88 typedef enum http_target_form_
89 {
90   HTTP_TARGET_ORIGIN_FORM,
91   HTTP_TARGET_ABSOLUTE_FORM,
92   HTTP_TARGET_AUTHORITY_FORM,
93   HTTP_TARGET_ASTERISK_FORM
94 } http_target_form_t;
95
96 #define foreach_http_content_type                                             \
97   _ (APP_7Z, ".7z", "application / x - 7z - compressed")                      \
98   _ (APP_DOC, ".doc", "application / msword")                                 \
99   _ (APP_DOCX, ".docx",                                                       \
100      "application / vnd.openxmlformats - "                                    \
101      "officedocument.wordprocessingml.document")                              \
102   _ (APP_EPUB, ".epub", "application / epub + zip")                           \
103   _ (APP_FONT, ".eot", "application / vnd.ms - fontobject")                   \
104   _ (APP_JAR, ".jar", "application / java - archive")                         \
105   _ (APP_JSON, ".json", "application / json")                                 \
106   _ (APP_JSON_LD, ".jsonld", "application / ld + json")                       \
107   _ (APP_MPKG, ".mpkg", "application / vnd.apple.installer + xml")            \
108   _ (APP_ODP, ".odp", "application / vnd.oasis.opendocument.presentation")    \
109   _ (APP_ODS, ".ods", "application / vnd.oasis.opendocument.spreadsheet")     \
110   _ (APP_ODT, ".odt", "application / vnd.oasis.opendocument.text")            \
111   _ (APP_OGX, ".ogx", "application / ogg")                                    \
112   _ (APP_PDF, ".pdf", "application / pdf")                                    \
113   _ (APP_PHP, ".php", "application / x - httpd - php")                        \
114   _ (APP_PPT, ".ppt", "application / vnd.ms - powerpoint")                    \
115   _ (APP_PPTX, ".pptx", "application / vnd.ms - powerpoint")                  \
116   _ (APP_RAR, ".rar", "application / vnd.rar")                                \
117   _ (APP_RTF, ".rtf", "application / rtf")                                    \
118   _ (APP_SH, ".sh", "application / x - sh")                                   \
119   _ (APP_TAR, ".tar", "application / x - tar")                                \
120   _ (APP_VSD, ".vsd", "application / vnd.visio")                              \
121   _ (APP_XHTML, ".xhtml", "application / xhtml + xml")                        \
122   _ (APP_XLS, ".xls", "application / vnd.ms - excel")                         \
123   _ (APP_XML, ".xml", "application / xml")                                    \
124   _ (APP_XSLX, ".xlsx",                                                       \
125      "application / vnd.openxmlformats - officedocument.spreadsheetml.sheet") \
126   _ (APP_XUL, ".xul", "application / vnd.mozilla.xul + xml")                  \
127   _ (APP_ZIP, ".zip", "application / zip")                                    \
128   _ (AUDIO_AAC, ".aac", "audio / aac")                                        \
129   _ (AUDIO_CD, ".cda", "application / x - cdf")                               \
130   _ (AUDIO_WAV, ".wav", "audio / wav")                                        \
131   _ (AUDIO_WEBA, ".weba", "audio / webm")                                     \
132   _ (AUDO_MIDI, ".midi", "audio / midi")                                      \
133   _ (AUDO_MID, ".mid", "audo / midi")                                         \
134   _ (AUDO_MP3, ".mp3", "audio / mpeg")                                        \
135   _ (AUDO_OGA, ".oga", "audio / ogg")                                         \
136   _ (AUDO_OPUS, ".opus", "audio / opus")                                      \
137   _ (APP_OCTET_STREAM, ".bin", "application / octet - stream")                \
138   _ (BZIP2, ".bz2", "application / x - bzip2")                                \
139   _ (BZIP, ".bz", "application / x - bzip")                                   \
140   _ (FONT_OTF, ".otf", "font / otf")                                          \
141   _ (FONT_TTF, ".ttf", "font / ttf")                                          \
142   _ (FONT_WOFF2, ".woff2", "font / woff2")                                    \
143   _ (FONT_WOFF, ".woff", "font / woff")                                       \
144   _ (GZIP, ".gz", "application / gzip")                                       \
145   _ (IMAGE_AVIF, ".avif", "image / avif")                                     \
146   _ (IMAGE_BMP, ".bmp", "image / bmp")                                        \
147   _ (IMAGE_GIF, ".gif", "image / gif")                                        \
148   _ (IMAGE_ICON, ".ico", "image / vnd.microsoft.icon")                        \
149   _ (IMAGE_JPEG, ".jpeg", "image / jpeg")                                     \
150   _ (IMAGE_JPG, ".jpg", "image / jpeg")                                       \
151   _ (IMAGE_PNG, ".png", "image / png")                                        \
152   _ (IMAGE_SVG, ".svg", "image / svg + xml")                                  \
153   _ (IMAGE_TIFF, ".tiff", "image / tiff")                                     \
154   _ (IMAGE_TIF, ".tif", "image / tiff")                                       \
155   _ (IMAGE_WEBP, ".webp", "image / webp")                                     \
156   _ (SCRIPT_CSH, ".csh", "application / x - csh")                             \
157   _ (TEXT_ABIWORD, ".abw", "application / x - abiword")                       \
158   _ (TEXT_ARCHIVE, ".arc", "application / x - freearc")                       \
159   _ (TEXT_AZW, ".azw", "application / vnd.amazon.ebook")                      \
160   _ (TEXT_CALENDAR, ".ics", "text / calendar")                                \
161   _ (TEXT_CSS, ".css", "text / css")                                          \
162   _ (TEXT_CSV, ".csv", "text / csv")                                          \
163   _ (TEXT_HTM, ".htm", "text / html")                                         \
164   _ (TEXT_HTML, ".html", "text / html")                                       \
165   _ (TEXT_JS, ".js", "text / javascript")                                     \
166   _ (TEXT_MJS, ".mjs", "text / javascript")                                   \
167   _ (TEXT_PLAIN, ".txt", "text / plain")                                      \
168   _ (VIDEO_3GP2, ".3g2", "video / 3gpp2")                                     \
169   _ (VIDEO_3GP, ".3gp", "video / 3gpp")                                       \
170   _ (VIDEO_AVI, ".avi", "video / x - msvideo")                                \
171   _ (VIDEO_MP4, ".mp4", "video / mp4")                                        \
172   _ (VIDEO_MPEG, ".mpeg", "video / mpeg")                                     \
173   _ (VIDEO_OGG, ".ogv", "video / ogg")                                        \
174   _ (VIDEO_TS, ".ts", "video / mp2t")                                         \
175   _ (VIDEO_WEBM, ".webm", "video / webm")
176
177 typedef enum http_content_type_
178 {
179 #define _(s, ext, str) HTTP_CONTENT_##s,
180   foreach_http_content_type
181 #undef _
182 } http_content_type_t;
183
184 #define foreach_http_status_code                                              \
185   _ (100, CONTINUE, "100 Continue")                                           \
186   _ (101, SWITCHING_PROTOCOLS, "101 Switching Protocols")                     \
187   _ (200, OK, "200 OK")                                                       \
188   _ (201, CREATED, "201 Created")                                             \
189   _ (202, ACCEPTED, "202 Accepted")                                           \
190   _ (203, NON_UTHORITATIVE_INFORMATION, "203 Non-Authoritative Information")  \
191   _ (204, NO_CONTENT, "204 No Content")                                       \
192   _ (205, RESET_CONTENT, "205 Reset Content")                                 \
193   _ (206, PARTIAL_CONTENT, "206 Partial Content")                             \
194   _ (300, MULTIPLE_CHOICES, "300 Multiple Choices")                           \
195   _ (301, MOVED, "301 Moved Permanently")                                     \
196   _ (302, FOUND, "302 Found")                                                 \
197   _ (303, SEE_OTHER, "303 See Other")                                         \
198   _ (304, NOT_MODIFIED, "304 Not Modified")                                   \
199   _ (305, USE_PROXY, "305 Use Proxy")                                         \
200   _ (307, TEMPORARY_REDIRECT, "307 Temporary Redirect")                       \
201   _ (308, PERMANENT_REDIRECT, "308 Permanent Redirect")                       \
202   _ (400, BAD_REQUEST, "400 Bad Request")                                     \
203   _ (401, UNAUTHORIZED, "401 Unauthorized")                                   \
204   _ (402, PAYMENT_REQUIRED, "402 Payment Required")                           \
205   _ (403, FORBIDDEN, "403 Forbidden")                                         \
206   _ (404, NOT_FOUND, "404 Not Found")                                         \
207   _ (405, METHOD_NOT_ALLOWED, "405 Method Not Allowed")                       \
208   _ (406, NOT_ACCEPTABLE, "406 Not Acceptable")                               \
209   _ (407, PROXY_AUTHENTICATION_REQUIRED, "407 Proxy Authentication Required") \
210   _ (408, REQUEST_TIMEOUT, "408 Request Timeout")                             \
211   _ (409, CONFLICT, "409 Conflict")                                           \
212   _ (410, GONE, "410 Gone")                                                   \
213   _ (411, LENGTH_REQUIRED, "411 Length Required")                             \
214   _ (412, PRECONDITION_FAILED, "412 Precondition Failed")                     \
215   _ (413, CONTENT_TOO_LARGE, "413 Content Too Large")                         \
216   _ (414, URI_TOO_LONG, "414 URI Too Long")                                   \
217   _ (415, UNSUPPORTED_MEDIA_TYPE, "415 Unsupported Media Type")               \
218   _ (416, RANGE_NOT_SATISFIABLE, "416 Range Not Satisfiable")                 \
219   _ (417, EXPECTATION_FAILED, "417 Expectation Failed")                       \
220   _ (421, MISDIRECTED_REQUEST, "421 Misdirected Request")                     \
221   _ (422, UNPROCESSABLE_CONTENT, "422 Unprocessable_Content")                 \
222   _ (426, UPGRADE_REQUIRED, "426 Upgrade Required")                           \
223   _ (500, INTERNAL_ERROR, "500 Internal Server Error")                        \
224   _ (501, NOT_IMPLEMENTED, "501 Not Implemented")                             \
225   _ (502, BAD_GATEWAY, "502 Bad Gateway")                                     \
226   _ (503, SERVICE_UNAVAILABLE, "503 Service Unavailable")                     \
227   _ (504, GATEWAY_TIMEOUT, "504 Gateway Timeout")                             \
228   _ (505, HTTP_VERSION_NOT_SUPPORTED, "505 HTTP Version Not Supported")
229
230 typedef enum http_status_code_
231 {
232 #define _(c, s, str) HTTP_STATUS_##s,
233   foreach_http_status_code
234 #undef _
235     HTTP_N_STATUS
236 } http_status_code_t;
237
238 #define HTTP_HEADER_ACCEPT                    "Accept"
239 #define HTTP_HEADER_ACCEPT_CHARSET            "Accept-Charset"
240 #define HTTP_HEADER_ACCEPT_ENCODING           "Accept-Encoding"
241 #define HTTP_HEADER_ACCEPT_LANGUAGE           "Accept-Language"
242 #define HTTP_HEADER_ACCEPT_RANGES             "Accept-Ranges"
243 #define HTTP_HEADER_ALLOW                     "Allow"
244 #define HTTP_HEADER_AUTHENTICATION_INFO       "Authentication-Info"
245 #define HTTP_HEADER_AUTHORIZATION             "Authorization"
246 #define HTTP_HEADER_CLOSE                     "Close"
247 #define HTTP_HEADER_CONNECTION                "Connection"
248 #define HTTP_HEADER_CONTENT_ENCODING          "Content-Encoding"
249 #define HTTP_HEADER_CONTENT_LANGUAGE          "Content-Language"
250 #define HTTP_HEADER_CONTENT_LENGTH            "Content-Length"
251 #define HTTP_HEADER_CONTENT_LOCATION          "Content-Location"
252 #define HTTP_HEADER_CONTENT_RANGE             "Content-Range"
253 #define HTTP_HEADER_CONTENT_TYPE              "Content-Type"
254 #define HTTP_HEADER_DATE                      "Date"
255 #define HTTP_HEADER_ETAG                      "ETag"
256 #define HTTP_HEADER_EXPECT                    "Expect"
257 #define HTTP_HEADER_FROM                      "From"
258 #define HTTP_HEADER_HOST                      "Host"
259 #define HTTP_HEADER_IF_MATCH                  "If-Match"
260 #define HTTP_HEADER_IF_MODIFIED_SINCE         "If-Modified-Since"
261 #define HTTP_HEADER_IF_NONE_MATCH             "If-None-Match"
262 #define HTTP_HEADER_IF_RANGE                  "If-Range"
263 #define HTTP_HEADER_IF_UNMODIFIED_SINCE       "If-Unmodified-Since"
264 #define HTTP_HEADER_LAST_MODIFIED             "Last-Modified"
265 #define HTTP_HEADER_LOCATION                  "Location"
266 #define HTTP_HEADER_MAX_FORWARDS              "Max-Forwards"
267 #define HTTP_HEADER_PROXY_AUTHENTICATE        "Proxy-Authenticate"
268 #define HTTP_HEADER_PROXY_AUTHENTICATION_INFO "Proxy-Authentication-Info"
269 #define HTTP_HEADER_PROXY_AUTHORIZATION       "Proxy-Authorization"
270 #define HTTP_HEADER_RANGE                     "Range"
271 #define HTTP_HEADER_REFERER                   "Referer"
272 #define HTTP_HEADER_RETRY_AFTER               "Retry-After"
273 #define HTTP_HEADER_SERVER                    "Server"
274 #define HTTP_HEADER_TE                        "TE"
275 #define HTTP_HEADER_TRAILER                   "Trailer"
276 #define HTTP_HEADER_TRANSFER_ENCODING         "Transfer-Encoding"
277 #define HTTP_HEADER_UPGRADE                   "Upgrade"
278 #define HTTP_HEADER_USER_AGENT                "User-Agent"
279 #define HTTP_HEADER_VARY                      "Vary"
280 #define HTTP_HEADER_VIA                       "Via"
281 #define HTTP_HEADER_WWW_AUTHENTICATE          "WWW-Authenticate"
282
283 typedef enum http_msg_data_type_
284 {
285   HTTP_MSG_DATA_INLINE,
286   HTTP_MSG_DATA_PTR
287 } http_msg_data_type_t;
288
289 typedef struct http_msg_data_
290 {
291   http_msg_data_type_t type;
292   u64 len;
293   http_target_form_t target_form;
294   u32 target_path_offset;
295   u32 target_path_len;
296   u32 target_query_offset;
297   u32 target_query_len;
298   u32 headers_offset;
299   u32 headers_len;
300   u32 body_offset;
301   u32 body_len;
302   u8 data[0];
303 } http_msg_data_t;
304
305 typedef struct http_msg_
306 {
307   http_msg_type_t type;
308   union
309   {
310     http_req_method_t method_type;
311     http_status_code_t code;
312   };
313   http_content_type_t content_type;
314   http_msg_data_t data;
315 } http_msg_t;
316
317 typedef struct http_tc_
318 {
319   union
320   {
321     transport_connection_t connection;
322     http_conn_id_t c_http_conn_id;
323   };
324 #define h_tc_session_handle c_http_conn_id.tc_session_handle
325 #define h_pa_wrk_index      c_http_conn_id.parent_app_wrk_index
326 #define h_pa_session_handle c_http_conn_id.app_session_handle
327 #define h_pa_app_api_ctx    c_http_conn_id.parent_app_api_ctx
328 #define h_hc_index          connection.c_index
329
330   http_conn_state_t state;
331   u32 timer_handle;
332   u8 *app_name;
333
334   /*
335    * Current request
336    */
337   http_state_t http_state;
338   http_req_method_t method;
339   u8 *rx_buf;
340   u32 rx_buf_offset;
341   http_buffer_t tx_buf;
342   u32 to_recv;
343   u32 bytes_dequeued;
344   http_target_form_t target_form;
345   u32 target_path_offset;
346   u32 target_path_len;
347   u32 target_query_offset;
348   u32 target_query_len;
349   u32 headers_offset;
350   u32 headers_len;
351   u32 body_offset;
352   u32 body_len;
353 } http_conn_t;
354
355 typedef struct http_worker_
356 {
357   http_conn_t *conn_pool;
358 } http_worker_t;
359
360 typedef struct http_main_
361 {
362   http_worker_t *wrk;
363   http_conn_t *listener_pool;
364   u32 app_index;
365
366   clib_timebase_t timebase;
367
368   /*
369    * Runtime config
370    */
371   u8 debug_level;
372
373   /*
374    * Config
375    */
376   u64 first_seg_size;
377   u64 add_seg_size;
378   u32 fifo_size;
379 } http_main_t;
380
381 always_inline int
382 _validate_target_syntax (u8 *target, int is_query, int *is_encoded)
383 {
384   int i, encoded = 0;
385
386   static uword valid_chars[4] = {
387     /* !$&'()*+,-./0123456789:;= */
388     0x2fffffd200000000,
389     /* @ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ */
390     0x47fffffe87ffffff,
391     0x0000000000000000,
392     0x0000000000000000,
393   };
394
395   for (i = 0; i < vec_len (target); i++)
396     {
397       if (clib_bitmap_get_no_check (valid_chars, target[i]))
398         continue;
399       /* target was already split after first question mark,
400        * for query it is valid character */
401       if (is_query && target[i] == '?')
402         continue;
403       /* pct-encoded = "%" HEXDIG HEXDIG */
404       if (target[i] == '%')
405         {
406           if ((i + 2) > vec_len (target))
407             return -1;
408           if (!isxdigit (target[i + 1]) || !isxdigit (target[i + 2]))
409             return -1;
410           i += 2;
411           encoded = 1;
412           continue;
413         }
414       clib_warning ("invalid character %d", target[i]);
415       return -1;
416     }
417   if (is_encoded)
418     *is_encoded = encoded;
419   return 0;
420 }
421
422 /**
423  * An "absolute-path" rule validation (RFC9110 section 4.1).
424  *
425  * @param path       Target path to validate.
426  * @param is_encoded Return flag that indicates if percent-encoded (optional).
427  *
428  * @return @c 0 on success.
429  */
430 always_inline int
431 http_validate_abs_path_syntax (u8 *path, int *is_encoded)
432 {
433   return _validate_target_syntax (path, 0, is_encoded);
434 }
435
436 /**
437  * A "query" rule validation (RFC3986 section 2.1).
438  *
439  * @param query      Target query to validate.
440  * @param is_encoded Return flag that indicates if percent-encoded (optional).
441  *
442  * @return @c 0 on success.
443  */
444 always_inline int
445 http_validate_query_syntax (u8 *query, int *is_encoded)
446 {
447   return _validate_target_syntax (query, 1, is_encoded);
448 }
449
450 #define htoi(x) (isdigit (x) ? (x - '0') : (tolower (x) - 'a' + 10))
451
452 /**
453  * Decode percent-encoded data.
454  *
455  * @param src Data to decode.
456  *
457  * @return New vector with decoded data.
458  *
459  * The caller is always responsible to free the returned vector.
460  */
461 always_inline u8 *
462 http_percent_decode (u8 *src)
463 {
464   int i;
465   u8 *decoded_uri = 0;
466
467   for (i = 0; i < vec_len (src); i++)
468     {
469       if (src[i] == '%')
470         {
471           u8 c = (htoi (src[i + 1]) << 4) | htoi (src[i + 2]);
472           vec_add1 (decoded_uri, c);
473           i += 2;
474         }
475       else
476         vec_add1 (decoded_uri, src[i]);
477     }
478   return decoded_uri;
479 }
480
481 /**
482  * Remove dot segments from path (RFC3986 section 5.2.4)
483  *
484  * @param path Path to sanitize.
485  *
486  * @return New vector with sanitized path.
487  *
488  * The caller is always responsible to free the returned vector.
489  */
490 always_inline u8 *
491 http_path_remove_dot_segments (u8 *path)
492 {
493   u32 *segments = 0, *segments_len = 0, segment_len;
494   u8 *new_path = 0;
495   int i, ii;
496
497   if (!path)
498     return vec_new (u8, 0);
499
500   segments = vec_new (u32, 1);
501   /* first segment */
502   segments[0] = 0;
503   /* find all segments */
504   for (i = 1; i < (vec_len (path) - 1); i++)
505     {
506       if (path[i] == '/')
507         vec_add1 (segments, i + 1);
508     }
509   /* dummy tail */
510   vec_add1 (segments, vec_len (path));
511
512   /* scan all segments for "." and ".." */
513   segments_len = vec_new (u32, vec_len (segments) - 1);
514   for (i = 0; i < vec_len (segments_len); i++)
515     {
516       segment_len = segments[i + 1] - segments[i];
517       if (segment_len == 2 && path[segments[i]] == '.')
518         segment_len = 0;
519       else if (segment_len == 3 && path[segments[i]] == '.' &&
520                path[segments[i] + 1] == '.')
521         {
522           segment_len = 0;
523           /* remove parent (if any) */
524           for (ii = i - 1; ii >= 0; ii--)
525             {
526               if (segments_len[ii])
527                 {
528                   segments_len[ii] = 0;
529                   break;
530                 }
531             }
532         }
533       segments_len[i] = segment_len;
534     }
535
536   /* we might end with empty path, so return at least empty vector */
537   new_path = vec_new (u8, 0);
538   /* append all valid segments */
539   for (i = 0; i < vec_len (segments_len); i++)
540     {
541       if (segments_len[i])
542         vec_add (new_path, path + segments[i], segments_len[i]);
543     }
544   vec_free (segments);
545   vec_free (segments_len);
546   return new_path;
547 }
548
549 always_inline int
550 _parse_field_name (u8 **pos, u8 *end, u8 **field_name_start,
551                    u32 *field_name_len)
552 {
553   u32 name_len = 0;
554   u8 *p;
555
556   static uword tchar[4] = {
557     /* !#$%'*+-.0123456789 */
558     0x03ff6cba00000000,
559     /* ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~ */
560     0x57ffffffc7fffffe,
561     0x0000000000000000,
562     0x0000000000000000,
563   };
564
565   p = *pos;
566
567   *field_name_start = p;
568   while (p != end)
569     {
570       if (clib_bitmap_get_no_check (tchar, *p))
571         {
572           name_len++;
573           p++;
574         }
575       else if (*p == ':')
576         {
577           if (name_len == 0)
578             {
579               clib_warning ("empty field name");
580               return -1;
581             }
582           *field_name_len = name_len;
583           p++;
584           *pos = p;
585           return 0;
586         }
587       else
588         {
589           clib_warning ("invalid character %d", *p);
590           return -1;
591         }
592     }
593   clib_warning ("field name end not found");
594   return -1;
595 }
596
597 always_inline int
598 _parse_field_value (u8 **pos, u8 *end, u8 **field_value_start,
599                     u32 *field_value_len)
600 {
601   u32 value_len = 0;
602   u8 *p;
603
604   p = *pos;
605
606   /* skip leading whitespace */
607   while (1)
608     {
609       if (p == end)
610         {
611           clib_warning ("field value not found");
612           return -1;
613         }
614       else if (*p != ' ' && *p != '\t')
615         {
616           break;
617         }
618       p++;
619     }
620
621   *field_value_start = p;
622   while (p != end)
623     {
624       if (*p == '\r')
625         {
626           if ((end - p) < 1)
627             {
628               clib_warning ("incorrect field line end");
629               return -1;
630             }
631           p++;
632           if (*p == '\n')
633             {
634               if (value_len == 0)
635                 {
636                   clib_warning ("empty field value");
637                   return -1;
638                 }
639               p++;
640               *pos = p;
641               /* skip trailing whitespace */
642               p = *field_value_start + value_len - 1;
643               while (*p == ' ' || *p == '\t')
644                 {
645                   p--;
646                   value_len--;
647                 }
648               *field_value_len = value_len;
649               return 0;
650             }
651           clib_warning ("CR without LF");
652           return -1;
653         }
654       if (*p < ' ' && *p != '\t')
655         {
656           clib_warning ("invalid character %d", *p);
657           return -1;
658         }
659       p++;
660       value_len++;
661     }
662
663   clib_warning ("field value end not found");
664   return -1;
665 }
666
667 typedef struct
668 {
669   u8 *name;
670   u8 *value;
671 } http_header_t;
672
673 typedef struct
674 {
675   http_header_t *headers;
676   uword *value_by_name;
677 } http_header_table_t;
678
679 /**
680  * Free header table's memory.
681  *
682  * @param ht Header table to free.
683  */
684 always_inline void
685 http_free_header_table (http_header_table_t *ht)
686 {
687   http_header_t *header;
688   vec_foreach (header, ht->headers)
689     {
690       vec_free (header->name);
691       vec_free (header->value);
692     }
693   vec_free (ht->headers);
694   hash_free (ht->value_by_name);
695   clib_mem_free (ht);
696 }
697
698 /**
699  * Parse headers in given vector.
700  *
701  * @param headers Vector to parse.
702  * @param [out] header_table Parsed headers in case of success.
703  *
704  * @return @c 0 on success.
705  *
706  * The caller is responsible to free the returned @c header_table
707  * using @c http_free_header_table .
708  */
709 always_inline int
710 http_parse_headers (u8 *headers, http_header_table_t **header_table)
711 {
712   u8 *pos, *end, *name_start, *value_start, *name;
713   u32 name_len, value_len;
714   int rv;
715   http_header_t *header;
716   http_header_table_t *ht;
717   uword *p;
718
719   end = headers + vec_len (headers);
720   pos = headers;
721
722   ht = clib_mem_alloc (sizeof (*ht));
723   ht->value_by_name = hash_create_string (0, sizeof (uword));
724   ht->headers = 0;
725   do
726     {
727       rv = _parse_field_name (&pos, end, &name_start, &name_len);
728       if (rv != 0)
729         {
730           http_free_header_table (ht);
731           return rv;
732         }
733       rv = _parse_field_value (&pos, end, &value_start, &value_len);
734       if (rv != 0)
735         {
736           http_free_header_table (ht);
737           return rv;
738         }
739       name = vec_new (u8, name_len);
740       clib_memcpy (name, name_start, name_len);
741       vec_terminate_c_string (name);
742       /* check if header is repeated */
743       p = hash_get_mem (ht->value_by_name, name);
744       if (p)
745         {
746           /* if yes combine values */
747           header = vec_elt_at_index (ht->headers, p[0]);
748           vec_pop (header->value); /* drop null byte */
749           header->value = format (header->value, ", %U%c", format_ascii_bytes,
750                                   value_start, value_len, 0);
751           vec_free (name);
752           continue;
753         }
754       /* or create new record */
755       vec_add2 (ht->headers, header, sizeof (*header));
756       header->name = name;
757       header->value = vec_new (u8, value_len);
758       clib_memcpy (header->value, value_start, value_len);
759       vec_terminate_c_string (header->value);
760       hash_set_mem (ht->value_by_name, header->name, header - ht->headers);
761     }
762   while (pos != end);
763
764   *header_table = ht;
765
766   return 0;
767 }
768
769 /**
770  * Try to find given header name in header table.
771  *
772  * @param header_table Header table to search.
773  * @param name Header name to match.
774  *
775  * @return Header's value in case of success, @c 0 otherwise.
776  */
777 always_inline const char *
778 http_get_header (http_header_table_t *header_table, const char *name)
779 {
780   uword *p;
781   http_header_t *header;
782
783   p = hash_get_mem (header_table->value_by_name, name);
784   if (p)
785     {
786       header = vec_elt_at_index (header_table->headers, p[0]);
787       return (const char *) header->value;
788     }
789
790   return 0;
791 }
792
793 #endif /* SRC_PLUGINS_HTTP_HTTP_H_ */
794
795 /*
796  * fd.io coding-style-patch-verification: ON
797  *
798  * Local Variables:
799  * eval: (c-set-style "gnu")
800  * End:
801  */