Imported Upstream version 3.2.0
[platform/upstream/libwebsockets.git] / lib / roles / http / server / server.c
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation:
9  *  version 2.1 of the License.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA  02110-1301  USA
20  */
21
22 #include "core/private.h"
23
24 const char * const method_names[] = {
25         "GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT", "HEAD",
26 #ifdef LWS_WITH_HTTP2
27         ":path",
28 #endif
29         };
30
31 static const char * const intermediates[] = { "private", "public" };
32
33 /*
34  * return 0: all done
35  *        1: nonfatal error
36  *       <0: fatal error
37  *
38  *       REQUIRES CONTEXT LOCK HELD
39  */
40
41 #ifndef LWS_NO_SERVER
42 int
43 _lws_vhost_init_server(const struct lws_context_creation_info *info,
44                        struct lws_vhost *vhost)
45 {
46         int n, opt = 1, limit = 1;
47         lws_sockfd_type sockfd;
48         struct lws_vhost *vh;
49         struct lws *wsi;
50         int m = 0, is;
51
52         (void)method_names;
53         (void)opt;
54
55         if (info) {
56                 vhost->iface = info->iface;
57                 vhost->listen_port = info->port;
58         }
59
60         /* set up our external listening socket we serve on */
61
62         if (vhost->listen_port == CONTEXT_PORT_NO_LISTEN ||
63             vhost->listen_port == CONTEXT_PORT_NO_LISTEN_SERVER)
64                 return 0;
65
66         vh = vhost->context->vhost_list;
67         while (vh) {
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))) &&
72                            vh->lserv_wsi
73                         ) {
74                                 lwsl_notice(" using listen skt from vhost %s\n",
75                                             vh->name);
76                                 return 0;
77                         }
78                 }
79                 vh = vh->vhost_next;
80         }
81
82         if (vhost->iface) {
83                 /*
84                  * let's check before we do anything else about the disposition
85                  * of the interface he wants to bind to...
86                  */
87                 is = lws_socket_bind(vhost, LWS_SOCK_INVALID, vhost->listen_port,
88                                 vhost->iface, 1);
89                 lwsl_debug("initial if check says %d\n", is);
90
91                 if (is == LWS_ITOSA_BUSY)
92                         /* treat as fatal */
93                         return -1;
94
95 deal:
96
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",
102                                                 (*pv)->name);
103                                 *pv = vhost->no_listener_vhost_list;
104                                 vhost->no_listener_vhost_list = NULL;
105                                 goto done_list;
106                         }
107                         if (is < LWS_ITOSA_USABLE && *pv == vhost)
108                                 goto done_list;
109                 } lws_end_foreach_llp(pv, no_listener_vhost_list);
110
111                 /* not on the list... */
112
113                 if (is < LWS_ITOSA_USABLE) {
114
115                         /* ... but needs to be: so add it */
116
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;
121                 }
122
123 done_list:
124
125                 switch (is) {
126                 default:
127                         break;
128                 case LWS_ITOSA_NOT_EXIST:
129                         /* can't add it */
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?
134                                 -1 : 1;
135                 case LWS_ITOSA_NOT_USABLE:
136                         /* can't add it */
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?
141                                 -1 : 1;
142                 }
143         }
144
145         (void)n;
146 #if defined(__linux__)
147 #ifdef LWS_WITH_UNIX_SOCK
148         /*
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.
155          */
156         if (!LWS_UNIX_SOCK_ENABLED(vhost))
157 #endif
158         limit = vhost->context->count_threads;
159 #endif
160
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);
165                 else
166 #endif
167 #ifdef LWS_WITH_IPV6
168                 if (LWS_IPV6_ENABLED(vhost))
169                         sockfd = socket(AF_INET6, SOCK_STREAM, 0);
170                 else
171 #endif
172                         sockfd = socket(AF_INET, SOCK_STREAM, 0);
173
174                 if (sockfd == LWS_SOCK_INVALID) {
175                         lwsl_err("ERROR opening socket\n");
176                         return 1;
177                 }
178 #if !defined(LWS_WITH_ESP32)
179 #if (defined(WIN32) || defined(_WIN32)) && defined(SO_EXCLUSIVEADDRUSE)
180                 /*
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
184                  *
185                  * for lws, to match Linux, we default to exclusive listen
186                  */
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);
193                                 return -1;
194                         }
195                 } else
196 #endif
197
198                 /*
199                  * allow us to restart even if old sockets in TIME_WAIT
200                  */
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);
205                         return -1;
206                 }
207
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);
216                                 return -1;
217                         }
218                 }
219 #endif
220
221 #if defined(__linux__) && defined(SO_REUSEPORT)
222                 /* keep coverity happy */
223 #if LWS_MAX_SMP > 1
224                 n = 1;
225 #else
226                 n = lws_check_opt(vhost->options,
227                                   LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE);
228 #endif
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);
233                                 return -1;
234                         }
235 #endif
236 #endif
237                 lws_plat_set_socket_options(vhost, sockfd, 0);
238
239                 is = lws_socket_bind(vhost, sockfd, vhost->listen_port, vhost->iface, 1);
240                 if (is == LWS_ITOSA_BUSY) {
241                         /* treat as fatal */
242                         compatible_close(sockfd);
243
244                         return -1;
245                 }
246
247                 /*
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.
251                  */
252                 if (is < 0) {
253                         lwsl_info("%s: lws_socket_bind says %d\n", __func__, is);
254                         compatible_close(sockfd);
255                         goto deal;
256                 }
257
258                 wsi = lws_zalloc(sizeof(struct lws), "listen wsi");
259                 if (wsi == NULL) {
260                         lwsl_err("Out of mem\n");
261                         goto bail;
262                 }
263
264 #ifdef LWS_WITH_UNIX_SOCK
265                 if (!LWS_UNIX_SOCK_ENABLED(vhost))
266 #endif
267                 {
268                         wsi->unix_skt = 1;
269                         vhost->listen_port = is;
270
271                         lwsl_debug("%s: lws_socket_bind says %d\n", __func__, is);
272                 }
273
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;
278                 wsi->tsi = m;
279                 lws_vhost_bind_wsi(vhost, wsi);
280                 wsi->listener = 1;
281
282                 if (wsi->context->event_loop_ops->init_vhost_listen_wsi)
283                         wsi->context->event_loop_ops->init_vhost_listen_wsi(wsi);
284
285                 if (__insert_wsi_socket_into_fds(vhost->context, wsi)) {
286                         lwsl_notice("inserting wsi socket into fds failed\n");
287                         goto bail;
288                 }
289
290                 vhost->context->count_wsi_allocated++;
291                 vhost->lserv_wsi = wsi;
292
293                 n = listen(wsi->desc.sockfd, LWS_SOMAXCONN);
294                 if (n < 0) {
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);
299                         goto bail;
300                 }
301         } /* for each thread able to independently listen */
302
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);
307                 else
308 #endif
309                         lwsl_info(" Listening on port %d\n", vhost->listen_port);
310         }
311
312         // info->port = vhost->listen_port;
313
314         return 0;
315
316 bail:
317         compatible_close(sockfd);
318
319         return -1;
320 }
321 #endif
322
323 struct lws_vhost *
324 lws_select_vhost(struct lws_context *context, int port, const char *servername)
325 {
326         struct lws_vhost *vhost = context->vhost_list;
327         const char *p;
328         int n, colon;
329
330         n = (int)strlen(servername);
331         colon = n;
332         p = strchr(servername, ':');
333         if (p)
334                 colon = lws_ptr_diff(p, servername);
335
336         /* Priotity 1: first try exact matches */
337
338         while (vhost) {
339                 if (port == vhost->listen_port &&
340                     !strncmp(vhost->name, servername, colon)) {
341                         lwsl_info("SNI: Found: %s\n", servername);
342                         return vhost;
343                 }
344                 vhost = vhost->vhost_next;
345         }
346
347         /*
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
352          * *.x.com.
353          */
354         vhost = context->vhost_list;
355         while (vhost) {
356                 int m = (int)strlen(vhost->name);
357                 if (port && port == vhost->listen_port &&
358                     m <= (colon - 2) &&
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);
363                         return vhost;
364                 }
365                 vhost = vhost->vhost_next;
366         }
367
368         /* Priority 3: match the first vhost on our port */
369
370         vhost = context->vhost_list;
371         while (vhost) {
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);
375                         return vhost;
376                 }
377                 vhost = vhost->vhost_next;
378         }
379
380         /* no match */
381
382         return NULL;
383 }
384
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" },
407 };
408
409 LWS_VISIBLE LWS_EXTERN const char *
410 lws_get_mimetype(const char *file, const struct lws_http_mount *m)
411 {
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;
416
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;
422                         continue;
423                 }
424
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);
428                         return pvo->value;
429                 }
430         }
431
432         /* fallback to server-defined mimetypes */
433         for (i = 0; i < LWS_ARRAY_SIZE(server_mimetypes); ++i) {
434                 mt = &server_mimetypes[i];
435
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);
439                         return mt->mimetype;
440                 }
441         }
442
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;
447         }
448
449         return NULL;
450 }
451
452 static lws_fop_flags_t
453 lws_vfs_prepare_flags(struct lws *wsi)
454 {
455         lws_fop_flags_t f = 0;
456
457         if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING))
458                 return f;
459
460         if (strstr(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING),
461                    "gzip")) {
462                 lwsl_info("client indicates GZIP is acceptable\n");
463                 f |= LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP;
464         }
465
466         return f;
467 }
468
469 #if !defined(LWS_AMAZON_RTOS)
470 static int
471 lws_http_serve(struct lws *wsi, char *uri, const char *origin,
472                const struct lws_http_mount *m)
473 {
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;
479         const char *vpath;
480         lws_fop_flags_t fflags = LWS_O_RDONLY;
481 #if defined(WIN32) && defined(LWS_HAVE__STAT32I64)
482         struct _stat32i64 st;
483 #else
484         struct stat st;
485 #endif
486         int spin = 0;
487 #endif
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)
492         size_t len;
493 #endif
494         int n;
495
496         wsi->handling_404 = 0;
497         if (!wsi->vhost)
498                 return -1;
499
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;
504 #endif
505
506         lws_snprintf(path, sizeof(path) - 1, "%s/%s", origin, uri);
507
508 #if !defined(_WIN32_WCE)
509
510         fflags |= lws_vfs_prepare_flags(wsi);
511
512         do {
513                 spin++;
514                 fops = lws_vfs_select_fops(wsi->context->fops, path, &vpath);
515
516                 if (wsi->http.fop_fd)
517                         lws_vfs_file_close(&wsi->http.fop_fd);
518
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);
524
525                         return 1;
526                 }
527
528                 /* if it can't be statted, don't try */
529                 if (fflags & LWS_FOP_FLAG_VIRTUAL)
530                         break;
531 #if defined(LWS_WITH_ESP32)
532                 break;
533 #endif
534 #if !defined(WIN32)
535                 if (fstat(wsi->http.fop_fd->fd, &st)) {
536                         lwsl_info("unable to stat %s\n", path);
537                         goto notfound;
538                 }
539 #else
540 #if defined(LWS_HAVE__STAT32I64)
541                 if (_stat32i64(path, &st)) {
542                         lwsl_info("unable to stat %s\n", path);
543                         goto notfound;
544                 }
545 #else
546                 if (stat(path, &st)) {
547                         lwsl_info("unable to stat %s\n", path);
548                         goto notfound;
549                 }
550 #endif
551 #endif
552
553                 wsi->http.fop_fd->mod_time = (uint32_t)st.st_mtime;
554                 fflags |= LWS_FOP_FLAG_MOD_TIME_VALID;
555
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);
559                         if (len) {
560                                 lwsl_err("Failed to read link %s\n", path);
561                                 goto notfound;
562                         }
563                         sym[len] = '\0';
564                         lwsl_debug("symlink %s -> %s\n", path, sym);
565                         lws_snprintf(path, sizeof(path) - 1, "%s", sym);
566                 }
567 #endif
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",
571                                  origin, uri);
572                 }
573
574         } while ((S_IFMT & st.st_mode) != S_IFREG && spin < 5);
575
576         if (spin == 5)
577                 lwsl_err("symlink loop %s \n", path);
578
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));
582
583         /* disable ranges if IF_RANGE token invalid */
584
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;
589
590         if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) {
591                 /*
592                  * he thinks he has some version of it already,
593                  * check if the tag matches
594                  */
595                 if (!strcmp(sym, lws_hdr_simple_ptr(wsi,
596                                         WSI_TOKEN_HTTP_IF_NONE_MATCH))) {
597
598                         char cache_control[50], *cc = "no-store";
599                         int cclen = 8;
600
601                         lwsl_debug("%s: ETAG match %s %s\n", __func__,
602                                    uri, origin);
603
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",
608                                                 __func__);
609                                 return -1;
610                         }
611
612                         if (lws_add_http_header_by_token(wsi,
613                                         WSI_TOKEN_HTTP_ETAG,
614                                         (unsigned char *)sym, n, &p, end))
615                                 return -1;
616
617                         /* but we still need to send cache control... */
618
619                         if (m->cache_max_age && m->cache_reusable) {
620                                 if (!m->cache_revalidate) {
621                                         cc = cache_control;
622                                         cclen = sprintf(cache_control,
623                                                 "%s, max-age=%u",
624                                                 intermediates[wsi->cache_intermediaries],
625                                                 m->cache_max_age);
626                                 } else {
627                                         cc = cache_control;
628                                         cclen = sprintf(cache_control,
629                                                 "must-revalidate, %s, max-age=%u",
630                                                 intermediates[wsi->cache_intermediaries],
631                                                 m->cache_max_age);
632                                 }
633                         }
634
635                         if (lws_add_http_header_by_token(wsi,
636                                         WSI_TOKEN_HTTP_CACHE_CONTROL,
637                                         (unsigned char *)cc, cclen, &p, end))
638                                 return -1;
639
640                         if (lws_finalize_http_header(wsi, &p, end))
641                                 return -1;
642
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,
648                                          (long)(p - start));
649                                 return -1;
650                         }
651
652                         lws_vfs_file_close(&wsi->http.fop_fd);
653
654                         if (lws_http_transaction_completed(wsi))
655                                 return -1;
656
657                         return 0;
658                 }
659         }
660
661         if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG,
662                         (unsigned char *)sym, n, &p, end))
663                 return -1;
664 #endif
665
666         mimetype = lws_get_mimetype(path, m);
667         if (!mimetype) {
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))
672                         return -1;
673
674                 return 0;
675         }
676         if (!mimetype[0])
677                 lwsl_debug("sending no mimetype for %s\n", path);
678
679         wsi->sending_chunked = 0;
680         wsi->interpreting = 0;
681
682         /*
683          * check if this is in the list of file suffixes to be interpreted by
684          * a protocol
685          */
686         while (pvo) {
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;
693
694                         wsi->protocol_interpret_idx =
695                                 lws_vhost_name_to_protocol(wsi->vhost,
696                                                            pvo->value) -
697                                 &lws_get_vhost(wsi)->protocols[0];
698
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__))
705                                 return -1;
706
707                         if (lws_ensure_user_space(wsi))
708                                 return -1;
709                         break;
710                 }
711                 pvo = pvo->next;
712         }
713
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,
718                                 &p, end))
719                         return -1;
720         }
721
722         if (m->protocol) {
723                 const struct lws_protocols *pp = lws_vhost_name_to_protocol(
724                                                        wsi->vhost, m->protocol);
725
726                 if (lws_bind_protocol(wsi, pp, __func__))
727                         return -1;
728                 args.p = (char *)p;
729                 args.max_len = lws_ptr_diff(end, p);
730                 if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS,
731                                           wsi->user_space, &args, 0))
732                         return -1;
733                 p = (unsigned char *)args.p;
734         }
735
736         *p = '\0';
737         n = lws_serve_http_file(wsi, path, mimetype, (char *)start,
738                                 lws_ptr_diff(p, start));
739
740         if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
741                 return -1; /* error or can't reuse connection: close the socket */
742
743         return 0;
744
745 notfound:
746
747         return 1;
748 }
749 #endif
750
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)
754 {
755         const struct lws_http_mount *hm, *hit = NULL;
756         int best = 0;
757
758         hm = wsi->vhost->http.mount_list;
759         while (hm) {
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)
765                     ) {
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)) ||
774                              hm->protocol) &&
775                             hm->mountpoint_len > best)) {
776                                 best = hm->mountpoint_len;
777                                 hit = hm;
778                         }
779                 }
780                 hm = hm->mount_next;
781         }
782
783         return hit;
784 }
785 #endif
786
787 #if !defined(LWS_WITH_ESP32)
788 static int
789 lws_find_string_in_file(const char *filename, const char *string, int stringlen)
790 {
791         char buf[128];
792         int fd, match = 0, pos = 0, n = 0, hit = 0;
793
794         fd = lws_open(filename, O_RDONLY);
795         if (fd < 0) {
796                 lwsl_err("can't open auth file: %s\n", filename);
797                 return 0;
798         }
799
800         while (1) {
801                 if (pos == n) {
802                         n = read(fd, buf, sizeof(buf));
803                         if (n <= 0) {
804                                 if (match == stringlen)
805                                         hit = 1;
806                                 break;
807                         }
808                         pos = 0;
809                 }
810
811                 if (match == stringlen) {
812                         if (buf[pos] == '\r' || buf[pos] == '\n') {
813                                 hit = 1;
814                                 break;
815                         }
816                         match = 0;
817                 }
818
819                 if (buf[pos] == string[match])
820                         match++;
821                 else
822                         match = 0;
823
824                 pos++;
825         }
826
827         close(fd);
828
829         return hit;
830 }
831 #endif
832
833 int
834 lws_unauthorised_basic_auth(struct lws *wsi)
835 {
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;
839         char buf[64];
840         int n;
841
842         /* no auth... tell him it is required */
843
844         if (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, &p, end))
845                 return -1;
846
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))
851                 return -1;
852
853         if (lws_add_http_header_content_length(wsi, 0, &p, end))
854                 return -1;
855
856         if (lws_finalize_http_header(wsi, &p, end))
857                 return -1;
858
859         n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS |
860                                              LWS_WRITE_H2_STREAM_END);
861         if (n < 0)
862                 return -1;
863
864         return lws_http_transaction_completed(wsi);
865
866 }
867
868 int lws_clean_url(char *p)
869 {
870         if (p[0] == 'h' && p[1] == 't' && p[2] == 't' && p[3] == 'p') {
871                 p += 4;
872                 if (*p == 's')
873                 p++;
874                 if (*p == ':') {
875                         p++;
876                         if (*p == '/')
877                         p++;
878                 }
879         }
880
881         while (*p) {
882                 if (p[0] == '/' && p[1] == '/') {
883                         char *p1 = p;
884                         while (*p1) {
885                                 *p1 = p1[1];
886                                 p1++;
887                         }
888                         continue;
889                 }
890                 p++;
891         }
892
893         return 0;
894 }
895
896 static const unsigned char methods[] = {
897         WSI_TOKEN_GET_URI,
898         WSI_TOKEN_POST_URI,
899         WSI_TOKEN_OPTIONS_URI,
900         WSI_TOKEN_PUT_URI,
901         WSI_TOKEN_PATCH_URI,
902         WSI_TOKEN_DELETE_URI,
903         WSI_TOKEN_CONNECT,
904         WSI_TOKEN_HEAD_URI,
905 #ifdef LWS_WITH_HTTP2
906         WSI_TOKEN_HTTP_COLON_PATH,
907 #endif
908 };
909
910 int
911 lws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)
912 {
913         int n, count = 0;
914
915         for (n = 0; n < (int)LWS_ARRAY_SIZE(methods); n++)
916                 if (lws_hdr_total_length(wsi, methods[n]))
917                         count++;
918         if (!count) {
919                 lwsl_warn("Missing URI in HTTP request\n");
920                 return -1;
921         }
922
923         if (count != 1 &&
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");
927                 return -1;
928         }
929
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]);
934                         return n;
935                 }
936
937         return -1;
938 }
939
940 enum lws_check_basic_auth_results
941 lws_check_basic_auth(struct lws *wsi, const char *basic_auth_login_file)
942 {
943         char b64[160], plain[(sizeof(b64) * 3) / 4], *pcolon;
944         int m, ml, fi;
945
946         if (!basic_auth_login_file)
947                 return LCBA_CONTINUE;
948
949         /* Did he send auth? */
950         ml = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
951         if (!ml)
952                 return LCBA_FAILED_AUTH;
953
954         /* Disallow fragmentation monkey business */
955
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;
960         }
961
962         m = lws_hdr_copy(wsi, b64, sizeof(b64),
963                          WSI_TOKEN_HTTP_AUTHORIZATION);
964         if (m < 7) {
965                 lwsl_err("b64 auth too long\n");
966                 return LCBA_END_TRANSACTION;
967         }
968
969         b64[5] = '\0';
970         if (strcasecmp(b64, "Basic")) {
971                 lwsl_err("auth missing basic: %s\n", b64);
972                 return LCBA_END_TRANSACTION;
973         }
974
975         /* It'll be like Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l */
976
977         m = lws_b64_decode_string(b64 + 6, plain, sizeof(plain) - 1);
978         if (m < 0) {
979                 lwsl_err("plain auth too long\n");
980                 return LCBA_END_TRANSACTION;
981         }
982
983         plain[m] = '\0';
984         pcolon = strchr(plain, ':');
985         if (!pcolon) {
986                 lwsl_err("basic auth format broken\n");
987                 return LCBA_END_TRANSACTION;
988         }
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;
992         }
993
994         /*
995          * Rewrite WSI_TOKEN_HTTP_AUTHORIZATION so it is just the
996          * authorized username
997          */
998
999         *pcolon = '\0';
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));
1006
1007         return LCBA_CONTINUE;
1008 }
1009
1010 #if defined(LWS_WITH_HTTP_PROXY)
1011 /*
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".
1015  */
1016 int
1017 lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit,
1018                      char *uri_ptr, char ws)
1019 {
1020         char ads[96], rpath[256], host[96], *pcolon, *pslash, unix_skt = 0;
1021         struct lws_client_connect_info i;
1022         struct lws *cwsi;
1023         int n, na;
1024
1025         if (ws)
1026                 /*
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.
1031                  *
1032                  * Instead these are both bound to our built-in "lws-ws-proxy"
1033                  * protocol, which understands how to proxy between the two
1034                  * sides.
1035                  *
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.
1039                  */
1040                 lws_bind_protocol(wsi, &lws_ws_proxy, __func__);
1041
1042         memset(&i, 0, sizeof(i));
1043         i.context = lws_get_context(wsi);
1044
1045         if (hit->origin[0] == '+')
1046                 unix_skt = 1;
1047
1048         pcolon = strchr(hit->origin, ':');
1049         pslash = strchr(hit->origin, '/');
1050         if (!pslash) {
1051                 lwsl_err("Proxy mount origin '%s' must have /\n", hit->origin);
1052                 return -1;
1053         }
1054
1055         if (unix_skt) {
1056                 if (!pcolon) {
1057                         lwsl_err("Proxy mount origin for unix skt must "
1058                                  "have address delimited by :\n");
1059
1060                         return -1;
1061                 }
1062                 n = lws_ptr_diff(pcolon, hit->origin);
1063                 pslash = pcolon;
1064         } else {
1065                 if (pcolon > pslash)
1066                         pcolon = NULL;
1067
1068                 if (pcolon)
1069                         n = (int)(pcolon - hit->origin);
1070                 else
1071                         n = (int)(pslash - hit->origin);
1072
1073                 if (n >= (int)sizeof(ads) - 2)
1074                         n = sizeof(ads) - 2;
1075         }
1076
1077         memcpy(ads, hit->origin, n);
1078         ads[n] = '\0';
1079
1080         i.address = ads;
1081         i.port = 80;
1082         if (hit->origin_protocol == LWSMPRO_HTTPS) {
1083                 i.port = 443;
1084                 i.ssl_connection = 1;
1085         }
1086         if (pcolon)
1087                 i.port = atoi(pcolon + 1);
1088
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);
1093         if (na) {
1094                 char *p = rpath + n;
1095
1096                 if (na >= (int)sizeof(rpath) - n - 2) {
1097                         lwsl_info("%s: query string %d longer "
1098                                   "than we can handle\n", __func__,
1099                                   na);
1100
1101                         return -1;
1102                 }
1103
1104                 *p++ = '?';
1105                 if (lws_hdr_copy(wsi, p,
1106                              (int)(&rpath[sizeof(rpath) - 1] - p),
1107                              WSI_TOKEN_HTTP_URI_ARGS) > 0)
1108                         while (na--) {
1109                                 if (*p == '\0')
1110                                         *p = '&';
1111                                 p++;
1112                         }
1113                 *p = '\0';
1114         }
1115
1116         i.path = rpath;
1117
1118         /* incoming may be h1 or h2... if he sends h1 HOST, use that
1119          * directly, otherwise we must convert h2 :authority to h1
1120          * host */
1121
1122         i.host = NULL;
1123         n = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY);
1124         if (n > 0)
1125                 i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY);
1126         else {
1127                 n = lws_hdr_total_length(wsi, WSI_TOKEN_HOST);
1128                 if (n > 0) {
1129                         i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
1130                 }
1131         }
1132
1133 #if 0
1134         if (i.address[0] != '+' ||
1135             !lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST))
1136                 i.host = i.address;
1137         else
1138                 i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
1139 #endif
1140         i.origin = NULL;
1141         if (!ws) {
1142                 if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI)
1143 #if defined(LWS_WITH_HTTP2)
1144                                                                 || (
1145                         lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) &&
1146                         !strcmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD), "post")
1147                         )
1148 #endif
1149                 )
1150                         i.method = "POST";
1151                 else
1152                         i.method = "GET";
1153         }
1154
1155         if (i.host)
1156                 lws_snprintf(host, sizeof(host), "%s:%u", i.host,
1157                                         wsi->vhost->listen_port);
1158         else
1159                 lws_snprintf(host, sizeof(host), "%s:%d", i.address, i.port);
1160
1161         i.host = host;
1162
1163         i.alpn = "http/1.1";
1164         i.parent_wsi = wsi;
1165         i.pwsi = &cwsi;
1166         i.protocol = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
1167         if (ws)
1168                 i.local_protocol_name = "lws-ws-proxy";
1169
1170 //      i.uri_replace_from = hit->origin;
1171 //      i.uri_replace_to = hit->mountpoint;
1172
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);
1176
1177         if (!lws_client_connect_via_info(&i)) {
1178                 lwsl_err("proxy connect fail\n");
1179
1180                 /*
1181                  * ... we can't do the proxy action, but we can
1182                  * cleanly return him a 503 and a description
1183                  */
1184
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.");
1191
1192                 return 1;
1193         }
1194
1195         lwsl_info("%s: setting proxy clientside on %p (parent %p)\n",
1196                   __func__, cwsi, lws_get_parent(cwsi));
1197
1198         cwsi->http.proxy_clientside = 1;
1199         if (ws) {
1200                 wsi->proxied_ws_parent = 1;
1201                 cwsi->h1_ws_proxied = 1;
1202                 if (i.protocol) {
1203                         lwsl_debug("%s: (requesting '%s')\n", __func__, i.protocol);
1204                 }
1205         }
1206
1207         return 0;
1208 }
1209 #endif
1210
1211 static const char * const oprot[] = {
1212         "http://", "https://"
1213 };
1214
1215 int
1216 lws_http_action(struct lws *wsi)
1217 {
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;
1229         unsigned int n;
1230
1231         meth = lws_http_get_uri_and_method(wsi, &uri_ptr, &uri_len);
1232         if (meth < 0 || meth >= (int)LWS_ARRAY_SIZE(method_names))
1233                 goto bail_nuke_ah;
1234
1235         /* we insist on absolute paths */
1236
1237         if (!uri_ptr || uri_ptr[0] != '/') {
1238                 lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
1239
1240                 goto bail_nuke_ah;
1241         }
1242
1243         lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth],
1244                   meth, uri_ptr);
1245
1246         if (wsi->role_ops && wsi->role_ops->check_upgrades)
1247                 switch (wsi->role_ops->check_upgrades(wsi)) {
1248                 case LWS_UPG_RET_DONE:
1249                         return 0;
1250                 case LWS_UPG_RET_CONTINUE:
1251                         break;
1252                 case LWS_UPG_RET_BAIL:
1253                         goto bail_nuke_ah;
1254                 }
1255
1256         if (lws_ensure_user_space(wsi))
1257                 goto bail_nuke_ah;
1258
1259         /* HTTP header had a content length? */
1260
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;
1267
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__);
1277                 }
1278         }
1279
1280         if (wsi->http2_substream) {
1281                 wsi->http.request_version = HTTP_VERSION_2;
1282         } else {
1283                 /* http_version? Default to 1.0, override with token: */
1284                 request_version = HTTP_VERSION_1_0;
1285
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;
1294
1295                 wsi->http.request_version = request_version;
1296
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;
1300                 else
1301                         conn_type = HTTP_CONNECTION_CLOSE;
1302
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;
1310                         else
1311                                 if (!strcasecmp(http_conn_str, "close"))
1312                                         conn_type = HTTP_CONNECTION_CLOSE;
1313                 }
1314                 wsi->http.conn_type = conn_type;
1315         }
1316
1317         n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION,
1318                                     wsi->user_space, uri_ptr, uri_len);
1319         if (n) {
1320                 lwsl_info("LWS_CALLBACK_HTTP closing\n");
1321
1322                 return 1;
1323         }
1324         /*
1325          * if there is content supposed to be coming,
1326          * put a timeout on it having arrived
1327          */
1328         lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
1329                         wsi->context->timeout_secs);
1330 #ifdef LWS_WITH_TLS
1331         if (wsi->tls.redirect_to_https) {
1332                 /*
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
1336                  */
1337                 unsigned char *start = pt->serv_buf + LWS_PRE, *p = start,
1338                               *end = p + wsi->context->pt_serv_buf_size - LWS_PRE;
1339
1340                 n = lws_hdr_total_length(wsi, WSI_TOKEN_HOST);
1341                 if (!n || n > 128)
1342                         goto bail_nuke_ah;
1343
1344                 p += lws_snprintf((char *)p, lws_ptr_diff(end, p), "https://");
1345                 memcpy(p, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST), n);
1346                 p += n;
1347                 *p++ = '/';
1348                 *p = '\0';
1349                 n = lws_ptr_diff(p, start);
1350
1351                 p += LWS_PRE;
1352                 n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
1353                                       start, n, &p, end);
1354                 if ((int)n < 0)
1355                         goto bail_nuke_ah;
1356
1357                 return lws_http_transaction_completed(wsi);
1358         }
1359 #endif
1360
1361 #ifdef LWS_WITH_ACCESS_LOG
1362         lws_prepare_access_log_info(wsi, uri_ptr, uri_len, meth);
1363 #endif
1364
1365         /* can we serve it from the mount list? */
1366
1367         hit = lws_find_mount(wsi, uri_ptr, uri_len);
1368         if (!hit) {
1369                 /* deferred cleanup and reset to protocols[0] */
1370
1371                 lwsl_info("no hit\n");
1372
1373                 if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0],
1374                                       "no mount hit"))
1375                         return 1;
1376
1377                 lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
1378
1379                 m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
1380                                     wsi->user_space, uri_ptr, uri_len);
1381
1382                 goto after;
1383         }
1384
1385         s = uri_ptr + hit->mountpoint_len;
1386
1387         /*
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".
1391          *
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.
1397          *
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.
1401          */
1402         if ((hit->mountpoint_len > 1 ||
1403              (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
1404               hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
1405             (*s != '/' ||
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 -
1412                                      LWS_PRE - 512;
1413
1414                 lwsl_info("Doing 301 '%s' org %s\n", s, hit->origin);
1415
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],
1421                                     hit->origin);
1422                 else {
1423                         if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
1424                                 if (!lws_hdr_total_length(wsi,
1425                                                 WSI_TOKEN_HTTP_COLON_AUTHORITY))
1426                                         goto bail_nuke_ah;
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),
1431                                     uri_ptr);
1432                         } else
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),
1436                                     uri_ptr);
1437                 }
1438
1439                 lws_clean_url((char *)end);
1440                 n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
1441                                       end, n, &p, end);
1442                 if ((int)n < 0)
1443                         goto bail_nuke_ah;
1444
1445                 return lws_http_transaction_completed(wsi);
1446         }
1447
1448         /* basic auth? */
1449
1450         switch(lws_check_basic_auth(wsi, hit->basic_auth_login_file)) {
1451         case LCBA_CONTINUE:
1452                 break;
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);
1458         }
1459
1460 #if defined(LWS_WITH_HTTP_PROXY)
1461         /*
1462          * The mount is a reverse proxy?
1463          */
1464
1465         // if (hit)
1466         // lwsl_notice("%s: origin_protocol: %d\n", __func__, hit->origin_protocol);
1467         //else
1468         //      lwsl_notice("%s: no hit\n", __func__);
1469
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);
1474                 if (n)
1475                         return n;
1476
1477                 goto deal_body;
1478         }
1479 #endif
1480
1481         /*
1482          * A particular protocol callback is mounted here?
1483          *
1484          * For the duration of this http transaction, bind us to the
1485          * associated protocol
1486          */
1487         if (hit->origin_protocol == LWSMPRO_CALLBACK || hit->protocol) {
1488                 const struct lws_protocols *pp;
1489                 const char *name = hit->origin;
1490                 if (hit->protocol)
1491                         name = hit->protocol;
1492
1493                 pp = lws_vhost_name_to_protocol(wsi->vhost, name);
1494                 if (!pp) {
1495                         n = -1;
1496                         lwsl_err("Unable to find plugin '%s'\n",
1497                                  hit->origin);
1498                         return 1;
1499                 }
1500
1501                 if (lws_bind_protocol(wsi, pp, "http action CALLBACK bind"))
1502                         return 1;
1503
1504                 lwsl_notice("%s: %s, checking access rights for mask 0x%x\n",
1505                                 __func__, hit->origin, hit->auth_mask);
1506
1507                 args.p = uri_ptr;
1508                 args.len = uri_len;
1509                 args.max_len = hit->auth_mask;
1510                 args.final = 0; /* used to signal callback dealt with it */
1511                 args.chunked = 0;
1512
1513                 n = wsi->protocol->callback(wsi,
1514                                             LWS_CALLBACK_CHECK_ACCESS_RIGHTS,
1515                                             wsi->user_space, &args, 0);
1516                 if (n) {
1517                         lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED,
1518                                                NULL);
1519                         goto bail_nuke_ah;
1520                 }
1521                 if (args.final) /* callback completely handled it well */
1522                         return 0;
1523
1524                 if (hit->cgienv && wsi->protocol->callback(wsi,
1525                                 LWS_CALLBACK_HTTP_PMO,
1526                                 wsi->user_space, (void *)hit->cgienv, 0))
1527                         return 1;
1528
1529                 if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
1530                         m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
1531                                             wsi->user_space,
1532                                             uri_ptr + hit->mountpoint_len,
1533                                             uri_len - hit->mountpoint_len);
1534                         goto after;
1535                 }
1536         }
1537
1538 #ifdef LWS_WITH_CGI
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 */
1543                         NULL
1544                 };
1545
1546                 lwsl_debug("%s: cgi\n", __func__);
1547                 cmd[0] = hit->origin;
1548
1549                 n = 5;
1550                 if (hit->cgi_timeout)
1551                         n = hit->cgi_timeout;
1552
1553                 n = lws_cgi(wsi, cmd, hit->mountpoint_len, n,
1554                             hit->cgienv);
1555                 if (n) {
1556                         lwsl_err("%s: cgi failed\n", __func__);
1557                         return -1;
1558                 }
1559
1560                 goto deal_body;
1561         }
1562 #endif
1563
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;
1567         if (!s)
1568                 s = "index.html";
1569
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;
1574
1575         m = 1;
1576 #if !defined(LWS_AMAZON_RTOS)
1577         if (hit->origin_protocol == LWSMPRO_FILE)
1578                 m = lws_http_serve(wsi, s, hit->origin, hit);
1579 #endif
1580
1581         if (m > 0) {
1582                 /*
1583                  * lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
1584                  */
1585                 if (hit->protocol) {
1586                         const struct lws_protocols *pp =
1587                                         lws_vhost_name_to_protocol(
1588                                                 wsi->vhost, hit->protocol);
1589
1590                         lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
1591
1592                         if (lws_bind_protocol(wsi, pp, "http_action HTTP"))
1593                                 return 1;
1594
1595                         m = pp->callback(wsi, LWS_CALLBACK_HTTP,
1596                                          wsi->user_space,
1597                                          uri_ptr + hit->mountpoint_len,
1598                                          uri_len - hit->mountpoint_len);
1599                 } else
1600                         m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
1601                                     wsi->user_space, uri_ptr, uri_len);
1602         }
1603
1604 after:
1605         if (m) {
1606                 lwsl_info("LWS_CALLBACK_HTTP closing\n");
1607
1608                 return 1;
1609         }
1610
1611 #if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)
1612 deal_body:
1613 #endif
1614         /*
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.
1618          *
1619          * In any case, return 0 and let lws_read decide how to
1620          * proceed based on state
1621          */
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);
1627
1628                 if (wsi->http.content_length_explicitly_zero &&
1629                     lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
1630
1631                         /*
1632                          * POST with an explicit content-length of zero
1633                          *
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).
1638                          *
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.
1642                          */
1643                         if (wsi->protocol->callback(wsi,
1644                                         LWS_CALLBACK_HTTP_BODY,
1645                                         wsi->user_space, NULL, 0))
1646                                 return 1;
1647                         if (wsi->protocol->callback(wsi,
1648                                         LWS_CALLBACK_HTTP_BODY_COMPLETION,
1649                                         wsi->user_space, NULL, 0))
1650                                 return 1;
1651
1652                         return 0;
1653                 }
1654
1655                 if (wsi->http.rx_content_length > 0) {
1656
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);
1661                         }
1662                         wsi->http.rx_content_remain =
1663                                         wsi->http.rx_content_length;
1664
1665                         /*
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.
1670                          */
1671
1672                         while (1) {
1673                                 struct lws_tokens ebuf;
1674                                 int m;
1675
1676                                 ebuf.len = (int)lws_buflist_next_segment_len(
1677                                                 &wsi->buflist,
1678                                                 &ebuf.token);
1679                                 if (!ebuf.len)
1680                                         break;
1681                                 lwsl_debug("%s: consuming %d\n", __func__,
1682                                                         (int)ebuf.len);
1683                                 m = lws_read_h1(wsi, ebuf.token,
1684                                                 ebuf.len);
1685                                 if (m < 0)
1686                                         return -1;
1687
1688                                 if (lws_buflist_aware_consume(wsi, &ebuf, m, 1))
1689                                         return -1;
1690                         }
1691                 }
1692         }
1693
1694         return 0;
1695
1696 bail_nuke_ah:
1697         lws_header_table_detach(wsi, 1);
1698
1699         return 1;
1700 }
1701
1702 int
1703 lws_confirm_host_header(struct lws *wsi)
1704 {
1705         struct lws_tokenize ts;
1706         lws_tokenize_elem e;
1707         char buf[128];
1708         int port = 80;
1709
1710         /*
1711          * this vhost wants us to validate what the
1712          * client sent against our vhost name
1713          */
1714
1715         if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
1716                 lwsl_info("%s: missing host on upgrade\n", __func__);
1717
1718                 return 1;
1719         }
1720
1721 #if defined(LWS_WITH_TLS)
1722         if (wsi->tls.ssl)
1723                 port = 443;
1724 #endif
1725
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);
1730         if (ts.len <= 0) {
1731                 lwsl_info("%s: missing or oversize host header\n", __func__);
1732                 return 1;
1733         }
1734
1735         if (lws_tokenize(&ts) != LWS_TOKZE_TOKEN)
1736                 goto bad_format;
1737
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);
1742                 return 1;
1743         }
1744
1745         e = lws_tokenize(&ts);
1746         if (e == LWS_TOKZE_DELIMITER && ts.token[0] == ':') {
1747                 if (lws_tokenize(&ts) != LWS_TOKZE_INTEGER)
1748                         goto bad_format;
1749                 else
1750                         port = atoi(ts.token);
1751         } else
1752                 if (e != LWS_TOKZE_ENDED)
1753                         goto bad_format;
1754
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);
1758                 return 1;
1759         }
1760
1761         lwsl_debug("%s: host header OK\n", __func__);
1762
1763         return 0;
1764
1765 bad_format:
1766         lwsl_info("%s: bad host header format\n", __func__);
1767
1768         return 1;
1769 }
1770
1771 #if !defined(LWS_NO_SERVER)
1772 int
1773 lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen)
1774 {
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];
1778         char ipbuf[64];
1779         int n;
1780
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);
1784
1785         if (wsi->vhost->listen_accept_protocol) {
1786                 p1 = lws_vhost_name_to_protocol(wsi->vhost,
1787                             wsi->vhost->listen_accept_protocol);
1788                 if (p1)
1789                         protocol = p1;
1790         }
1791
1792         lws_bind_protocol(wsi, protocol, __func__);
1793
1794         lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED, role);
1795
1796         lws_header_table_detach(wsi, 0);
1797         lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
1798
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)];
1802
1803         ipbuf[0] = '\0';
1804 #if !defined(LWS_PLAT_OPTEE)
1805         lws_get_peer_simple(wsi, ipbuf, sizeof(ipbuf));
1806 #endif
1807
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);
1811
1812         if ((wsi->protocol->callback)(wsi, n, wsi->user_space, NULL, 0))
1813                 return 1;
1814
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))
1819                 return 1;
1820
1821         return 0;
1822 }
1823
1824 int
1825 lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
1826 {
1827         struct lws_context *context = lws_get_context(wsi);
1828 #if defined(LWS_WITH_HTTP2)
1829         struct allocated_headers *ah;
1830 #endif
1831         unsigned char *obuf = *buf;
1832 #if defined(LWS_WITH_HTTP2)
1833         char tbuf[128], *p;
1834 #endif
1835         size_t olen = len;
1836         int n = 0, m, i;
1837
1838         if (len >= 10000000) {
1839                 lwsl_err("%s: assert: len %ld\n", __func__, (long)len);
1840                 assert(0);
1841         }
1842
1843         if (!wsi->http.ah) {
1844                 lwsl_err("%s: assert: NULL ah\n", __func__);
1845                 assert(0);
1846         }
1847
1848         while (len) {
1849                 if (!lwsi_role_server(wsi) || !lwsi_role_http(wsi)) {
1850                         lwsl_err("%s: bad wsi role 0x%x\n", __func__,
1851                                         lwsi_role(wsi));
1852                         goto bail_nuke_ah;
1853                 }
1854
1855                 i = (int)len;
1856                 m = lws_parse(wsi, *buf, &i);
1857                 lwsl_info("%s: parsed count %d\n", __func__, (int)len - i);
1858                 (*buf) += (int)len - i;
1859                 len = i;
1860
1861                 if (m == LPR_DO_FALLBACK) {
1862
1863                         /*
1864                          * http parser went off the rails and
1865                          * LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_
1866                          * ACCEPT_CONFIG is set on this vhost.
1867                          *
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
1871                          * ESTABLISHED.
1872                          */
1873 raw_transition:
1874
1875                         if (lws_http_to_fallback(wsi, obuf, olen)) {
1876                                 lwsl_info("%s: fallback -> close\n", __func__);
1877                                 goto bail_nuke_ah;
1878                         }
1879
1880                         (*buf) = obuf + olen;
1881
1882                         return 0;
1883                 }
1884                 if (m) {
1885                         lwsl_info("lws_parse failed\n");
1886                         goto bail_nuke_ah;
1887                 }
1888
1889                 if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
1890                         continue;
1891
1892                 lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
1893
1894                 /* select vhost */
1895
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));
1901
1902                         if (vhost)
1903                                 lws_vhost_bind_wsi(vhost, wsi);
1904                 } else
1905                         lwsl_info("no host\n");
1906
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;
1912                         }
1913                 }
1914
1915                 /* check for unwelcome guests */
1916
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;
1921
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;
1926                                 int meth, uri_len;
1927 #endif
1928                                 ua[sizeof(ua) - 1] = '\0';
1929                                 while (rej) {
1930                                         if (!strstr(ua, rej->name)) {
1931                                                 rej = rej->next;
1932                                                 continue;
1933                                         }
1934
1935                                         msg = strchr(rej->value, ' ');
1936                                         if (msg)
1937                                                 msg++;
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);
1943                                         if (meth >= 0)
1944                                                 lws_prepare_access_log_info(wsi,
1945                                                         uri_ptr, uri_len, meth);
1946
1947                                         /* wsi close will do the log */
1948 #endif
1949                                         wsi->vhost->conn_stats.rejected++;
1950                                         /*
1951                                          * We don't want anything from
1952                                          * this rejected guy.  Follow
1953                                          * the close flow, not the
1954                                          * transaction complete flow.
1955                                          */
1956                                         goto bail_nuke_ah;
1957                                 }
1958                         }
1959                 }
1960
1961
1962                 if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECT)) {
1963                         lwsl_info("Changing to RAW mode\n");
1964                         m = 0;
1965                         goto raw_transition;
1966                 }
1967
1968                 lwsi_set_state(wsi, LRS_PRE_WS_SERVING_ACCEPT);
1969                 lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
1970
1971                 if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
1972
1973                         const char *up = lws_hdr_simple_ptr(wsi,
1974                                                             WSI_TOKEN_UPGRADE);
1975
1976                         if (strcasecmp(up, "websocket") &&
1977                             strcasecmp(up, "h2c")) {
1978                                 lwsl_info("Unknown upgrade '%s'\n", up);
1979
1980                                 if (lws_return_http_status(wsi,
1981                                                 HTTP_STATUS_FORBIDDEN, NULL) ||
1982                                     lws_http_transaction_completed(wsi))
1983                                         goto bail_nuke_ah;
1984                         }
1985
1986                         n = user_callback_handle_rxflow(wsi->protocol->callback,
1987                                         wsi, LWS_CALLBACK_HTTP_CONFIRM_UPGRADE,
1988                                         wsi->user_space, (char *)up, 0);
1989
1990                         /* just hang up? */
1991
1992                         if (n < 0)
1993                                 goto bail_nuke_ah;
1994
1995                         /* callback returned headers already, do t_c? */
1996
1997                         if (n > 0) {
1998                                 if (lws_http_transaction_completed(wsi))
1999                                         goto bail_nuke_ah;
2000
2001                                 /* continue on */
2002
2003                                 return 0;
2004                         }
2005
2006                         /* callback said 0, it was allowed */
2007
2008                         if (wsi->vhost->options &
2009                             LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK &&
2010                             lws_confirm_host_header(wsi))
2011                                 goto bail_nuke_ah;
2012
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");
2017                                 goto upgrade_ws;
2018 #endif
2019                         }
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");
2024                                 goto upgrade_h2c;
2025                         }
2026 #endif
2027                 }
2028
2029                 /* no upgrade ack... he remained as HTTP */
2030
2031                 lwsl_info("%s: %p: No upgrade\n", __func__, wsi);
2032
2033                 lwsi_set_state(wsi, LRS_ESTABLISHED);
2034                 wsi->http.fop_fd = NULL;
2035
2036 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2037                 lws_http_compression_validate(wsi);
2038 #endif
2039
2040                 lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
2041                            (void *)wsi->http.ah);
2042
2043                 n = lws_http_action(wsi);
2044
2045                 return n;
2046
2047 #if defined(LWS_WITH_HTTP2)
2048 upgrade_h2c:
2049                 if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
2050                         lwsl_info("missing http2_settings\n");
2051                         goto bail_nuke_ah;
2052                 }
2053
2054                 lwsl_info("h2c upgrade...\n");
2055
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));
2059                 if (n < 0) {
2060                         lwsl_parser("HTTP2_SETTINGS too long\n");
2061                         return 1;
2062                 }
2063
2064                 wsi->upgraded_to_http2 = 1;
2065
2066                 /* adopt the header info */
2067
2068                 ah = wsi->http.ah;
2069                 lws_role_transition(wsi, LWSIFR_SERVER, LRS_H2_AWAIT_PREFACE,
2070                                     &role_ops_h2);
2071
2072                 /* http2 union member has http union struct at start */
2073                 wsi->http.ah = ah;
2074
2075                 if (!wsi->h2.h2n) {
2076                         wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n),
2077                                                    "h2n");
2078                         if (!wsi->h2.h2n)
2079                                 return 1;
2080                 }
2081
2082                 lws_h2_init(wsi);
2083
2084                 /* HTTP2 union */
2085
2086                 lws_h2_settings(wsi, &wsi->h2.h2n->set, (unsigned char *)tbuf, n);
2087
2088                 lws_hpack_dynamic_size(wsi, wsi->h2.h2n->set.s[
2089                                                       H2SET_HEADER_TABLE_SIZE]);
2090
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);
2096                 if (n != m) {
2097                         lwsl_debug("http2 switch: ERROR writing to socket\n");
2098                         return 1;
2099                 }
2100
2101                 return 0;
2102 #endif
2103 #if defined(LWS_ROLE_WS)
2104 upgrade_ws:
2105                 if (lws_process_ws_upgrade(wsi))
2106                         goto bail_nuke_ah;
2107
2108                 return 0;
2109 #endif
2110         } /* while all chars are handled */
2111
2112         return 0;
2113
2114 bail_nuke_ah:
2115         /* drop the header info */
2116         lws_header_table_detach(wsi, 1);
2117
2118         return 1;
2119 }
2120 #endif
2121
2122 LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
2123 lws_http_transaction_completed(struct lws *wsi)
2124 {
2125         int n = NO_PENDING_TIMEOUT;
2126
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
2131 #endif
2132         ) {
2133                 /*
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.
2137                  *
2138                  * Defer the transaction completed until the last part of the
2139                  * partial is sent.
2140                  */
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);
2144
2145                 return 0;
2146         }
2147         /*
2148          * Are we finishing the transaction before we have consumed any body?
2149          *
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
2152          * connection.
2153          */
2154         if (wsi->http.rx_content_length && wsi->http.rx_content_remain) {
2155                 /*
2156                  * are we already in LRS_DISCARD_BODY and didn't clear the
2157                  * remaining before trying to complete the transaction again?
2158                  */
2159                 if (lwsi_state(wsi) == LRS_DISCARD_BODY)
2160                         return -1;
2161                 /*
2162                  * let's defer transaction completed processing until we
2163                  * discarded the remaining body
2164                  */
2165                 lwsi_set_state(wsi, LRS_DISCARD_BODY);
2166
2167                 return 0;
2168         }
2169
2170         lwsl_info("%s: wsi %p\n", __func__, wsi);
2171
2172 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2173         lws_http_compression_destroy(wsi);
2174 #endif
2175         lws_access_log(wsi);
2176
2177         if (!wsi->hdr_parsing_completed) {
2178                 char peer[64];
2179
2180 #if !defined(LWS_PLAT_OPTEE)
2181                 lws_get_peer_simple(wsi, peer, sizeof(peer) - 1);
2182 #else
2183                 peer[0] = '\0';
2184 #endif
2185                 peer[sizeof(peer) - 1] = '\0';
2186                 lwsl_notice("%s: (from %s) ignoring, ah parsing incomplete\n",
2187                                 __func__, peer);
2188                 return 0;
2189         }
2190
2191         /* if we can't go back to accept new headers, drop the connection */
2192         if (wsi->http2_substream)
2193                 return 1;
2194
2195         if (wsi->seen_zero_length_recv)
2196                 return 1;
2197
2198         if (wsi->http.conn_type != HTTP_CONNECTION_KEEP_ALIVE) {
2199                 lwsl_info("%s: %p: close connection\n", __func__, wsi);
2200                 return 1;
2201         }
2202
2203         if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0], __func__))
2204                 return 1;
2205
2206         /*
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.
2211          */
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;
2221 #endif
2222
2223         if (wsi->vhost->keepalive_timeout)
2224                 n = PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE;
2225         lws_set_timeout(wsi, n, wsi->vhost->keepalive_timeout);
2226
2227         /*
2228          * We already know we are on http1.1 / keepalive and the next thing
2229          * coming will be another header set.
2230          *
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.)
2234          *
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.
2238          */
2239         if (wsi->http.ah) {
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",
2243                                   __func__, wsi);
2244                         lws_header_table_detach(wsi, 1);
2245 #ifdef LWS_WITH_TLS
2246                         /*
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
2250                          */
2251
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",
2257                                           __func__);
2258                                 return 1;
2259                         }
2260 #endif
2261                 } else {
2262                         lwsl_info("%s: %p: resetting/keeping ah as pipeline\n",
2263                                   __func__, wsi);
2264                         lws_header_table_reset(wsi, 0);
2265                         /*
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
2269                          * open.
2270                          */
2271                         lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,
2272                                         wsi->vhost->keepalive_timeout);
2273                 }
2274                 /* If we're (re)starting on headers, need other implied init */
2275                 if (wsi->http.ah)
2276                         wsi->http.ah->ues = URIES_IDLE;
2277
2278                 //lwsi_set_state(wsi, LRS_ESTABLISHED); // !!!
2279         } else
2280                 if (lws_buflist_next_segment_len(&wsi->buflist, NULL))
2281                         if (lws_header_table_attach(wsi, 0))
2282                                 lwsl_debug("acquired ah\n");
2283
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);
2287
2288         return 0;
2289 }
2290
2291 #if !defined(LWS_AMAZON_RTOS)
2292 LWS_VISIBLE int
2293 lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
2294                     const char *other_headers, int other_headers_len)
2295 {
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;
2301 #endif
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;
2309         const char *vpath;
2310 #if defined(LWS_WITH_RANGES)
2311         int ranges;
2312 #endif
2313
2314         if (wsi->handling_404)
2315                 n = HTTP_STATUS_NOT_FOUND;
2316
2317         /*
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.
2321          *
2322          * If wsi->http.fop_fd is already set, the caller already opened it
2323          */
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,
2333                                                    NULL))
2334                                                 return -1;
2335                         return !wsi->http2_substream;
2336                 }
2337         }
2338         wsi->http.filelen = lws_vfs_get_length(wsi->http.fop_fd);
2339         total_content_length = wsi->http.filelen;
2340
2341 #if defined(LWS_WITH_RANGES)
2342         ranges = lws_ranges_init(wsi, rp, wsi->http.filelen);
2343
2344         lwsl_debug("Range count %d\n", ranges);
2345         /*
2346          * no ranges -> 200;
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
2351          */
2352         if (ranges < 0) {
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 */
2358
2359                 lws_vfs_file_close(&wsi->http.fop_fd);
2360
2361                 return 0; /* == 0 means we did the transaction complete */
2362         }
2363         if (ranges)
2364                 n = HTTP_STATUS_PARTIAL_CONTENT;
2365 #endif
2366
2367         if (lws_add_http_header_status(wsi, n, &p, end))
2368                 return -1;
2369
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))
2376                         return -1;
2377                 lwsl_info("file is being provided in gzip\n");
2378         }
2379 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2380         else {
2381                 /*
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
2385                  */
2386
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);
2392         }
2393 #endif
2394
2395         if (
2396 #if defined(LWS_WITH_RANGES)
2397             ranges < 2 &&
2398 #endif
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),
2404                                                  &p, end))
2405                         return -1;
2406
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));
2411
2412                 if (lws_add_http_header_by_token(wsi,
2413                                                  WSI_TOKEN_HTTP_CONTENT_TYPE,
2414                                                  (unsigned char *)
2415                                                  "multipart/byteranges; "
2416                                                  "boundary=_lws",
2417                                                  20, &p, end))
2418                         return -1;
2419
2420                 /*
2421                  *  our overall content length has to include
2422                  *
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
2426                  *  - n x /r/n
2427                  *  - the actual payloads (aggregated in rp->agg)
2428                  *
2429                  *  Precompute it for the main response header
2430                  */
2431
2432                 total_content_length = (lws_filepos_t)rp->agg +
2433                                        6 /* final _lws\r\n */;
2434
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);
2440
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 */
2446                                 15 + n + 2 +
2447                                 2; /* /r/n */
2448                 }
2449
2450                 lws_ranges_reset(rp);
2451                 lws_ranges_next(rp);
2452         }
2453
2454         if (ranges == 1) {
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);
2459
2460                 if (lws_add_http_header_by_token(wsi,
2461                                                  WSI_TOKEN_HTTP_CONTENT_RANGE,
2462                                                  (unsigned char *)cache_control,
2463                                                  n, &p, end))
2464                         return -1;
2465         }
2466
2467         wsi->http.range.inside = 0;
2468
2469         if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ACCEPT_RANGES,
2470                                          (unsigned char *)"bytes", 5, &p, end))
2471                 return -1;
2472 #endif
2473
2474         if (!wsi->http2_substream) {
2475                 /* for http/1.1 ... */
2476                 if (!wsi->sending_chunked
2477 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2478                                 && !wsi->http.lcs
2479 #endif
2480                 ) {
2481                         /* ... if not already using chunked and not using an
2482                          * http compression translation, then send the naive
2483                          * content length
2484                          */
2485                         if (lws_add_http_header_content_length(wsi,
2486                                                 total_content_length, &p, end))
2487                                 return -1;
2488                 } else {
2489
2490 #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
2491                         if (wsi->http.lcs) {
2492
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...
2500                                  */
2501                                 if (lws_add_http_header_by_token(wsi,
2502                                                 WSI_TOKEN_HTTP_TRANSFER_ENCODING,
2503                                                 (unsigned char *)"chunked", 7,
2504                                                 &p, end))
2505                                         return -1;
2506
2507                                 /*
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.
2511                                  *
2512                                  * h2 doesn't need (or support) any of this
2513                                  * crap.
2514                                  */
2515                                 lwsl_debug("setting chunking\n");
2516                                 wsi->http.comp_ctx.chunking = 1;
2517                         }
2518 #endif
2519                 }
2520         }
2521
2522         if (wsi->cache_secs && wsi->cache_reuse) {
2523                 if (!wsi->cache_revalidate) {
2524                         cc = cache_control;
2525                         cclen = sprintf(cache_control, "%s, max-age=%u",
2526                                     intermediates[wsi->cache_intermediaries],
2527                                     wsi->cache_secs);
2528                 } else {
2529                         cc = cache_control;
2530                         cclen = sprintf(cache_control,
2531                                         "must-revalidate, %s, max-age=%u",
2532                                 intermediates[wsi->cache_intermediaries],
2533                                                     wsi->cache_secs);
2534
2535                 }
2536         }
2537
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))
2545                         return -1;
2546         }
2547
2548         if (other_headers) {
2549                 if ((end - p) < other_headers_len)
2550                         return -1;
2551                 memcpy(p, other_headers, other_headers_len);
2552                 p += other_headers_len;
2553         }
2554
2555         if (lws_finalize_http_header(wsi, &p, end))
2556                 return -1;
2557
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));
2562                 return -1;
2563         }
2564
2565         wsi->http.filepos = 0;
2566         lwsi_set_state(wsi, LRS_ISSUING_FILE);
2567
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))
2571                         return -1;
2572
2573                 return 0;
2574         }
2575
2576         lws_callback_on_writable(wsi);
2577
2578         return 0;
2579 }
2580 #endif
2581
2582 LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
2583 {
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;
2591 #endif
2592         int n, m;
2593
2594         lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream);
2595
2596         do {
2597
2598                 /* priority 1: buffered output */
2599
2600                 if (lws_has_buffered_out(wsi)) {
2601                         if (lws_issue_raw(wsi, NULL, 0) < 0) {
2602                                 lwsl_info("%s: closing\n", __func__);
2603                                 goto file_had_it;
2604                         }
2605                         break;
2606                 }
2607
2608                 /* priority 2: buffered pre-compression-transform */
2609
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;
2614
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);
2618
2619                 if (wsi->role_ops->write_role_protocol(wsi, NULL, 0, &wp) < 0) {
2620                         lwsl_info("%s signalling to close\n", __func__);
2621                         goto file_had_it;
2622                 }
2623                 lws_callback_on_writable(wsi);
2624
2625                 break;
2626         }
2627 #endif
2628
2629                 if (wsi->http.filepos == wsi->http.filelen)
2630                         goto all_sent;
2631
2632                 n = 0;
2633
2634                 pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH;
2635
2636                 p = pstart;
2637
2638 #if defined(LWS_WITH_RANGES)
2639                 if (wsi->http.range.count_ranges && !wsi->http.range.inside) {
2640
2641                         lwsl_notice("%s: doing range start %llu\n", __func__,
2642                                     wsi->http.range.start);
2643
2644                         if ((long long)lws_vfs_file_seek_cur(wsi->http.fop_fd,
2645                                                    wsi->http.range.start -
2646                                                    wsi->http.filepos) < 0)
2647                                 goto file_had_it;
2648
2649                         wsi->http.filepos = wsi->http.range.start;
2650
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,
2655                                         "_lws\x0d\x0a"
2656                                         "Content-Type: %s\x0d\x0a"
2657                                         "Content-Range: bytes "
2658                                                 "%llu-%llu/%llu\x0d\x0a"
2659                                         "\x0d\x0a",
2660                                         wsi->http.multipart_content_type,
2661                                         wsi->http.range.start,
2662                                         wsi->http.range.end,
2663                                         wsi->http.range.extent);
2664                                 p += n;
2665                         }
2666
2667                         wsi->http.range.budget = wsi->http.range.end -
2668                                                    wsi->http.range.start + 1;
2669                         wsi->http.range.inside = 1;
2670                 }
2671 #endif
2672
2673                 poss = context->pt_serv_buf_size - n -
2674                                 LWS_H2_FRAME_HEADER_LENGTH;
2675
2676                 if (wsi->http.tx_content_length)
2677                         if (poss > wsi->http.tx_content_remain)
2678                                 poss = wsi->http.tx_content_remain;
2679
2680                 /*
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.
2683                  */
2684                 if (wsi->protocol->tx_packet_size &&
2685                     poss > wsi->protocol->tx_packet_size)
2686                         poss = wsi->protocol->tx_packet_size;
2687
2688                 if (wsi->role_ops->tx_credit) {
2689                         lws_filepos_t txc = wsi->role_ops->tx_credit(wsi);
2690
2691                         if (!txc) {
2692                                 lwsl_info("%s: came here with no tx credit\n",
2693                                                 __func__);
2694                                 return 0;
2695                         }
2696                         if (txc < poss)
2697                                 poss = txc;
2698
2699                         /*
2700                          * consumption of the actual payload amount sent will be
2701                          * handled when the role data frame is sent
2702                          */
2703                 }
2704
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;
2711                 }
2712 #endif
2713                 if (wsi->sending_chunked) {
2714                         /* we need to drop the chunk size in here */
2715                         p += 10;
2716                         /* allow for the chunk to grow by 128 in translation */
2717                         poss -= 10 + 128;
2718                 }
2719
2720                 if (lws_vfs_file_read(wsi->http.fop_fd, &amount, p, poss) < 0)
2721                         goto file_had_it; /* caller will close */
2722
2723                 if (wsi->sending_chunked)
2724                         n = (int)amount;
2725                 else
2726                         n = lws_ptr_diff(p, pstart) + (int)amount;
2727
2728                 lwsl_debug("%s: sending %d\n", __func__, n);
2729
2730                 if (n) {
2731                         lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
2732                                         context->timeout_secs);
2733
2734                         if (wsi->interpreting) {
2735                                 args.p = (char *)p;
2736                                 args.len = n;
2737                                 args.max_len = (unsigned int)poss + 128;
2738                                 args.final = wsi->http.filepos + n ==
2739                                              wsi->http.filelen;
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)
2746                                         goto file_had_it;
2747                                 n = args.len;
2748                                 p = (unsigned char *)args.p;
2749                         } else
2750                                 p = pstart;
2751
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");
2760                         }
2761 #endif
2762                         m = lws_write(wsi, p, n, wsi->http.filepos + amount ==
2763                                         wsi->http.filelen ?
2764                                          LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP);
2765                         if (m < 0)
2766                                 goto file_had_it;
2767
2768                         wsi->http.filepos += amount;
2769
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++;
2777
2778                                         if (lws_ranges_next(&wsi->http.range) < 1) {
2779                                                 finished = 1;
2780                                                 goto all_sent;
2781                                         }
2782                                 }
2783                         }
2784 #endif
2785
2786                         if (m != n) {
2787                                 /* adjust for what was not sent */
2788                                 if (lws_vfs_file_seek_cur(wsi->http.fop_fd,
2789                                                            m - n) ==
2790                                                              (lws_fileofs_t)-1)
2791                                         goto file_had_it;
2792                         }
2793                 }
2794
2795 all_sent:
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
2800 #endif
2801                     ) && (wsi->http.filepos >= wsi->http.filelen
2802 #if defined(LWS_WITH_RANGES)
2803                     || finished)
2804 #else
2805                 )
2806 #endif
2807                 ) {
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);
2811
2812                         lwsl_debug("file completed\n");
2813
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) {
2818                                         /*
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.
2823                                          *
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
2830                                          * network level
2831                                          */
2832                                         if (wsi->http2_substream)
2833                                                 return 1;
2834                                         else
2835                                                 return -1;
2836                                 }
2837
2838                         return 1;  /* >0 indicates completed */
2839                 }
2840                 /*
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
2843                  * whole chunk...
2844                  */
2845         } while (!lws_send_pipe_choked(wsi));
2846
2847         lws_callback_on_writable(wsi);
2848
2849         return 0; /* indicates further processing must be done */
2850
2851 file_had_it:
2852         lws_vfs_file_close(&wsi->http.fop_fd);
2853
2854         return -1;
2855 }
2856
2857 #ifndef LWS_NO_SERVER
2858 LWS_VISIBLE void
2859 lws_server_get_canonical_hostname(struct lws_context *context,
2860                                   const struct lws_context_creation_info *info)
2861 {
2862         if (lws_check_opt(info->options,
2863                         LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME))
2864                 return;
2865 #if !defined(LWS_WITH_ESP32)
2866         /* find canonical hostname */
2867         gethostname((char *)context->canonical_hostname,
2868                     sizeof(context->canonical_hostname) - 1);
2869
2870         lwsl_info(" canonical_hostname = %s\n", context->canonical_hostname);
2871 #else
2872         (void)context;
2873 #endif
2874 }
2875 #endif
2876
2877 LWS_VISIBLE LWS_EXTERN int
2878 lws_chunked_html_process(struct lws_process_html_args *args,
2879                          struct lws_process_html_state *s)
2880 {
2881         char *sp, buffer[32];
2882         const char *pc;
2883         int old_len, n;
2884
2885         /* do replacements */
2886         sp = args->p;
2887         old_len = args->len;
2888         args->len = 0;
2889         s->start = sp;
2890         while (sp < args->p + old_len) {
2891
2892                 if (args->len + 7 >= args->max_len) {
2893                         lwsl_err("Used up interpret padding\n");
2894                         return -1;
2895                 }
2896
2897                 if ((!s->pos && *sp == '$') || s->pos) {
2898                         int hits = 0, hit = 0;
2899
2900                         if (!s->pos)
2901                                 s->start = sp;
2902                         s->swallow[s->pos++] = *sp;
2903                         if (s->pos == sizeof(s->swallow) - 1)
2904                                 goto skip;
2905                         for (n = 0; n < s->count_vars; n++)
2906                                 if (!strncmp(s->swallow, s->vars[n], s->pos)) {
2907                                         hits++;
2908                                         hit = n;
2909                                 }
2910                         if (!hits) {
2911 skip:
2912                                 s->swallow[s->pos] = '\0';
2913                                 memcpy(s->start, s->swallow, s->pos);
2914                                 args->len++;
2915                                 s->pos = 0;
2916                                 sp = s->start + 1;
2917                                 continue;
2918                         }
2919                         if (hits == 1 && s->pos == (int)strlen(s->vars[hit])) {
2920                                 pc = s->replace(s->data, hit);
2921                                 if (!pc)
2922                                         pc = "NULL";
2923                                 n = (int)strlen(pc);
2924                                 s->swallow[s->pos] = '\0';
2925                                 if (n != s->pos) {
2926                                         memmove(s->start + n, s->start + s->pos,
2927                                                 old_len - (sp - args->p) - 1);
2928                                         old_len += (n - s->pos) + 1;
2929                                 }
2930                                 memcpy(s->start, pc, n);
2931                                 args->len++;
2932                                 sp = s->start + 1;
2933
2934                                 s->pos = 0;
2935                         }
2936                         sp++;
2937                         continue;
2938                 }
2939
2940                 args->len++;
2941                 sp++;
2942         }
2943
2944         if (args->chunked) {
2945                 /* no space left for final chunk trailer */
2946                 if (args->final && args->len + 7 >= args->max_len)
2947                         return -1;
2948
2949                 n = sprintf(buffer, "%X\x0d\x0a", args->len);
2950
2951                 args->p -= n;
2952                 memcpy(args->p, buffer, n);
2953                 args->len += n;
2954
2955                 if (args->final) {
2956                         sp = args->p + args->len;
2957                         *sp++ = '\x0d';
2958                         *sp++ = '\x0a';
2959                         *sp++ = '0';
2960                         *sp++ = '\x0d';
2961                         *sp++ = '\x0a';
2962                         *sp++ = '\x0d';
2963                         *sp++ = '\x0a';
2964                         args->len += 7;
2965                 } else {
2966                         sp = args->p + args->len;
2967                         *sp++ = '\x0d';
2968                         *sp++ = '\x0a';
2969                         args->len += 2;
2970                 }
2971         }
2972
2973         return 0;
2974 }