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 const char * const method_names[] = {
25 "GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT", "HEAD",
31 static const char * const intermediates[] = { "private", "public" };
38 * REQUIRES CONTEXT LOCK HELD
43 _lws_vhost_init_server(const struct lws_context_creation_info *info,
44 struct lws_vhost *vhost)
46 int n, opt = 1, limit = 1;
47 lws_sockfd_type sockfd;
56 vhost->iface = info->iface;
57 vhost->listen_port = info->port;
60 /* set up our external listening socket we serve on */
62 if (vhost->listen_port == CONTEXT_PORT_NO_LISTEN ||
63 vhost->listen_port == CONTEXT_PORT_NO_LISTEN_SERVER)
66 vh = vhost->context->vhost_list;
68 if (vh->listen_port == vhost->listen_port) {
69 if (((!vhost->iface && !vh->iface) ||
70 (vhost->iface && vh->iface &&
71 !strcmp(vhost->iface, vh->iface))) &&
74 lwsl_notice(" using listen skt from vhost %s\n",
84 * let's check before we do anything else about the disposition
85 * of the interface he wants to bind to...
87 is = lws_socket_bind(vhost, LWS_SOCK_INVALID, vhost->listen_port,
89 lwsl_debug("initial if check says %d\n", is);
91 if (is == LWS_ITOSA_BUSY)
97 lws_start_foreach_llp(struct lws_vhost **, pv,
98 vhost->context->no_listener_vhost_list) {
99 if (is >= LWS_ITOSA_USABLE && *pv == vhost) {
100 /* on the list and shouldn't be: remove it */
101 lwsl_debug("deferred iface: removing vh %s\n",
103 *pv = vhost->no_listener_vhost_list;
104 vhost->no_listener_vhost_list = NULL;
107 if (is < LWS_ITOSA_USABLE && *pv == vhost)
109 } lws_end_foreach_llp(pv, no_listener_vhost_list);
111 /* not on the list... */
113 if (is < LWS_ITOSA_USABLE) {
115 /* ... but needs to be: so add it */
117 lwsl_debug("deferred iface: adding vh %s\n", vhost->name);
118 vhost->no_listener_vhost_list =
119 vhost->context->no_listener_vhost_list;
120 vhost->context->no_listener_vhost_list = vhost;
128 case LWS_ITOSA_NOT_EXIST:
130 if (info) /* first time */
131 lwsl_err("VH %s: iface %s port %d DOESN'T EXIST\n",
132 vhost->name, vhost->iface, vhost->listen_port);
133 return (info->options & LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND) == LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND?
135 case LWS_ITOSA_NOT_USABLE:
137 if (info) /* first time */
138 lwsl_err("VH %s: iface %s port %d NOT USABLE\n",
139 vhost->name, vhost->iface, vhost->listen_port);
140 return (info->options & LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND) == LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND?
146 #if defined(__linux__)
147 #ifdef LWS_WITH_UNIX_SOCK
149 * A Unix domain sockets cannot be bound for several times, even if we set
150 * the SO_REUSE* options on.
151 * However, fortunately, each thread is able to independently listen when
152 * running on a reasonably new Linux kernel. So we can safely assume
153 * creating just one listening socket for a multi-threaded environment won't
154 * fail in most cases.
156 if (!LWS_UNIX_SOCK_ENABLED(vhost))
158 limit = vhost->context->count_threads;
161 for (m = 0; m < limit; m++) {
162 #ifdef LWS_WITH_UNIX_SOCK
163 if (LWS_UNIX_SOCK_ENABLED(vhost))
164 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
168 if (LWS_IPV6_ENABLED(vhost))
169 sockfd = socket(AF_INET6, SOCK_STREAM, 0);
172 sockfd = socket(AF_INET, SOCK_STREAM, 0);
174 if (sockfd == LWS_SOCK_INVALID) {
175 lwsl_err("ERROR opening socket\n");
178 #if !defined(LWS_WITH_ESP32)
179 #if (defined(WIN32) || defined(_WIN32)) && defined(SO_EXCLUSIVEADDRUSE)
181 * only accept that we are the only listener on the port
182 * https://msdn.microsoft.com/zh-tw/library/
183 * windows/desktop/ms740621(v=vs.85).aspx
185 * for lws, to match Linux, we default to exclusive listen
187 if (!lws_check_opt(vhost->options,
188 LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE)) {
189 if (setsockopt(sockfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
190 (const void *)&opt, sizeof(opt)) < 0) {
191 lwsl_err("reuseaddr failed\n");
192 compatible_close(sockfd);
199 * allow us to restart even if old sockets in TIME_WAIT
201 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
202 (const void *)&opt, sizeof(opt)) < 0) {
203 lwsl_err("reuseaddr failed\n");
204 compatible_close(sockfd);
208 #if defined(LWS_WITH_IPV6) && defined(IPV6_V6ONLY)
209 if (LWS_IPV6_ENABLED(vhost) &&
210 vhost->options & LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) {
211 int value = (vhost->options &
212 LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE) ? 1 : 0;
213 if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
214 (const void*)&value, sizeof(value)) < 0) {
215 compatible_close(sockfd);
221 #if defined(__linux__) && defined(SO_REUSEPORT)
222 /* keep coverity happy */
226 n = lws_check_opt(vhost->options,
227 LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE);
229 if (n && vhost->context->count_threads > 1)
230 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,
231 (const void *)&opt, sizeof(opt)) < 0) {
232 compatible_close(sockfd);
237 lws_plat_set_socket_options(vhost, sockfd, 0);
239 is = lws_socket_bind(vhost, sockfd, vhost->listen_port, vhost->iface, 1);
240 if (is == LWS_ITOSA_BUSY) {
242 compatible_close(sockfd);
248 * There is a race where the network device may come up and then
249 * go away and fail here. So correctly handle unexpected failure
250 * here despite we earlier confirmed it.
253 lwsl_info("%s: lws_socket_bind says %d\n", __func__, is);
254 compatible_close(sockfd);
258 wsi = lws_zalloc(sizeof(struct lws), "listen wsi");
260 lwsl_err("Out of mem\n");
264 #ifdef LWS_WITH_UNIX_SOCK
265 if (!LWS_UNIX_SOCK_ENABLED(vhost))
269 vhost->listen_port = is;
271 lwsl_debug("%s: lws_socket_bind says %d\n", __func__, is);
274 wsi->context = vhost->context;
275 wsi->desc.sockfd = sockfd;
276 lws_role_transition(wsi, 0, LRS_UNCONNECTED, &role_ops_listen);
277 wsi->protocol = vhost->protocols;
279 lws_vhost_bind_wsi(vhost, wsi);
282 if (wsi->context->event_loop_ops->init_vhost_listen_wsi)
283 wsi->context->event_loop_ops->init_vhost_listen_wsi(wsi);
285 if (__insert_wsi_socket_into_fds(vhost->context, wsi)) {
286 lwsl_notice("inserting wsi socket into fds failed\n");
290 vhost->context->count_wsi_allocated++;
291 vhost->lserv_wsi = wsi;
293 n = listen(wsi->desc.sockfd, LWS_SOMAXCONN);
295 lwsl_err("listen failed with error %d\n", LWS_ERRNO);
296 vhost->lserv_wsi = NULL;
297 vhost->context->count_wsi_allocated--;
298 __remove_wsi_socket_from_fds(wsi);
301 } /* for each thread able to independently listen */
303 if (!lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) {
304 #ifdef LWS_WITH_UNIX_SOCK
305 if (LWS_UNIX_SOCK_ENABLED(vhost))
306 lwsl_info(" Listening on \"%s\"\n", vhost->iface);
309 lwsl_info(" Listening on port %d\n", vhost->listen_port);
312 // info->port = vhost->listen_port;
317 compatible_close(sockfd);
324 lws_select_vhost(struct lws_context *context, int port, const char *servername)
326 struct lws_vhost *vhost = context->vhost_list;
330 n = (int)strlen(servername);
332 p = strchr(servername, ':');
334 colon = lws_ptr_diff(p, servername);
336 /* Priotity 1: first try exact matches */
339 if (port == vhost->listen_port &&
340 !strncmp(vhost->name, servername, colon)) {
341 lwsl_info("SNI: Found: %s\n", servername);
344 vhost = vhost->vhost_next;
348 * Priority 2: if no exact matches, try matching *.vhost-name
349 * unintentional matches are possible but resolve to x.com for *.x.com
350 * which is reasonable. If exact match exists we already chose it and
351 * never reach here. SSL will still fail it if the cert doesn't allow
354 vhost = context->vhost_list;
356 int m = (int)strlen(vhost->name);
357 if (port && port == vhost->listen_port &&
359 servername[colon - m - 1] == '.' &&
360 !strncmp(vhost->name, servername + colon - m, m)) {
361 lwsl_info("SNI: Found %s on wildcard: %s\n",
362 servername, vhost->name);
365 vhost = vhost->vhost_next;
368 /* Priority 3: match the first vhost on our port */
370 vhost = context->vhost_list;
372 if (port && port == vhost->listen_port) {
373 lwsl_info("%s: vhost match to %s based on port %d\n",
374 __func__, vhost->name, port);
377 vhost = vhost->vhost_next;
385 static const struct lws_mimetype {
386 const char *extension;
387 const char *mimetype;
388 } server_mimetypes[] = {
389 { ".html", "text/html" },
390 { ".htm", "text/html" },
391 { ".js", "text/javascript" },
392 { ".css", "text/css" },
393 { ".png", "image/png" },
394 { ".jpg", "image/jpeg" },
395 { ".jpeg", "image/jpeg" },
396 { ".ico", "image/x-icon" },
397 { ".gif", "image/gif" },
398 { ".svg", "image/svg+xml" },
399 { ".ttf", "application/x-font-ttf" },
400 { ".otf", "application/font-woff" },
401 { ".woff", "application/font-woff" },
402 { ".woff2", "application/font-woff2" },
403 { ".gz", "application/gzip" },
404 { ".txt", "text/plain" },
405 { ".xml", "application/xml" },
406 { ".json", "application/json" },
409 LWS_VISIBLE LWS_EXTERN const char *
410 lws_get_mimetype(const char *file, const struct lws_http_mount *m)
412 const struct lws_protocol_vhost_options *pvo;
413 size_t n = strlen(file), len, i;
414 const char *fallback_mimetype = NULL;
415 const struct lws_mimetype *mt;
417 /* prioritize user-defined mimetypes */
418 for (pvo = m ? m->extra_mimetypes : NULL; pvo; pvo = pvo->next) {
419 /* ie, match anything */
420 if (!fallback_mimetype && pvo->name[0] == '*') {
421 fallback_mimetype = pvo->value;
425 len = strlen(pvo->name);
426 if (n > len && !strcasecmp(&file[n - len], pvo->name)) {
427 lwsl_info("%s: match to user mimetype: %s\n", __func__, pvo->value);
432 /* fallback to server-defined mimetypes */
433 for (i = 0; i < LWS_ARRAY_SIZE(server_mimetypes); ++i) {
434 mt = &server_mimetypes[i];
436 len = strlen(mt->extension);
437 if (n > len && !strcasecmp(&file[n - len], mt->extension)) {
438 lwsl_info("%s: match to server mimetype: %s\n", __func__, mt->mimetype);
443 /* fallback to '*' if defined */
444 if (fallback_mimetype) {
445 lwsl_info("%s: match to any mimetype: %s\n", __func__, fallback_mimetype);
446 return fallback_mimetype;
452 static lws_fop_flags_t
453 lws_vfs_prepare_flags(struct lws *wsi)
455 lws_fop_flags_t f = 0;
457 if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING))
460 if (strstr(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING),
462 lwsl_info("client indicates GZIP is acceptable\n");
463 f |= LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP;
469 #if !defined(LWS_AMAZON_RTOS)
471 lws_http_serve(struct lws *wsi, char *uri, const char *origin,
472 const struct lws_http_mount *m)
474 const struct lws_protocol_vhost_options *pvo = m->interpret;
475 struct lws_process_html_args args;
476 const char *mimetype;
477 #if !defined(_WIN32_WCE)
478 const struct lws_plat_file_ops *fops;
480 lws_fop_flags_t fflags = LWS_O_RDONLY;
481 #if defined(WIN32) && defined(LWS_HAVE__STAT32I64)
482 struct _stat32i64 st;
488 char path[256], sym[2048];
489 unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p;
490 unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE;
491 #if !defined(WIN32) && !defined(LWS_WITH_ESP32)
496 wsi->handling_404 = 0;
500 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
501 if (wsi->vhost->http.error_document_404 &&
502 !strcmp(uri, wsi->vhost->http.error_document_404))
503 wsi->handling_404 = 1;
506 lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
508 #if !defined(_WIN32_WCE)
510 fflags |= lws_vfs_prepare_flags(wsi);
514 fops = lws_vfs_select_fops(wsi->context->fops, path, &vpath);
516 if (wsi->http.fop_fd)
517 lws_vfs_file_close(&wsi->http.fop_fd);
519 wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
520 path, vpath, &fflags);
521 if (!wsi->http.fop_fd) {
522 lwsl_info("%s: Unable to open '%s': errno %d\n",
523 __func__, path, errno);
528 /* if it can't be statted, don't try */
529 if (fflags & LWS_FOP_FLAG_VIRTUAL)
531 #if defined(LWS_WITH_ESP32)
535 if (fstat(wsi->http.fop_fd->fd, &st)) {
536 lwsl_info("unable to stat %s\n", path);
540 #if defined(LWS_HAVE__STAT32I64)
541 if (_stat32i64(path, &st)) {
542 lwsl_info("unable to stat %s\n", path);
546 if (stat(path, &st)) {
547 lwsl_info("unable to stat %s\n", path);
553 wsi->http.fop_fd->mod_time = (uint32_t)st.st_mtime;
554 fflags |= LWS_FOP_FLAG_MOD_TIME_VALID;
556 #if !defined(WIN32) && !defined(LWS_WITH_ESP32)
557 if ((S_IFMT & st.st_mode) == S_IFLNK) {
558 len = readlink(path, sym, sizeof(sym) - 1);
560 lwsl_err("Failed to read link %s\n", path);
564 lwsl_debug("symlink %s -> %s\n", path, sym);
565 lws_snprintf(path, sizeof(path) - 1, "%s", sym);
568 if ((S_IFMT & st.st_mode) == S_IFDIR) {
569 lwsl_debug("default filename append to dir\n");
570 lws_snprintf(path, sizeof(path) - 1, "%s/%s/index.html",
574 } while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5);
577 lwsl_err("symlink loop %s \n", path);
579 n = sprintf(sym, "%08llX%08lX",
580 (unsigned long long)lws_vfs_get_length(wsi->http.fop_fd),
581 (unsigned long)lws_vfs_get_mod_time(wsi->http.fop_fd));
583 /* disable ranges if IF_RANGE token invalid */
585 if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_RANGE))
586 if (strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_RANGE)))
587 /* differs - defeat Range: */
588 wsi->http.ah->frag_index[WSI_TOKEN_HTTP_RANGE] = 0;
590 if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) {
592 * he thinks he has some version of it already,
593 * check if the tag matches
595 if (!strcmp(sym, lws_hdr_simple_ptr(wsi,
596 WSI_TOKEN_HTTP_IF_NONE_MATCH))) {
598 char cache_control[50], *cc = "no-store";
601 lwsl_debug("%s: ETAG match %s %s\n", __func__,
604 /* we don't need to send the payload */
605 if (lws_add_http_header_status(wsi,
606 HTTP_STATUS_NOT_MODIFIED, &p, end)) {
607 lwsl_err("%s: failed adding not modified\n",
612 if (lws_add_http_header_by_token(wsi,
614 (unsigned char *)sym, n, &p, end))
617 /* but we still need to send cache control... */
619 if (m->cache_max_age && m->cache_reusable) {
620 if (!m->cache_revalidate) {
622 cclen = sprintf(cache_control,
624 intermediates[wsi->cache_intermediaries],
628 cclen = sprintf(cache_control,
629 "must-revalidate, %s, max-age=%u",
630 intermediates[wsi->cache_intermediaries],
635 if (lws_add_http_header_by_token(wsi,
636 WSI_TOKEN_HTTP_CACHE_CONTROL,
637 (unsigned char *)cc, cclen, &p, end))
640 if (lws_finalize_http_header(wsi, &p, end))
643 n = lws_write(wsi, start, p - start,
644 LWS_WRITE_HTTP_HEADERS |
645 LWS_WRITE_H2_STREAM_END);
646 if (n != (p - start)) {
647 lwsl_err("_write returned %d from %ld\n", n,
652 lws_vfs_file_close(&wsi->http.fop_fd);
654 if (lws_http_transaction_completed(wsi))
661 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG,
662 (unsigned char *)sym, n, &p, end))
666 mimetype = lws_get_mimetype(path, m);
668 lwsl_info("unknown mimetype for %s\n", path);
669 if (lws_return_http_status(wsi,
670 HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL) ||
671 lws_http_transaction_completed(wsi))
677 lwsl_debug("sending no mimetype for %s\n", path);
679 wsi->sending_chunked = 0;
680 wsi->interpreting = 0;
683 * check if this is in the list of file suffixes to be interpreted by
687 n = (int)strlen(path);
688 if (n > (int)strlen(pvo->name) &&
689 !strcmp(&path[n - strlen(pvo->name)], pvo->name)) {
690 wsi->interpreting = 1;
691 if (!wsi->http2_substream)
692 wsi->sending_chunked = 1;
694 wsi->protocol_interpret_idx =
695 lws_vhost_name_to_protocol(wsi->vhost,
697 &lws_get_vhost(wsi)->protocols[0];
699 lwsl_debug("want %s interpreted by %s (pcol is %s)\n", path,
700 wsi->vhost->protocols[
701 (int)wsi->protocol_interpret_idx].name,
702 wsi->protocol->name);
703 if (lws_bind_protocol(wsi, &wsi->vhost->protocols[
704 (int)wsi->protocol_interpret_idx], __func__))
707 if (lws_ensure_user_space(wsi))
714 if (wsi->sending_chunked) {
715 if (lws_add_http_header_by_token(wsi,
716 WSI_TOKEN_HTTP_TRANSFER_ENCODING,
717 (unsigned char *)"chunked", 7,
723 const struct lws_protocols *pp = lws_vhost_name_to_protocol(
724 wsi->vhost, m->protocol);
726 if (lws_bind_protocol(wsi, pp, __func__))
729 args.max_len = lws_ptr_diff(end, p);
730 if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS,
731 wsi->user_space, &args, 0))
733 p = (unsigned char *)args.p;
737 n = lws_serve_http_file(wsi, path, mimetype, (char *)start,
738 lws_ptr_diff(p, start));
740 if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
741 return -1; /* error or can't reuse connection: close the socket */
751 #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
752 const struct lws_http_mount *
753 lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len)
755 const struct lws_http_mount *hm, *hit = NULL;
758 hm = wsi->vhost->http.mount_list;
760 if (uri_len >= hm->mountpoint_len &&
761 !strncmp(uri_ptr, hm->mountpoint, hm->mountpoint_len) &&
762 (uri_ptr[hm->mountpoint_len] == '\0' ||
763 uri_ptr[hm->mountpoint_len] == '/' ||
764 hm->mountpoint_len == 1)
766 if (hm->origin_protocol == LWSMPRO_CALLBACK ||
767 ((hm->origin_protocol == LWSMPRO_CGI ||
768 lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) ||
769 lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
770 lws_hdr_total_length(wsi, WSI_TOKEN_HEAD_URI) ||
771 (wsi->http2_substream &&
772 lws_hdr_total_length(wsi,
773 WSI_TOKEN_HTTP_COLON_PATH)) ||
775 hm->mountpoint_len > best)) {
776 best = hm->mountpoint_len;
787 #if !defined(LWS_WITH_ESP32)
789 lws_find_string_in_file(const char *filename, const char *string, int stringlen)
792 int fd, match = 0, pos = 0, n = 0, hit = 0;
794 fd = lws_open(filename, O_RDONLY);
796 lwsl_err("can't open auth file: %s\n", filename);
802 n = read(fd, buf, sizeof(buf));
804 if (match == stringlen)
811 if (match == stringlen) {
812 if (buf[pos] == '\r' || buf[pos] == '\n') {
819 if (buf[pos] == string[match])
834 lws_unauthorised_basic_auth(struct lws *wsi)
836 struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
837 unsigned char *start = pt->serv_buf + LWS_PRE,
838 *p = start, *end = p + 2048;
842 /* no auth... tell him it is required */
844 if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end))
847 n = lws_snprintf(buf, sizeof(buf), "Basic realm=\"lwsws\"");
848 if (lws_add_http_header_by_token(wsi,
849 WSI_TOKEN_HTTP_WWW_AUTHENTICATE,
850 (unsigned char *)buf, n, &p, end))
853 if (lws_add_http_header_content_length(wsi, 0, &p, end))
856 if (lws_finalize_http_header(wsi, &p, end))
859 n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS |
860 LWS_WRITE_H2_STREAM_END);
864 return lws_http_transaction_completed(wsi);
868 int lws_clean_url(char *p)
870 if (p[0] == 'h' && p[1] == 't' && p[2] == 't' && p[3] == 'p') {
882 if (p[0] == '/' && p[1] == '/') {
896 static const unsigned char methods[] = {
899 WSI_TOKEN_OPTIONS_URI,
902 WSI_TOKEN_DELETE_URI,
905 #ifdef LWS_WITH_HTTP2
906 WSI_TOKEN_HTTP_COLON_PATH,
911 lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)
915 for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++)
916 if (lws_hdr_total_length(wsi, methods[n]))
919 lwsl_warn("Missing URI in HTTP request\n");
924 !((wsi->http2_substream || wsi->h2_stream_carries_ws) &&
925 lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH))) {
926 lwsl_warn("multiple methods?\n");
930 for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++)
931 if (lws_hdr_total_length(wsi, methods[n])) {
932 *puri_ptr = lws_hdr_simple_ptr(wsi, methods[n]);
933 *puri_len = lws_hdr_total_length(wsi, methods[n]);
940 enum lws_check_basic_auth_results
941 lws_check_basic_auth(struct lws *wsi, const char *basic_auth_login_file)
943 char b64[160], plain[(sizeof(b64) * 3) / 4], *pcolon;
946 if (!basic_auth_login_file)
947 return LCBA_CONTINUE;
949 /* Did he send auth? */
950 ml = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
952 return LCBA_FAILED_AUTH;
954 /* Disallow fragmentation monkey business */
956 fi = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_AUTHORIZATION];
957 if (wsi->http.ah->frags[fi].nfrag) {
958 lwsl_err("fragmented basic auth header not allowed\n");
959 return LCBA_FAILED_AUTH;
962 m = lws_hdr_copy(wsi, b64, sizeof(b64),
963 WSI_TOKEN_HTTP_AUTHORIZATION);
965 lwsl_err("b64 auth too long\n");
966 return LCBA_END_TRANSACTION;
970 if (strcasecmp(b64, "Basic")) {
971 lwsl_err("auth missing basic: %s\n", b64);
972 return LCBA_END_TRANSACTION;
975 /* It'll be like Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l */
977 m = lws_b64_decode_string(b64 + 6, plain, sizeof(plain) - 1);
979 lwsl_err("plain auth too long\n");
980 return LCBA_END_TRANSACTION;
984 pcolon = strchr(plain, ':');
986 lwsl_err("basic auth format broken\n");
987 return LCBA_END_TRANSACTION;
989 if (!lws_find_string_in_file(basic_auth_login_file, plain, m)) {
990 lwsl_err("basic auth lookup failed\n");
991 return LCBA_FAILED_AUTH;
995 * Rewrite WSI_TOKEN_HTTP_AUTHORIZATION so it is just the
996 * authorized username
1000 wsi->http.ah->frags[fi].len = lws_ptr_diff(pcolon, plain);
1001 pcolon = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
1002 strncpy(pcolon, plain, ml - 1);
1003 pcolon[ml - 1] = '\0';
1004 lwsl_info("%s: basic auth accepted for %s\n", __func__,
1005 lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION));
1007 return LCBA_CONTINUE;
1010 #if defined(LWS_WITH_HTTP_PROXY)
1012 * Set up an onward http proxy connection according to the mount this
1013 * uri falls under. Notice this can also be starting the proxying of what was
1014 * originally an incoming h1 upgrade, or an h2 ws "upgrade".
1017 lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit,
1018 char *uri_ptr, char ws)
1020 char ads[96], rpath[256], host[96], *pcolon, *pslash, unix_skt = 0;
1021 struct lws_client_connect_info i;
1027 * Neither our inbound ws upgrade request side, nor our onward
1028 * ws client connection on our side can bind to the actual
1029 * protocol that only the remote inbound side and the remote
1030 * onward side understand.
1032 * Instead these are both bound to our built-in "lws-ws-proxy"
1033 * protocol, which understands how to proxy between the two
1036 * We bind the parent, inbound part here and our side of the
1037 * onward client connection is bound to the same handler using
1038 * the .local_protocol_name.
1040 lws_bind_protocol(wsi, &lws_ws_proxy, __func__);
1042 memset(&i, 0, sizeof(i));
1043 i.context = lws_get_context(wsi);
1045 if (hit->origin[0] == '+')
1048 pcolon = strchr(hit->origin, ':');
1049 pslash = strchr(hit->origin, '/');
1051 lwsl_err("Proxy mount origin '%s' must have /\n", hit->origin);
1057 lwsl_err("Proxy mount origin for unix skt must "
1058 "have address delimited by :\n");
1062 n = lws_ptr_diff(pcolon, hit->origin);
1065 if (pcolon > pslash)
1069 n = (int)(pcolon - hit->origin);
1071 n = (int)(pslash - hit->origin);
1073 if (n >= (int)sizeof(ads) - 2)
1074 n = sizeof(ads) - 2;
1077 memcpy(ads, hit->origin, n);
1082 if (hit->origin_protocol == LWSMPRO_HTTPS) {
1084 i.ssl_connection = 1;
1087 i.port = atoi(pcolon + 1);
1089 n = lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s",
1090 pslash + 1, uri_ptr + hit->mountpoint_len) - 2;
1091 lws_clean_url(rpath);
1092 na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS);
1094 char *p = rpath + n;
1096 if (na >= (int)sizeof(rpath) - n - 2) {
1097 lwsl_info("%s: query string %d longer "
1098 "than we can handle\n", __func__,
1105 if (lws_hdr_copy(wsi, p,
1106 (int)(&rpath[sizeof(rpath) - 1] - p),
1107 WSI_TOKEN_HTTP_URI_ARGS) > 0)
1118 /* incoming may be h1 or h2... if he sends h1 HOST, use that
1119 * directly, otherwise we must convert h2 :authority to h1
1123 n = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY);
1125 i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY);
1127 n = lws_hdr_total_length(wsi, WSI_TOKEN_HOST);
1129 i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
1134 if (i.address[0] != '+' ||
1135 !lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST))
1138 i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
1142 if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI)
1143 #if defined(LWS_WITH_HTTP2)
1145 lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) &&
1146 !strcmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD), "post")
1156 lws_snprintf(host, sizeof(host), "%s:%u", i.host,
1157 wsi->vhost->listen_port);
1159 lws_snprintf(host, sizeof(host), "%s:%d", i.address, i.port);
1163 i.alpn = "http/1.1";
1166 i.protocol = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
1168 i.local_protocol_name = "lws-ws-proxy";
1170 // i.uri_replace_from = hit->origin;
1171 // i.uri_replace_to = hit->mountpoint;
1173 lwsl_info("proxying to %s port %d url %s, ssl %d, from %s, to %s\n",
1174 i.address, i.port, i.path, i.ssl_connection,
1175 i.uri_replace_from, i.uri_replace_to);
1177 if (!lws_client_connect_via_info(&i)) {
1178 lwsl_err("proxy connect fail\n");
1181 * ... we can't do the proxy action, but we can
1182 * cleanly return him a 503 and a description
1185 lws_return_http_status(wsi,
1186 HTTP_STATUS_SERVICE_UNAVAILABLE,
1187 "<h1>Service Temporarily Unavailable</h1>"
1188 "The server is temporarily unable to service "
1189 "your request due to maintenance downtime or "
1190 "capacity problems. Please try again later.");
1195 lwsl_info("%s: setting proxy clientside on %p (parent %p)\n",
1196 __func__, cwsi, lws_get_parent(cwsi));
1198 cwsi->http.proxy_clientside = 1;
1200 wsi->proxied_ws_parent = 1;
1201 cwsi->h1_ws_proxied = 1;
1203 lwsl_debug("%s: (requesting '%s')\n", __func__, i.protocol);
1211 static const char * const oprot[] = {
1212 "http://", "https://"
1216 lws_http_action(struct lws *wsi)
1218 struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
1219 const struct lws_http_mount *hit = NULL;
1220 enum http_version request_version;
1221 struct lws_process_html_args args;
1222 enum http_conn_type conn_type;
1223 char content_length_str[32];
1224 char http_version_str[12];
1225 char *uri_ptr = NULL, *s;
1226 int uri_len = 0, meth, m;
1227 char http_conn_str[25];
1228 int http_version_len;
1231 meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
1232 if (meth < 0 || meth >= (int)LWS_ARRAY_SIZE(method_names))
1235 /* we insist on absolute paths */
1237 if (!uri_ptr || uri_ptr[0] != '/') {
1238 lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
1243 lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth],
1246 if (wsi->role_ops && wsi->role_ops->check_upgrades)
1247 switch (wsi->role_ops->check_upgrades(wsi)) {
1248 case LWS_UPG_RET_DONE:
1250 case LWS_UPG_RET_CONTINUE:
1252 case LWS_UPG_RET_BAIL:
1256 if (lws_ensure_user_space(wsi))
1259 /* HTTP header had a content length? */
1261 wsi->http.rx_content_length = 0;
1262 wsi->http.content_length_explicitly_zero = 0;
1263 if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
1264 lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
1265 lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI))
1266 wsi->http.rx_content_length = 100 * 1024 * 1024;
1268 if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) &&
1269 lws_hdr_copy(wsi, content_length_str,
1270 sizeof(content_length_str) - 1,
1271 WSI_TOKEN_HTTP_CONTENT_LENGTH) > 0) {
1272 wsi->http.rx_content_remain = wsi->http.rx_content_length =
1273 atoll(content_length_str);
1274 if (!wsi->http.rx_content_length) {
1275 wsi->http.content_length_explicitly_zero = 1;
1276 lwsl_debug("%s: explicit 0 content-length\n", __func__);
1280 if (wsi->http2_substream) {
1281 wsi->http.request_version = HTTP_VERSION_2;
1283 /* http_version? Default to 1.0, override with token: */
1284 request_version = HTTP_VERSION_1_0;
1286 /* Works for single digit HTTP versions. : */
1287 http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
1288 if (http_version_len > 7 &&
1289 lws_hdr_copy(wsi, http_version_str,
1290 sizeof(http_version_str) - 1,
1291 WSI_TOKEN_HTTP) > 0 &&
1292 http_version_str[5] == '1' && http_version_str[7] == '1')
1293 request_version = HTTP_VERSION_1_1;
1295 wsi->http.request_version = request_version;
1297 /* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
1298 if (request_version == HTTP_VERSION_1_1)
1299 conn_type = HTTP_CONNECTION_KEEP_ALIVE;
1301 conn_type = HTTP_CONNECTION_CLOSE;
1303 /* Override default if http "Connection:" header: */
1304 if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION) &&
1305 lws_hdr_copy(wsi, http_conn_str, sizeof(http_conn_str) - 1,
1306 WSI_TOKEN_CONNECTION) > 0) {
1307 http_conn_str[sizeof(http_conn_str) - 1] = '\0';
1308 if (!strcasecmp(http_conn_str, "keep-alive"))
1309 conn_type = HTTP_CONNECTION_KEEP_ALIVE;
1311 if (!strcasecmp(http_conn_str, "close"))
1312 conn_type = HTTP_CONNECTION_CLOSE;
1314 wsi->http.conn_type = conn_type;
1317 n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION,
1318 wsi->user_space, uri_ptr, uri_len);
1320 lwsl_info("LWS_CALLBACK_HTTP closing\n");
1325 * if there is content supposed to be coming,
1326 * put a timeout on it having arrived
1328 lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
1329 wsi->context->timeout_secs);
1331 if (wsi->tls.redirect_to_https) {
1333 * we accepted http:// only so we could redirect to
1334 * https://, so issue the redirect. Create the redirection
1335 * URI from the host: header and ignore the path part
1337 unsigned char *start = pt->serv_buf + LWS_PRE, *p = start,
1338 *end = p + wsi->context->pt_serv_buf_size - LWS_PRE;
1340 n = lws_hdr_total_length(wsi, WSI_TOKEN_HOST);
1344 p += lws_snprintf((char *)p, lws_ptr_diff(end, p), "https://");
1345 memcpy(p, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST), n);
1349 n = lws_ptr_diff(p, start);
1352 n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
1357 return lws_http_transaction_completed(wsi);
1361 #ifdef LWS_WITH_ACCESS_LOG
1362 lws_prepare_access_log_info(wsi, uri_ptr, uri_len, meth);
1365 /* can we serve it from the mount list? */
1367 hit = lws_find_mount(wsi, uri_ptr, uri_len);
1369 /* deferred cleanup and reset to protocols[0] */
1371 lwsl_info("no hit\n");
1373 if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0],
1377 lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
1379 m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
1380 wsi->user_space, uri_ptr, uri_len);
1385 s = uri_ptr + hit->mountpoint_len;
1388 * if we have a mountpoint like https://xxx.com/yyy
1389 * there is an implied / at the end for our purposes since
1390 * we can only mount on a "directory".
1392 * But if we just go with that, the browser cannot understand
1393 * that he is actually looking down one "directory level", so
1394 * even though we give him /yyy/abc.html he acts like the
1395 * current directory level is /. So relative urls like "x.png"
1396 * wrongly look outside the mountpoint.
1398 * Therefore if we didn't come in on a url with an explicit
1399 * / at the end, we must redirect to add it so the browser
1400 * understands he is one "directory level" down.
1402 if ((hit->mountpoint_len > 1 ||
1403 (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
1404 hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
1406 (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
1407 hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
1408 (hit->origin_protocol != LWSMPRO_CGI &&
1409 hit->origin_protocol != LWSMPRO_CALLBACK)) {
1410 unsigned char *start = pt->serv_buf + LWS_PRE, *p = start,
1411 *end = p + wsi->context->pt_serv_buf_size -
1414 lwsl_info("Doing 301 '%s' org %s\n", s, hit->origin);
1416 /* > at start indicates deal with by redirect */
1417 if (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
1418 hit->origin_protocol == LWSMPRO_REDIR_HTTPS)
1419 n = lws_snprintf((char *)end, 256, "%s%s",
1420 oprot[hit->origin_protocol & 1],
1423 if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
1424 if (!lws_hdr_total_length(wsi,
1425 WSI_TOKEN_HTTP_COLON_AUTHORITY))
1427 n = lws_snprintf((char *)end, 256,
1428 "%s%s%s/", oprot[!!lws_is_ssl(wsi)],
1429 lws_hdr_simple_ptr(wsi,
1430 WSI_TOKEN_HTTP_COLON_AUTHORITY),
1433 n = lws_snprintf((char *)end, 256,
1434 "%s%s%s/", oprot[!!lws_is_ssl(wsi)],
1435 lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
1439 lws_clean_url((char *)end);
1440 n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
1445 return lws_http_transaction_completed(wsi);
1450 switch(lws_check_basic_auth(wsi, hit->basic_auth_login_file)) {
1453 case LCBA_FAILED_AUTH:
1454 return lws_unauthorised_basic_auth(wsi);
1455 case LCBA_END_TRANSACTION:
1456 lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
1457 return lws_http_transaction_completed(wsi);
1460 #if defined(LWS_WITH_HTTP_PROXY)
1462 * The mount is a reverse proxy?
1466 // lwsl_notice("%s: origin_protocol: %d\n", __func__, hit->origin_protocol);
1468 // lwsl_notice("%s: no hit\n", __func__);
1470 if (hit->origin_protocol == LWSMPRO_HTTPS ||
1471 hit->origin_protocol == LWSMPRO_HTTP) {
1472 n = lws_http_proxy_start(wsi, hit, uri_ptr, 0);
1473 // lwsl_notice("proxy start says %d\n", n);
1482 * A particular protocol callback is mounted here?
1484 * For the duration of this http transaction, bind us to the
1485 * associated protocol
1487 if (hit->origin_protocol == LWSMPRO_CALLBACK || hit->protocol) {
1488 const struct lws_protocols *pp;
1489 const char *name = hit->origin;
1491 name = hit->protocol;
1493 pp = lws_vhost_name_to_protocol(wsi->vhost, name);
1496 lwsl_err("Unable to find plugin '%s'\n",
1501 if (lws_bind_protocol(wsi, pp, "http action CALLBACK bind"))
1504 lwsl_notice("%s: %s, checking access rights for mask 0x%x\n",
1505 __func__, hit->origin, hit->auth_mask);
1509 args.max_len = hit->auth_mask;
1510 args.final = 0; /* used to signal callback dealt with it */
1513 n = wsi->protocol->callback(wsi,
1514 LWS_CALLBACK_CHECK_ACCESS_RIGHTS,
1515 wsi->user_space, &args, 0);
1517 lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED,
1521 if (args.final) /* callback completely handled it well */
1524 if (hit->cgienv && wsi->protocol->callback(wsi,
1525 LWS_CALLBACK_HTTP_PMO,
1526 wsi->user_space, (void *)hit->cgienv, 0))
1529 if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
1530 m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
1532 uri_ptr + hit->mountpoint_len,
1533 uri_len - hit->mountpoint_len);
1539 /* did we hit something with a cgi:// origin? */
1540 if (hit->origin_protocol == LWSMPRO_CGI) {
1541 const char *cmd[] = {
1542 NULL, /* replace with cgi path */
1546 lwsl_debug("%s: cgi\n", __func__);
1547 cmd[0] = hit->origin;
1550 if (hit->cgi_timeout)
1551 n = hit->cgi_timeout;
1553 n = lws_cgi(wsi, cmd, hit->mountpoint_len, n,
1556 lwsl_err("%s: cgi failed\n", __func__);
1564 n = uri_len - lws_ptr_diff(s, uri_ptr); // (int)strlen(s);
1565 if (s[0] == '\0' || (n == 1 && s[n - 1] == '/'))
1566 s = (char *)hit->def;
1570 wsi->cache_secs = hit->cache_max_age;
1571 wsi->cache_reuse = hit->cache_reusable;
1572 wsi->cache_revalidate = hit->cache_revalidate;
1573 wsi->cache_intermediaries = hit->cache_intermediaries;
1576 #if !defined(LWS_AMAZON_RTOS)
1577 if (hit->origin_protocol == LWSMPRO_FILE)
1578 m = lws_http_serve(wsi, s, hit->origin, hit);
1583 * lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
1585 if (hit->protocol) {
1586 const struct lws_protocols *pp =
1587 lws_vhost_name_to_protocol(
1588 wsi->vhost, hit->protocol);
1590 lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
1592 if (lws_bind_protocol(wsi, pp, "http_action HTTP"))
1595 m = pp->callback(wsi, LWS_CALLBACK_HTTP,
1597 uri_ptr + hit->mountpoint_len,
1598 uri_len - hit->mountpoint_len);
1600 m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
1601 wsi->user_space, uri_ptr, uri_len);
1606 lwsl_info("LWS_CALLBACK_HTTP closing\n");
1611 #if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
1615 * If we're not issuing a file, check for content_length or
1616 * HTTP keep-alive. No keep-alive header allocation for
1617 * ISSUING_FILE, as this uses HTTP/1.0.
1619 * In any case, return 0 and let lws_read decide how to
1620 * proceed based on state
1622 if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
1623 /* Prepare to read body if we have a content length: */
1624 lwsl_debug("wsi->http.rx_content_length %lld %d %d\n",
1625 (long long)wsi->http.rx_content_length,
1626 wsi->upgraded_to_http2, wsi->http2_substream);
1628 if (wsi->http.content_length_explicitly_zero &&
1629 lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
1632 * POST with an explicit content-length of zero
1634 * If we don't give the user code the empty HTTP_BODY
1635 * callback, he may become confused to hear the
1636 * HTTP_BODY_COMPLETION (due to, eg, instantiation of
1637 * lws_spa never happened).
1639 * HTTP_BODY_COMPLETION is responsible for sending the
1640 * result status code and result body if any, and
1641 * do the transaction complete processing.
1643 if (wsi->protocol->callback(wsi,
1644 LWS_CALLBACK_HTTP_BODY,
1645 wsi->user_space, NULL, 0))
1647 if (wsi->protocol->callback(wsi,
1648 LWS_CALLBACK_HTTP_BODY_COMPLETION,
1649 wsi->user_space, NULL, 0))
1655 if (wsi->http.rx_content_length > 0) {
1657 if (lwsi_state(wsi) != LRS_DISCARD_BODY) {
1658 lwsi_set_state(wsi, LRS_BODY);
1659 lwsl_info("%s: %p: LRS_BODY state set (0x%x)\n",
1660 __func__, wsi, wsi->wsistate);
1662 wsi->http.rx_content_remain =
1663 wsi->http.rx_content_length;
1666 * At this point we have transitioned from deferred
1667 * action to expecting BODY on the stream wsi, if it's
1668 * in a bundle like h2. So if the stream wsi has its
1669 * own buflist, we need to deal with that first.
1673 struct lws_tokens ebuf;
1676 ebuf.len = (int)lws_buflist_next_segment_len(
1681 lwsl_debug("%s: consuming %d\n", __func__,
1683 m = lws_read_h1(wsi, ebuf.token,
1688 if (lws_buflist_aware_consume(wsi, &ebuf, m, 1))
1697 lws_header_table_detach(wsi, 1);
1703 lws_confirm_host_header(struct lws *wsi)
1705 struct lws_tokenize ts;
1706 lws_tokenize_elem e;
1711 * this vhost wants us to validate what the
1712 * client sent against our vhost name
1715 if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
1716 lwsl_info("%s: missing host on upgrade\n", __func__);
1721 #if defined(LWS_WITH_TLS)
1726 lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_DOT_NONTERM /* server.com */|
1727 LWS_TOKENIZE_F_NO_FLOATS /* 1.server.com */|
1728 LWS_TOKENIZE_F_MINUS_NONTERM /* a-b.com */);
1729 ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_HOST);
1731 lwsl_info("%s: missing or oversize host header\n", __func__);
1735 if (lws_tokenize(&ts) != LWS_TOKZE_TOKEN)
1738 if (strncmp(ts.token, wsi->vhost->name, ts.token_len)) {
1739 buf[(ts.token - buf) + ts.token_len] = '\0';
1740 lwsl_info("%s: '%s' in host hdr but vhost name %s\n",
1741 __func__, ts.token, wsi->vhost->name);
1745 e = lws_tokenize(&ts);
1746 if (e == LWS_TOKZE_DELIMITER && ts.token[0] == ':') {
1747 if (lws_tokenize(&ts) != LWS_TOKZE_INTEGER)
1750 port = atoi(ts.token);
1752 if (e != LWS_TOKZE_ENDED)
1755 if (wsi->vhost->listen_port != port) {
1756 lwsl_info("%s: host port %d mismatches vhost port %d\n",
1757 __func__, port, wsi->vhost->listen_port);
1761 lwsl_debug("%s: host header OK\n", __func__);
1766 lwsl_info("%s: bad host header format\n", __func__);
1771 #if !defined(LWS_NO_SERVER)
1773 lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen)
1775 const struct lws_role_ops *role = &role_ops_raw_skt;
1776 const struct lws_protocols *p1, *protocol =
1777 &wsi->vhost->protocols[wsi->vhost->raw_protocol_index];
1781 if (wsi->vhost->listen_accept_role &&
1782 lws_role_by_name(wsi->vhost->listen_accept_role))
1783 role = lws_role_by_name(wsi->vhost->listen_accept_role);
1785 if (wsi->vhost->listen_accept_protocol) {
1786 p1 = lws_vhost_name_to_protocol(wsi->vhost,
1787 wsi->vhost->listen_accept_protocol);
1792 lws_bind_protocol(wsi, protocol, __func__);
1794 lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED, role);
1796 lws_header_table_detach(wsi, 0);
1797 lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
1799 n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;
1800 if (wsi->role_ops->adoption_cb[lwsi_role_server(wsi)])
1801 n = wsi->role_ops->adoption_cb[lwsi_role_server(wsi)];
1804 #if !defined(LWS_PLAT_OPTEE)
1805 lws_get_peer_simple(wsi, ipbuf, sizeof(ipbuf));
1808 lwsl_notice("%s: vh %s, peer: %s, role %s, "
1809 "protocol %s, cb %d, ah %p\n", __func__, wsi->vhost->name,
1810 ipbuf, role->name, protocol->name, n, wsi->http.ah);
1812 if ((wsi->protocol->callback)(wsi, n, wsi->user_space, NULL, 0))
1815 n = LWS_CALLBACK_RAW_RX;
1816 if (wsi->role_ops->rx_cb[lwsi_role_server(wsi)])
1817 n = wsi->role_ops->rx_cb[lwsi_role_server(wsi)];
1818 if (wsi->protocol->callback(wsi, n, wsi->user_space, obuf, olen))
1825 lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
1827 struct lws_context *context = lws_get_context(wsi);
1828 #if defined(LWS_WITH_HTTP2)
1829 struct allocated_headers *ah;
1831 unsigned char *obuf = *buf;
1832 #if defined(LWS_WITH_HTTP2)
1838 if (len >= 10000000) {
1839 lwsl_err("%s: assert: len %ld\n", __func__, (long)len);
1843 if (!wsi->http.ah) {
1844 lwsl_err("%s: assert: NULL ah\n", __func__);
1849 if (!lwsi_role_server(wsi) || !lwsi_role_http(wsi)) {
1850 lwsl_err("%s: bad wsi role 0x%x\n", __func__,
1856 m = lws_parse(wsi, *buf, &i);
1857 lwsl_info("%s: parsed count %d\n", __func__, (int)len - i);
1858 (*buf) += (int)len - i;
1861 if (m == LPR_DO_FALLBACK) {
1864 * http parser went off the rails and
1865 * LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_
1866 * ACCEPT_CONFIG is set on this vhost.
1868 * We are transitioning from http with an AH, to
1869 * a backup role (raw-skt, by default). Drop
1870 * the ah, bind to the role with mode as
1875 if (lws_http_to_fallback(wsi, obuf, olen)) {
1876 lwsl_info("%s: fallback -> close\n", __func__);
1880 (*buf) = obuf + olen;
1885 lwsl_info("lws_parse failed\n");
1889 if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
1892 lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
1896 if (wsi->vhost->listen_port &&
1897 lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
1898 struct lws_vhost *vhost = lws_select_vhost(
1899 context, wsi->vhost->listen_port,
1900 lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
1903 lws_vhost_bind_wsi(vhost, wsi);
1905 lwsl_info("no host\n");
1907 if (!lwsi_role_h2(wsi) || !lwsi_role_server(wsi)) {
1908 wsi->vhost->conn_stats.h1_trans++;
1909 if (!wsi->conn_stat_done) {
1910 wsi->vhost->conn_stats.h1_conn++;
1911 wsi->conn_stat_done = 1;
1915 /* check for unwelcome guests */
1917 if (wsi->context->reject_service_keywords) {
1918 const struct lws_protocol_vhost_options *rej =
1919 wsi->context->reject_service_keywords;
1920 char ua[384], *msg = NULL;
1922 if (lws_hdr_copy(wsi, ua, sizeof(ua) - 1,
1923 WSI_TOKEN_HTTP_USER_AGENT) > 0) {
1924 #ifdef LWS_WITH_ACCESS_LOG
1925 char *uri_ptr = NULL;
1928 ua[sizeof(ua) - 1] = '\0';
1930 if (!strstr(ua, rej->name)) {
1935 msg = strchr(rej->value, ' ');
1938 lws_return_http_status(wsi,
1939 atoi(rej->value), msg);
1940 #ifdef LWS_WITH_ACCESS_LOG
1941 meth = lws_http_get_uri_and_method(wsi,
1942 &uri_ptr, &uri_len);
1944 lws_prepare_access_log_info(wsi,
1945 uri_ptr, uri_len, meth);
1947 /* wsi close will do the log */
1949 wsi->vhost->conn_stats.rejected++;
1951 * We don't want anything from
1952 * this rejected guy. Follow
1953 * the close flow, not the
1954 * transaction complete flow.
1962 if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECT)) {
1963 lwsl_info("Changing to RAW mode\n");
1965 goto raw_transition;
1968 lwsi_set_state(wsi, LRS_PRE_WS_SERVING_ACCEPT);
1969 lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
1971 if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
1973 const char *up = lws_hdr_simple_ptr(wsi,
1976 if (strcasecmp(up, "websocket") &&
1977 strcasecmp(up, "h2c")) {
1978 lwsl_info("Unknown upgrade '%s'\n", up);
1980 if (lws_return_http_status(wsi,
1981 HTTP_STATUS_FORBIDDEN, NULL) ||
1982 lws_http_transaction_completed(wsi))
1986 n = user_callback_handle_rxflow(wsi->protocol->callback,
1987 wsi, LWS_CALLBACK_HTTP_CONFIRM_UPGRADE,
1988 wsi->user_space, (char *)up, 0);
1995 /* callback returned headers already, do t_c? */
1998 if (lws_http_transaction_completed(wsi))
2006 /* callback said 0, it was allowed */
2008 if (wsi->vhost->options &
2009 LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK &&
2010 lws_confirm_host_header(wsi))
2013 if (!strcasecmp(up, "websocket")) {
2014 #if defined(LWS_ROLE_WS)
2015 wsi->vhost->conn_stats.ws_upg++;
2016 lwsl_info("Upgrade to ws\n");
2020 #if defined(LWS_WITH_HTTP2)
2021 if (!strcasecmp(up, "h2c")) {
2022 wsi->vhost->conn_stats.h2_upg++;
2023 lwsl_info("Upgrade to h2c\n");
2029 /* no upgrade ack... he remained as HTTP */
2031 lwsl_info("%s: %p: No upgrade\n", __func__, wsi);
2033 lwsi_set_state(wsi, LRS_ESTABLISHED);
2034 wsi->http.fop_fd = NULL;
2036 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2037 lws_http_compression_validate(wsi);
2040 lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
2041 (void *)wsi->http.ah);
2043 n = lws_http_action(wsi);
2047 #if defined(LWS_WITH_HTTP2)
2049 if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
2050 lwsl_info("missing http2_settings\n");
2054 lwsl_info("h2c upgrade...\n");
2056 p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
2057 /* convert the peer's HTTP-Settings */
2058 n = lws_b64_decode_string(p, tbuf, sizeof(tbuf));
2060 lwsl_parser("HTTP2_SETTINGS too long\n");
2064 wsi->upgraded_to_http2 = 1;
2066 /* adopt the header info */
2069 lws_role_transition(wsi, LWSIFR_SERVER, LRS_H2_AWAIT_PREFACE,
2072 /* http2 union member has http union struct at start */
2076 wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n),
2086 lws_h2_settings(wsi, &wsi->h2.h2n->set, (unsigned char *)tbuf, n);
2088 lws_hpack_dynamic_size(wsi, wsi->h2.h2n->set.s[
2089 H2SET_HEADER_TABLE_SIZE]);
2091 strcpy(tbuf, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
2092 "Connection: Upgrade\x0d\x0a"
2093 "Upgrade: h2c\x0d\x0a\x0d\x0a");
2094 m = (int)strlen(tbuf);
2095 n = lws_issue_raw(wsi, (unsigned char *)tbuf, m);
2097 lwsl_debug("http2 switch: ERROR writing to socket\n");
2103 #if defined(LWS_ROLE_WS)
2105 if (lws_process_ws_upgrade(wsi))
2110 } /* while all chars are handled */
2115 /* drop the header info */
2116 lws_header_table_detach(wsi, 1);
2122 LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
2123 lws_http_transaction_completed(struct lws *wsi)
2125 int n = NO_PENDING_TIMEOUT;
2127 if (lws_has_buffered_out(wsi)
2128 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2129 || wsi->http.comp_ctx.buflist_comp ||
2130 wsi->http.comp_ctx.may_have_more
2134 * ...so he tried to send something large as the http reply,
2135 * it went as a partial, but he immediately said the
2136 * transaction was completed.
2138 * Defer the transaction completed until the last part of the
2141 lwsl_debug("%s: %p: deferring due to partial\n", __func__, wsi);
2142 wsi->http.deferred_transaction_completed = 1;
2143 lws_callback_on_writable(wsi);
2148 * Are we finishing the transaction before we have consumed any body?
2150 * For h1 this would kill keepalive pipelining, and for h2, considering
2151 * it can extend over multiple DATA frames, it would kill the network
2154 if (wsi->http.rx_content_length && wsi->http.rx_content_remain) {
2156 * are we already in LRS_DISCARD_BODY and didn't clear the
2157 * remaining before trying to complete the transaction again?
2159 if (lwsi_state(wsi) == LRS_DISCARD_BODY)
2162 * let's defer transaction completed processing until we
2163 * discarded the remaining body
2165 lwsi_set_state(wsi, LRS_DISCARD_BODY);
2170 lwsl_info("%s: wsi %p\n", __func__, wsi);
2172 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2173 lws_http_compression_destroy(wsi);
2175 lws_access_log(wsi);
2177 if (!wsi->hdr_parsing_completed) {
2180 #if !defined(LWS_PLAT_OPTEE)
2181 lws_get_peer_simple(wsi, peer, sizeof(peer) - 1);
2185 peer[sizeof(peer) - 1] = '\0';
2186 lwsl_notice("%s: (from %s) ignoring, ah parsing incomplete\n",
2191 /* if we can't go back to accept new headers, drop the connection */
2192 if (wsi->http2_substream)
2195 if (wsi->seen_zero_length_recv)
2198 if (wsi->http.conn_type != HTTP_CONNECTION_KEEP_ALIVE) {
2199 lwsl_info("%s: %p: close connection\n", __func__, wsi);
2203 if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0], __func__))
2207 * otherwise set ourselves up ready to go again, but because we have no
2208 * idea about the wsi writability, we make put it in a holding state
2209 * until we can verify POLLOUT. The part of this that confirms POLLOUT
2210 * with no partials is in lws_server_socket_service() below.
2212 lwsl_debug("%s: %p: setting DEF_ACT from 0x%x\n", __func__,
2213 wsi, wsi->wsistate);
2214 lwsi_set_state(wsi, LRS_DEFERRING_ACTION);
2215 wsi->http.tx_content_length = 0;
2216 wsi->http.tx_content_remain = 0;
2217 wsi->hdr_parsing_completed = 0;
2218 wsi->sending_chunked = 0;
2219 #ifdef LWS_WITH_ACCESS_LOG
2220 wsi->http.access_log.sent = 0;
2223 if (wsi->vhost->keepalive_timeout)
2224 n = PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE;
2225 lws_set_timeout(wsi, n, wsi->vhost->keepalive_timeout);
2228 * We already know we are on http1.1 / keepalive and the next thing
2229 * coming will be another header set.
2231 * If there is no pending rx and we still have the ah, drop it and
2232 * reacquire a new ah when the new headers start to arrive. (Otherwise
2233 * we needlessly hog an ah indefinitely.)
2235 * However if there is pending rx and we know from the keepalive state
2236 * that is already at least the start of another header set, simply
2237 * reset the existing header table and keep it.
2240 // lws_buflist_describe(&wsi->buflist, wsi);
2241 if (!lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
2242 lwsl_debug("%s: %p: nothing in buflist, detaching ah\n",
2244 lws_header_table_detach(wsi, 1);
2247 * additionally... if we are hogging an SSL instance
2248 * with no pending pipelined headers (or ah now), and
2249 * SSL is scarce, drop this connection without waiting
2252 if (wsi->vhost->tls.use_ssl &&
2253 wsi->context->simultaneous_ssl_restriction &&
2254 wsi->context->simultaneous_ssl ==
2255 wsi->context->simultaneous_ssl_restriction) {
2256 lwsl_info("%s: simultaneous_ssl_restriction\n",
2262 lwsl_info("%s: %p: resetting/keeping ah as pipeline\n",
2264 lws_header_table_reset(wsi, 0);
2266 * If we kept the ah, we should restrict the amount
2267 * of time we are willing to keep it. Otherwise it
2268 * will be bound the whole time the connection remains
2271 lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
2272 wsi->vhost->keepalive_timeout);
2274 /* If we're (re)starting on headers, need other implied init */
2276 wsi->http.ah->ues = URIES_IDLE;
2278 //lwsi_set_state(wsi, LRS_ESTABLISHED); // !!!
2280 if (lws_buflist_next_segment_len(&wsi->buflist, NULL))
2281 if (lws_header_table_attach(wsi, 0))
2282 lwsl_debug("acquired ah\n");
2284 lwsl_debug("%s: %p: keep-alive await new transaction (state 0x%x)\n",
2285 __func__, wsi, wsi->wsistate);
2286 lws_callback_on_writable(wsi);
2291 #if !defined(LWS_AMAZON_RTOS)
2293 lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
2294 const char *other_headers, int other_headers_len)
2296 struct lws_context *context = lws_get_context(wsi);
2297 struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
2298 unsigned char *response = pt->serv_buf + LWS_PRE;
2299 #if defined(LWS_WITH_RANGES)
2300 struct lws_range_parsing *rp = &wsi->http.range;
2302 int ret = 0, cclen = 8, n = HTTP_STATUS_OK;
2303 char cache_control[50], *cc = "no-store";
2304 lws_fop_flags_t fflags = LWS_O_RDONLY;
2305 const struct lws_plat_file_ops *fops;
2306 lws_filepos_t total_content_length;
2307 unsigned char *p = response;
2308 unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
2310 #if defined(LWS_WITH_RANGES)
2314 if (wsi->handling_404)
2315 n = HTTP_STATUS_NOT_FOUND;
2318 * We either call the platform fops .open with first arg platform fops,
2319 * or we call fops_zip .open with first arg platform fops, and fops_zip
2320 * open will decide whether to switch to fops_zip or stay with fops_def.
2322 * If wsi->http.fop_fd is already set, the caller already opened it
2324 if (!wsi->http.fop_fd) {
2325 fops = lws_vfs_select_fops(wsi->context->fops, file, &vpath);
2326 fflags |= lws_vfs_prepare_flags(wsi);
2327 wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
2328 file, vpath, &fflags);
2329 if (!wsi->http.fop_fd) {
2330 lwsl_info("%s: Unable to open: '%s': errno %d\n",
2331 __func__, file, errno);
2332 if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND,
2335 return !wsi->http2_substream;
2338 wsi->http.filelen = lws_vfs_get_length(wsi->http.fop_fd);
2339 total_content_length = wsi->http.filelen;
2341 #if defined(LWS_WITH_RANGES)
2342 ranges = lws_ranges_init(wsi, rp, wsi->http.filelen);
2344 lwsl_debug("Range count %d\n", ranges);
2347 * 1 range -> 206 + Content-Type: normal; Content-Range;
2348 * more -> 206 + Content-Type: multipart/byteranges
2349 * Repeat the true Content-Type in each multipart header
2350 * along with Content-Range
2353 /* it means he expressed a range in Range:, but it was illegal */
2354 lws_return_http_status(wsi,
2355 HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE, NULL);
2356 if (lws_http_transaction_completed(wsi))
2357 return -1; /* <0 means just hang up */
2359 lws_vfs_file_close(&wsi->http.fop_fd);
2361 return 0; /* == 0 means we did the transaction complete */
2364 n = HTTP_STATUS_PARTIAL_CONTENT;
2367 if (lws_add_http_header_status(wsi, n, &p, end))
2370 if ((wsi->http.fop_fd->flags & (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP |
2371 LWS_FOP_FLAG_COMPR_IS_GZIP)) ==
2372 (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | LWS_FOP_FLAG_COMPR_IS_GZIP)) {
2373 if (lws_add_http_header_by_token(wsi,
2374 WSI_TOKEN_HTTP_CONTENT_ENCODING,
2375 (unsigned char *)"gzip", 4, &p, end))
2377 lwsl_info("file is being provided in gzip\n");
2379 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2382 * if we know its very compressible, and we can use
2383 * compression, then use the most preferred compression
2384 * method that the client said he will accept
2387 if (!wsi->interpreting && (
2388 !strncmp(content_type, "text/", 5) ||
2389 !strcmp(content_type, "application/javascript") ||
2390 !strcmp(content_type, "image/svg+xml")))
2391 lws_http_compression_apply(wsi, NULL, &p, end, 0);
2396 #if defined(LWS_WITH_RANGES)
2399 content_type && content_type[0])
2400 if (lws_add_http_header_by_token(wsi,
2401 WSI_TOKEN_HTTP_CONTENT_TYPE,
2402 (unsigned char *)content_type,
2403 (int)strlen(content_type),
2407 #if defined(LWS_WITH_RANGES)
2408 if (ranges >= 2) { /* multipart byteranges */
2409 lws_strncpy(wsi->http.multipart_content_type, content_type,
2410 sizeof(wsi->http.multipart_content_type));
2412 if (lws_add_http_header_by_token(wsi,
2413 WSI_TOKEN_HTTP_CONTENT_TYPE,
2415 "multipart/byteranges; "
2421 * our overall content length has to include
2423 * - (n + 1) x "_lws\r\n"
2424 * - n x Content-Type: xxx/xxx\r\n
2425 * - n x Content-Range: bytes xxx-yyy/zzz\r\n
2427 * - the actual payloads (aggregated in rp->agg)
2429 * Precompute it for the main response header
2432 total_content_length = (lws_filepos_t)rp->agg +
2433 6 /* final _lws\r\n */;
2435 lws_ranges_reset(rp);
2436 while (lws_ranges_next(rp)) {
2437 n = lws_snprintf(cache_control, sizeof(cache_control),
2438 "bytes %llu-%llu/%llu",
2439 rp->start, rp->end, rp->extent);
2441 total_content_length +=
2442 6 /* header _lws\r\n */ +
2443 /* Content-Type: xxx/xxx\r\n */
2444 14 + strlen(content_type) + 2 +
2445 /* Content-Range: xxxx\r\n */
2450 lws_ranges_reset(rp);
2451 lws_ranges_next(rp);
2455 total_content_length = (lws_filepos_t)rp->agg;
2456 n = lws_snprintf(cache_control, sizeof(cache_control),
2457 "bytes %llu-%llu/%llu",
2458 rp->start, rp->end, rp->extent);
2460 if (lws_add_http_header_by_token(wsi,
2461 WSI_TOKEN_HTTP_CONTENT_RANGE,
2462 (unsigned char *)cache_control,
2467 wsi->http.range.inside = 0;
2469 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ACCEPT_RANGES,
2470 (unsigned char *)"bytes", 5, &p, end))
2474 if (!wsi->http2_substream) {
2475 /* for http/1.1 ... */
2476 if (!wsi->sending_chunked
2477 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2481 /* ... if not already using chunked and not using an
2482 * http compression translation, then send the naive
2485 if (lws_add_http_header_content_length(wsi,
2486 total_content_length, &p, end))
2490 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2491 if (wsi->http.lcs) {
2493 /* ...otherwise, for http 1 it must go chunked.
2494 * For the compression case, the reason is we
2495 * compress on the fly and do not know the
2496 * compressed content-length until it has all
2497 * been sent. Http/1.1 pipelining must be able
2498 * to know where the transaction boundaries are
2499 * ... so chunking...
2501 if (lws_add_http_header_by_token(wsi,
2502 WSI_TOKEN_HTTP_TRANSFER_ENCODING,
2503 (unsigned char *)"chunked", 7,
2508 * ...this is fun, isn't it :-) For h1 that is
2509 * using an http compression translation, the
2510 * compressor must chunk its output privately.
2512 * h2 doesn't need (or support) any of this
2515 lwsl_debug("setting chunking\n");
2516 wsi->http.comp_ctx.chunking = 1;
2522 if (wsi->cache_secs && wsi->cache_reuse) {
2523 if (!wsi->cache_revalidate) {
2525 cclen = sprintf(cache_control, "%s, max-age=%u",
2526 intermediates[wsi->cache_intermediaries],
2530 cclen = sprintf(cache_control,
2531 "must-revalidate, %s, max-age=%u",
2532 intermediates[wsi->cache_intermediaries],
2538 /* Only add cache control if its not specified by any other_headers. */
2539 if (!other_headers ||
2540 (!strstr(other_headers, "cache-control") &&
2541 !strstr(other_headers, "Cache-Control"))) {
2542 if (lws_add_http_header_by_token(wsi,
2543 WSI_TOKEN_HTTP_CACHE_CONTROL,
2544 (unsigned char *)cc, cclen, &p, end))
2548 if (other_headers) {
2549 if ((end - p) < other_headers_len)
2551 memcpy(p, other_headers, other_headers_len);
2552 p += other_headers_len;
2555 if (lws_finalize_http_header(wsi, &p, end))
2558 ret = lws_write(wsi, response, p - response, LWS_WRITE_HTTP_HEADERS);
2559 if (ret != (p - response)) {
2560 lwsl_err("_write returned %d from %ld\n", ret,
2561 (long)(p - response));
2565 wsi->http.filepos = 0;
2566 lwsi_set_state(wsi, LRS_ISSUING_FILE);
2568 if (lws_hdr_total_length(wsi, WSI_TOKEN_HEAD_URI)) {
2569 /* we do not emit the body */
2570 if (lws_http_transaction_completed(wsi))
2576 lws_callback_on_writable(wsi);
2582 LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
2584 struct lws_context *context = wsi->context;
2585 struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
2586 struct lws_process_html_args args;
2587 lws_filepos_t amount, poss;
2588 unsigned char *p, *pstart;
2589 #if defined(LWS_WITH_RANGES)
2590 unsigned char finished = 0;
2594 lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream);
2598 /* priority 1: buffered output */
2600 if (lws_has_buffered_out(wsi)) {
2601 if (lws_issue_raw(wsi, NULL, 0) < 0) {
2602 lwsl_info("%s: closing\n", __func__);
2608 /* priority 2: buffered pre-compression-transform */
2610 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2611 if (wsi->http.comp_ctx.buflist_comp ||
2612 wsi->http.comp_ctx.may_have_more) {
2613 enum lws_write_protocol wp = LWS_WRITE_HTTP;
2615 lwsl_info("%s: completing comp partial (buflist %p, may %d)\n",
2616 __func__, wsi->http.comp_ctx.buflist_comp,
2617 wsi->http.comp_ctx.may_have_more);
2619 if (wsi->role_ops->write_role_protocol(wsi, NULL, 0, &wp) < 0) {
2620 lwsl_info("%s signalling to close\n", __func__);
2623 lws_callback_on_writable(wsi);
2629 if (wsi->http.filepos == wsi->http.filelen)
2634 pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH;
2638 #if defined(LWS_WITH_RANGES)
2639 if (wsi->http.range.count_ranges && !wsi->http.range.inside) {
2641 lwsl_notice("%s: doing range start %llu\n", __func__,
2642 wsi->http.range.start);
2644 if ((long long)lws_vfs_file_seek_cur(wsi->http.fop_fd,
2645 wsi->http.range.start -
2646 wsi->http.filepos) < 0)
2649 wsi->http.filepos = wsi->http.range.start;
2651 if (wsi->http.range.count_ranges > 1) {
2652 n = lws_snprintf((char *)p,
2653 context->pt_serv_buf_size -
2654 LWS_H2_FRAME_HEADER_LENGTH,
2656 "Content-Type: %s\x0d\x0a"
2657 "Content-Range: bytes "
2658 "%llu-%llu/%llu\x0d\x0a"
2660 wsi->http.multipart_content_type,
2661 wsi->http.range.start,
2662 wsi->http.range.end,
2663 wsi->http.range.extent);
2667 wsi->http.range.budget = wsi->http.range.end -
2668 wsi->http.range.start + 1;
2669 wsi->http.range.inside = 1;
2673 poss = context->pt_serv_buf_size - n -
2674 LWS_H2_FRAME_HEADER_LENGTH;
2676 if (wsi->http.tx_content_length)
2677 if (poss > wsi->http.tx_content_remain)
2678 poss = wsi->http.tx_content_remain;
2681 * if there is a hint about how much we will do well to send at
2682 * one time, restrict ourselves to only trying to send that.
2684 if (wsi->protocol->tx_packet_size &&
2685 poss > wsi->protocol->tx_packet_size)
2686 poss = wsi->protocol->tx_packet_size;
2688 if (wsi->role_ops->tx_credit) {
2689 lws_filepos_t txc = wsi->role_ops->tx_credit(wsi);
2692 lwsl_info("%s: came here with no tx credit\n",
2700 * consumption of the actual payload amount sent will be
2701 * handled when the role data frame is sent
2705 #if defined(LWS_WITH_RANGES)
2706 if (wsi->http.range.count_ranges) {
2707 if (wsi->http.range.count_ranges > 1)
2708 poss -= 7; /* allow for final boundary */
2709 if (poss > wsi->http.range.budget)
2710 poss = wsi->http.range.budget;
2713 if (wsi->sending_chunked) {
2714 /* we need to drop the chunk size in here */
2716 /* allow for the chunk to grow by 128 in translation */
2720 if (lws_vfs_file_read(wsi->http.fop_fd, &amount, p, poss) < 0)
2721 goto file_had_it; /* caller will close */
2723 if (wsi->sending_chunked)
2726 n = lws_ptr_diff(p, pstart) + (int)amount;
2728 lwsl_debug("%s: sending %d\n", __func__, n);
2731 lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
2732 context->timeout_secs);
2734 if (wsi->interpreting) {
2737 args.max_len = (unsigned int)poss + 128;
2738 args.final = wsi->http.filepos + n ==
2740 args.chunked = wsi->sending_chunked;
2741 if (user_callback_handle_rxflow(
2742 wsi->vhost->protocols[
2743 (int)wsi->protocol_interpret_idx].callback,
2744 wsi, LWS_CALLBACK_PROCESS_HTML,
2745 wsi->user_space, &args, 0) < 0)
2748 p = (unsigned char *)args.p;
2752 #if defined(LWS_WITH_RANGES)
2753 if (wsi->http.range.send_ctr + 1 ==
2754 wsi->http.range.count_ranges && // last range
2755 wsi->http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
2756 wsi->http.range.budget - amount == 0) {// final part
2757 n += lws_snprintf((char *)pstart + n, 6,
2758 "_lws\x0d\x0a"); // append trailing boundary
2759 lwsl_debug("added trailing boundary\n");
2762 m = lws_write(wsi, p, n, wsi->http.filepos + amount ==
2764 LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP);
2768 wsi->http.filepos += amount;
2770 #if defined(LWS_WITH_RANGES)
2771 if (wsi->http.range.count_ranges >= 1) {
2772 wsi->http.range.budget -= amount;
2773 if (wsi->http.range.budget == 0) {
2774 lwsl_notice("range budget exhausted\n");
2775 wsi->http.range.inside = 0;
2776 wsi->http.range.send_ctr++;
2778 if (lws_ranges_next(&wsi->http.range) < 1) {
2787 /* adjust for what was not sent */
2788 if (lws_vfs_file_seek_cur(wsi->http.fop_fd,
2796 if ((!lws_has_buffered_out(wsi)
2797 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2798 && !wsi->http.comp_ctx.buflist_comp &&
2799 !wsi->http.comp_ctx.may_have_more
2801 ) && (wsi->http.filepos >= wsi->http.filelen
2802 #if defined(LWS_WITH_RANGES)
2808 lwsi_set_state(wsi, LRS_ESTABLISHED);
2809 /* we might be in keepalive, so close it off here */
2810 lws_vfs_file_close(&wsi->http.fop_fd);
2812 lwsl_debug("file completed\n");
2814 if (wsi->protocol->callback &&
2815 user_callback_handle_rxflow(wsi->protocol->callback,
2816 wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,
2817 wsi->user_space, NULL, 0) < 0) {
2819 * For http/1.x, the choices from
2820 * transaction_completed are either
2821 * 0 to use the connection for pipelined
2822 * or nonzero to hang it up.
2824 * However for http/2. while we are
2825 * still interested in hanging up the
2826 * nwsi if there was a network-level
2827 * fatal error, simply completing the
2828 * transaction is a matter of the stream
2829 * state, not the root connection at the
2832 if (wsi->http2_substream)
2838 return 1; /* >0 indicates completed */
2841 * while(1) here causes us to spam the whole file contents into
2842 * a hugely bloated output buffer if it ever can't send the
2845 } while (!lws_send_pipe_choked(wsi));
2847 lws_callback_on_writable(wsi);
2849 return 0; /* indicates further processing must be done */
2852 lws_vfs_file_close(&wsi->http.fop_fd);
2857 #ifndef LWS_NO_SERVER
2859 lws_server_get_canonical_hostname(struct lws_context *context,
2860 const struct lws_context_creation_info *info)
2862 if (lws_check_opt(info->options,
2863 LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME))
2865 #if !defined(LWS_WITH_ESP32)
2866 /* find canonical hostname */
2867 gethostname((char *)context->canonical_hostname,
2868 sizeof(context->canonical_hostname) - 1);
2870 lwsl_info(" canonical_hostname = %s\n", context->canonical_hostname);
2877 LWS_VISIBLE LWS_EXTERN int
2878 lws_chunked_html_process(struct lws_process_html_args *args,
2879 struct lws_process_html_state *s)
2881 char *sp, buffer[32];
2885 /* do replacements */
2887 old_len = args->len;
2890 while (sp < args->p + old_len) {
2892 if (args->len + 7 >= args->max_len) {
2893 lwsl_err("Used up interpret padding\n");
2897 if ((!s->pos && *sp == '$') || s->pos) {
2898 int hits = 0, hit = 0;
2902 s->swallow[s->pos++] = *sp;
2903 if (s->pos == sizeof(s->swallow) - 1)
2905 for (n = 0; n < s->count_vars; n++)
2906 if (!strncmp(s->swallow, s->vars[n], s->pos)) {
2912 s->swallow[s->pos] = '\0';
2913 memcpy(s->start, s->swallow, s->pos);
2919 if (hits == 1 && s->pos == (int)strlen(s->vars[hit])) {
2920 pc = s->replace(s->data, hit);
2923 n = (int)strlen(pc);
2924 s->swallow[s->pos] = '\0';
2926 memmove(s->start + n, s->start + s->pos,
2927 old_len - (sp - args->p) - 1);
2928 old_len += (n - s->pos) + 1;
2930 memcpy(s->start, pc, n);
2944 if (args->chunked) {
2945 /* no space left for final chunk trailer */
2946 if (args->final && args->len + 7 >= args->max_len)
2949 n = sprintf(buffer, "%X\x0d\x0a", args->len);
2952 memcpy(args->p, buffer, n);
2956 sp = args->p + args->len;
2966 sp = args->p + args->len;