Skip to content
Snippets Groups Projects
server.c 68.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION,
    
    				    wsi->user_space, uri_ptr, uri_len);
    
    Andy Green's avatar
    Andy Green committed
    	if (n) {
    		lwsl_info("LWS_CALLBACK_HTTP closing\n");
    
    Andy Green's avatar
    Andy Green committed
    		return 1;
    
    Andy Green's avatar
    Andy Green committed
    	 * if there is content supposed to be coming,
    	 * put a timeout on it having arrived
    
    Andy Green's avatar
    Andy Green committed
    	lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
    
    Andy Green's avatar
    Andy Green committed
    			wsi->context->timeout_secs);
    
    Andy Green's avatar
    Andy Green committed
    #ifdef LWS_WITH_TLS
    
    	if (wsi->tls.redirect_to_https) {
    
    		 * we accepted http:// only so we could redirect to
    
    		 * https://, so issue the redirect.  Create the redirection
    		 * URI from the host: header and ignore the path part
    		 */
    		unsigned char *start = pt->serv_buf + LWS_PRE, *p = start,
    
    			      *end = p + wsi->context->pt_serv_buf_size - LWS_PRE;
    
    
    		if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
    			goto bail_nuke_ah;
    
    Andy Green's avatar
    Andy Green committed
    		n = sprintf((char *)end, "https://%s/",
    
    			    lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
    
    Andy Green's avatar
    Andy Green committed
    		n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
    				      end, n, &p, end);
    
    			goto bail_nuke_ah;
    
    		return lws_http_transaction_completed(wsi);
    	}
    
    Andy Green's avatar
    Andy Green committed
    #endif
    
    #ifdef LWS_WITH_ACCESS_LOG
    
    	lws_prepare_access_log_info(wsi, uri_ptr, uri_len, meth);
    
    Andy Green's avatar
    Andy Green committed
    	/* can we serve it from the mount list? */
    
    
    Andy Green's avatar
    Andy Green committed
    	hit = lws_find_mount(wsi, uri_ptr, uri_len);
    	if (!hit) {
    		/* deferred cleanup and reset to protocols[0] */
    
    Andy Green's avatar
    Andy Green committed
    		lwsl_info("no hit\n");
    
    Andy Green's avatar
    Andy Green committed
    		if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0], "no mount hit"))
    
    Andy Green's avatar
    Andy Green committed
    			return 1;
    
    Andy Green's avatar
    Andy Green committed
    		lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
    
    
    		m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
    
    Andy Green's avatar
    Andy Green committed
    				    wsi->user_space, uri_ptr, uri_len);
    
    Andy Green's avatar
    Andy Green committed
    		goto after;
    	}
    
    Andy Green's avatar
    Andy Green committed
    	s = uri_ptr + hit->mountpoint_len;
    
    Andy Green's avatar
    Andy Green committed
    	/*
    	 * if we have a mountpoint like https://xxx.com/yyy
    	 * there is an implied / at the end for our purposes since
    	 * we can only mount on a "directory".
    	 *
    	 * But if we just go with that, the browser cannot understand
    	 * that he is actually looking down one "directory level", so
    	 * even though we give him /yyy/abc.html he acts like the
    	 * current directory level is /.  So relative urls like "x.png"
    	 * wrongly look outside the mountpoint.
    	 *
    	 * Therefore if we didn't come in on a url with an explicit
    	 * / at the end, we must redirect to add it so the browser
    	 * understands he is one "directory level" down.
    	 */
    	if ((hit->mountpoint_len > 1 ||
    	     (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
    	      hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
    	    (*s != '/' ||
    	     (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
    	      hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
    	    (hit->origin_protocol != LWSMPRO_CGI &&
    
    Andy Green's avatar
    Andy Green committed
    	     hit->origin_protocol != LWSMPRO_CALLBACK)) {
    
    Andy Green's avatar
    Andy Green committed
    		unsigned char *start = pt->serv_buf + LWS_PRE,
    
    			      *p = start, *end = p + wsi->context->pt_serv_buf_size -
    			      	      LWS_PRE - 512;
    
    		lwsl_info("Doing 301 '%s' org %s\n", s, hit->origin);
    
    Andy Green's avatar
    Andy Green committed
    
    		/* > at start indicates deal with by redirect */
    		if (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
    		    hit->origin_protocol == LWSMPRO_REDIR_HTTPS)
    
    Andy Green's avatar
    Andy Green committed
    			n = lws_snprintf((char *)end, 256, "%s%s",
    
    Andy Green's avatar
    Andy Green committed
    				    oprot[hit->origin_protocol & 1],
    				    hit->origin);
    
    Andy Green's avatar
    Andy Green committed
    		else {
    			if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
    
    				if (!lws_hdr_total_length(wsi,
    						WSI_TOKEN_HTTP_COLON_AUTHORITY))
    
    Andy Green's avatar
    Andy Green committed
    					goto bail_nuke_ah;
    				n = lws_snprintf((char *)end, 256,
    				    "%s%s%s/", oprot[!!lws_is_ssl(wsi)],
    
    				    lws_hdr_simple_ptr(wsi,
    						WSI_TOKEN_HTTP_COLON_AUTHORITY),
    
    Andy Green's avatar
    Andy Green committed
    				    uri_ptr);
    			} else
    				n = lws_snprintf((char *)end, 256,
    				    "%s%s%s/", oprot[!!lws_is_ssl(wsi)],
    				    lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
    				    uri_ptr);
    		}
    
    Andy Green's avatar
    Andy Green committed
    		lws_clean_url((char *)end);
    
    Andy Green's avatar
    Andy Green committed
    		n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
    				      end, n, &p, end);
    		if ((int)n < 0)
    			goto bail_nuke_ah;
    
    Andy Green's avatar
    Andy Green committed
    		return lws_http_transaction_completed(wsi);
    	}
    
    
    Andy Green's avatar
    Andy Green committed
    	/* basic auth? */
    
    	if (hit->basic_auth_login_file) {
    
    		char b64[160], plain[(sizeof(b64) * 3) / 4], *pcolon;
    		int m, ml, fi;
    
    Andy Green's avatar
    Andy Green committed
    
    		/* Did he send auth? */
    
    		ml = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
    		if (!ml)
    
    Andy Green's avatar
    Andy Green committed
    			return lws_unauthorised_basic_auth(wsi);
    
    
    		/* Disallow fragmentation monkey business */
    
    		fi = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_AUTHORIZATION];
    		if (wsi->http.ah->frags[fi].nfrag) {
    			lwsl_err("fragmented basic auth header not allowed\n");
    			return lws_unauthorised_basic_auth(wsi);
    		}
    
    
    Andy Green's avatar
    Andy Green committed
    		n = HTTP_STATUS_FORBIDDEN;
    
    
    		m = lws_hdr_copy(wsi, b64, sizeof(b64),
    				 WSI_TOKEN_HTTP_AUTHORIZATION);
    
    Andy Green's avatar
    Andy Green committed
    		if (m < 7) {
    			lwsl_err("b64 auth too long\n");
    			goto transaction_result_n;
    		}
    
    		b64[5] = '\0';
    		if (strcasecmp(b64, "Basic")) {
    			lwsl_err("auth missing basic: %s\n", b64);
    			goto transaction_result_n;
    		}
    
    		/* It'll be like Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l */
    
    
    		m = lws_b64_decode_string(b64 + 6, plain, sizeof(plain) - 1);
    
    Andy Green's avatar
    Andy Green committed
    		if (m < 0) {
    			lwsl_err("plain auth too long\n");
    			goto transaction_result_n;
    		}
    
    
    		plain[m] = '\0';
    		pcolon = strchr(plain, ':');
    		if (!pcolon) {
    			lwsl_err("basic auth format broken\n");
    			return lws_unauthorised_basic_auth(wsi);
    		}
    
    		if (!lws_find_string_in_file(hit->basic_auth_login_file,
    					     plain, m)) {
    
    Andy Green's avatar
    Andy Green committed
    			lwsl_err("basic auth lookup failed\n");
    			return lws_unauthorised_basic_auth(wsi);
    		}
    
    
    		/*
    		 * Rewrite WSI_TOKEN_HTTP_AUTHORIZATION so it is just the
    		 * authorized username
    		 */
    
    Andy Green's avatar
    Andy Green committed
    
    
    		*pcolon = '\0';
    		wsi->http.ah->frags[fi].len = lws_ptr_diff(pcolon, plain);
    		pcolon = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);
    		strncpy(pcolon, plain, ml - 1);
    		pcolon[ml - 1] = '\0';
    		lwsl_info("%s: basic auth accepted for %s\n", __func__,
    			 lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION));
    
    Andy Green's avatar
    Andy Green committed
    	}
    
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_HTTP_PROXY)
    	/*
    	 * The mount is a reverse proxy?
    	 */
    
    
    	// lwsl_notice("%s: origin_protocol: %d\n", __func__, hit->origin_protocol);
    
    
    Andy Green's avatar
    Andy Green committed
    	if (hit->origin_protocol == LWSMPRO_HTTPS ||
    	    hit->origin_protocol == LWSMPRO_HTTP)  {
    		struct lws_client_connect_info i;
    
    		struct lws *cwsi;
    
    Andy Green's avatar
    Andy Green committed
    		char ads[96], rpath[256], *pcolon, *pslash, unix_skt = 0;
    
    Andy Green's avatar
    Andy Green committed
    		int n, na;
    
    		memset(&i, 0, sizeof(i));
    		i.context = lws_get_context(wsi);
    
    
    		if (hit->origin[0] == '+')
    			unix_skt = 1;
    
    
    Andy Green's avatar
    Andy Green committed
    		pcolon = strchr(hit->origin, ':');
    		pslash = strchr(hit->origin, '/');
    		if (!pslash) {
    
    			lwsl_err("Proxy mount origin '%s' must have /\n",
    				 hit->origin);
    
    Andy Green's avatar
    Andy Green committed
    			return -1;
    		}
    
    		if (unix_skt) {
    			if (!pcolon) {
    				lwsl_err("Proxy mount origin for unix skt must "
    					 "have address delimited by :\n");
    
    				return -1;
    			}
    			n = lws_ptr_diff(pcolon, hit->origin);
    			pslash = pcolon;
    		} else {
    			if (pcolon > pslash)
    				pcolon = NULL;
    
    			if (pcolon)
    
    Andy Green's avatar
    Andy Green committed
    				n = (int)(pcolon - hit->origin);
    
    Andy Green's avatar
    Andy Green committed
    				n = (int)(pslash - hit->origin);
    
    
    			if (n >= (int)sizeof(ads) - 2)
    				n = sizeof(ads) - 2;
    		}
    
    Andy Green's avatar
    Andy Green committed
    
    		memcpy(ads, hit->origin, n);
    		ads[n] = '\0';
    
    		i.address = ads;
    		i.port = 80;
    
    		if (hit->origin_protocol == LWSMPRO_HTTPS) {
    
    Andy Green's avatar
    Andy Green committed
    			i.port = 443;
    			i.ssl_connection = 1;
    		}
    		if (pcolon)
    			i.port = atoi(pcolon + 1);
    
    Andy Green's avatar
    Andy Green committed
    		n = lws_snprintf(rpath, sizeof(rpath) - 1, "/%s/%s",
    				   pslash + 1, uri_ptr +
    
    						   hit->mountpoint_len) - 2;
    
    Andy Green's avatar
    Andy Green committed
    		lws_clean_url(rpath);
    		na = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS);
    		if (na) {
    
    Andy Green's avatar
    Andy Green committed
    			char *p = rpath + n;
    
    			if (na >= (int)sizeof(rpath) - n - 2) {
    				lwsl_info("%s: query string %d longer "
    					  "than we can handle\n", __func__,
    					  na);
    
    				return -1;
    			}
    
    
    Andy Green's avatar
    Andy Green committed
    			*p++ = '?';
    
    Andy Green's avatar
    Andy Green committed
    				     (int)(&rpath[sizeof(rpath) - 1] - p),
    
    Andy Green's avatar
    Andy Green committed
    		i.path = rpath;
    
    		if (i.address[0] != '+' ||
    		    !lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST))
    			i.host = i.address;
    		else
    			i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);
    
    Andy Green's avatar
    Andy Green committed
    		i.origin = NULL;
    		i.method = "GET";
    
    		i.alpn = "http/1.1";
    
    Andy Green's avatar
    Andy Green committed
    		i.parent_wsi = wsi;
    
    		i.pwsi = &cwsi;
    
    	//	i.uri_replace_from = hit->origin;
    	//	i.uri_replace_to = hit->mountpoint;
    
    		lwsl_info("proxying to %s port %d url %s, ssl %d, "
    
    Andy Green's avatar
    Andy Green committed
    			    "from %s, to %s\n",
    			    i.address, i.port, i.path, i.ssl_connection,
    			    i.uri_replace_from, i.uri_replace_to);
    
    Andy Green's avatar
    Andy Green committed
    		if (!lws_client_connect_via_info(&i)) {
    			lwsl_err("proxy connect fail\n");
    
    
    			/*
    			 * ... we can't do the proxy action, but we can
    			 * cleanly return him a 503 and a description
    			 */
    
    			lws_return_http_status(wsi,
    				HTTP_STATUS_SERVICE_UNAVAILABLE,
    				"<h1>Service Temporarily Unavailable</h1>"
    				"The server is temporarily unable to service "
    				"your request due to maintenance downtime or "
    				"capacity problems. Please try again later.");
    
    
    		lwsl_info("%s: setting proxy clientside on %p (parent %p)\n",
    			  __func__, cwsi, lws_get_parent(cwsi));
    		cwsi->http.proxy_clientside = 1;
    
    
    Andy Green's avatar
    Andy Green committed
    		return 0;
    	}
    #endif
    
    
    Andy Green's avatar
    Andy Green committed
    	/*
    	 * A particular protocol callback is mounted here?
    	 *
    	 * For the duration of this http transaction, bind us to the
    	 * associated protocol
    	 */
    	if (hit->origin_protocol == LWSMPRO_CALLBACK || hit->protocol) {
    		const struct lws_protocols *pp;
    		const char *name = hit->origin;
    		if (hit->protocol)
    			name = hit->protocol;
    
    		pp = lws_vhost_name_to_protocol(wsi->vhost, name);
    		if (!pp) {
    			n = -1;
    			lwsl_err("Unable to find plugin '%s'\n",
    				 hit->origin);
    			return 1;
    
    Andy Green's avatar
    Andy Green committed
    		if (lws_bind_protocol(wsi, pp, "http action CALLBACK bind"))
    
    Andy Green's avatar
    Andy Green committed
    			return 1;
    
    		args.p = uri_ptr;
    		args.len = uri_len;
    		args.max_len = hit->auth_mask;
    		args.final = 0; /* used to signal callback dealt with it */
    
    Andy Green's avatar
    Andy Green committed
    		n = wsi->protocol->callback(wsi,
    					    LWS_CALLBACK_CHECK_ACCESS_RIGHTS,
    
    					    wsi->user_space, &args, 0);
    		if (n) {
    			lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED,
    					       NULL);
    			goto bail_nuke_ah;
    		}
    		if (args.final) /* callback completely handled it well */
    			return 0;
    
    
    Andy Green's avatar
    Andy Green committed
    		if (hit->cgienv && wsi->protocol->callback(wsi,
    				LWS_CALLBACK_HTTP_PMO,
    				wsi->user_space, (void *)hit->cgienv, 0))
    			return 1;
    
    Andy Green's avatar
    Andy Green committed
    		if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
    
    			m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
    
    Andy Green's avatar
    Andy Green committed
    					    wsi->user_space,
    					    uri_ptr + hit->mountpoint_len,
    					    uri_len - hit->mountpoint_len);
    
    			goto after;
    		}
    
    Andy Green's avatar
    Andy Green committed
    #ifdef LWS_WITH_CGI
    
    Andy Green's avatar
    Andy Green committed
    	/* did we hit something with a cgi:// origin? */
    	if (hit->origin_protocol == LWSMPRO_CGI) {
    		const char *cmd[] = {
    			NULL, /* replace with cgi path */
    			NULL
    		};
    
    Andy Green's avatar
    Andy Green committed
    		lwsl_debug("%s: cgi\n", __func__);
    		cmd[0] = hit->origin;
    
    Andy Green's avatar
    Andy Green committed
    		n = 5;
    		if (hit->cgi_timeout)
    			n = hit->cgi_timeout;
    
    		n = lws_cgi(wsi, cmd, hit->mountpoint_len, n,
    			    hit->cgienv);
    		if (n) {
    
    			lwsl_err("%s: cgi failed\n", __func__);
    
    Andy Green's avatar
    Andy Green committed
    			return -1;
    
    Andy Green's avatar
    Andy Green committed
    		goto deal_body;
    	}
    #endif
    
    	n = uri_len - lws_ptr_diff(s, uri_ptr); // (int)strlen(s);
    
    Andy Green's avatar
    Andy Green committed
    	if (s[0] == '\0' || (n == 1 && s[n - 1] == '/'))
    		s = (char *)hit->def;
    	if (!s)
    		s = "index.html";
    
    Andy Green's avatar
    Andy Green committed
    	wsi->cache_secs = hit->cache_max_age;
    	wsi->cache_reuse = hit->cache_reusable;
    	wsi->cache_revalidate = hit->cache_revalidate;
    	wsi->cache_intermediaries = hit->cache_intermediaries;
    
    	if (hit->origin_protocol == LWSMPRO_FILE)
    
    		m = lws_http_serve(wsi, s, hit->origin, hit);
    
    	if (m > 0) {
    
    Andy Green's avatar
    Andy Green committed
    		/*
    
    Andy Green's avatar
    Andy Green committed
    		 * lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
    
    Andy Green's avatar
    Andy Green committed
    		 */
    		if (hit->protocol) {
    
    Andy Green's avatar
    Andy Green committed
    			const struct lws_protocols *pp =
    					lws_vhost_name_to_protocol(
    						wsi->vhost, hit->protocol);
    
    Andy Green's avatar
    Andy Green committed
    			lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
    
    			if (lws_bind_protocol(wsi, pp, "http_action HTTP"))
    
    Andy Green's avatar
    Andy Green committed
    				return 1;
    
    			m = pp->callback(wsi, LWS_CALLBACK_HTTP,
    
    Andy Green's avatar
    Andy Green committed
    					 wsi->user_space,
    					 uri_ptr + hit->mountpoint_len,
    					 uri_len - hit->mountpoint_len);
    		} else
    
    			m = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
    
    Andy Green's avatar
    Andy Green committed
    				    wsi->user_space, uri_ptr, uri_len);
    
    		lwsl_info("LWS_CALLBACK_HTTP closing\n");
    
    		return 1;
    	}
    
    Andy Green's avatar
    Andy Green committed
    #ifdef LWS_WITH_CGI
    deal_body:
    #endif
    
    	 * If we're not issuing a file, check for content_length or
    	 * HTTP keep-alive. No keep-alive header allocation for
    
    	 * ISSUING_FILE, as this uses HTTP/1.0.
    	 *
    
    	 * In any case, return 0 and let lws_read decide how to
    
    	 * proceed based on state
    	 */
    
    	if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
    
    		/* Prepare to read body if we have a content length: */
    
    		lwsl_debug("wsi->http.rx_content_length %lld %d %d\n",
    			   (long long)wsi->http.rx_content_length,
    
    			   wsi->upgraded_to_http2, wsi->http2_substream);
    
    Andy Green's avatar
    Andy Green committed
    
    		if (wsi->http.content_length_explicitly_zero &&
    		    lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
    
    			/*
    			 * POST with an explicit content-length of zero
    			 *
    			 * If we don't give the user code the empty HTTP_BODY
    			 * callback, he may become confused to hear the
    			 * HTTP_BODY_COMPLETION (due to, eg, instantiation of
    			 * lws_spa never happened).
    			 *
    			 * HTTP_BODY_COMPLETION is responsible for sending the
    			 * result status code and result body if any, and
    			 * do the transaction complete processing.
    			 */
    			if (wsi->protocol->callback(wsi,
    					LWS_CALLBACK_HTTP_BODY,
    					wsi->user_space, NULL, 0))
    				return 1;
    			if (wsi->protocol->callback(wsi,
    					LWS_CALLBACK_HTTP_BODY_COMPLETION,
    					wsi->user_space, NULL, 0))
    				return 1;
    
    			return 0;
    		}
    
    
    		if (wsi->http.rx_content_length > 0) {
    
    			lwsi_set_state(wsi, LRS_BODY);
    			lwsl_info("%s: %p: LRS_BODY state set (0x%x)\n",
    				    __func__, wsi, wsi->wsistate);
    
    			wsi->http.rx_content_remain =
    					wsi->http.rx_content_length;
    
    
    			/*
    			 * At this point we have transitioned from deferred
    			 * action to expecting BODY on the stream wsi, if it's
    			 * in a bundle like h2.  So if the stream wsi has its
    			 * own buflist, we need to deal with that first.
    			 */
    
    			while (1) {
    
    Andy Green's avatar
    Andy Green committed
    				struct lws_tokens ebuf;
    				int m;
    
    
    				ebuf.len = (int)lws_buflist_next_segment_len(
    
    Andy Green's avatar
    Andy Green committed
    						&wsi->buflist,
    						(uint8_t **)&ebuf.token);
    
    				if (!ebuf.len)
    					break;
    
    Andy Green's avatar
    Andy Green committed
    				lwsl_notice("%s: consuming %d\n", __func__,
    							(int)ebuf.len);
    				m = lws_read_h1(wsi, (uint8_t *)ebuf.token,
    						ebuf.len);
    
    				if (lws_buflist_aware_consume(wsi, &ebuf, m, 1))
    					return -1;
    
    Andy Green's avatar
    Andy Green committed
    		}
    	}
    
    Andy Green's avatar
    Andy Green committed
    	lws_header_table_detach(wsi, 1);
    
    Andy Green's avatar
    Andy Green committed
    
    
    Andy Green's avatar
    Andy Green committed
    transaction_result_n:
    	lws_return_http_status(wsi, n, NULL);
    
    Andy Green's avatar
    Andy Green committed
    	return lws_http_transaction_completed(wsi);
    
    int
    lws_confirm_host_header(struct lws *wsi)
    {
    	struct lws_tokenize ts;
    	lws_tokenize_elem e;
    	char buf[128];
    	int port = 80;
    
    	/*
    	 * this vhost wants us to validate what the
    	 * client sent against our vhost name
    	 */
    
    	if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
    		lwsl_info("%s: missing host on upgrade\n", __func__);
    
    		return 1;
    	}
    
    #if defined(LWS_WITH_TLS)
    	if (wsi->tls.ssl)
    		port = 443;
    #endif
    
    	lws_tokenize_init(&ts, buf, LWS_TOKENIZE_F_DOT_NONTERM /* server.com */|
    				    LWS_TOKENIZE_F_NO_FLOATS /* 1.server.com */|
    				    LWS_TOKENIZE_F_MINUS_NONTERM /* a-b.com */);
    	ts.len = lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_HOST);
    	if (ts.len <= 0) {
    		lwsl_info("%s: missing or oversize host header\n", __func__);
    		return 1;
    	}
    
    	if (lws_tokenize(&ts) != LWS_TOKZE_TOKEN)
    		goto bad_format;
    
    	if (strncmp(ts.token, wsi->vhost->name, ts.token_len)) {
    		buf[(ts.token - buf) + ts.token_len] = '\0';
    		lwsl_info("%s: '%s' in host hdr but vhost name %s\n",
    			  __func__, ts.token, wsi->vhost->name);
    		return 1;
    	}
    
    	e = lws_tokenize(&ts);
    	if (e == LWS_TOKZE_DELIMITER && ts.token[0] == ':') {
    		if (lws_tokenize(&ts) != LWS_TOKZE_INTEGER)
    			goto bad_format;
    		else
    			port = atoi(ts.token);
    	} else
    		if (e != LWS_TOKZE_ENDED)
    			goto bad_format;
    
    	if (wsi->vhost->listen_port != port) {
    		lwsl_info("%s: host port %d mismatches vhost port %d\n",
    			  __func__, port, wsi->vhost->listen_port);
    		return 1;
    	}
    
    	lwsl_debug("%s: host header OK\n", __func__);
    
    	return 0;
    
    bad_format:
    	lwsl_info("%s: bad host header format\n", __func__);
    
    	return 1;
    }
    
    
    int
    lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
    
    Andy Green's avatar
    Andy Green committed
    	struct lws_context *context = lws_get_context(wsi);
    
    	unsigned char *obuf = *buf;
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_HTTP2)
    	char tbuf[128], *p;
    #endif
    
    	size_t olen = len;
    
    Andy Green's avatar
    Andy Green committed
    	int n = 0, m, i;
    
    Andy Green's avatar
    Andy Green committed
    	if (len >= 10000000) {
    		lwsl_err("%s: assert: len %ld\n", __func__, (long)len);
    		assert(0);
    	}
    
    
    Andy Green's avatar
    Andy Green committed
    		lwsl_err("%s: assert: NULL ah\n", __func__);
    		assert(0);
    	}
    
    	while (len) {
    
    Andy Green's avatar
    Andy Green committed
    		if (!lwsi_role_server(wsi) || !lwsi_role_http(wsi)) {
    
    			lwsl_err("%s: bad wsi role 0x%x\n", __func__,
    					lwsi_role(wsi));
    
    Andy Green's avatar
    Andy Green committed
    			goto bail_nuke_ah;
    		}
    
    		i = (int)len;
    		m = lws_parse(wsi, *buf, &i);
    
    Andy Green's avatar
    Andy Green committed
    		lwsl_info("%s: parsed count %d\n", __func__, (int)len - i);
    
    		(*buf) += (int)len - i;
    
    Andy Green's avatar
    Andy Green committed
    		len = i;
    
    		if (m) {
    			if (m == 2) {
    				/*
    				 * we are transitioning from http with
    				 * an AH, to raw.  Drop the ah and set
    				 * the mode.
    				 */
    raw_transition:
    				lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
    				lws_bind_protocol(wsi, &wsi->vhost->protocols[
    				                        wsi->vhost->
    
    Andy Green's avatar
    Andy Green committed
    				                        raw_protocol_index],
    						__func__);
    
    				lwsl_info("transition to raw vh %s prot %d\n",
    					  wsi->vhost->name,
    					  wsi->vhost->raw_protocol_index);
    				if ((wsi->protocol->callback)(wsi,
    						LWS_CALLBACK_RAW_ADOPT,
    						wsi->user_space, NULL, 0))
    					goto bail_nuke_ah;
    
    
    Andy Green's avatar
    Andy Green committed
    				lws_role_transition(wsi, 0, LRS_ESTABLISHED,
    						    &role_ops_raw_skt);
    
    				lws_header_table_detach(wsi, 1);
    
    
    Andy Green's avatar
    Andy Green committed
    				if (wsi->protocol->callback(wsi,
    
    						LWS_CALLBACK_RAW_RX,
    						wsi->user_space, obuf, olen))
    					return 1;
    
    				return 0;
    			}
    
    			lwsl_info("lws_parse failed\n");
    
    		if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE)
    
    		lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
    
    		if (wsi->vhost->listen_port &&
    		    lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
    
    			struct lws_vhost *vhost = lws_select_vhost(
    				context, wsi->vhost->listen_port,
    				lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
    
    			if (vhost)
    
    				lws_vhost_bind_wsi(vhost, wsi);
    
    		} else
    			lwsl_info("no host\n");
    
    
    Andy Green's avatar
    Andy Green committed
    		if (!lwsi_role_h2(wsi) || !lwsi_role_server(wsi)) {
    
    Andy Green's avatar
    Andy Green committed
    			wsi->vhost->conn_stats.h1_trans++;
    			if (!wsi->conn_stat_done) {
    				wsi->vhost->conn_stats.h1_conn++;
    				wsi->conn_stat_done = 1;
    			}
    
    		/* check for unwelcome guests */
    
    		if (wsi->context->reject_service_keywords) {
    			const struct lws_protocol_vhost_options *rej =
    					wsi->context->reject_service_keywords;
    			char ua[384], *msg = NULL;
    
    			if (lws_hdr_copy(wsi, ua, sizeof(ua) - 1,
    
    					 WSI_TOKEN_HTTP_USER_AGENT) > 0) {
    #ifdef LWS_WITH_ACCESS_LOG
    
    Andy Green's avatar
    Andy Green committed
    				char *uri_ptr = NULL;
    				int meth, uri_len;
    
    Andy Green's avatar
    Andy Green committed
    				ua[sizeof(ua) - 1] = '\0';
    				while (rej) {
    					if (!strstr(ua, rej->name)) {
    						rej = rej->next;
    						continue;
    					}
    
    Andy Green's avatar
    Andy Green committed
    					msg = strchr(rej->value, ' ');
    					if (msg)
    						msg++;
    					lws_return_http_status(wsi,
    						atoi(rej->value), msg);
    
    #ifdef LWS_WITH_ACCESS_LOG
    
    Andy Green's avatar
    Andy Green committed
    					meth = lws_http_get_uri_and_method(wsi,
    							&uri_ptr, &uri_len);
    					if (meth >= 0)
    						lws_prepare_access_log_info(wsi,
    
    							uri_ptr, uri_len, meth);
    
    Andy Green's avatar
    Andy Green committed
    					/* wsi close will do the log */
    
    Andy Green's avatar
    Andy Green committed
    					wsi->vhost->conn_stats.rejected++;
    					/*
    					 * We don't want anything from
    					 * this rejected guy.  Follow
    					 * the close flow, not the
    					 * transaction complete flow.
    					 */
    					goto bail_nuke_ah;
    
    		if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECT)) {
    			lwsl_info("Changing to RAW mode\n");
    
    			m = 0;
    			goto raw_transition;
    
    		lwsi_set_state(wsi, LRS_PRE_WS_SERVING_ACCEPT);
    
    		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
    
    		if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
    
    
    			const char *up = lws_hdr_simple_ptr(wsi,
    							    WSI_TOKEN_UPGRADE);
    
    			if (strcasecmp(up, "websocket") &&
    			    strcasecmp(up, "h2c")) {
    				lwsl_info("Unknown upgrade '%s'\n", up);
    
    				if (lws_return_http_status(wsi,
    						HTTP_STATUS_FORBIDDEN, NULL) ||
    				    lws_http_transaction_completed(wsi))
    					goto bail_nuke_ah;
    			}
    
    			n = user_callback_handle_rxflow(wsi->protocol->callback,
    					wsi, LWS_CALLBACK_HTTP_CONFIRM_UPGRADE,
    					wsi->user_space, (char *)up, 0);
    
    			/* just hang up? */
    
    			if (n < 0)
    				goto bail_nuke_ah;
    
    			/* callback returned headers already, do t_c? */
    
    			if (n > 0) {
    				if (lws_http_transaction_completed(wsi))
    					goto bail_nuke_ah;
    
    				/* continue on */
    
    				return 0;
    			}
    
    			/* callback said 0, it was allowed */
    
    
    			if (wsi->vhost->options &
    			    LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK &&
    			    lws_confirm_host_header(wsi))
    				goto bail_nuke_ah;
    
    
    			if (!strcasecmp(up, "websocket")) {
    
    #if defined(LWS_ROLE_WS)
    
    Andy Green's avatar
    Andy Green committed
    				wsi->vhost->conn_stats.ws_upg++;
    
    				lwsl_info("Upgrade to ws\n");
    				goto upgrade_ws;
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_HTTP2)
    
    			if (!strcasecmp(up, "h2c")) {
    
    Andy Green's avatar
    Andy Green committed
    				wsi->vhost->conn_stats.h2_upg++;
    
    Andy Green's avatar
    Andy Green committed
    				lwsl_info("Upgrade to h2c\n");
    
    				goto upgrade_h2c;
    			}
    #endif
    		}
    
    		/* no upgrade ack... he remained as HTTP */
    
    Andy Green's avatar
    Andy Green committed
    		lwsl_info("%s: %p: No upgrade\n", __func__, wsi);
    
    		lwsi_set_state(wsi, LRS_ESTABLISHED);
    
    		wsi->http.fop_fd = NULL;
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
    		lws_http_compression_validate(wsi);
    #endif
    
    
    Andy Green's avatar
    Andy Green committed
    		lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
    
    		n = lws_http_action(wsi);
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_HTTP2)
    
    upgrade_h2c:
    		if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
    
    			lwsl_info("missing http2_settings\n");
    
    		lwsl_info("h2c upgrade...\n");
    
    		p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
    		/* convert the peer's HTTP-Settings */
    
    Andy Green's avatar
    Andy Green committed
    		n = lws_b64_decode_string(p, tbuf, sizeof(tbuf));
    
    		if (n < 0) {
    			lwsl_parser("HTTP2_SETTINGS too long\n");
    			return 1;
    		}
    
    		/* adopt the header info */
    
    
    		if (!wsi->h2.h2n) {
    			wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n),
    
    Andy Green's avatar
    Andy Green committed
    						   "h2n");
    
    			if (!wsi->h2.h2n)
    
    Andy Green's avatar
    Andy Green committed
    				return 1;
    		}
    
    		lws_h2_init(wsi);
    
    		/* HTTP2 union */
    
    Andy Green's avatar
    Andy Green committed
    		lws_h2_settings(wsi, &wsi->h2.h2n->set, (unsigned char *)tbuf, n);
    
    		lws_hpack_dynamic_size(wsi, wsi->h2.h2n->set.s[
    
    		                                      H2SET_HEADER_TABLE_SIZE]);
    
    Andy Green's avatar
    Andy Green committed
    
    
    Andy Green's avatar
    Andy Green committed
    		strcpy(tbuf, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
    			      "Connection: Upgrade\x0d\x0a"
    			      "Upgrade: h2c\x0d\x0a\x0d\x0a");
    		m = (int)strlen(tbuf);
    		n = lws_issue_raw(wsi, (unsigned char *)tbuf, m);
    		if (n != m) {
    
    			lwsl_debug("http2 switch: ERROR writing to socket\n");
    			return 1;
    		}
    
    		lwsi_set_state(wsi, LRS_H2_AWAIT_PREFACE);
    
    Andy Green's avatar
    Andy Green committed
    		wsi->upgraded_to_http2 = 1;
    
    #if defined(LWS_ROLE_WS)
    
    upgrade_ws:
    
    Andy Green's avatar
    Andy Green committed
    		if (lws_process_ws_upgrade(wsi))
    
    	} /* while all chars are handled */
    
    	return 0;
    
    bail_nuke_ah:
    	/* drop the header info */
    
    Andy Green's avatar
    Andy Green committed
    	lws_header_table_detach(wsi, 1);
    
    Andy Green's avatar
    Andy Green committed
    
    
    LWS_VISIBLE int LWS_WARN_UNUSED_RESULT
    lws_http_transaction_completed(struct lws *wsi)
    
    Andy Green's avatar
    Andy Green committed
    {
    
    Andy Green's avatar
    Andy Green committed
    	int n = NO_PENDING_TIMEOUT;
    
    
    Andy Green's avatar
    Andy Green committed
    	if (lws_has_buffered_out(wsi)
    #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
    			|| wsi->http.comp_ctx.buflist_comp ||
    	    wsi->http.comp_ctx.may_have_more
    #endif
    	) {
    
    		/*
    		 * ...so he tried to send something large as the http reply,
    		 * it went as a partial, but he immediately said the
    		 * transaction was completed.
    		 *
    		 * Defer the transaction completed until the last part of the
    		 * partial is sent.
    		 */
    
    Andy Green's avatar
    Andy Green committed
    		lwsl_debug("%s: %p: deferring due to partial\n", __func__, wsi);
    
    		wsi->http.deferred_transaction_completed = 1;
    
    Andy Green's avatar
    Andy Green committed
    		lws_callback_on_writable(wsi);
    
    	lwsl_info("%s: wsi %p\n", __func__, wsi);
    
    Andy Green's avatar
    Andy Green committed
    #if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)
    	lws_http_compression_destroy(wsi);
    #endif
    
    	lws_access_log(wsi);
    
    
    	if (!wsi->hdr_parsing_completed) {
    
    		char peer[64];
    		lws_get_peer_simple(wsi, peer, sizeof(peer) - 1);
    		peer[sizeof(peer) - 1] = '\0';
    		lwsl_notice("%s: (from %s) ignoring, ah parsing incomplete\n",
    				__func__, peer);
    
    Andy Green's avatar
    Andy Green committed
    	/* if we can't go back to accept new headers, drop the connection */
    
    Andy Green's avatar
    Andy Green committed
    	if (wsi->http2_substream)
    
    		return 1;
    
    Andy Green's avatar
    Andy Green committed
    
    	if (wsi->seen_zero_length_recv)
    		return 1;
    
    
    Andy Green's avatar
    Andy Green committed
    	if (wsi->http.conn_type != HTTP_CONNECTION_KEEP_ALIVE) {
    
    		lwsl_info("%s: %p: close connection\n", __func__, wsi);
    
    Andy Green's avatar
    Andy Green committed
    		return 1;
    	}
    
    
    Andy Green's avatar
    Andy Green committed
    	if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0], __func__))
    
    Andy Green's avatar
    Andy Green committed
    		return 1;
    
    	/*
    	 * otherwise set ourselves up ready to go again, but because we have no
    	 * idea about the wsi writability, we make put it in a holding state
    	 * until we can verify POLLOUT.  The part of this that confirms POLLOUT
    	 * with no partials is in lws_server_socket_service() below.
    	 */
    
    Andy Green's avatar
    Andy Green committed
    	lwsl_debug("%s: %p: setting DEF_ACT from 0x%x\n", __func__,
    		   wsi, wsi->wsistate);
    
    	lwsi_set_state(wsi, LRS_DEFERRING_ACTION);
    
    	wsi->http.tx_content_length = 0;
    	wsi->http.tx_content_remain = 0;
    
    	wsi->hdr_parsing_completed = 0;
    
    Andy Green's avatar
    Andy Green committed
    	wsi->sending_chunked = 0;
    
    Andy Green's avatar
    Andy Green committed
    #ifdef LWS_WITH_ACCESS_LOG
    
    	wsi->http.access_log.sent = 0;
    
    Andy Green's avatar
    Andy Green committed
    #endif
    
    Andy Green's avatar
    Andy Green committed
    
    	if (wsi->vhost->keepalive_timeout)
    		n = PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE;
    	lws_set_timeout(wsi, n, wsi->vhost->keepalive_timeout);
    
    	/*
    	 * We already know we are on http1.1 / keepalive and the next thing
    	 * coming will be another header set.
    	 *
    	 * If there is no pending rx and we still have the ah, drop it and
    	 * reacquire a new ah when the new headers start to arrive.  (Otherwise
    	 * we needlessly hog an ah indefinitely.)
    	 *
    	 * However if there is pending rx and we know from the keepalive state