Imported Upstream version 3.2.0
[platform/upstream/libwebsockets.git] / lib / roles / http / server / parsers.c
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation:
9  *  version 2.1 of the License.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA  02110-1301  USA
20  */
21
22 #include "core/private.h"
23
24 static const unsigned char lextable[] = {
25 #if defined(LWS_AMAZON_RTOS) || defined(LWS_AMAZON_LINUX)
26         #include "roles/http/lextable.h"
27 #else
28         #include "../lextable.h"
29 #endif
30 };
31
32 #define FAIL_CHAR 0x08
33
34 #if defined(LWS_WITH_CUSTOM_HEADERS)
35
36 #define UHO_NLEN        0
37 #define UHO_VLEN        2
38 #define UHO_LL          4
39 #define UHO_NAME        8
40
41 static uint16_t
42 lws_un16be_get(const void *_p)
43 {
44         const uint8_t *p = _p;
45
46         return ((uint16_t)p[0] << 8) | p[1];
47 }
48
49 static void
50 lws_un16be_set(void *_p, uint16_t v)
51 {
52         uint8_t *p = _p;
53
54         *p++ = (uint8_t)(v >> 8);
55         *p++ = (uint8_t)v;
56 }
57
58 static uint32_t
59 lws_un32be_get(const void *_p)
60 {
61         const uint8_t *p = _p;
62
63         return (uint32_t)((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
64 }
65
66 static void
67 lws_un32be_set(void *_p, uint32_t v)
68 {
69         uint8_t *p = _p;
70
71         *p++ = (uint8_t)(v >> 24);
72         *p++ = (uint8_t)(v >> 16);
73         *p++ = (uint8_t)(v >> 8);
74         *p = (uint8_t)v;
75 }
76 #endif
77
78 static struct allocated_headers *
79 _lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size)
80 {
81         struct allocated_headers *ah = lws_zalloc(sizeof(*ah), "ah struct");
82
83         if (!ah)
84                 return NULL;
85
86         ah->data = lws_malloc(data_size, "ah data");
87         if (!ah->data) {
88                 lws_free(ah);
89
90                 return NULL;
91         }
92         ah->next = pt->http.ah_list;
93         pt->http.ah_list = ah;
94         ah->data_length = data_size;
95         pt->http.ah_pool_length++;
96
97         lwsl_info("%s: created ah %p (size %d): pool length %ld\n", __func__,
98                     ah, (int)data_size, (unsigned long)pt->http.ah_pool_length);
99
100         return ah;
101 }
102
103 int
104 _lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah)
105 {
106         lws_start_foreach_llp(struct allocated_headers **, a, pt->http.ah_list) {
107                 if ((*a) == ah) {
108                         *a = ah->next;
109                         pt->http.ah_pool_length--;
110                         lwsl_info("%s: freed ah %p : pool length %ld\n",
111                                     __func__, ah,
112                                     (unsigned long)pt->http.ah_pool_length);
113                         if (ah->data)
114                                 lws_free(ah->data);
115                         lws_free(ah);
116
117                         return 0;
118                 }
119         } lws_end_foreach_llp(a, next);
120
121         return 1;
122 }
123
124 void
125 _lws_header_table_reset(struct allocated_headers *ah)
126 {
127         /* init the ah to reflect no headers or data have appeared yet */
128         memset(ah->frag_index, 0, sizeof(ah->frag_index));
129         memset(ah->frags, 0, sizeof(ah->frags));
130         ah->nfrag = 0;
131         ah->pos = 0;
132         ah->http_response = 0;
133         ah->parser_state = WSI_TOKEN_NAME_PART;
134         ah->lextable_pos = 0;
135 #if defined(LWS_WITH_CUSTOM_HEADERS)
136         ah->unk_pos = 0;
137         ah->unk_ll_head = 0;
138         ah->unk_ll_tail = 0;
139 #endif
140 }
141
142 // doesn't scrub the ah rxbuffer by default, parent must do if needed
143
144 void
145 __lws_header_table_reset(struct lws *wsi, int autoservice)
146 {
147         struct allocated_headers *ah = wsi->http.ah;
148         struct lws_context_per_thread *pt;
149         struct lws_pollfd *pfd;
150
151         /* if we have the idea we're resetting 'our' ah, must be bound to one */
152         assert(ah);
153         /* ah also concurs with ownership */
154         assert(ah->wsi == wsi);
155
156         _lws_header_table_reset(ah);
157
158         /* since we will restart the ah, our new headers are not completed */
159         wsi->hdr_parsing_completed = 0;
160
161         /* while we hold the ah, keep a timeout on the wsi */
162         __lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
163                           wsi->vhost->timeout_secs_ah_idle);
164
165         time(&ah->assigned);
166
167         if (wsi->position_in_fds_table != LWS_NO_FDS_POS &&
168             lws_buflist_next_segment_len(&wsi->buflist, NULL) &&
169             autoservice) {
170                 lwsl_debug("%s: service on readbuf ah\n", __func__);
171
172                 pt = &wsi->context->pt[(int)wsi->tsi];
173                 /*
174                  * Unlike a normal connect, we have the headers already
175                  * (or the first part of them anyway)
176                  */
177                 pfd = &pt->fds[wsi->position_in_fds_table];
178                 pfd->revents |= LWS_POLLIN;
179                 lwsl_err("%s: calling service\n", __func__);
180                 lws_service_fd_tsi(wsi->context, pfd, wsi->tsi);
181         }
182 }
183
184 void
185 lws_header_table_reset(struct lws *wsi, int autoservice)
186 {
187         struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
188
189         lws_pt_lock(pt, __func__);
190
191         __lws_header_table_reset(wsi, autoservice);
192
193         lws_pt_unlock(pt);
194 }
195
196 static void
197 _lws_header_ensure_we_are_on_waiting_list(struct lws *wsi)
198 {
199         struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
200         struct lws_pollargs pa;
201         struct lws **pwsi = &pt->http.ah_wait_list;
202
203         while (*pwsi) {
204                 if (*pwsi == wsi)
205                         return;
206                 pwsi = &(*pwsi)->http.ah_wait_list;
207         }
208
209         lwsl_info("%s: wsi: %p\n", __func__, wsi);
210         wsi->http.ah_wait_list = pt->http.ah_wait_list;
211         pt->http.ah_wait_list = wsi;
212         pt->http.ah_wait_list_length++;
213
214         /* we cannot accept input then */
215
216         _lws_change_pollfd(wsi, LWS_POLLIN, 0, &pa);
217 }
218
219 static int
220 __lws_remove_from_ah_waiting_list(struct lws *wsi)
221 {
222         struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
223         struct lws **pwsi =&pt->http.ah_wait_list;
224
225         while (*pwsi) {
226                 if (*pwsi == wsi) {
227                         lwsl_info("%s: wsi %p\n", __func__, wsi);
228                         /* point prev guy to our next */
229                         *pwsi = wsi->http.ah_wait_list;
230                         /* we shouldn't point anywhere now */
231                         wsi->http.ah_wait_list = NULL;
232                         pt->http.ah_wait_list_length--;
233
234                         return 1;
235                 }
236                 pwsi = &(*pwsi)->http.ah_wait_list;
237         }
238
239         return 0;
240 }
241
242 int LWS_WARN_UNUSED_RESULT
243 lws_header_table_attach(struct lws *wsi, int autoservice)
244 {
245         struct lws_context *context = wsi->context;
246         struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
247         struct lws_pollargs pa;
248         int n;
249
250         lwsl_info("%s: wsi %p: ah %p (tsi %d, count = %d) in\n", __func__,
251                   (void *)wsi, (void *)wsi->http.ah, wsi->tsi,
252                   pt->http.ah_count_in_use);
253
254         if (!lwsi_role_http(wsi)) {
255                 lwsl_err("%s: bad role %s\n", __func__, wsi->role_ops->name);
256                 assert(0);
257                 return -1;
258         }
259
260         lws_pt_lock(pt, __func__);
261
262         /* if we are already bound to one, just clear it down */
263         if (wsi->http.ah) {
264                 lwsl_info("%s: cleardown\n", __func__);
265                 goto reset;
266         }
267
268         n = pt->http.ah_count_in_use == context->max_http_header_pool;
269 #if defined(LWS_WITH_PEER_LIMITS)
270         if (!n) {
271                 n = lws_peer_confirm_ah_attach_ok(context, wsi->peer);
272                 if (n)
273                         lws_stats_bump(pt, LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
274         }
275 #endif
276         if (n) {
277                 /*
278                  * Pool is either all busy, or we don't want to give this
279                  * particular guy an ah right now...
280                  *
281                  * Make sure we are on the waiting list, and return that we
282                  * weren't able to provide the ah
283                  */
284                 _lws_header_ensure_we_are_on_waiting_list(wsi);
285
286                 goto bail;
287         }
288
289         __lws_remove_from_ah_waiting_list(wsi);
290
291         wsi->http.ah = _lws_create_ah(pt, context->max_http_header_data);
292         if (!wsi->http.ah) { /* we could not create an ah */
293                 _lws_header_ensure_we_are_on_waiting_list(wsi);
294
295                 goto bail;
296         }
297
298         wsi->http.ah->in_use = 1;
299         wsi->http.ah->wsi = wsi; /* mark our owner */
300         pt->http.ah_count_in_use++;
301
302 #if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || \
303     defined(LWS_ROLE_H2))
304         lws_context_lock(context, "ah attach"); /* <========================= */
305         if (wsi->peer)
306                 wsi->peer->http.count_ah++;
307         lws_context_unlock(context); /* ====================================> */
308 #endif
309
310         _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
311
312         lwsl_info("%s: did attach wsi %p: ah %p: count %d (on exit)\n", __func__,
313                   (void *)wsi, (void *)wsi->http.ah, pt->http.ah_count_in_use);
314
315 reset:
316         __lws_header_table_reset(wsi, autoservice);
317
318         lws_pt_unlock(pt);
319
320 #ifndef LWS_NO_CLIENT
321         if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED)
322                 if (!lws_http_client_connect_via_info2(wsi))
323                         /* our client connect has failed, the wsi
324                          * has been closed
325                          */
326                         return -1;
327 #endif
328
329         return 0;
330
331 bail:
332         lws_pt_unlock(pt);
333
334         return 1;
335 }
336
337 int __lws_header_table_detach(struct lws *wsi, int autoservice)
338 {
339         struct lws_context *context = wsi->context;
340         struct allocated_headers *ah = wsi->http.ah;
341         struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
342         struct lws_pollargs pa;
343         struct lws **pwsi, **pwsi_eligible;
344         time_t now;
345
346         __lws_remove_from_ah_waiting_list(wsi);
347
348         if (!ah)
349                 return 0;
350
351         lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
352                   (void *)wsi, (void *)ah, wsi->tsi,
353                   pt->http.ah_count_in_use);
354
355         /* we did have an ah attached */
356         time(&now);
357         if (ah->assigned && now - ah->assigned > 3) {
358                 /*
359                  * we're detaching the ah, but it was held an
360                  * unreasonably long time
361                  */
362                 lwsl_debug("%s: wsi %p: ah held %ds, role/state 0x%lx 0x%x,"
363                             "\n", __func__, wsi, (int)(now - ah->assigned),
364                             (unsigned long)lwsi_role(wsi), lwsi_state(wsi));
365         }
366
367         ah->assigned = 0;
368
369         /* if we think we're detaching one, there should be one in use */
370         assert(pt->http.ah_count_in_use > 0);
371         /* and this specific one should have been in use */
372         assert(ah->in_use);
373         memset(&wsi->http.ah, 0, sizeof(wsi->http.ah));
374
375 #if defined(LWS_WITH_PEER_LIMITS)
376         if (ah->wsi)
377                 lws_peer_track_ah_detach(context, wsi->peer);
378 #endif
379         ah->wsi = NULL; /* no owner */
380
381         pwsi = &pt->http.ah_wait_list;
382
383         /* oh there is nobody on the waiting list... leave the ah unattached */
384         if (!*pwsi)
385                 goto nobody_usable_waiting;
386
387         /*
388          * at least one wsi on the same tsi is waiting, give it to oldest guy
389          * who is allowed to take it (if any)
390          */
391         lwsl_info("pt wait list %p\n", *pwsi);
392         wsi = NULL;
393         pwsi_eligible = NULL;
394
395         while (*pwsi) {
396 #if defined(LWS_WITH_PEER_LIMITS)
397                 /* are we willing to give this guy an ah? */
398                 if (!lws_peer_confirm_ah_attach_ok(context, (*pwsi)->peer))
399 #endif
400                 {
401                         wsi = *pwsi;
402                         pwsi_eligible = pwsi;
403                 }
404 #if defined(LWS_WITH_PEER_LIMITS)
405                 else
406                         if (!(*pwsi)->http.ah_wait_list)
407                                 lws_stats_bump(pt,
408                                         LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
409 #endif
410                 pwsi = &(*pwsi)->http.ah_wait_list;
411         }
412
413         if (!wsi) /* everybody waiting already has too many ah... */
414                 goto nobody_usable_waiting;
415
416         lwsl_info("%s: transferring ah to last eligible wsi in wait list "
417                   "%p (wsistate 0x%lx)\n", __func__, wsi,
418                   (unsigned long)wsi->wsistate);
419
420         wsi->http.ah = ah;
421         ah->wsi = wsi; /* new owner */
422
423         __lws_header_table_reset(wsi, autoservice);
424 #if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || \
425     defined(LWS_ROLE_H2))
426         lws_context_lock(context, "ah detach"); /* <========================= */
427         if (wsi->peer)
428                 wsi->peer->http.count_ah++;
429         lws_context_unlock(context); /* ====================================> */
430 #endif
431
432         /* clients acquire the ah and then insert themselves in fds table... */
433         if (wsi->position_in_fds_table != LWS_NO_FDS_POS) {
434                 lwsl_info("%s: Enabling %p POLLIN\n", __func__, wsi);
435
436                 /* he has been stuck waiting for an ah, but now his wait is
437                  * over, let him progress */
438
439                 _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
440         }
441
442         /* point prev guy to next guy in list instead */
443         *pwsi_eligible = wsi->http.ah_wait_list;
444         /* the guy who got one is out of the list */
445         wsi->http.ah_wait_list = NULL;
446         pt->http.ah_wait_list_length--;
447
448 #ifndef LWS_NO_CLIENT
449         if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED) {
450                 lws_pt_unlock(pt);
451
452                 if (!lws_http_client_connect_via_info2(wsi)) {
453                         /* our client connect has failed, the wsi
454                          * has been closed
455                          */
456
457                         return -1;
458                 }
459                 return 0;
460         }
461 #endif
462
463         assert(!!pt->http.ah_wait_list_length ==
464                         !!(lws_intptr_t)pt->http.ah_wait_list);
465 bail:
466         lwsl_info("%s: wsi %p: ah %p (tsi=%d, count = %d)\n", __func__,
467                   (void *)wsi, (void *)ah, pt->tid, pt->http.ah_count_in_use);
468
469         return 0;
470
471 nobody_usable_waiting:
472         lwsl_info("%s: nobody usable waiting\n", __func__);
473         _lws_destroy_ah(pt, ah);
474         pt->http.ah_count_in_use--;
475
476         goto bail;
477 }
478
479 int lws_header_table_detach(struct lws *wsi, int autoservice)
480 {
481         struct lws_context *context = wsi->context;
482         struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
483         int n;
484
485         lws_pt_lock(pt, __func__);
486         n = __lws_header_table_detach(wsi, autoservice);
487         lws_pt_unlock(pt);
488
489         return n;
490 }
491
492 LWS_VISIBLE int
493 lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx)
494 {
495         int n;
496
497         if (!wsi->http.ah)
498                 return 0;
499
500         n = wsi->http.ah->frag_index[h];
501         if (!n)
502                 return 0;
503         do {
504                 if (!frag_idx)
505                         return wsi->http.ah->frags[n].len;
506                 n = wsi->http.ah->frags[n].nfrag;
507         } while (frag_idx-- && n);
508
509         return 0;
510 }
511
512 LWS_VISIBLE int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h)
513 {
514         int n;
515         int len = 0;
516
517         if (!wsi->http.ah)
518                 return 0;
519
520         n = wsi->http.ah->frag_index[h];
521         if (!n)
522                 return 0;
523         do {
524                 len += wsi->http.ah->frags[n].len;
525                 n = wsi->http.ah->frags[n].nfrag;
526
527                 if (n && h != WSI_TOKEN_HTTP_COOKIE)
528                         ++len;
529
530         } while (n);
531
532         return len;
533 }
534
535 LWS_VISIBLE int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len,
536                                       enum lws_token_indexes h, int frag_idx)
537 {
538         int n = 0;
539         int f;
540
541         if (!wsi->http.ah)
542                 return -1;
543
544         f = wsi->http.ah->frag_index[h];
545
546         if (!f)
547                 return -1;
548
549         while (n < frag_idx) {
550                 f = wsi->http.ah->frags[f].nfrag;
551                 if (!f)
552                         return -1;
553                 n++;
554         }
555
556         if (wsi->http.ah->frags[f].len >= len)
557                 return -1;
558
559         memcpy(dst, wsi->http.ah->data + wsi->http.ah->frags[f].offset,
560                wsi->http.ah->frags[f].len);
561         dst[wsi->http.ah->frags[f].len] = '\0';
562
563         return wsi->http.ah->frags[f].len;
564 }
565
566 LWS_VISIBLE int lws_hdr_copy(struct lws *wsi, char *dst, int len,
567                              enum lws_token_indexes h)
568 {
569         int toklen = lws_hdr_total_length(wsi, h);
570         int n;
571         int comma;
572
573         *dst = '\0';
574         if (!toklen)
575                 return 0;
576
577         if (toklen >= len)
578                 return -1;
579
580         if (!wsi->http.ah)
581                 return -1;
582
583         n = wsi->http.ah->frag_index[h];
584         if (!n)
585                 return 0;
586
587         do {
588                 comma = (wsi->http.ah->frags[n].nfrag &&
589                         h != WSI_TOKEN_HTTP_COOKIE) ? 1 : 0;
590
591                 if (wsi->http.ah->frags[n].len + comma >= len)
592                         return -1;
593                 strncpy(dst, &wsi->http.ah->data[wsi->http.ah->frags[n].offset],
594                         wsi->http.ah->frags[n].len);
595                 dst += wsi->http.ah->frags[n].len;
596                 len -= wsi->http.ah->frags[n].len;
597                 n = wsi->http.ah->frags[n].nfrag;
598
599                 if (comma)
600                         *dst++ = ',';
601                                 
602         } while (n);
603         *dst = '\0';
604
605         return toklen;
606 }
607
608 #if defined(LWS_WITH_CUSTOM_HEADERS)
609 LWS_VISIBLE int
610 lws_hdr_custom_length(struct lws *wsi, const char *name, int nlen)
611 {
612         ah_data_idx_t ll;
613
614         if (!wsi->http.ah || wsi->http2_substream)
615                 return -1;
616
617         ll = wsi->http.ah->unk_ll_head;
618         while (ll) {
619                 if (ll >= wsi->http.ah->data_length)
620                         return -1;
621                 if (nlen == lws_un16be_get(&wsi->http.ah->data[ll + UHO_NLEN]) &&
622                     !strncmp(name, &wsi->http.ah->data[ll + UHO_NAME], nlen))
623                         return lws_un16be_get(&wsi->http.ah->data[ll + UHO_VLEN]);
624
625                 ll = lws_un32be_get(&wsi->http.ah->data[ll + UHO_LL]);
626         }
627
628         return -1;
629 }
630
631 LWS_VISIBLE int
632 lws_hdr_custom_copy(struct lws *wsi, char *dst, int len, const char *name,
633                     int nlen)
634 {
635         ah_data_idx_t ll;
636         int n;
637
638         if (!wsi->http.ah || wsi->http2_substream)
639                 return -1;
640
641         *dst = '\0';
642
643         ll = wsi->http.ah->unk_ll_head;
644         while (ll) {
645                 if (ll >= wsi->http.ah->data_length)
646                         return -1;
647                 if (nlen == lws_un16be_get(&wsi->http.ah->data[ll + UHO_NLEN]) &&
648                     !strncmp(name, &wsi->http.ah->data[ll + UHO_NAME], nlen)) {
649                         n = lws_un16be_get(&wsi->http.ah->data[ll + UHO_VLEN]);
650                         if (n + 1 > len)
651                                 return -1;
652                         strncpy(dst, &wsi->http.ah->data[ll + UHO_NAME + nlen], n);
653                         dst[n] = '\0';
654
655                         return n;
656                 }
657                 ll = lws_un32be_get(&wsi->http.ah->data[ll + UHO_LL]);
658         }
659
660         return -1;
661 }
662 #endif
663
664 char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h)
665 {
666         int n;
667
668         if (!wsi->http.ah)
669                 return NULL;
670
671         n = wsi->http.ah->frag_index[h];
672         if (!n)
673                 return NULL;
674
675         return wsi->http.ah->data + wsi->http.ah->frags[n].offset;
676 }
677
678 static int LWS_WARN_UNUSED_RESULT
679 lws_pos_in_bounds(struct lws *wsi)
680 {
681         if (!wsi->http.ah)
682                 return -1;
683
684         if (wsi->http.ah->pos <
685             (unsigned int)wsi->context->max_http_header_data)
686                 return 0;
687
688         if ((int)wsi->http.ah->pos == wsi->context->max_http_header_data) {
689                 lwsl_err("Ran out of header data space\n");
690                 return 1;
691         }
692
693         /*
694          * with these tests everywhere, it should never be able to exceed
695          * the limit, only meet it
696          */
697         lwsl_err("%s: pos %ld, limit %ld\n", __func__,
698                  (unsigned long)wsi->http.ah->pos,
699                  (unsigned long)wsi->context->max_http_header_data);
700         assert(0);
701
702         return 1;
703 }
704
705 int LWS_WARN_UNUSED_RESULT
706 lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s)
707 {
708         wsi->http.ah->nfrag++;
709         if (wsi->http.ah->nfrag == LWS_ARRAY_SIZE(wsi->http.ah->frags)) {
710                 lwsl_warn("More hdr frags than we can deal with, dropping\n");
711                 return -1;
712         }
713
714         wsi->http.ah->frag_index[h] = wsi->http.ah->nfrag;
715
716         wsi->http.ah->frags[wsi->http.ah->nfrag].offset = wsi->http.ah->pos;
717         wsi->http.ah->frags[wsi->http.ah->nfrag].len = 0;
718         wsi->http.ah->frags[wsi->http.ah->nfrag].nfrag = 0;
719
720         do {
721                 if (lws_pos_in_bounds(wsi))
722                         return -1;
723
724                 wsi->http.ah->data[wsi->http.ah->pos++] = *s;
725                 if (*s)
726                         wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
727         } while (*s++);
728
729         return 0;
730 }
731
732 static int LWS_WARN_UNUSED_RESULT
733 issue_char(struct lws *wsi, unsigned char c)
734 {
735         unsigned short frag_len;
736
737         if (lws_pos_in_bounds(wsi))
738                 return -1;
739
740         frag_len = wsi->http.ah->frags[wsi->http.ah->nfrag].len;
741         /*
742          * If we haven't hit the token limit, just copy the character into
743          * the header
744          */
745         if (!wsi->http.ah->current_token_limit ||
746             frag_len < wsi->http.ah->current_token_limit) {
747                 wsi->http.ah->data[wsi->http.ah->pos++] = c;
748                 if (c)
749                         wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
750                 return 0;
751         }
752
753         /* Insert a null character when we *hit* the limit: */
754         if (frag_len == wsi->http.ah->current_token_limit) {
755                 if (lws_pos_in_bounds(wsi))
756                         return -1;
757
758                 wsi->http.ah->data[wsi->http.ah->pos++] = '\0';
759                 lwsl_warn("header %li exceeds limit %ld\n",
760                           (long)wsi->http.ah->parser_state,
761                           (long)wsi->http.ah->current_token_limit);
762         }
763
764         return 1;
765 }
766
767 int
768 lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
769 {
770         struct allocated_headers *ah = wsi->http.ah;
771         unsigned int enc = 0;
772         uint8_t c = *_c;
773
774         // lwsl_notice("ah->ups %d\n", ah->ups);
775
776         /*
777          * PRIORITY 1
778          * special URI processing... convert %xx
779          */
780         switch (ah->ues) {
781         case URIES_IDLE:
782                 if (c == '%') {
783                         ah->ues = URIES_SEEN_PERCENT;
784                         goto swallow;
785                 }
786                 break;
787         case URIES_SEEN_PERCENT:
788                 if (char_to_hex(c) < 0)
789                         /* illegal post-% char */
790                         goto forbid;
791
792                 ah->esc_stash = c;
793                 ah->ues = URIES_SEEN_PERCENT_H1;
794                 goto swallow;
795
796         case URIES_SEEN_PERCENT_H1:
797                 if (char_to_hex(c) < 0)
798                         /* illegal post-% char */
799                         goto forbid;
800
801                 *_c = (char_to_hex(ah->esc_stash) << 4) |
802                                 char_to_hex(c);
803                 c = *_c;
804                 enc = 1;
805                 ah->ues = URIES_IDLE;
806                 break;
807         }
808
809         /*
810          * PRIORITY 2
811          * special URI processing...
812          *  convert /.. or /... or /../ etc to /
813          *  convert /./ to /
814          *  convert // or /// etc to /
815          *  leave /.dir or whatever alone
816          */
817
818         switch (ah->ups) {
819         case URIPS_IDLE:
820                 if (!c)
821                         return -1;
822                 /* genuine delimiter */
823                 if ((c == '&' || c == ';') && !enc) {
824                         if (issue_char(wsi, '\0') < 0)
825                                 return -1;
826                         /* link to next fragment */
827                         ah->frags[ah->nfrag].nfrag = ah->nfrag + 1;
828                         ah->nfrag++;
829                         if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags))
830                                 goto excessive;
831                         /* start next fragment after the & */
832                         ah->post_literal_equal = 0;
833                         ah->frags[ah->nfrag].offset = ++ah->pos;
834                         ah->frags[ah->nfrag].len = 0;
835                         ah->frags[ah->nfrag].nfrag = 0;
836                         goto swallow;
837                 }
838                 /* uriencoded = in the name part, disallow */
839                 if (c == '=' && enc &&
840                     ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
841                     !ah->post_literal_equal) {
842                         c = '_';
843                         *_c =c;
844                 }
845
846                 /* after the real =, we don't care how many = */
847                 if (c == '=' && !enc)
848                         ah->post_literal_equal = 1;
849
850                 /* + to space */
851                 if (c == '+' && !enc) {
852                         c = ' ';
853                         *_c = c;
854                 }
855                 /* issue the first / always */
856                 if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
857                         ah->ups = URIPS_SEEN_SLASH;
858                 break;
859         case URIPS_SEEN_SLASH:
860                 /* swallow subsequent slashes */
861                 if (c == '/')
862                         goto swallow;
863                 /* track and swallow the first . after / */
864                 if (c == '.') {
865                         ah->ups = URIPS_SEEN_SLASH_DOT;
866                         goto swallow;
867                 }
868                 ah->ups = URIPS_IDLE;
869                 break;
870         case URIPS_SEEN_SLASH_DOT:
871                 /* swallow second . */
872                 if (c == '.') {
873                         ah->ups = URIPS_SEEN_SLASH_DOT_DOT;
874                         goto swallow;
875                 }
876                 /* change /./ to / */
877                 if (c == '/') {
878                         ah->ups = URIPS_SEEN_SLASH;
879                         goto swallow;
880                 }
881                 /* it was like /.dir ... regurgitate the . */
882                 ah->ups = URIPS_IDLE;
883                 if (issue_char(wsi, '.') < 0)
884                         return -1;
885                 break;
886
887         case URIPS_SEEN_SLASH_DOT_DOT:
888
889                 /* /../ or /..[End of URI] --> backup to last / */
890                 if (c == '/' || c == '?') {
891                         /*
892                          * back up one dir level if possible
893                          * safe against header fragmentation because
894                          * the method URI can only be in 1 fragment
895                          */
896                         if (ah->frags[ah->nfrag].len > 2) {
897                                 ah->pos--;
898                                 ah->frags[ah->nfrag].len--;
899                                 do {
900                                         ah->pos--;
901                                         ah->frags[ah->nfrag].len--;
902                                 } while (ah->frags[ah->nfrag].len > 1 &&
903                                          ah->data[ah->pos] != '/');
904                         }
905                         ah->ups = URIPS_SEEN_SLASH;
906                         if (ah->frags[ah->nfrag].len > 1)
907                                 break;
908                         goto swallow;
909                 }
910
911                 /*  /..[^/] ... regurgitate and allow */
912
913                 if (issue_char(wsi, '.') < 0)
914                         return -1;
915                 if (issue_char(wsi, '.') < 0)
916                         return -1;
917                 ah->ups = URIPS_IDLE;
918                 break;
919         }
920
921         if (c == '?' && !enc &&
922             !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI args */
923                 if (ah->ues != URIES_IDLE)
924                         goto forbid;
925
926                 /* seal off uri header */
927                 if (issue_char(wsi, '\0') < 0)
928                         return -1;
929
930                 /* move to using WSI_TOKEN_HTTP_URI_ARGS */
931                 ah->nfrag++;
932                 if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags))
933                         goto excessive;
934                 ah->frags[ah->nfrag].offset = ++ah->pos;
935                 ah->frags[ah->nfrag].len = 0;
936                 ah->frags[ah->nfrag].nfrag = 0;
937
938                 ah->post_literal_equal = 0;
939                 ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
940                 ah->ups = URIPS_IDLE;
941                 goto swallow;
942         }
943
944         return LPUR_CONTINUE;
945
946 swallow:
947         return LPUR_SWALLOW;
948
949 forbid:
950         return LPUR_FORBID;
951
952 excessive:
953         return LPUR_EXCESSIVE;
954 }
955
956 static const unsigned char methods[] = {
957         WSI_TOKEN_GET_URI,
958         WSI_TOKEN_POST_URI,
959         WSI_TOKEN_OPTIONS_URI,
960         WSI_TOKEN_PUT_URI,
961         WSI_TOKEN_PATCH_URI,
962         WSI_TOKEN_DELETE_URI,
963         WSI_TOKEN_CONNECT,
964         WSI_TOKEN_HEAD_URI,
965 };
966
967 /*
968  * possible returns:, -1 fail, 0 ok or 2, transition to raw
969  */
970
971 int LWS_WARN_UNUSED_RESULT
972 lws_parse(struct lws *wsi, unsigned char *buf, int *len)
973 {
974         struct allocated_headers *ah = wsi->http.ah;
975         struct lws_context *context = wsi->context;
976         unsigned int n, m;
977         unsigned char c;
978         int r, pos;
979
980         assert(wsi->http.ah);
981
982         do {
983                 (*len)--;
984                 c = *buf++;
985
986                 switch (ah->parser_state) {
987 #if defined(LWS_WITH_CUSTOM_HEADERS)
988                 case WSI_TOKEN_UNKNOWN_VALUE_PART:
989
990                         if (c == '\r')
991                                 break;
992                         if (c == '\n') {
993                                 lws_un16be_set(&ah->data[ah->unk_pos + 2],
994                                                ah->pos - ah->unk_value_pos);
995                                 ah->parser_state = WSI_TOKEN_NAME_PART;
996                                 ah->unk_pos = 0;
997                                 ah->lextable_pos = 0;
998                                 break;
999                         }
1000
1001                         /* trim leading whitespace */
1002                         if (ah->pos != ah->unk_value_pos ||
1003                             (c != ' ' && c != '\t')) {
1004
1005                                 if (lws_pos_in_bounds(wsi))
1006                                         return -1;
1007
1008                                 ah->data[ah->pos++] = c;
1009                         }
1010                         pos = ah->lextable_pos;
1011                         break;
1012 #endif
1013                 default:
1014
1015                         lwsl_parser("WSI_TOK_(%d) '%c'\n", ah->parser_state, c);
1016
1017                         /* collect into malloc'd buffers */
1018                         /* optional initial space swallow */
1019                         if (!ah->frags[ah->frag_index[ah->parser_state]].len &&
1020                             c == ' ')
1021                                 break;
1022
1023                         for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1024                                 if (ah->parser_state == methods[m])
1025                                         break;
1026                         if (m == LWS_ARRAY_SIZE(methods))
1027                                 /* it was not any of the methods */
1028                                 goto check_eol;
1029
1030                         /* special URI processing... end at space */
1031
1032                         if (c == ' ') {
1033                                 /* enforce starting with / */
1034                                 if (!ah->frags[ah->nfrag].len)
1035                                         if (issue_char(wsi, '/') < 0)
1036                                                 return -1;
1037
1038                                 if (ah->ups == URIPS_SEEN_SLASH_DOT_DOT) {
1039                                         /*
1040                                          * back up one dir level if possible
1041                                          * safe against header fragmentation
1042                                          * because the method URI can only be
1043                                          * in 1 fragment
1044                                          */
1045                                         if (ah->frags[ah->nfrag].len > 2) {
1046                                                 ah->pos--;
1047                                                 ah->frags[ah->nfrag].len--;
1048                                                 do {
1049                                                         ah->pos--;
1050                                                         ah->frags[ah->nfrag].len--;
1051                                                 } while (ah->frags[ah->nfrag].len > 1 &&
1052                                                          ah->data[ah->pos] != '/');
1053                                         }
1054                                 }
1055
1056                                 /* begin parsing HTTP version: */
1057                                 if (issue_char(wsi, '\0') < 0)
1058                                         return -1;
1059                                 ah->parser_state = WSI_TOKEN_HTTP;
1060                                 goto start_fragment;
1061                         }
1062
1063                         r = lws_parse_urldecode(wsi, &c);
1064                         switch (r) {
1065                         case LPUR_CONTINUE:
1066                                 break;
1067                         case LPUR_SWALLOW:
1068                                 goto swallow;
1069                         case LPUR_FORBID:
1070                                 goto forbid;
1071                         case LPUR_EXCESSIVE:
1072                                 goto excessive;
1073                         default:
1074                                 return LPR_FAIL;
1075                         }
1076 check_eol:
1077                         /* bail at EOL */
1078                         if (ah->parser_state != WSI_TOKEN_CHALLENGE &&
1079                             c == '\x0d') {
1080                                 if (ah->ues != URIES_IDLE)
1081                                         goto forbid;
1082
1083                                 c = '\0';
1084                                 ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
1085                                 lwsl_parser("*\n");
1086                         }
1087
1088                         n = issue_char(wsi, c);
1089                         if ((int)n < 0)
1090                                 return LPR_FAIL;
1091                         if (n > 0)
1092                                 ah->parser_state = WSI_TOKEN_SKIPPING;
1093
1094 swallow:
1095                         /* per-protocol end of headers management */
1096
1097                         if (ah->parser_state == WSI_TOKEN_CHALLENGE)
1098                                 goto set_parsing_complete;
1099                         break;
1100
1101                         /* collecting and checking a name part */
1102                 case WSI_TOKEN_NAME_PART:
1103                         lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X "
1104                                     "(role=0x%lx) "
1105                                     "wsi->lextable_pos=%d\n", c, c,
1106                                     (unsigned long)lwsi_role(wsi),
1107                                     ah->lextable_pos);
1108
1109                         if (c >= 'A' && c <= 'Z')
1110                                 c += 'a' - 'A';
1111
1112 #if defined(LWS_WITH_CUSTOM_HEADERS)
1113                         /*
1114                          * ...in case it's an unknown header, speculatively
1115                          * store it as the name comes in.  If we recognize it as
1116                          * a known header, we'll snip this.
1117                          */
1118
1119                         if (!ah->unk_pos) {
1120                                 ah->unk_pos = ah->pos;
1121                                 /*
1122                                  * Prepare new unknown header linked-list entry
1123                                  *
1124                                  *  - 16-bit BE: name part length
1125                                  *  - 16-bit BE: value part length
1126                                  *  - 32-bit BE: data offset of next, or 0
1127                                  */
1128                                 for (n = 0; n < 8; n++)
1129                                         if (!lws_pos_in_bounds(wsi))
1130                                                 ah->data[ah->pos++] = 0;
1131                         }
1132 #endif
1133
1134                         if (lws_pos_in_bounds(wsi))
1135                                 return -1;
1136
1137                         ah->data[ah->pos++] = c;
1138                         pos = ah->lextable_pos;
1139
1140 #if defined(LWS_WITH_CUSTOM_HEADERS)
1141                         if (pos < 0 && c == ':') {
1142                                 /*
1143                                  * process unknown headers
1144                                  *
1145                                  * register us in the unknown hdr ll
1146                                  */
1147
1148                                 if (!ah->unk_ll_head)
1149                                         ah->unk_ll_head = ah->unk_pos;
1150
1151                                 if (ah->unk_ll_tail)
1152                                         lws_un32be_set(&ah->data[ah->unk_ll_tail + UHO_LL],
1153                                                        ah->unk_pos);
1154
1155                                 ah->unk_ll_tail = ah->unk_pos;
1156
1157                                 lwsl_debug("%s: unk header %d '%.*s'\n",
1158                                            __func__,
1159                                            ah->pos - (ah->unk_pos + UHO_NAME),
1160                                            ah->pos - (ah->unk_pos + UHO_NAME),
1161                                            &ah->data[ah->unk_pos + UHO_NAME]);
1162
1163                                 /* set the unknown header name part length */
1164
1165                                 lws_un16be_set(&ah->data[ah->unk_pos],
1166                                                (ah->pos - ah->unk_pos) - UHO_NAME);
1167
1168                                 ah->unk_value_pos = ah->pos;
1169
1170                                 /*
1171                                  * collect whatever's coming for the unknown header
1172                                  * argument until the next CRLF
1173                                  */
1174                                 ah->parser_state = WSI_TOKEN_UNKNOWN_VALUE_PART;
1175                                 break;
1176                         }
1177 #endif
1178                         if (pos < 0)
1179                                 break;
1180
1181                         while (1) {
1182                                 if (lextable[pos] & (1 << 7)) {
1183                                         /* 1-byte, fail on mismatch */
1184                                         if ((lextable[pos] & 0x7f) != c) {
1185 nope:
1186                                                 ah->lextable_pos = -1;
1187                                                 break;
1188                                         }
1189                                         /* fall thru */
1190                                         pos++;
1191                                         if (lextable[pos] == FAIL_CHAR)
1192                                                 goto nope;
1193
1194                                         ah->lextable_pos = pos;
1195                                         break;
1196                                 }
1197
1198                                 if (lextable[pos] == FAIL_CHAR)
1199                                         goto nope;
1200
1201                                 /* b7 = 0, end or 3-byte */
1202                                 if (lextable[pos] < FAIL_CHAR) {
1203 #if defined(LWS_WITH_CUSTOM_HEADERS)
1204                                         /*
1205                                          * We hit a terminal marker, so we
1206                                          * recognized this header... drop the
1207                                          * speculative name part storage
1208                                          */
1209                                         ah->pos = ah->unk_pos;
1210                                         ah->unk_pos = 0;
1211 #endif
1212                                         ah->lextable_pos = pos;
1213                                         break;
1214                                 }
1215
1216                                 if (lextable[pos] == c) { /* goto */
1217                                         ah->lextable_pos = pos +
1218                                                 (lextable[pos + 1]) +
1219                                                 (lextable[pos + 2] << 8);
1220                                         break;
1221                                 }
1222
1223                                 /* fall thru goto */
1224                                 pos += 3;
1225                                 /* continue */
1226                         }
1227
1228                         /*
1229                          * If it's h1, server needs to be on the look out for
1230                          * unknown methods...
1231                          */
1232                         if (ah->lextable_pos < 0 && lwsi_role_h1(wsi) &&
1233                             lwsi_role_server(wsi)) {
1234                                 /*
1235                                  * this is not a header we know about... did
1236                                  * we get a valid method (GET, POST etc)
1237                                  * already, or is this the bogus method?
1238                                  */
1239                                 for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1240                                         if (ah->frag_index[methods[m]]) {
1241                                                 /*
1242                                                  * already had the method
1243                                                  */
1244 #if !defined(LWS_WITH_CUSTOM_HEADERS)
1245                                                 ah->parser_state = WSI_TOKEN_SKIPPING;
1246 #endif
1247                                                 break;
1248                                         }
1249
1250                                 if (m != LWS_ARRAY_SIZE(methods))
1251 #if defined(LWS_WITH_CUSTOM_HEADERS)
1252                                         /*
1253                                          * We have the method, this is just an
1254                                          * unknown header then
1255                                          */
1256                                         goto unknown_hdr;
1257 #else
1258                                         break;
1259 #endif
1260                                 /*
1261                                  * ...it's an unknown http method from a client
1262                                  * in fact, it cannot be valid http.
1263                                  *
1264                                  * Are we set up to transition to another role
1265                                  * in these cases?
1266                                  */
1267                                 if (lws_check_opt(wsi->vhost->options,
1268                     LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG)) {
1269                                         lwsl_notice("%s: http fail fallback\n",
1270                                                     __func__);
1271                                          /* transition to other role */
1272                                         return LPR_DO_FALLBACK;
1273                                 }
1274
1275                                 lwsl_info("Unknown method - dropping\n");
1276                                 goto forbid;
1277                         }
1278                         if (ah->lextable_pos < 0) {
1279 #if defined(LWS_WITH_CUSTOM_HEADERS)
1280                                 goto unknown_hdr;
1281 #else
1282                                 /*
1283                                  * ...otherwise for a client, let him ignore
1284                                  * unknown headers coming from the server
1285                                  */
1286                                 ah->parser_state = WSI_TOKEN_SKIPPING;
1287                                 break;
1288 #endif
1289                         }
1290
1291                         if (lextable[ah->lextable_pos] < FAIL_CHAR) {
1292                                 /* terminal state */
1293
1294                                 n = ((unsigned int)lextable[ah->lextable_pos] << 8) |
1295                                                 lextable[ah->lextable_pos + 1];
1296
1297                                 lwsl_parser("known hdr %d\n", n);
1298                                 for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1299                                         if (n == methods[m] &&
1300                                             ah->frag_index[methods[m]]) {
1301                                                 lwsl_warn("Duplicated method\n");
1302                                                 return LPR_FAIL;
1303                                         }
1304
1305                                 /*
1306                                  * WSORIGIN is protocol equiv to ORIGIN,
1307                                  * JWebSocket likes to send it, map to ORIGIN
1308                                  */
1309                                 if (n == WSI_TOKEN_SWORIGIN)
1310                                         n = WSI_TOKEN_ORIGIN;
1311
1312                                 ah->parser_state = (enum lws_token_indexes)
1313                                                         (WSI_TOKEN_GET_URI + n);
1314                                 ah->ups = URIPS_IDLE;
1315
1316                                 if (context->token_limits)
1317                                         ah->current_token_limit = context->
1318                                                 token_limits->token_limit[
1319                                                               ah->parser_state];
1320                                 else
1321                                         ah->current_token_limit =
1322                                                 wsi->context->max_http_header_data;
1323
1324                                 if (ah->parser_state == WSI_TOKEN_CHALLENGE)
1325                                         goto set_parsing_complete;
1326
1327                                 goto start_fragment;
1328                         }
1329                         break;
1330
1331 #if defined(LWS_WITH_CUSTOM_HEADERS)
1332 unknown_hdr:
1333                         //ah->parser_state = WSI_TOKEN_SKIPPING;
1334                         //break;
1335                         break;
1336 #endif
1337
1338 start_fragment:
1339                         ah->nfrag++;
1340 excessive:
1341                         if (ah->nfrag == LWS_ARRAY_SIZE(ah->frags)) {
1342                                 lwsl_warn("More hdr frags than we can deal with\n");
1343                                 return LPR_FAIL;
1344                         }
1345
1346                         ah->frags[ah->nfrag].offset = ah->pos;
1347                         ah->frags[ah->nfrag].len = 0;
1348                         ah->frags[ah->nfrag].nfrag = 0;
1349                         ah->frags[ah->nfrag].flags = 2;
1350
1351                         n = ah->frag_index[ah->parser_state];
1352                         if (!n) { /* first fragment */
1353                                 ah->frag_index[ah->parser_state] = ah->nfrag;
1354                                 ah->hdr_token_idx = ah->parser_state;
1355                                 break;
1356                         }
1357                         /* continuation */
1358                         while (ah->frags[n].nfrag)
1359                                 n = ah->frags[n].nfrag;
1360                         ah->frags[n].nfrag = ah->nfrag;
1361
1362                         if (issue_char(wsi, ' ') < 0)
1363                                 return LPR_FAIL;
1364                         break;
1365
1366                         /* skipping arg part of a name we didn't recognize */
1367                 case WSI_TOKEN_SKIPPING:
1368                         lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c);
1369
1370                         if (c == '\x0d')
1371                                 ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
1372                         break;
1373
1374                 case WSI_TOKEN_SKIPPING_SAW_CR:
1375                         lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
1376                         if (ah->ues != URIES_IDLE)
1377                                 goto forbid;
1378                         if (c == '\x0a') {
1379                                 ah->parser_state = WSI_TOKEN_NAME_PART;
1380 #if defined(LWS_WITH_CUSTOM_HEADERS)
1381                                 ah->unk_pos = 0;
1382 #endif
1383                                 ah->lextable_pos = 0;
1384                         } else
1385                                 ah->parser_state = WSI_TOKEN_SKIPPING;
1386                         break;
1387                         /* we're done, ignore anything else */
1388
1389                 case WSI_PARSING_COMPLETE:
1390                         lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c);
1391                         break;
1392                 }
1393
1394         } while (*len);
1395
1396         return LPR_OK;
1397
1398 set_parsing_complete:
1399         if (ah->ues != URIES_IDLE)
1400                 goto forbid;
1401
1402         if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
1403                 if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
1404                         wsi->rx_frame_type = /* temp for ws version index */
1405                                atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
1406
1407                 lwsl_parser("v%02d hdrs done\n", wsi->rx_frame_type);
1408         }
1409         ah->parser_state = WSI_PARSING_COMPLETE;
1410         wsi->hdr_parsing_completed = 1;
1411
1412         return LPR_OK;
1413
1414 forbid:
1415         lwsl_notice(" forbidding on uri sanitation\n");
1416         lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
1417
1418         return LPR_FORBIDDEN;
1419 }
1420