blob: 7b1f68a0e63e90b8c963c929e18d58ba2ea09e59 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file http/msg.c HTTP Message decode
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <re_types.h>
7#include <re_mem.h>
8#include <re_mbuf.h>
9#include <re_sa.h>
10#include <re_list.h>
11#include <re_hash.h>
12#include <re_fmt.h>
13#include <re_msg.h>
14#include <re_http.h>
15
16
17enum {
18 STARTLINE_MAX = 8192,
19};
20
21
22static void hdr_destructor(void *arg)
23{
24 struct http_hdr *hdr = arg;
25
26 list_unlink(&hdr->le);
27}
28
29
30static void destructor(void *arg)
31{
32 struct http_msg *msg = arg;
33
34 list_flush(&msg->hdrl);
35 mem_deref(msg->_mb);
36 mem_deref(msg->mb);
37}
38
39
40static enum http_hdrid hdr_hash(const struct pl *name)
41{
42 if (!name->l)
43 return HTTP_HDR_NONE;
44
45 switch (name->p[0]) {
46
47 case 'x':
48 case 'X':
49 if (name->l > 1 && name->p[1] == '-')
50 return HTTP_HDR_NONE;
51
52 break;
53 }
54
55 return (enum http_hdrid)(hash_joaat_ci(name->p, name->l) & 0xfff);
56}
57
58
59static inline bool hdr_comma_separated(enum http_hdrid id)
60{
61 switch (id) {
62
63 case HTTP_HDR_ACCEPT:
64 case HTTP_HDR_ACCEPT_CHARSET:
65 case HTTP_HDR_ACCEPT_ENCODING:
66 case HTTP_HDR_ACCEPT_LANGUAGE:
67 case HTTP_HDR_ACCEPT_RANGES:
68 case HTTP_HDR_ALLOW:
69 case HTTP_HDR_CACHE_CONTROL:
70 case HTTP_HDR_CONNECTION:
71 case HTTP_HDR_CONTENT_ENCODING:
72 case HTTP_HDR_CONTENT_LANGUAGE:
73 case HTTP_HDR_EXPECT:
74 case HTTP_HDR_IF_MATCH:
75 case HTTP_HDR_IF_NONE_MATCH:
76 case HTTP_HDR_PRAGMA:
77 case HTTP_HDR_SEC_WEBSOCKET_EXTENSIONS:
78 case HTTP_HDR_SEC_WEBSOCKET_PROTOCOL:
79 case HTTP_HDR_SEC_WEBSOCKET_VERSION:
80 case HTTP_HDR_TE:
81 case HTTP_HDR_TRAILER:
82 case HTTP_HDR_TRANSFER_ENCODING:
83 case HTTP_HDR_UPGRADE:
84 case HTTP_HDR_VARY:
85 case HTTP_HDR_VIA:
86 case HTTP_HDR_WARNING:
87 return true;
88
89 default:
90 return false;
91 }
92}
93
94
95static inline int hdr_add(struct http_msg *msg, const struct pl *name,
96 enum http_hdrid id, const char *p, ssize_t l)
97{
98 struct http_hdr *hdr;
99 int err = 0;
100
101 hdr = mem_zalloc(sizeof(*hdr), hdr_destructor);
102 if (!hdr)
103 return ENOMEM;
104
105 hdr->name = *name;
106 hdr->val.p = p;
107 hdr->val.l = MAX(l, 0);
108 hdr->id = id;
109
110 list_append(&msg->hdrl, &hdr->le, hdr);
111
112 /* parse common headers */
113 switch (id) {
114
115 case HTTP_HDR_CONTENT_TYPE:
116 err = msg_ctype_decode(&msg->ctyp, &hdr->val);
117 break;
118
119 case HTTP_HDR_CONTENT_LENGTH:
120 msg->clen = pl_u32(&hdr->val);
121 break;
122
123 default:
124 break;
125 }
126
127 if (err)
128 mem_deref(hdr);
129
130 return err;
131}
132
133
134/**
135 * Decode a HTTP message
136 *
137 * @param msgp Pointer to allocated HTTP Message
138 * @param mb Buffer containing HTTP Message
139 * @param req True for request, false for response
140 *
141 * @return 0 if success, otherwise errorcode
142 */
143int http_msg_decode(struct http_msg **msgp, struct mbuf *mb, bool req)
144{
145 struct pl b, s, e, name, scode;
146 const char *p, *cv;
147 struct http_msg *msg;
148 bool comsep, quote;
149 enum http_hdrid id = HTTP_HDR_NONE;
150 uint32_t ws, lf;
151 size_t l;
152 int err;
153
154 if (!msgp || !mb)
155 return EINVAL;
156
157 p = (const char *)mbuf_buf(mb);
158 l = mbuf_get_left(mb);
159
160 if (re_regex(p, l, "[\r\n]*[^\r\n]+[\r]*[\n]1", &b, &s, NULL, &e))
161 return (l > STARTLINE_MAX) ? EBADMSG : ENODATA;
162
163 msg = mem_zalloc(sizeof(*msg), destructor);
164 if (!msg)
165 return ENOMEM;
166
167 msg->_mb = mem_ref(mb);
168
169 msg->mb = mbuf_alloc(8192);
170 if (!msg->mb) {
171 err = ENOMEM;
172 goto out;
173 }
174
175 if (req) {
176 if (re_regex(s.p, s.l, "[a-z]+ [^? ]+[^ ]* HTTP/[0-9.]+",
177 &msg->met, &msg->path, &msg->prm, &msg->ver) ||
178 msg->met.p != s.p) {
179 err = EBADMSG;
180 goto out;
181 }
182 }
183 else {
184 if (re_regex(s.p, s.l, "HTTP/[0-9.]+ [0-9]+[ ]*[^]*",
185 &msg->ver, &scode, NULL, &msg->reason) ||
186 msg->ver.p != s.p + 5) {
187 err = EBADMSG;
188 goto out;
189 }
190
191 msg->scode = pl_u32(&scode);
192 }
193
194 l -= e.p + e.l - p;
195 p = e.p + e.l;
196
197 name.p = cv = NULL;
198 name.l = ws = lf = 0;
199 comsep = false;
200 quote = false;
201
202 for (; l > 0; p++, l--) {
203
204 switch (*p) {
205
206 case ' ':
207 case '\t':
208 lf = 0; /* folding */
209 ++ws;
210 break;
211
212 case '\r':
213 ++ws;
214 break;
215
216 case '\n':
217 ++ws;
218
219 if (!name.p) {
220 ++p; --l; /* no headers */
221 err = 0;
222 goto out;
223 }
224
225 if (!lf++)
226 break;
227
228 ++p; --l; /* eoh */
229
230 /*@fallthrough@*/
231
232 default:
233 if (lf || (*p == ',' && comsep && !quote)) {
234
235 if (!name.l) {
236 err = EBADMSG;
237 goto out;
238 }
239
240 err = hdr_add(msg, &name, id, cv ? cv : p,
241 cv ? p - cv - ws : 0);
242 if (err)
243 goto out;
244
245 if (!lf) { /* comma separated */
246 cv = NULL;
247 break;
248 }
249
250 if (lf > 1) { /* eoh */
251 err = 0;
252 goto out;
253 }
254
255 comsep = false;
256 name.p = NULL;
257 cv = NULL;
258 lf = 0;
259 }
260
261 if (!name.p) {
262 name.p = p;
263 name.l = 0;
264 ws = 0;
265 }
266
267 if (!name.l) {
268 if (*p != ':') {
269 ws = 0;
270 break;
271 }
272
273 name.l = MAX((int)(p - name.p - ws), 0);
274 if (!name.l) {
275 err = EBADMSG;
276 goto out;
277 }
278
279 id = hdr_hash(&name);
280 comsep = hdr_comma_separated(id);
281 break;
282 }
283
284 if (!cv) {
285 quote = false;
286 cv = p;
287 }
288
289 if (*p == '"')
290 quote = !quote;
291
292 ws = 0;
293 break;
294 }
295 }
296
297 err = ENODATA;
298
299 out:
300 if (err)
301 mem_deref(msg);
302 else {
303 *msgp = msg;
304 mb->pos = mb->end - l;
305 }
306
307 return err;
308}
309
310
311/**
312 * Get a HTTP Header from a HTTP Message
313 *
314 * @param msg HTTP Message
315 * @param id HTTP Header ID
316 *
317 * @return HTTP Header if found, NULL if not found
318 */
319const struct http_hdr *http_msg_hdr(const struct http_msg *msg,
320 enum http_hdrid id)
321{
322 return http_msg_hdr_apply(msg, true, id, NULL, NULL);
323}
324
325
326/**
327 * Apply a function handler to certain HTTP Headers
328 *
329 * @param msg HTTP Message
330 * @param fwd True to traverse forwards, false to traverse backwards
331 * @param id HTTP Header ID
332 * @param h Function handler
333 * @param arg Handler argument
334 *
335 * @return HTTP Header if handler returns true, otherwise NULL
336 */
337const struct http_hdr *http_msg_hdr_apply(const struct http_msg *msg,
338 bool fwd, enum http_hdrid id,
339 http_hdr_h *h, void *arg)
340{
341 struct le *le;
342
343 if (!msg)
344 return NULL;
345
346 le = fwd ? msg->hdrl.head : msg->hdrl.tail;
347
348 while (le) {
349 const struct http_hdr *hdr = le->data;
350
351 le = fwd ? le->next : le->prev;
352
353 if (hdr->id != id)
354 continue;
355
356 if (!h || h(hdr, arg))
357 return hdr;
358 }
359
360 return NULL;
361}
362
363
364/**
365 * Get an unknown HTTP Header from a HTTP Message
366 *
367 * @param msg HTTP Message
368 * @param name Header name
369 *
370 * @return HTTP Header if found, NULL if not found
371 */
372const struct http_hdr *http_msg_xhdr(const struct http_msg *msg,
373 const char *name)
374{
375 return http_msg_xhdr_apply(msg, true, name, NULL, NULL);
376}
377
378
379/**
380 * Apply a function handler to certain unknown HTTP Headers
381 *
382 * @param msg HTTP Message
383 * @param fwd True to traverse forwards, false to traverse backwards
384 * @param name HTTP Header name
385 * @param h Function handler
386 * @param arg Handler argument
387 *
388 * @return HTTP Header if handler returns true, otherwise NULL
389 */
390const struct http_hdr *http_msg_xhdr_apply(const struct http_msg *msg,
391 bool fwd, const char *name,
392 http_hdr_h *h, void *arg)
393{
394 struct le *le;
395 struct pl pl;
396
397 if (!msg || !name)
398 return NULL;
399
400 pl_set_str(&pl, name);
401
402 le = fwd ? msg->hdrl.head : msg->hdrl.tail;
403
404 while (le) {
405 const struct http_hdr *hdr = le->data;
406
407 le = fwd ? le->next : le->prev;
408
409 if (pl_casecmp(&hdr->name, &pl))
410 continue;
411
412 if (!h || h(hdr, arg))
413 return hdr;
414 }
415
416 return NULL;
417}
418
419
420static bool count_handler(const struct http_hdr *hdr, void *arg)
421{
422 uint32_t *n = arg;
423 (void)hdr;
424
425 ++(*n);
426
427 return false;
428}
429
430
431/**
432 * Count the number of HTTP Headers
433 *
434 * @param msg HTTP Message
435 * @param id HTTP Header ID
436 *
437 * @return Number of HTTP Headers
438 */
439uint32_t http_msg_hdr_count(const struct http_msg *msg, enum http_hdrid id)
440{
441 uint32_t n = 0;
442
443 http_msg_hdr_apply(msg, true, id, count_handler, &n);
444
445 return n;
446}
447
448
449/**
450 * Count the number of unknown HTTP Headers
451 *
452 * @param msg HTTP Message
453 * @param name HTTP Header name
454 *
455 * @return Number of HTTP Headers
456 */
457uint32_t http_msg_xhdr_count(const struct http_msg *msg, const char *name)
458{
459 uint32_t n = 0;
460
461 http_msg_xhdr_apply(msg, true, name, count_handler, &n);
462
463 return n;
464}
465
466
467static bool value_handler(const struct http_hdr *hdr, void *arg)
468{
469 return 0 == pl_strcasecmp(&hdr->val, (const char *)arg);
470}
471
472
473/**
474 * Check if a HTTP Header matches a certain value
475 *
476 * @param msg HTTP Message
477 * @param id HTTP Header ID
478 * @param value Header value to check
479 *
480 * @return True if value matches, false if not
481 */
482bool http_msg_hdr_has_value(const struct http_msg *msg, enum http_hdrid id,
483 const char *value)
484{
485 return NULL != http_msg_hdr_apply(msg, true, id, value_handler,
486 (void *)value);
487}
488
489
490/**
491 * Check if an unknown HTTP Header matches a certain value
492 *
493 * @param msg HTTP Message
494 * @param name HTTP Header name
495 * @param value Header value to check
496 *
497 * @return True if value matches, false if not
498 */
499bool http_msg_xhdr_has_value(const struct http_msg *msg, const char *name,
500 const char *value)
501{
502 return NULL != http_msg_xhdr_apply(msg, true, name, value_handler,
503 (void *)value);
504}
505
506
507/**
508 * Print a HTTP Message
509 *
510 * @param pf Print function for output
511 * @param msg HTTP Message
512 *
513 * @return 0 if success, otherwise errorcode
514 */
515int http_msg_print(struct re_printf *pf, const struct http_msg *msg)
516{
517 struct le *le;
518 int err;
519
520 if (!msg)
521 return 0;
522
523 if (pl_isset(&msg->met))
524 err = re_hprintf(pf, "%r %r%r HTTP/%r\n", &msg->met,
525 &msg->path, &msg->prm, &msg->ver);
526 else
527 err = re_hprintf(pf, "HTTP/%r %u %r\n", &msg->ver, msg->scode,
528 &msg->reason);
529
530 for (le=msg->hdrl.head; le; le=le->next) {
531
532 const struct http_hdr *hdr = le->data;
533
534 err |= re_hprintf(pf, "%r: %r (%i)\n", &hdr->name, &hdr->val,
535 hdr->id);
536 }
537
538 return err;
539}