2 * libwebsockets - small server side websockets and web server implementation
4 * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
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.
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.
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,
22 #include "core/private.h"
24 static const unsigned char lextable[] = {
25 #if defined(LWS_AMAZON_RTOS) || defined(LWS_AMAZON_LINUX)
26 #include "roles/http/lextable.h"
28 #include "../lextable.h"
32 #define FAIL_CHAR 0x08
34 #if defined(LWS_WITH_CUSTOM_HEADERS)
42 lws_un16be_get(const void *_p)
44 const uint8_t *p = _p;
46 return ((uint16_t)p[0] << 8) | p[1];
50 lws_un16be_set(void *_p, uint16_t v)
54 *p++ = (uint8_t)(v >> 8);
59 lws_un32be_get(const void *_p)
61 const uint8_t *p = _p;
63 return (uint32_t)((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
67 lws_un32be_set(void *_p, uint32_t v)
71 *p++ = (uint8_t)(v >> 24);
72 *p++ = (uint8_t)(v >> 16);
73 *p++ = (uint8_t)(v >> 8);
78 static struct allocated_headers *
79 _lws_create_ah(struct lws_context_per_thread *pt, ah_data_idx_t data_size)
81 struct allocated_headers *ah = lws_zalloc(sizeof(*ah), "ah struct");
86 ah->data = lws_malloc(data_size, "ah data");
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++;
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);
104 _lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah)
106 lws_start_foreach_llp(struct allocated_headers **, a, pt->http.ah_list) {
109 pt->http.ah_pool_length--;
110 lwsl_info("%s: freed ah %p : pool length %ld\n",
112 (unsigned long)pt->http.ah_pool_length);
119 } lws_end_foreach_llp(a, next);
125 _lws_header_table_reset(struct allocated_headers *ah)
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));
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)
142 // doesn't scrub the ah rxbuffer by default, parent must do if needed
145 __lws_header_table_reset(struct lws *wsi, int autoservice)
147 struct allocated_headers *ah = wsi->http.ah;
148 struct lws_context_per_thread *pt;
149 struct lws_pollfd *pfd;
151 /* if we have the idea we're resetting 'our' ah, must be bound to one */
153 /* ah also concurs with ownership */
154 assert(ah->wsi == wsi);
156 _lws_header_table_reset(ah);
158 /* since we will restart the ah, our new headers are not completed */
159 wsi->hdr_parsing_completed = 0;
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);
167 if (wsi->position_in_fds_table != LWS_NO_FDS_POS &&
168 lws_buflist_next_segment_len(&wsi->buflist, NULL) &&
170 lwsl_debug("%s: service on readbuf ah\n", __func__);
172 pt = &wsi->context->pt[(int)wsi->tsi];
174 * Unlike a normal connect, we have the headers already
175 * (or the first part of them anyway)
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);
185 lws_header_table_reset(struct lws *wsi, int autoservice)
187 struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
189 lws_pt_lock(pt, __func__);
191 __lws_header_table_reset(wsi, autoservice);
197 _lws_header_ensure_we_are_on_waiting_list(struct lws *wsi)
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;
206 pwsi = &(*pwsi)->http.ah_wait_list;
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++;
214 /* we cannot accept input then */
216 _lws_change_pollfd(wsi, LWS_POLLIN, 0, &pa);
220 __lws_remove_from_ah_waiting_list(struct lws *wsi)
222 struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
223 struct lws **pwsi =&pt->http.ah_wait_list;
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--;
236 pwsi = &(*pwsi)->http.ah_wait_list;
242 int LWS_WARN_UNUSED_RESULT
243 lws_header_table_attach(struct lws *wsi, int autoservice)
245 struct lws_context *context = wsi->context;
246 struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
247 struct lws_pollargs pa;
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);
254 if (!lwsi_role_http(wsi)) {
255 lwsl_err("%s: bad role %s\n", __func__, wsi->role_ops->name);
260 lws_pt_lock(pt, __func__);
262 /* if we are already bound to one, just clear it down */
264 lwsl_info("%s: cleardown\n", __func__);
268 n = pt->http.ah_count_in_use == context->max_http_header_pool;
269 #if defined(LWS_WITH_PEER_LIMITS)
271 n = lws_peer_confirm_ah_attach_ok(context, wsi->peer);
273 lws_stats_bump(pt, LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
278 * Pool is either all busy, or we don't want to give this
279 * particular guy an ah right now...
281 * Make sure we are on the waiting list, and return that we
282 * weren't able to provide the ah
284 _lws_header_ensure_we_are_on_waiting_list(wsi);
289 __lws_remove_from_ah_waiting_list(wsi);
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);
298 wsi->http.ah->in_use = 1;
299 wsi->http.ah->wsi = wsi; /* mark our owner */
300 pt->http.ah_count_in_use++;
302 #if defined(LWS_WITH_PEER_LIMITS) && (defined(LWS_ROLE_H1) || \
303 defined(LWS_ROLE_H2))
304 lws_context_lock(context, "ah attach"); /* <========================= */
306 wsi->peer->http.count_ah++;
307 lws_context_unlock(context); /* ====================================> */
310 _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
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);
316 __lws_header_table_reset(wsi, autoservice);
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
337 int __lws_header_table_detach(struct lws *wsi, int autoservice)
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;
346 __lws_remove_from_ah_waiting_list(wsi);
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);
355 /* we did have an ah attached */
357 if (ah->assigned && now - ah->assigned > 3) {
359 * we're detaching the ah, but it was held an
360 * unreasonably long time
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));
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 */
373 memset(&wsi->http.ah, 0, sizeof(wsi->http.ah));
375 #if defined(LWS_WITH_PEER_LIMITS)
377 lws_peer_track_ah_detach(context, wsi->peer);
379 ah->wsi = NULL; /* no owner */
381 pwsi = &pt->http.ah_wait_list;
383 /* oh there is nobody on the waiting list... leave the ah unattached */
385 goto nobody_usable_waiting;
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)
391 lwsl_info("pt wait list %p\n", *pwsi);
393 pwsi_eligible = NULL;
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))
402 pwsi_eligible = pwsi;
404 #if defined(LWS_WITH_PEER_LIMITS)
406 if (!(*pwsi)->http.ah_wait_list)
408 LWSSTATS_C_PEER_LIMIT_AH_DENIED, 1);
410 pwsi = &(*pwsi)->http.ah_wait_list;
413 if (!wsi) /* everybody waiting already has too many ah... */
414 goto nobody_usable_waiting;
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);
421 ah->wsi = wsi; /* new owner */
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"); /* <========================= */
428 wsi->peer->http.count_ah++;
429 lws_context_unlock(context); /* ====================================> */
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);
436 /* he has been stuck waiting for an ah, but now his wait is
437 * over, let him progress */
439 _lws_change_pollfd(wsi, 0, LWS_POLLIN, &pa);
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--;
448 #ifndef LWS_NO_CLIENT
449 if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED) {
452 if (!lws_http_client_connect_via_info2(wsi)) {
453 /* our client connect has failed, the wsi
463 assert(!!pt->http.ah_wait_list_length ==
464 !!(lws_intptr_t)pt->http.ah_wait_list);
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);
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--;
479 int lws_header_table_detach(struct lws *wsi, int autoservice)
481 struct lws_context *context = wsi->context;
482 struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
485 lws_pt_lock(pt, __func__);
486 n = __lws_header_table_detach(wsi, autoservice);
493 lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx)
500 n = wsi->http.ah->frag_index[h];
505 return wsi->http.ah->frags[n].len;
506 n = wsi->http.ah->frags[n].nfrag;
507 } while (frag_idx-- && n);
512 LWS_VISIBLE int lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h)
520 n = wsi->http.ah->frag_index[h];
524 len += wsi->http.ah->frags[n].len;
525 n = wsi->http.ah->frags[n].nfrag;
527 if (n && h != WSI_TOKEN_HTTP_COOKIE)
535 LWS_VISIBLE int lws_hdr_copy_fragment(struct lws *wsi, char *dst, int len,
536 enum lws_token_indexes h, int frag_idx)
544 f = wsi->http.ah->frag_index[h];
549 while (n < frag_idx) {
550 f = wsi->http.ah->frags[f].nfrag;
556 if (wsi->http.ah->frags[f].len >= len)
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';
563 return wsi->http.ah->frags[f].len;
566 LWS_VISIBLE int lws_hdr_copy(struct lws *wsi, char *dst, int len,
567 enum lws_token_indexes h)
569 int toklen = lws_hdr_total_length(wsi, h);
583 n = wsi->http.ah->frag_index[h];
588 comma = (wsi->http.ah->frags[n].nfrag &&
589 h != WSI_TOKEN_HTTP_COOKIE) ? 1 : 0;
591 if (wsi->http.ah->frags[n].len + comma >= len)
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;
608 #if defined(LWS_WITH_CUSTOM_HEADERS)
610 lws_hdr_custom_length(struct lws *wsi, const char *name, int nlen)
614 if (!wsi->http.ah || wsi->http2_substream)
617 ll = wsi->http.ah->unk_ll_head;
619 if (ll >= wsi->http.ah->data_length)
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]);
625 ll = lws_un32be_get(&wsi->http.ah->data[ll + UHO_LL]);
632 lws_hdr_custom_copy(struct lws *wsi, char *dst, int len, const char *name,
638 if (!wsi->http.ah || wsi->http2_substream)
643 ll = wsi->http.ah->unk_ll_head;
645 if (ll >= wsi->http.ah->data_length)
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]);
652 strncpy(dst, &wsi->http.ah->data[ll + UHO_NAME + nlen], n);
657 ll = lws_un32be_get(&wsi->http.ah->data[ll + UHO_LL]);
664 char *lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h)
671 n = wsi->http.ah->frag_index[h];
675 return wsi->http.ah->data + wsi->http.ah->frags[n].offset;
678 static int LWS_WARN_UNUSED_RESULT
679 lws_pos_in_bounds(struct lws *wsi)
684 if (wsi->http.ah->pos <
685 (unsigned int)wsi->context->max_http_header_data)
688 if ((int)wsi->http.ah->pos == wsi->context->max_http_header_data) {
689 lwsl_err("Ran out of header data space\n");
694 * with these tests everywhere, it should never be able to exceed
695 * the limit, only meet it
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);
705 int LWS_WARN_UNUSED_RESULT
706 lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s)
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");
714 wsi->http.ah->frag_index[h] = wsi->http.ah->nfrag;
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;
721 if (lws_pos_in_bounds(wsi))
724 wsi->http.ah->data[wsi->http.ah->pos++] = *s;
726 wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
732 static int LWS_WARN_UNUSED_RESULT
733 issue_char(struct lws *wsi, unsigned char c)
735 unsigned short frag_len;
737 if (lws_pos_in_bounds(wsi))
740 frag_len = wsi->http.ah->frags[wsi->http.ah->nfrag].len;
742 * If we haven't hit the token limit, just copy the character into
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;
749 wsi->http.ah->frags[wsi->http.ah->nfrag].len++;
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))
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);
768 lws_parse_urldecode(struct lws *wsi, uint8_t *_c)
770 struct allocated_headers *ah = wsi->http.ah;
771 unsigned int enc = 0;
774 // lwsl_notice("ah->ups %d\n", ah->ups);
778 * special URI processing... convert %xx
783 ah->ues = URIES_SEEN_PERCENT;
787 case URIES_SEEN_PERCENT:
788 if (char_to_hex(c) < 0)
789 /* illegal post-% char */
793 ah->ues = URIES_SEEN_PERCENT_H1;
796 case URIES_SEEN_PERCENT_H1:
797 if (char_to_hex(c) < 0)
798 /* illegal post-% char */
801 *_c = (char_to_hex(ah->esc_stash) << 4) |
805 ah->ues = URIES_IDLE;
811 * special URI processing...
812 * convert /.. or /... or /../ etc to /
814 * convert // or /// etc to /
815 * leave /.dir or whatever alone
822 /* genuine delimiter */
823 if ((c == '&' || c == ';') && !enc) {
824 if (issue_char(wsi, '\0') < 0)
826 /* link to next fragment */
827 ah->frags[ah->nfrag].nfrag = ah->nfrag + 1;
829 if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags))
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;
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) {
846 /* after the real =, we don't care how many = */
847 if (c == '=' && !enc)
848 ah->post_literal_equal = 1;
851 if (c == '+' && !enc) {
855 /* issue the first / always */
856 if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS])
857 ah->ups = URIPS_SEEN_SLASH;
859 case URIPS_SEEN_SLASH:
860 /* swallow subsequent slashes */
863 /* track and swallow the first . after / */
865 ah->ups = URIPS_SEEN_SLASH_DOT;
868 ah->ups = URIPS_IDLE;
870 case URIPS_SEEN_SLASH_DOT:
871 /* swallow second . */
873 ah->ups = URIPS_SEEN_SLASH_DOT_DOT;
876 /* change /./ to / */
878 ah->ups = URIPS_SEEN_SLASH;
881 /* it was like /.dir ... regurgitate the . */
882 ah->ups = URIPS_IDLE;
883 if (issue_char(wsi, '.') < 0)
887 case URIPS_SEEN_SLASH_DOT_DOT:
889 /* /../ or /..[End of URI] --> backup to last / */
890 if (c == '/' || c == '?') {
892 * back up one dir level if possible
893 * safe against header fragmentation because
894 * the method URI can only be in 1 fragment
896 if (ah->frags[ah->nfrag].len > 2) {
898 ah->frags[ah->nfrag].len--;
901 ah->frags[ah->nfrag].len--;
902 } while (ah->frags[ah->nfrag].len > 1 &&
903 ah->data[ah->pos] != '/');
905 ah->ups = URIPS_SEEN_SLASH;
906 if (ah->frags[ah->nfrag].len > 1)
911 /* /..[^/] ... regurgitate and allow */
913 if (issue_char(wsi, '.') < 0)
915 if (issue_char(wsi, '.') < 0)
917 ah->ups = URIPS_IDLE;
921 if (c == '?' && !enc &&
922 !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI args */
923 if (ah->ues != URIES_IDLE)
926 /* seal off uri header */
927 if (issue_char(wsi, '\0') < 0)
930 /* move to using WSI_TOKEN_HTTP_URI_ARGS */
932 if (ah->nfrag >= LWS_ARRAY_SIZE(ah->frags))
934 ah->frags[ah->nfrag].offset = ++ah->pos;
935 ah->frags[ah->nfrag].len = 0;
936 ah->frags[ah->nfrag].nfrag = 0;
938 ah->post_literal_equal = 0;
939 ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag;
940 ah->ups = URIPS_IDLE;
944 return LPUR_CONTINUE;
953 return LPUR_EXCESSIVE;
956 static const unsigned char methods[] = {
959 WSI_TOKEN_OPTIONS_URI,
962 WSI_TOKEN_DELETE_URI,
968 * possible returns:, -1 fail, 0 ok or 2, transition to raw
971 int LWS_WARN_UNUSED_RESULT
972 lws_parse(struct lws *wsi, unsigned char *buf, int *len)
974 struct allocated_headers *ah = wsi->http.ah;
975 struct lws_context *context = wsi->context;
980 assert(wsi->http.ah);
986 switch (ah->parser_state) {
987 #if defined(LWS_WITH_CUSTOM_HEADERS)
988 case WSI_TOKEN_UNKNOWN_VALUE_PART:
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;
997 ah->lextable_pos = 0;
1001 /* trim leading whitespace */
1002 if (ah->pos != ah->unk_value_pos ||
1003 (c != ' ' && c != '\t')) {
1005 if (lws_pos_in_bounds(wsi))
1008 ah->data[ah->pos++] = c;
1010 pos = ah->lextable_pos;
1015 lwsl_parser("WSI_TOK_(%d) '%c'\n", ah->parser_state, c);
1017 /* collect into malloc'd buffers */
1018 /* optional initial space swallow */
1019 if (!ah->frags[ah->frag_index[ah->parser_state]].len &&
1023 for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1024 if (ah->parser_state == methods[m])
1026 if (m == LWS_ARRAY_SIZE(methods))
1027 /* it was not any of the methods */
1030 /* special URI processing... end at space */
1033 /* enforce starting with / */
1034 if (!ah->frags[ah->nfrag].len)
1035 if (issue_char(wsi, '/') < 0)
1038 if (ah->ups == URIPS_SEEN_SLASH_DOT_DOT) {
1040 * back up one dir level if possible
1041 * safe against header fragmentation
1042 * because the method URI can only be
1045 if (ah->frags[ah->nfrag].len > 2) {
1047 ah->frags[ah->nfrag].len--;
1050 ah->frags[ah->nfrag].len--;
1051 } while (ah->frags[ah->nfrag].len > 1 &&
1052 ah->data[ah->pos] != '/');
1056 /* begin parsing HTTP version: */
1057 if (issue_char(wsi, '\0') < 0)
1059 ah->parser_state = WSI_TOKEN_HTTP;
1060 goto start_fragment;
1063 r = lws_parse_urldecode(wsi, &c);
1071 case LPUR_EXCESSIVE:
1078 if (ah->parser_state != WSI_TOKEN_CHALLENGE &&
1080 if (ah->ues != URIES_IDLE)
1084 ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
1088 n = issue_char(wsi, c);
1092 ah->parser_state = WSI_TOKEN_SKIPPING;
1095 /* per-protocol end of headers management */
1097 if (ah->parser_state == WSI_TOKEN_CHALLENGE)
1098 goto set_parsing_complete;
1101 /* collecting and checking a name part */
1102 case WSI_TOKEN_NAME_PART:
1103 lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X "
1105 "wsi->lextable_pos=%d\n", c, c,
1106 (unsigned long)lwsi_role(wsi),
1109 if (c >= 'A' && c <= 'Z')
1112 #if defined(LWS_WITH_CUSTOM_HEADERS)
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.
1120 ah->unk_pos = ah->pos;
1122 * Prepare new unknown header linked-list entry
1124 * - 16-bit BE: name part length
1125 * - 16-bit BE: value part length
1126 * - 32-bit BE: data offset of next, or 0
1128 for (n = 0; n < 8; n++)
1129 if (!lws_pos_in_bounds(wsi))
1130 ah->data[ah->pos++] = 0;
1134 if (lws_pos_in_bounds(wsi))
1137 ah->data[ah->pos++] = c;
1138 pos = ah->lextable_pos;
1140 #if defined(LWS_WITH_CUSTOM_HEADERS)
1141 if (pos < 0 && c == ':') {
1143 * process unknown headers
1145 * register us in the unknown hdr ll
1148 if (!ah->unk_ll_head)
1149 ah->unk_ll_head = ah->unk_pos;
1151 if (ah->unk_ll_tail)
1152 lws_un32be_set(&ah->data[ah->unk_ll_tail + UHO_LL],
1155 ah->unk_ll_tail = ah->unk_pos;
1157 lwsl_debug("%s: unk header %d '%.*s'\n",
1159 ah->pos - (ah->unk_pos + UHO_NAME),
1160 ah->pos - (ah->unk_pos + UHO_NAME),
1161 &ah->data[ah->unk_pos + UHO_NAME]);
1163 /* set the unknown header name part length */
1165 lws_un16be_set(&ah->data[ah->unk_pos],
1166 (ah->pos - ah->unk_pos) - UHO_NAME);
1168 ah->unk_value_pos = ah->pos;
1171 * collect whatever's coming for the unknown header
1172 * argument until the next CRLF
1174 ah->parser_state = WSI_TOKEN_UNKNOWN_VALUE_PART;
1182 if (lextable[pos] & (1 << 7)) {
1183 /* 1-byte, fail on mismatch */
1184 if ((lextable[pos] & 0x7f) != c) {
1186 ah->lextable_pos = -1;
1191 if (lextable[pos] == FAIL_CHAR)
1194 ah->lextable_pos = pos;
1198 if (lextable[pos] == FAIL_CHAR)
1201 /* b7 = 0, end or 3-byte */
1202 if (lextable[pos] < FAIL_CHAR) {
1203 #if defined(LWS_WITH_CUSTOM_HEADERS)
1205 * We hit a terminal marker, so we
1206 * recognized this header... drop the
1207 * speculative name part storage
1209 ah->pos = ah->unk_pos;
1212 ah->lextable_pos = pos;
1216 if (lextable[pos] == c) { /* goto */
1217 ah->lextable_pos = pos +
1218 (lextable[pos + 1]) +
1219 (lextable[pos + 2] << 8);
1223 /* fall thru goto */
1229 * If it's h1, server needs to be on the look out for
1230 * unknown methods...
1232 if (ah->lextable_pos < 0 && lwsi_role_h1(wsi) &&
1233 lwsi_role_server(wsi)) {
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?
1239 for (m = 0; m < LWS_ARRAY_SIZE(methods); m++)
1240 if (ah->frag_index[methods[m]]) {
1242 * already had the method
1244 #if !defined(LWS_WITH_CUSTOM_HEADERS)
1245 ah->parser_state = WSI_TOKEN_SKIPPING;
1250 if (m != LWS_ARRAY_SIZE(methods))
1251 #if defined(LWS_WITH_CUSTOM_HEADERS)
1253 * We have the method, this is just an
1254 * unknown header then
1261 * ...it's an unknown http method from a client
1262 * in fact, it cannot be valid http.
1264 * Are we set up to transition to another role
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",
1271 /* transition to other role */
1272 return LPR_DO_FALLBACK;
1275 lwsl_info("Unknown method - dropping\n");
1278 if (ah->lextable_pos < 0) {
1279 #if defined(LWS_WITH_CUSTOM_HEADERS)
1283 * ...otherwise for a client, let him ignore
1284 * unknown headers coming from the server
1286 ah->parser_state = WSI_TOKEN_SKIPPING;
1291 if (lextable[ah->lextable_pos] < FAIL_CHAR) {
1292 /* terminal state */
1294 n = ((unsigned int)lextable[ah->lextable_pos] << 8) |
1295 lextable[ah->lextable_pos + 1];
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");
1306 * WSORIGIN is protocol equiv to ORIGIN,
1307 * JWebSocket likes to send it, map to ORIGIN
1309 if (n == WSI_TOKEN_SWORIGIN)
1310 n = WSI_TOKEN_ORIGIN;
1312 ah->parser_state = (enum lws_token_indexes)
1313 (WSI_TOKEN_GET_URI + n);
1314 ah->ups = URIPS_IDLE;
1316 if (context->token_limits)
1317 ah->current_token_limit = context->
1318 token_limits->token_limit[
1321 ah->current_token_limit =
1322 wsi->context->max_http_header_data;
1324 if (ah->parser_state == WSI_TOKEN_CHALLENGE)
1325 goto set_parsing_complete;
1327 goto start_fragment;
1331 #if defined(LWS_WITH_CUSTOM_HEADERS)
1333 //ah->parser_state = WSI_TOKEN_SKIPPING;
1341 if (ah->nfrag == LWS_ARRAY_SIZE(ah->frags)) {
1342 lwsl_warn("More hdr frags than we can deal with\n");
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;
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;
1358 while (ah->frags[n].nfrag)
1359 n = ah->frags[n].nfrag;
1360 ah->frags[n].nfrag = ah->nfrag;
1362 if (issue_char(wsi, ' ') < 0)
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);
1371 ah->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
1374 case WSI_TOKEN_SKIPPING_SAW_CR:
1375 lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
1376 if (ah->ues != URIES_IDLE)
1379 ah->parser_state = WSI_TOKEN_NAME_PART;
1380 #if defined(LWS_WITH_CUSTOM_HEADERS)
1383 ah->lextable_pos = 0;
1385 ah->parser_state = WSI_TOKEN_SKIPPING;
1387 /* we're done, ignore anything else */
1389 case WSI_PARSING_COMPLETE:
1390 lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c);
1398 set_parsing_complete:
1399 if (ah->ues != URIES_IDLE)
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));
1407 lwsl_parser("v%02d hdrs done\n", wsi->rx_frame_type);
1409 ah->parser_state = WSI_PARSING_COMPLETE;
1410 wsi->hdr_parsing_completed = 1;
1415 lwsl_notice(" forbidding on uri sanitation\n");
1416 lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
1418 return LPR_FORBIDDEN;