示例#1
0
int
libwebsocket_read(struct libwebsocket_context *context, struct libwebsocket *wsi,
                        unsigned char * buf, size_t len)
{
    size_t n;

    switch (wsi->state) {
    case WSI_STATE_HTTP:
        wsi->state = WSI_STATE_HTTP_HEADERS;
        wsi->parser_state = WSI_TOKEN_NAME_PART;
        /* fallthru */
    case WSI_STATE_HTTP_HEADERS:

        debug("issuing %d bytes to parser\n", (int)len);
#ifdef DEBUG
        fwrite(buf, 1, len, stderr);
#endif

        switch (wsi->mode) {
        case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
        case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
        case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
        case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
        case LWS_CONNMODE_WS_CLIENT:
            for (n = 0; n < len; n++)
                libwebsocket_client_rx_sm(wsi, *buf++);

            return 0;
        default:
            break;
        }

        /* LWS_CONNMODE_WS_SERVING */

        for (n = 0; n < len; n++)
            libwebsocket_parse(wsi, *buf++);

        if (wsi->parser_state != WSI_PARSING_COMPLETE)
            break;

        fprintf(stderr, "seem to be serving, mode is %d\n", wsi->mode);

        fprintf(stderr, "libwebsocket_parse sees parsing complete\n");

        /* is this websocket protocol or normal http 1.0? */

        if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
                 !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len) {
            if (wsi->protocol->callback)
                (wsi->protocol->callback)(context, wsi,
                   LWS_CALLBACK_HTTP, wsi->user_space,
                   wsi->utf8_token[WSI_TOKEN_GET_URI].token, 0);
            wsi->state = WSI_STATE_HTTP;
            return 0;
        }

        if (!wsi->protocol) {
            fprintf(stderr, "NULL protocol coming on libwebsocket_read\n");
        }

        /*
         * It's websocket
         *
         * Make sure user side is happy about protocol
         */

        while (wsi->protocol->callback) {

            if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL) {
                if (wsi->protocol->name == NULL)
                    break;
            } else
                if (strcmp(
                     wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
                              wsi->protocol->name) == 0)
                    break;

            wsi->protocol++;
        }

        /* we didn't find a protocol he wanted? */

        if (wsi->protocol->callback == NULL) {
            if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL)
                fprintf(stderr, "[no protocol] "
                    "not supported (use NULL .name)\n");
            else
                fprintf(stderr, "Requested protocol %s "
                        "not supported\n",
                     wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
            goto bail;
        }

        /*
         * find out which spec version the client is using
         * if this header is not given, we default to 00 (aka 76)
         */

        if (wsi->utf8_token[WSI_TOKEN_VERSION].token_len)
            wsi->ietf_spec_revision =
                 atoi(wsi->utf8_token[WSI_TOKEN_VERSION].token);

        /*
         * Give the user code a chance to study the request and
         * have the opportunity to deny it
         */

        if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
                LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
                        &wsi->utf8_token[0], NULL, 0)) {
            fprintf(stderr, "User code denied connection\n");
            goto bail;
        }


        /*
         * Perform the handshake according to the protocol version the
         * client announced
         */

        switch (wsi->ietf_spec_revision) {
        case 0: /* applies to 76 and 00 */
            wsi->xor_mask = xor_no_mask;
            if (handshake_00(context, wsi))
                goto bail;
            break;
        case 4: /* 04 */
            wsi->xor_mask = xor_mask_04;
            debug("libwebsocket_parse calling handshake_04\n");
            if (handshake_0405(context, wsi))
                goto bail;
            break;
        case 5:
        case 6:
        case 7:
        case 8:
        case 13:
            wsi->xor_mask = xor_mask_05;
            debug("libwebsocket_parse calling handshake_04\n");
            if (handshake_0405(context, wsi))
                goto bail;
            break;

        default:
            fprintf(stderr, "Unknown client spec version %d\n",
                               wsi->ietf_spec_revision);
            goto bail;
        }

        fprintf(stderr, "accepted v%02d connection\n",
                               wsi->ietf_spec_revision);

        break;

    case WSI_STATE_AWAITING_CLOSE_ACK:
    case WSI_STATE_ESTABLISHED:
        switch (wsi->mode) {
        case LWS_CONNMODE_WS_CLIENT:
            for (n = 0; n < len; n++)
                if (libwebsocket_client_rx_sm(wsi, *buf++) < 0)
                    goto bail;

            return 0;
        default:
            break;
        }

        /* LWS_CONNMODE_WS_SERVING */

        if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0)
            goto bail;

        break;
    default:
        break;
    }

    return 0;

bail:
    libwebsocket_close_and_free_session(context, wsi,
                             LWS_CLOSE_STATUS_NOSTATUS);

    return -1;
}
示例#2
0
int lws_context_init_server(struct lws_context_creation_info *info,
			    struct lws_context *context)
{
#ifdef LWS_USE_IPV6
	struct sockaddr_in6 serv_addr6;
#endif
#if LWS_POSIX
	struct sockaddr_in serv_addr4;
	socklen_t len = sizeof(struct sockaddr);
	struct sockaddr_in sin;
	struct sockaddr *v;
	int n, opt = 1;
#endif
	lws_sockfd_type sockfd;
	struct lws *wsi;

	/* set up our external listening socket we serve on */

	if (info->port == CONTEXT_PORT_NO_LISTEN)
		return 0;

#if LWS_POSIX
#ifdef LWS_USE_IPV6
	if (LWS_IPV6_ENABLED(context))
		sockfd = socket(AF_INET6, SOCK_STREAM, 0);
	else
#endif
		sockfd = socket(AF_INET, SOCK_STREAM, 0);

	if (sockfd == -1) {
#else
	sockfd = mbed3_create_tcp_stream_socket();
	if (!lws_sockfd_valid(sockfd)) {
#endif
		lwsl_err("ERROR opening socket\n");
		return 1;
	}

#if LWS_POSIX
	/*
	 * allow us to restart even if old sockets in TIME_WAIT
	 */
	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
		       (const void *)&opt, sizeof(opt)) < 0) {
		compatible_close(sockfd);
		return 1;
	}
#endif
	lws_plat_set_socket_options(context, sockfd);

#if LWS_POSIX
#ifdef LWS_USE_IPV6
	if (LWS_IPV6_ENABLED(context)) {
		v = (struct sockaddr *)&serv_addr6;
		n = sizeof(struct sockaddr_in6);
		bzero((char *) &serv_addr6, sizeof(serv_addr6));
		serv_addr6.sin6_addr = in6addr_any;
		serv_addr6.sin6_family = AF_INET6;
		serv_addr6.sin6_port = htons(info->port);
	} else
#endif
	{
		v = (struct sockaddr *)&serv_addr4;
		n = sizeof(serv_addr4);
		bzero((char *) &serv_addr4, sizeof(serv_addr4));
		serv_addr4.sin_addr.s_addr = INADDR_ANY;
		serv_addr4.sin_family = AF_INET;

		if (info->iface && interface_to_sa(context, info->iface,
				   (struct sockaddr_in *)v, n) < 0) {
			lwsl_err("Unable to find interface %s\n", info->iface);
			goto bail;
		}

		serv_addr4.sin_port = htons(info->port);
	} /* ipv4 */

	n = bind(sockfd, v, n);
	if (n < 0) {
		lwsl_err("ERROR on binding to port %d (%d %d)\n",
					      info->port, n, LWS_ERRNO);
		goto bail;
	}

	if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1)
		lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO));
	else
		info->port = ntohs(sin.sin_port);
#endif
	context->listen_port = info->port;

	wsi = lws_zalloc(sizeof(struct lws));
	if (wsi == NULL) {
		lwsl_err("Out of mem\n");
		goto bail;
	}
	wsi->context = context;
	wsi->sock = sockfd;
	wsi->mode = LWSCM_SERVER_LISTENER;
	wsi->protocol = context->protocols;

	context->wsi_listening = wsi;
	if (insert_wsi_socket_into_fds(context, wsi))
		goto bail;

	context->lserv_mod = LWS_lserv_mod;
	context->lserv_count = 0;
	context->lserv_fd = sockfd;

#if LWS_POSIX
	listen(sockfd, LWS_SOMAXCONN);
#else
	mbed3_tcp_stream_bind(sockfd, info->port, wsi);
#endif
	lwsl_notice(" Listening on port %d\n", info->port);

	return 0;

bail:
	compatible_close(sockfd);

	return 1;
}

int
_lws_server_listen_accept_flow_control(struct lws_context *context, int on)
{
	struct lws *wsi = context->wsi_listening;
	int n;

	if (!wsi)
		return 0;

	lwsl_debug("%s: wsi %p: state %d\n", __func__, (void *)wsi, on);

	if (on)
		n = lws_change_pollfd(wsi, 0, LWS_POLLIN);
	else
		n = lws_change_pollfd(wsi, LWS_POLLIN, 0);

	return n;
}

int lws_http_action(struct lws *wsi)
{
	enum http_connection_type connection_type;
	enum http_version request_version;
	char content_length_str[32];
	unsigned int n, count = 0;
	char http_version_str[10];
	char http_conn_str[20];
	int http_version_len;
	char *uri_ptr = NULL;
	int uri_len = 0;

	static const unsigned char methods[] = {
		WSI_TOKEN_GET_URI,
		WSI_TOKEN_POST_URI,
		WSI_TOKEN_OPTIONS_URI,
		WSI_TOKEN_PUT_URI,
		WSI_TOKEN_PATCH_URI,
		WSI_TOKEN_DELETE_URI,
#ifdef LWS_USE_HTTP2
		WSI_TOKEN_HTTP_COLON_PATH,
#endif
	};
#ifdef _DEBUG
	static const char * const method_names[] = {
		"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE",
#ifdef LWS_USE_HTTP2
		":path",
#endif
	};
#endif

	/* it's not websocket.... shall we accept it as http? */

	for (n = 0; n < ARRAY_SIZE(methods); n++)
		if (lws_hdr_total_length(wsi, methods[n]))
			count++;
	if (!count) {
		lwsl_warn("Missing URI in HTTP request\n");
		goto bail_nuke_ah;
	}

	if (count != 1) {
		lwsl_warn("multiple methods?\n");
		goto bail_nuke_ah;
	}

	if (lws_ensure_user_space(wsi))
		goto bail_nuke_ah;

	for (n = 0; n < ARRAY_SIZE(methods); n++)
		if (lws_hdr_total_length(wsi, methods[n])) {
			uri_ptr = lws_hdr_simple_ptr(wsi, methods[n]);
			uri_len = lws_hdr_total_length(wsi, methods[n]);
			lwsl_info("Method: %s request for '%s'\n",
				  	method_names[n], uri_ptr);
			break;
		}

	/* HTTP header had a content length? */

	wsi->u.http.content_length = 0;
	if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
		lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
		lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI))
		wsi->u.http.content_length = 100 * 1024 * 1024;

	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
		lws_hdr_copy(wsi, content_length_str,
			     sizeof(content_length_str) - 1,
			     WSI_TOKEN_HTTP_CONTENT_LENGTH);
		wsi->u.http.content_length = atoi(content_length_str);
	}

	/* http_version? Default to 1.0, override with token: */
	request_version = HTTP_VERSION_1_0;

	/* Works for single digit HTTP versions. : */
	http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
	if (http_version_len > 7) {
		lws_hdr_copy(wsi, http_version_str,
				sizeof(http_version_str) - 1, WSI_TOKEN_HTTP);
		if (http_version_str[5] == '1' && http_version_str[7] == '1')
			request_version = HTTP_VERSION_1_1;
	}
	wsi->u.http.request_version = request_version;

	/* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
	if (request_version == HTTP_VERSION_1_1)
		connection_type = HTTP_CONNECTION_KEEP_ALIVE;
	else
		connection_type = HTTP_CONNECTION_CLOSE;

	/* Override default if http "Connection:" header: */
	if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
		lws_hdr_copy(wsi, http_conn_str, sizeof(http_conn_str) - 1,
			     WSI_TOKEN_CONNECTION);
		http_conn_str[sizeof(http_conn_str) - 1] = '\0';
		if (!strcasecmp(http_conn_str, "keep-alive"))
			connection_type = HTTP_CONNECTION_KEEP_ALIVE;
		else
			if (!strcasecmp(http_conn_str, "close"))
				connection_type = HTTP_CONNECTION_CLOSE;
	}
	wsi->u.http.connection_type = connection_type;

	n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION,
				    wsi->user_space, uri_ptr, uri_len);

	if (!n) {
		/*
		 * if there is content supposed to be coming,
		 * put a timeout on it having arrived
		 */
		lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
				AWAITING_TIMEOUT);

		n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
					    wsi->user_space, uri_ptr, uri_len);
	}

	/* now drop the header info we kept a pointer to */
	lws_free_header_table(wsi);

	if (n) {
		lwsl_info("LWS_CALLBACK_HTTP closing\n");
		return 1; /* struct ah ptr already nuked */		}

	/*
	 * 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 (wsi->state != LWSS_HTTP_ISSUING_FILE)
		/* Prepare to read body if we have a content length: */
		if (wsi->u.http.content_length > 0)
			wsi->state = LWSS_HTTP_BODY;

	return 0;

bail_nuke_ah:
	lws_free_header_table(wsi);

	return 1;
}


int lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
{
	struct lws_context *context = lws_get_context(wsi);
	struct allocated_headers *ah;
	int protocol_len, n, hit;
	char protocol_list[128];
	char protocol_name[32];
	char *p;

	/* LWSCM_WS_SERVING */

	while (len--) {
		if (lws_parse(wsi, *(*buf)++)) {
			lwsl_info("lws_parse failed\n");
			goto bail_nuke_ah;
		}

		if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
			continue;

		lwsl_parser("lws_parse sees parsing complete\n");

		wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT;
		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

		/* is this websocket protocol or normal http 1.0? */

		if (!lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) ||
		    !lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {

			ah = wsi->u.hdr.ah;

			lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED);
			wsi->state = LWSS_HTTP;
			wsi->u.http.fd = LWS_INVALID_FILE;

			/* expose it at the same offset as u.hdr */
			wsi->u.http.ah = ah;
			lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi, (void *)wsi->u.hdr.ah);

			n = lws_http_action(wsi);

			return n;
		}

		if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
				"websocket"))
			goto upgrade_ws;
#ifdef LWS_USE_HTTP2
		if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
								"h2c-14"))
			goto upgrade_h2c;
#endif
		/* dunno what he wanted to upgrade to */
		goto bail_nuke_ah;

#ifdef LWS_USE_HTTP2
upgrade_h2c:
		if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
			lwsl_err("missing http2_settings\n");
			goto bail_nuke_ah;
		}

		lwsl_err("h2c upgrade...\n");

		p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
		/* convert the peer's HTTP-Settings */
		n = lws_b64_decode_string(p, protocol_list,
					  sizeof(protocol_list));
		if (n < 0) {
			lwsl_parser("HTTP2_SETTINGS too long\n");
			return 1;
		}

		/* adopt the header info */

		ah = wsi->u.hdr.ah;

		lws_union_transition(wsi, LWSCM_HTTP2_SERVING);

		/* http2 union member has http union struct at start */
		wsi->u.http.ah = ah;

		lws_http2_init(&wsi->u.http2.peer_settings);
		lws_http2_init(&wsi->u.http2.my_settings);

		/* HTTP2 union */

		lws_http2_interpret_settings_payload(&wsi->u.http2.peer_settings,
				(unsigned char *)protocol_list, n);

		strcpy(protocol_list,
		       "HTTP/1.1 101 Switching Protocols\x0d\x0a"
		      "Connection: Upgrade\x0d\x0a"
		      "Upgrade: h2c\x0d\x0a\x0d\x0a");
		n = lws_issue_raw(wsi, (unsigned char *)protocol_list,
					strlen(protocol_list));
		if (n != strlen(protocol_list)) {
			lwsl_debug("http2 switch: ERROR writing to socket\n");
			return 1;
		}

		wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE;

		return 0;
#endif

upgrade_ws:
		if (!wsi->protocol)
			lwsl_err("NULL protocol at lws_read\n");

		/*
		 * It's websocket
		 *
		 * Select the first protocol we support from the list
		 * the client sent us.
		 *
		 * Copy it to remove header fragmentation
		 */

		if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
				 WSI_TOKEN_PROTOCOL) < 0) {
			lwsl_err("protocol list too long");
			goto bail_nuke_ah;
		}

		protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
		protocol_list[protocol_len] = '\0';
		p = protocol_list;
		hit = 0;

		while (*p && !hit) {
			unsigned int n = 0;
			while (n < sizeof(protocol_name) - 1 && *p && *p !=',')
				protocol_name[n++] = *p++;
			protocol_name[n] = '\0';
			if (*p)
				p++;

			lwsl_info("checking %s\n", protocol_name);

			n = 0;
			while (context->protocols[n].callback) {
				if (context->protocols[n].name &&
				    !strcmp(context->protocols[n].name,
					    protocol_name)) {
					lwsl_info("prot match %d\n", n);
					wsi->protocol = &context->protocols[n];
					hit = 1;
					break;
				}

				n++;
			}
		}

		/* we didn't find a protocol he wanted? */

		if (!hit) {
			if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
				lwsl_err("No protocol from \"%s\" supported\n",
					 protocol_list);
				goto bail_nuke_ah;
			}
			/*
			 * some clients only have one protocol and
			 * do not sent the protocol list header...
			 * allow it and match to protocol 0
			 */
			lwsl_info("defaulting to prot 0 handler\n");
			wsi->protocol = &context->protocols[0];
		}

		/* allocate wsi->user storage */
		if (lws_ensure_user_space(wsi))
			goto bail_nuke_ah;

		/*
		 * Give the user code a chance to study the request and
		 * have the opportunity to deny it
		 */

		if ((wsi->protocol->callback)(wsi,
				LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
				wsi->user_space,
			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
			lwsl_warn("User code denied connection\n");
			goto bail_nuke_ah;
		}

		/*
		 * Perform the handshake according to the protocol version the
		 * client announced
		 */

		switch (wsi->ietf_spec_revision) {
		case 13:
			lwsl_parser("lws_parse calling handshake_04\n");
			if (handshake_0405(context, wsi)) {
				lwsl_info("hs0405 has failed the connection\n");
				goto bail_nuke_ah;
			}
			break;

		default:
			lwsl_warn("Unknown client spec version %d\n",
						       wsi->ietf_spec_revision);
			goto bail_nuke_ah;
		}

		/* drop the header info -- no bail_nuke_ah after this */
		lws_free_header_table(wsi);

		lws_union_transition(wsi, LWSCM_WS_SERVING);

		/*
		 * create the frame buffer for this connection according to the
		 * size mentioned in the protocol definition.  If 0 there, use
		 * a big default for compatibility
		 */

		n = wsi->protocol->rx_buffer_size;
		if (!n)
			n = LWS_MAX_SOCKET_IO_BUF;
		n += LWS_PRE;
		wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */);
		if (!wsi->u.ws.rx_ubuf) {
			lwsl_err("Out of Mem allocating rx buffer %d\n", n);
			return 1;
		}
		lwsl_info("Allocating RX buffer %d\n", n);
#if LWS_POSIX
		if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF,
			       (const char *)&n, sizeof n)) {
			lwsl_warn("Failed to set SNDBUF to %d", n);
			return 1;
		}
#endif
		lwsl_parser("accepted v%02d connection\n",
						       wsi->ietf_spec_revision);
	} /* while all chars are handled */

	return 0;

bail_nuke_ah:
	/* drop the header info */
	lws_free_header_table(wsi);
	return 1;
}

struct lws *
lws_create_new_server_wsi(struct lws_context *context)
{
	struct lws *new_wsi;

	new_wsi = lws_zalloc(sizeof(struct lws));
	if (new_wsi == NULL) {
		lwsl_err("Out of memory for new connection\n");
		return NULL;
	}

	new_wsi->context = context;
	new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
	new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;

	/* intialize the instance struct */

	new_wsi->state = LWSS_HTTP;
	new_wsi->mode = LWSCM_HTTP_SERVING;
	new_wsi->hdr_parsing_completed = 0;

#ifdef LWS_OPENSSL_SUPPORT
	new_wsi->use_ssl = LWS_SSL_ENABLED(context);
#endif

	if (lws_allocate_header_table(new_wsi)) {
		lws_free(new_wsi);
		return NULL;
	}

	/*
	 * these can only be set once the protocol is known
	 * we set an unestablished connection's protocol pointer
	 * to the start of the supported list, so it can look
	 * for matching ones during the handshake
	 */
	new_wsi->protocol = context->protocols;
	new_wsi->user_space = NULL;
	new_wsi->ietf_spec_revision = 0;
	new_wsi->sock = LWS_SOCK_INVALID;

	/*
	 * outermost create notification for wsi
	 * no user_space because no protocol selection
	 */
	context->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE, NULL,
				       NULL, 0);

	return new_wsi;
}

/**
 * lws_http_transaction_completed() - wait for new http transaction or close
 * @wsi:	websocket connection
 *
 *	Returns 1 if the HTTP connection must close now
 *	Returns 0 and resets connection to wait for new HTTP header /
 *	  transaction if possible
 */

LWS_VISIBLE
int lws_http_transaction_completed(struct lws *wsi)
{
	lwsl_debug("%s: wsi %p\n", __func__, wsi);
	/* if we can't go back to accept new headers, drop the connection */
	if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) {
		lwsl_info("%s: close connection\n", __func__);
		return 1;
	}

	/* otherwise set ourselves up ready to go again */
	wsi->state = LWSS_HTTP;
	wsi->mode = LWSCM_HTTP_SERVING;
	wsi->u.http.content_length = 0;

	/* He asked for it to stay alive indefinitely */
	lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

	if (lws_allocate_header_table(wsi))
		return 1;

	/* If we're (re)starting on headers, need other implied init */
	wsi->u.hdr.ues = URIES_IDLE;

	lwsl_info("%s: keep-alive await new transaction\n", __func__);

	return 0;
}
示例#3
0
int lws_context_init_server(struct lws_context_creation_info *info,
			    struct lws_context *context)
{
#ifdef LWS_USE_IPV6
	struct sockaddr_in6 serv_addr6;
#endif
#if LWS_POSIX
	struct sockaddr_in serv_addr4;
	socklen_t len = sizeof(struct sockaddr);
	struct sockaddr_in sin;
	struct sockaddr *v;
	int n, opt = 1;
#endif
	lws_sockfd_type sockfd;
	struct lws *wsi;

	/* set up our external listening socket we serve on */

	if (info->port == CONTEXT_PORT_NO_LISTEN)
		return 0;

#if LWS_POSIX
#ifdef LWS_USE_IPV6
	if (LWS_IPV6_ENABLED(context))
		sockfd = socket(AF_INET6, SOCK_STREAM, 0);
	else
#endif
		sockfd = socket(AF_INET, SOCK_STREAM, 0);
		
	if (sockfd == -1) {
#else
	sockfd = mbed3_create_tcp_stream_socket();
	if (!lws_sockfd_valid(sockfd)) {
#endif
		lwsl_err("ERROR opening socket\n");
		return 1;
	}

#if LWS_POSIX
	/*
	 * allow us to restart even if old sockets in TIME_WAIT
	 */
	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
		       (const void *)&opt, sizeof(opt)) < 0) {
		compatible_close(sockfd);
		return 1;
	}
#endif
	lws_plat_set_socket_options(context, sockfd);

#if LWS_POSIX
#ifdef LWS_USE_IPV6
	if (LWS_IPV6_ENABLED(context)) {
		v = (struct sockaddr *)&serv_addr6;
		n = sizeof(struct sockaddr_in6);
		bzero((char *) &serv_addr6, sizeof(serv_addr6));
		serv_addr6.sin6_addr = in6addr_any;
		serv_addr6.sin6_family = AF_INET6;
		serv_addr6.sin6_port = htons(info->port);
	} else
#endif
	{
		v = (struct sockaddr *)&serv_addr4;
		n = sizeof(serv_addr4);
		bzero((char *) &serv_addr4, sizeof(serv_addr4));
		serv_addr4.sin_addr.s_addr = INADDR_ANY;
		serv_addr4.sin_family = AF_INET;

		if (info->iface) {
			if (interface_to_sa(context, info->iface,
				   (struct sockaddr_in *)v, n) < 0) {
				lwsl_err("Unable to find interface %s\n",
							info->iface);
				compatible_close(sockfd);
				return 1;
			}
		}

		serv_addr4.sin_port = htons(info->port);
	} /* ipv4 */

	n = bind(sockfd, v, n);
	if (n < 0) {
		lwsl_err("ERROR on binding to port %d (%d %d)\n",
					      info->port, n, LWS_ERRNO);
		compatible_close(sockfd);
		return 1;
	}

	if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1)
		lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO));
	else
		info->port = ntohs(sin.sin_port);
#endif

	context->listen_port = info->port;

	wsi = lws_zalloc(sizeof(struct lws));
	if (wsi == NULL) {
		lwsl_err("Out of mem\n");
		compatible_close(sockfd);
		return 1;
	}
	wsi->sock = sockfd;
	wsi->mode = LWS_CONNMODE_SERVER_LISTENER;
	wsi->protocol = context->protocols;

	if (insert_wsi_socket_into_fds(context, wsi)) {
		compatible_close(sockfd);
		return 1;
	}

	context->listen_service_modulo = LWS_LISTEN_SERVICE_MODULO;
	context->listen_service_count = 0;
	context->listen_service_fd = sockfd;

#if LWS_POSIX
	listen(sockfd, LWS_SOMAXCONN);
#else
	mbed3_tcp_stream_bind(sockfd, info->port, wsi);
#endif
	lwsl_notice(" Listening on port %d\n", info->port);

	return 0;
}

int
_lws_rx_flow_control(struct lws *wsi)
{
	struct lws_context *context = wsi->protocol->owning_server;

	/* there is no pending change */
	if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE))
		return 0;

	/* stuff is still buffered, not ready to really accept new input */
	if (wsi->rxflow_buffer) {
		/* get ourselves called back to deal with stashed buffer */
		lws_callback_on_writable(context, wsi);
		return 0;
	}

	/* pending is cleared, we can change rxflow state */

	wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE;

	lwsl_info("rxflow: wsi %p change_to %d\n", wsi,
			      wsi->rxflow_change_to & LWS_RXFLOW_ALLOW);

	/* adjust the pollfd for this wsi */

	if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) {
		if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
			lwsl_info("%s: fail\n", __func__);
			return -1;
		}
	} else
		if (lws_change_pollfd(wsi, LWS_POLLIN, 0))
			return -1;

	return 0;
}

int lws_http_action(struct lws_context *context, struct lws *wsi)
{
	enum http_connection_type connection_type = HTTP_CONNECTION_KEEP_ALIVE;
	enum http_version request_version;
	char content_length_str[32];
	unsigned int n, count = 0;
	char http_version_str[10];
	char http_conn_str[20];
	int http_version_len;
	char *uri_ptr = NULL;
	int uri_len = 0;

	static const unsigned char methods[] = {
		WSI_TOKEN_GET_URI,
		WSI_TOKEN_POST_URI,
		WSI_TOKEN_OPTIONS_URI,
		WSI_TOKEN_PUT_URI,
		WSI_TOKEN_PATCH_URI,
		WSI_TOKEN_DELETE_URI,
#ifdef LWS_USE_HTTP2
		WSI_TOKEN_HTTP_COLON_PATH,
#endif
	};
#ifdef _DEBUG
	static const char * const method_names[] = {
		"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE",
#ifdef LWS_USE_HTTP2
		":path",
#endif
	};
#endif
	
	/* it's not websocket.... shall we accept it as http? */

	for (n = 0; n < ARRAY_SIZE(methods); n++)
		if (lws_hdr_total_length(wsi, methods[n]))
			count++;
	if (!count) {
		lwsl_warn("Missing URI in HTTP request\n");
		goto bail_nuke_ah;
	}

	if (count != 1) {
		lwsl_warn("multiple methods?\n");
		goto bail_nuke_ah;
	}

	if (lws_ensure_user_space(wsi))
		goto bail_nuke_ah;

	for (n = 0; n < ARRAY_SIZE(methods); n++)
		if (lws_hdr_total_length(wsi, methods[n])) {
			uri_ptr = lws_hdr_simple_ptr(wsi, methods[n]);
			uri_len = lws_hdr_total_length(wsi, methods[n]);
			lwsl_info("Method: %s request for '%s'\n",
				  	method_names[n], uri_ptr);
			break;
		}

	/* HTTP header had a content length? */

	wsi->u.http.content_length = 0;
	if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
		lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
		lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI))
		wsi->u.http.content_length = 100 * 1024 * 1024;

	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
		lws_hdr_copy(wsi, content_length_str,
				sizeof(content_length_str) - 1,
						WSI_TOKEN_HTTP_CONTENT_LENGTH);
		wsi->u.http.content_length = atoi(content_length_str);
	}

	/* http_version? Default to 1.0, override with token: */
	request_version = HTTP_VERSION_1_0;

	/* Works for single digit HTTP versions. : */
	http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
	if (http_version_len > 7) {
		lws_hdr_copy(wsi, http_version_str,
				sizeof(http_version_str) - 1, WSI_TOKEN_HTTP);
		if (http_version_str[5] == '1' && http_version_str[7] == '1')
			request_version = HTTP_VERSION_1_1;
	}
	wsi->u.http.request_version = request_version;

	/* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
	if (request_version == HTTP_VERSION_1_1)
		connection_type = HTTP_CONNECTION_KEEP_ALIVE;
	else
		connection_type = HTTP_CONNECTION_CLOSE;

	/* Override default if http "Connection:" header: */
	if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
		lws_hdr_copy(wsi, http_conn_str, sizeof(http_conn_str) - 1,
			     WSI_TOKEN_CONNECTION);
		http_conn_str[sizeof(http_conn_str) - 1] = '\0';
		if (!strcasecmp(http_conn_str, "keep-alive"))
			connection_type = HTTP_CONNECTION_KEEP_ALIVE;
		else
			if (!strcasecmp(http_conn_str, "close"))
				connection_type = HTTP_CONNECTION_CLOSE;
	}
	wsi->u.http.connection_type = connection_type;

	n = wsi->protocol->callback(context, wsi,
				    LWS_CALLBACK_FILTER_HTTP_CONNECTION,
				    wsi->user_space, uri_ptr, uri_len);

	if (!n) {
		/*
		 * if there is content supposed to be coming,
		 * put a timeout on it having arrived
		 */
		lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
				AWAITING_TIMEOUT);

		n = wsi->protocol->callback(context, wsi, LWS_CALLBACK_HTTP,
			    wsi->user_space, uri_ptr, uri_len);
	}

	/* now drop the header info we kept a pointer to */
	lws_free2(wsi->u.http.ah);

	if (n) {
		lwsl_info("LWS_CALLBACK_HTTP closing\n");
		return 1; /* struct ah ptr already nuked */		}

	/* 
	 * 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 (wsi->state != WSI_STATE_HTTP_ISSUING_FILE)
		/* Prepare to read body if we have a content length: */
		if (wsi->u.http.content_length > 0)
			wsi->state = WSI_STATE_HTTP_BODY;

	return 0;

bail_nuke_ah:
	/* drop the header info */
	lws_free2(wsi->u.hdr.ah);

	return 1;
}


int lws_handshake_server(struct lws_context *context, struct lws *wsi,
			 unsigned char **buf, size_t len)
{
	struct allocated_headers *ah;
	int protocol_len, n, hit;
	char protocol_list[128];
	char protocol_name[32];
	char *p;

	/* LWS_CONNMODE_WS_SERVING */

	while (len--) {
		if (lws_parse(context, wsi, *(*buf)++)) {
			lwsl_info("lws_parse failed\n");
			goto bail_nuke_ah;
		}

		if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
			continue;

		lwsl_parser("lws_parse sees parsing complete\n");

		wsi->mode = LWS_CONNMODE_PRE_WS_SERVING_ACCEPT;
		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

		/* is this websocket protocol or normal http 1.0? */

		if (!lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) ||
			     !lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
			
			ah = wsi->u.hdr.ah;
			
			lws_union_transition(wsi, LWS_CONNMODE_HTTP_SERVING_ACCEPTED);
			wsi->state = WSI_STATE_HTTP;
			wsi->u.http.fd = LWS_INVALID_FILE;

			/* expose it at the same offset as u.hdr */
			wsi->u.http.ah = ah;
			
			n = lws_http_action(context, wsi);

			return n;
		}

		if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
				"websocket"))
			goto upgrade_ws;
#ifdef LWS_USE_HTTP2
		if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
								"h2c-14"))
			goto upgrade_h2c;
#endif
		/* dunno what he wanted to upgrade to */
		goto bail_nuke_ah;

#ifdef LWS_USE_HTTP2
upgrade_h2c:
		if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
			lwsl_err("missing http2_settings\n");
			goto bail_nuke_ah;
		}

		lwsl_err("h2c upgrade...\n");

		p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
		/* convert the peer's HTTP-Settings */
		n = lws_b64_decode_string(p, protocol_list,
					  sizeof(protocol_list));
		if (n < 0) {
			lwsl_parser("HTTP2_SETTINGS too long\n");
			return 1;
		}

		/* adopt the header info */

		ah = wsi->u.hdr.ah;

		lws_union_transition(wsi, LWS_CONNMODE_HTTP2_SERVING);
		
		/* http2 union member has http union struct at start */
		wsi->u.http.ah = ah;
		
		lws_http2_init(&wsi->u.http2.peer_settings);
		lws_http2_init(&wsi->u.http2.my_settings);
		
		/* HTTP2 union */
		
		lws_http2_interpret_settings_payload(&wsi->u.http2.peer_settings,
				(unsigned char *)protocol_list, n);

		strcpy(protocol_list,
		       "HTTP/1.1 101 Switching Protocols\x0d\x0a"
		      "Connection: Upgrade\x0d\x0a"
		      "Upgrade: h2c\x0d\x0a\x0d\x0a");
		n = lws_issue_raw(wsi, (unsigned char *)protocol_list,
					strlen(protocol_list));
		if (n != strlen(protocol_list)) {
			lwsl_debug("http2 switch: ERROR writing to socket\n");
			return 1;
		}
		
		wsi->state = WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE;
		
		return 0;
#endif

upgrade_ws:
		if (!wsi->protocol)
			lwsl_err("NULL protocol at lws_read\n");

		/*
		 * It's websocket
		 *
		 * Select the first protocol we support from the list
		 * the client sent us.
		 *
		 * Copy it to remove header fragmentation
		 */

		if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
				 WSI_TOKEN_PROTOCOL) < 0) {
			lwsl_err("protocol list too long");
			goto bail_nuke_ah;
		}

		protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
		protocol_list[protocol_len] = '\0';
		p = protocol_list;
		hit = 0;

		while (*p && !hit) {
			unsigned int n = 0;
			while (n < sizeof(protocol_name) - 1 && *p && *p !=',')
				protocol_name[n++] = *p++;
			protocol_name[n] = '\0';
			if (*p)
				p++;

			lwsl_info("checking %s\n", protocol_name);

			n = 0;
			while (wsi->protocol && context->protocols[n].callback) {
				if (!wsi->protocol->name) {
					n++;
					continue;
				}
				if (!strcmp(context->protocols[n].name,
					    protocol_name)) {
					lwsl_info("prot match %d\n", n);
					wsi->protocol = &context->protocols[n];
					hit = 1;
					break;
				}

				n++;
			}
		}

		/* we didn't find a protocol he wanted? */

		if (!hit) {
			if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL) ==
									 NULL) {
				/*
				 * some clients only have one protocol and
				 * do not sent the protocol list header...
				 * allow it and match to protocol 0
				 */
				lwsl_info("defaulting to prot 0 handler\n");
				wsi->protocol = &context->protocols[0];
			} else {
				lwsl_err("No protocol from list \"%s\" supported\n",
					 protocol_list);
				goto bail_nuke_ah;
			}
		}

		/* allocate wsi->user storage */
		if (lws_ensure_user_space(wsi))
			goto bail_nuke_ah;

		/*
		 * Give the user code a chance to study the request and
		 * have the opportunity to deny it
		 */

		if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
				LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
				wsi->user_space,
			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
			lwsl_warn("User code denied connection\n");
			goto bail_nuke_ah;
		}


		/*
		 * Perform the handshake according to the protocol version the
		 * client announced
		 */

		switch (wsi->ietf_spec_revision) {
		case 13:
			lwsl_parser("lws_parse calling handshake_04\n");
			if (handshake_0405(context, wsi)) {
				lwsl_info("hs0405 has failed the connection\n");
				goto bail_nuke_ah;
			}
			break;

		default:
			lwsl_warn("Unknown client spec version %d\n",
						       wsi->ietf_spec_revision);
			goto bail_nuke_ah;
		}

		/* drop the header info -- no bail_nuke_ah after this */
		lws_free_header_table(wsi);

		lws_union_transition(wsi, LWS_CONNMODE_WS_SERVING);

		/*
		 * create the frame buffer for this connection according to the
		 * size mentioned in the protocol definition.  If 0 there, use
		 * a big default for compatibility
		 */

		n = wsi->protocol->rx_buffer_size;
		if (!n)
			n = LWS_MAX_SOCKET_IO_BUF;
		n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
		wsi->u.ws.rx_user_buffer = lws_malloc(n);
		if (!wsi->u.ws.rx_user_buffer) {
			lwsl_err("Out of Mem allocating rx buffer %d\n", n);
			return 1;
		}
		lwsl_info("Allocating RX buffer %d\n", n);
#if LWS_POSIX
		if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF,
			       (const char *)&n, sizeof n)) {
			lwsl_warn("Failed to set SNDBUF to %d", n);
			return 1;
		}
#endif
		lwsl_parser("accepted v%02d connection\n",
						       wsi->ietf_spec_revision);
	} /* while all chars are handled */

	return 0;

bail_nuke_ah:
	/* drop the header info */
	lws_free_header_table(wsi);
	return 1;
}

struct lws *
lws_create_new_server_wsi(struct lws_context *context)
{
	struct lws *new_wsi;

	new_wsi = lws_zalloc(sizeof(struct lws));
	if (new_wsi == NULL) {
		lwsl_err("Out of memory for new connection\n");
		return NULL;
	}

	new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
	new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;

	/* intialize the instance struct */

	new_wsi->state = WSI_STATE_HTTP;
	new_wsi->mode = LWS_CONNMODE_HTTP_SERVING;
	new_wsi->hdr_parsing_completed = 0;

#ifdef LWS_OPENSSL_SUPPORT
	new_wsi->use_ssl = LWS_SSL_ENABLED(context);
#endif

	if (lws_allocate_header_table(new_wsi)) {
		lws_free(new_wsi);
		return NULL;
	}

	/*
	 * these can only be set once the protocol is known
	 * we set an unestablished connection's protocol pointer
	 * to the start of the supported list, so it can look
	 * for matching ones during the handshake
	 */
	new_wsi->protocol = context->protocols;
	new_wsi->user_space = NULL;
	new_wsi->ietf_spec_revision = 0;
	new_wsi->sock = LWS_SOCK_INVALID;
	
	/*
	 * outermost create notification for wsi
	 * no user_space because no protocol selection
	 */
	context->protocols[0].callback(context, new_wsi,
			LWS_CALLBACK_WSI_CREATE, NULL, NULL, 0);

	return new_wsi;
}

/**
 * lws_http_transaction_completed() - wait for new http transaction or close
 * @wsi:	websocket connection
 *
 *	Returns 1 if the HTTP connection must close now
 *	Returns 0 and resets connection to wait for new HTTP header /
 *	  transaction if possible
 */

LWS_VISIBLE
int lws_http_transaction_completed(struct lws *wsi)
{
	/* if we can't go back to accept new headers, drop the connection */
	if (wsi->u.http.connection_type == HTTP_CONNECTION_CLOSE) {
		lwsl_info("%s: close connection\n", __func__);
		return 1;
	}

	/* otherwise set ourselves up ready to go again */
	wsi->state = WSI_STATE_HTTP;
	wsi->mode = LWS_CONNMODE_HTTP_SERVING;
	wsi->u.http.content_length = 0;

	/* He asked for it to stay alive indefinitely */
	lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

	if (lws_allocate_header_table(wsi))
		return 1;

	/* If we're (re)starting on headers, need other implied init */
	wsi->u.hdr.ues = URIES_IDLE;
	
	lwsl_info("%s: keep-alive await new transaction\n", __func__);
	
	return 0;
}

LWS_VISIBLE
int lws_server_socket_service(struct lws_context *context,
			      struct lws *wsi, struct lws_pollfd *pollfd)
{
	lws_sockfd_type accept_fd = LWS_SOCK_INVALID;
#if LWS_POSIX
	struct sockaddr_in cli_addr;
	socklen_t clilen;
#endif
	struct lws *new_wsi = NULL;
	int n, len;

	switch (wsi->mode) {

	case LWS_CONNMODE_HTTP_SERVING:
	case LWS_CONNMODE_HTTP_SERVING_ACCEPTED:
	case LWS_CONNMODE_HTTP2_SERVING:

		/* handle http headers coming in */

		/* pending truncated sends have uber priority */

		if (wsi->truncated_send_len) {
			if (pollfd->revents & LWS_POLLOUT)
				if (lws_issue_raw(wsi, wsi->truncated_send_malloc +
						       wsi->truncated_send_offset,
						  wsi->truncated_send_len) < 0) {
					goto fail;
				}
			/*
			 * we can't afford to allow input processing send
			 * something new, so spin around he event loop until
			 * he doesn't have any partials
			 */
			break;
		}

		/* any incoming data ready? */

		if (pollfd->revents & LWS_POLLIN) {
			len = lws_ssl_capable_read(context, wsi,
						   context->service_buffer,
						   sizeof(context->service_buffer));
			lwsl_debug("%s: read %d\r\n", __func__, len);
			switch (len) {
			case 0:
				lwsl_info("lws_server_skt_srv: read 0 len\n");
				/* lwsl_info("   state=%d\n", wsi->state); */
				if (!wsi->hdr_parsing_completed)
					lws_free_header_table(wsi);
				/* fallthru */
			case LWS_SSL_CAPABLE_ERROR:
				goto fail;
			case LWS_SSL_CAPABLE_MORE_SERVICE:
				goto try_pollout;
			}

			/* just ignore incoming if waiting for close */
			if (wsi->state != WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
			
				/*
				 * hm this may want to send
				 * (via HTTP callback for example)
				 */
				n = lws_read(context, wsi,
					     context->service_buffer, len);
				if (n < 0) /* we closed wsi */
					return 1;

				/* hum he may have used up the
				 * writability above */
				break;
			}
		}

try_pollout:
		/* this handles POLLOUT for http serving fragments */

		if (!(pollfd->revents & LWS_POLLOUT))
			break;

		/* one shot */
		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
			goto fail;
		
		lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE);

		if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) {
			n = user_callback_handle_rxflow(
					wsi->protocol->callback,
					wsi->protocol->owning_server,
					wsi, LWS_CALLBACK_HTTP_WRITEABLE,
					wsi->user_space,
					NULL,
					0);
			if (n < 0)
				goto fail;
			break;
		}

		/* >0 == completion, <0 == error */
		n = lws_serve_http_file_fragment(context, wsi);
		if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi)))
			goto fail;
		break;

	case LWS_CONNMODE_SERVER_LISTENER:

#if LWS_POSIX
		/* pollin means a client has connected to us then */

		if (!(pollfd->revents & LWS_POLLIN))
			break;

		/* listen socket got an unencrypted connection... */
		
		clilen = sizeof(cli_addr);
		lws_latency_pre(context, wsi);
		accept_fd  = accept(pollfd->fd, (struct sockaddr *)&cli_addr,
				    &clilen);
		lws_latency(context, wsi,
			"unencrypted accept LWS_CONNMODE_SERVER_LISTENER",
						     accept_fd, accept_fd >= 0);
		if (accept_fd < 0) {
			if (LWS_ERRNO == LWS_EAGAIN ||
			    LWS_ERRNO == LWS_EWOULDBLOCK) {
				lwsl_debug("accept asks to try again\n");
				break;
			}
			lwsl_warn("ERROR on accept: %s\n", strerror(LWS_ERRNO));
			break;
		}

		lws_plat_set_socket_options(context, accept_fd);
#else
		/* not very beautiful... */
		accept_fd = (lws_sockfd_type)pollfd;
#endif
		/*
		 * look at who we connected to and give user code a chance
		 * to reject based on client IP.  There's no protocol selected
		 * yet so we issue this to protocols[0]
		 */

		if ((context->protocols[0].callback)(context, wsi,
				LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
					   NULL, (void *)(long)accept_fd, 0)) {
			lwsl_debug("Callback denied network connection\n");
			compatible_close(accept_fd);
			break;
		}

		new_wsi = lws_create_new_server_wsi(context);
		if (new_wsi == NULL) {
			compatible_close(accept_fd);
			break;
		}

		new_wsi->sock = accept_fd;

		/* the transport is accepted... give him time to negotiate */
		lws_set_timeout(new_wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
				AWAITING_TIMEOUT);

#if LWS_POSIX == 0
		mbed3_tcp_stream_accept(accept_fd, new_wsi);
#endif
		
		/*
		 * A new connection was accepted. Give the user a chance to
		 * set properties of the newly created wsi. There's no protocol
		 * selected yet so we issue this to protocols[0]
		 */
		(context->protocols[0].callback)(context, new_wsi,
			LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED,
			NULL, NULL, 0);

		lws_libev_accept(context, new_wsi, accept_fd);

		if (!LWS_SSL_ENABLED(context)) {
#if LWS_POSIX
			lwsl_debug("accepted new conn  port %u on fd=%d\n",
					  ntohs(cli_addr.sin_port), accept_fd);
#endif
			if (insert_wsi_socket_into_fds(context, new_wsi))
				goto fail;
		}
		break;

	default:
		break;
	}

	if (!lws_server_socket_service_ssl(context, &wsi, new_wsi, accept_fd,
					   pollfd))
		return 0;

fail:
	lws_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS);

	return 1;
}
示例#4
0
int lws_handshake_server(struct libwebsocket_context *context,
                         struct libwebsocket *wsi, unsigned char **buf, size_t len)
{
    struct allocated_headers *ah;
    char *uri_ptr = NULL;
    int uri_len = 0;
    enum http_version request_version;
    enum http_connection_type connection_type;
    int http_version_len, protocol_len;
    char content_length_str[32];
    char protocol_list[128];
    char protocol_name[32];
    char http_version_str[10];
    char *p;
    int n, hit;

    /* LWS_CONNMODE_WS_SERVING */

    while (len--) {
        if (libwebsocket_parse(context, wsi, *(*buf)++)) {
            lwsl_info("libwebsocket_parse failed\n");
            goto bail_nuke_ah;
        }

        if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
            continue;

        lwsl_parser("libwebsocket_parse sees parsing complete\n");

        wsi->mode = LWS_CONNMODE_PRE_WS_SERVING_ACCEPT;
        libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

        /* is this websocket protocol or normal http 1.0? */

        if (!lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) ||
                !lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {

            /* it's not websocket.... shall we accept it as http? */

            if (!lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) &&
                    !lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) &&
                    !lws_hdr_total_length(wsi, WSI_TOKEN_OPTIONS_URI)) {
                lwsl_warn("Missing URI in HTTP request\n");
                goto bail_nuke_ah;
            }

            if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) &&
                    lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
                lwsl_warn("GET and POST methods?\n");
                goto bail_nuke_ah;
            }

            if (libwebsocket_ensure_user_space(wsi))
                goto bail_nuke_ah;

            if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) {
                uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
                uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
                lwsl_info("HTTP GET request for '%s'\n",
                          lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI));

            }
            if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
                lwsl_info("HTTP POST request for '%s'\n",
                          lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI));
                uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI);
                uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI);
            }
            if (lws_hdr_total_length(wsi, WSI_TOKEN_OPTIONS_URI)) {
                lwsl_info("HTTP OPTIONS request for '%s'\n",
                          lws_hdr_simple_ptr(wsi, WSI_TOKEN_OPTIONS_URI));
                uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_OPTIONS_URI);
                uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_OPTIONS_URI);
            }

            /*
             * Hm we still need the headers so the
             * callback can look at leaders like the URI, but we
             * need to transition to http union state.... hold a
             * copy of u.hdr.ah and deallocate afterwards
             */
            ah = wsi->u.hdr.ah;

            /* union transition */
            memset(&wsi->u, 0, sizeof(wsi->u));
            wsi->mode = LWS_CONNMODE_HTTP_SERVING_ACCEPTED;
            wsi->state = WSI_STATE_HTTP;
            wsi->u.http.fd = LWS_INVALID_FILE;

            /* expose it at the same offset as u.hdr */
            wsi->u.http.ah = ah;

            /* HTTP header had a content length? */

            wsi->u.http.content_length = 0;
            if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
                wsi->u.http.content_length = 100 * 1024 * 1024;

            if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
                lws_hdr_copy(wsi, content_length_str,
                             sizeof(content_length_str) - 1,
                             WSI_TOKEN_HTTP_CONTENT_LENGTH);
                wsi->u.http.content_length = atoi(content_length_str);
            }

            /* http_version? Default to 1.0, override with token: */
            request_version = HTTP_VERSION_1_0;

            /* Works for single digit HTTP versions. : */
            http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
            if (http_version_len > 7) {
                lws_hdr_copy(wsi, http_version_str,
                             sizeof(http_version_str) - 1, WSI_TOKEN_HTTP);
                if (http_version_str[5] == '1' &&
                        http_version_str[7] == '1')
                    request_version = HTTP_VERSION_1_1;
            }
            wsi->u.http.request_version = request_version;

            /* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
            if (request_version == HTTP_VERSION_1_1)
                connection_type = HTTP_CONNECTION_KEEP_ALIVE;
            else
                connection_type = HTTP_CONNECTION_CLOSE;

            /* Override default if http "Connection:" header: */
            if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
                char http_conn_str[20];
                lws_hdr_copy(wsi, http_conn_str,
                             sizeof(http_conn_str)-1, WSI_TOKEN_CONNECTION);
                http_conn_str[sizeof(http_conn_str)-1] = '\0';
                if (strcasecmp(http_conn_str,"keep-alive") == 0)
                    connection_type = HTTP_CONNECTION_KEEP_ALIVE;

                else if (strcasecmp(http_conn_str,"close") == 0)
                    connection_type = HTTP_CONNECTION_CLOSE;

            }
            wsi->u.http.connection_type = connection_type;

            n = 0;
            if (wsi->protocol->callback)
                n = wsi->protocol->callback(context, wsi,
                                            LWS_CALLBACK_FILTER_HTTP_CONNECTION,
                                            wsi->user_space, uri_ptr, uri_len);

            if (!n) {
                /*
                 * if there is content supposed to be coming,
                 * put a timeout on it having arrived
                 */
                libwebsocket_set_timeout(wsi,
                                         PENDING_TIMEOUT_HTTP_CONTENT,
                                         AWAITING_TIMEOUT);

                if (wsi->protocol->callback)
                    n = wsi->protocol->callback(context, wsi,
                                                LWS_CALLBACK_HTTP,
                                                wsi->user_space, uri_ptr, uri_len);
            }

            /* now drop the header info we kept a pointer to */
            if (ah)
                free(ah);
            /* not possible to continue to use past here */
            wsi->u.http.ah = NULL;

            if (n) {
                lwsl_info("LWS_CALLBACK_HTTP closing\n");
                return 1; /* struct ah ptr already nuked */
            }

            /* 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 libwebsocket_read decide how to
             * proceed based on state. */
            if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE)

                /* Prepare to read body if we have a content length: */
                if (wsi->u.http.content_length > 0)
                    wsi->state = WSI_STATE_HTTP_BODY;


            return 0; /* don't bail out of libwebsocket_read, just yet */
        }

        if (!wsi->protocol)
            lwsl_err("NULL protocol at libwebsocket_read\n");

        /*
         * It's websocket
         *
         * Select the first protocol we support from the list
         * the client sent us.
         *
         * Copy it to remove header fragmentation
         */

        if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
                         WSI_TOKEN_PROTOCOL) < 0) {
            lwsl_err("protocol list too long");
            goto bail_nuke_ah;
        }

        protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
        protocol_list[protocol_len] = '\0';
        p = protocol_list;
        hit = 0;

        while (*p && !hit) {
            n = 0;
            while (n < sizeof(protocol_name) - 1 && *p && *p !=',')
                protocol_name[n++] = *p++;
            protocol_name[n] = '\0';
            if (*p)
                p++;

            lwsl_info("checking %s\n", protocol_name);

            n = 0;
            while (context->protocols[n].callback) {
                if (!wsi->protocol->name) {
                    n++;
                    continue;
                }
                if (!strcmp(context->protocols[n].name,
                            protocol_name)) {
                    lwsl_info("prot match %d\n", n);
                    wsi->protocol = &context->protocols[n];
                    hit = 1;
                    break;
                }

                n++;
            }
        }

        /* we didn't find a protocol he wanted? */

        if (!hit) {
            if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL) ==
                    NULL) {
                /*
                 * some clients only have one protocol and
                 * do not sent the protocol list header...
                 * allow it and match to protocol 0
                 */
                lwsl_info("defaulting to prot 0 handler\n");
                wsi->protocol = &context->protocols[0];
            } else {
                lwsl_err("No protocol from list \"%s\" supported\n",
                         protocol_list);
                goto bail_nuke_ah;
            }
        }

        /* allocate wsi->user storage */
        if (libwebsocket_ensure_user_space(wsi))
            goto bail_nuke_ah;

        /*
         * Give the user code a chance to study the request and
         * have the opportunity to deny it
         */

        if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
                                      LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
                                      wsi->user_space,
                                      lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
            lwsl_warn("User code denied connection\n");
            goto bail_nuke_ah;
        }


        /*
         * Perform the handshake according to the protocol version the
         * client announced
         */

        switch (wsi->ietf_spec_revision) {
        case 13:
            lwsl_parser("lws_parse calling handshake_04\n");
            if (handshake_0405(context, wsi)) {
                lwsl_info("hs0405 has failed the connection\n");
                goto bail_nuke_ah;
            }
            break;

        default:
            lwsl_warn("Unknown client spec version %d\n",
                      wsi->ietf_spec_revision);
            goto bail_nuke_ah;
        }

        /* drop the header info -- no bail_nuke_ah after this */

        if (wsi->u.hdr.ah)
            free(wsi->u.hdr.ah);

        wsi->mode = LWS_CONNMODE_WS_SERVING;

        /* union transition */
        memset(&wsi->u, 0, sizeof(wsi->u));
        wsi->u.ws.rxflow_change_to = LWS_RXFLOW_ALLOW;

        /*
         * create the frame buffer for this connection according to the
         * size mentioned in the protocol definition.  If 0 there, use
         * a big default for compatibility
         */

        n = wsi->protocol->rx_buffer_size;
        if (!n)
            n = LWS_MAX_SOCKET_IO_BUF;
        n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
        wsi->u.ws.rx_user_buffer = malloc(n);
        if (!wsi->u.ws.rx_user_buffer) {
            lwsl_err("Out of Mem allocating rx buffer %d\n", n);
            return 1;
        }
        lwsl_info("Allocating RX buffer %d\n", n);

        if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, (const char *)&n, sizeof n)) {
            lwsl_warn("Failed to set SNDBUF to %d", n);
            return 1;
        }

        lwsl_parser("accepted v%02d connection\n",
                    wsi->ietf_spec_revision);
    } /* while all chars are handled */

    return 0;

bail_nuke_ah:
    /* drop the header info */
    if (wsi->u.hdr.ah)
        free(wsi->u.hdr.ah);
    return 1;
}
LWS_VISIBLE int
libwebsocket_read(struct libwebsocket_context *context,
		     struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
	size_t n;
	struct allocated_headers *ah;
	char *uri_ptr;
	int uri_len;

	switch (wsi->state) {
	case WSI_STATE_HTTP_ISSUING_FILE:
	case WSI_STATE_HTTP:
		wsi->state = WSI_STATE_HTTP_HEADERS;
		wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
		wsi->u.hdr.lextable_pos = 0;
		/* fallthru */
	case WSI_STATE_HTTP_HEADERS:

		lwsl_parser("issuing %d bytes to parser\n", (int)len);

#ifndef LWS_NO_CLIENT
		switch (wsi->mode) {
		case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
		case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
		case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
		case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
		case LWS_CONNMODE_WS_CLIENT:
			for (n = 0; n < len; n++)
				if (libwebsocket_client_rx_sm(wsi, *buf++)) {
					lwsl_info("client_rx_sm failed\n");
					goto bail;
				}
			return 0;
		default:
			break;
		}
#endif
#ifndef LWS_NO_SERVER
		/* LWS_CONNMODE_WS_SERVING */

		for (n = 0; n < len; n++)
			if (libwebsocket_parse(wsi, *buf++)) {
				lwsl_info("libwebsocket_parse failed\n");
				goto bail_nuke_ah;
			}

		if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
			break;

		lwsl_parser("libwebsocket_parse sees parsing complete\n");

		wsi->mode = LWS_CONNMODE_PRE_WS_SERVING_ACCEPT;

		/* is this websocket protocol or normal http 1.0? */

		if (!lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) ||
			     !lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {

			/* it's not websocket.... shall we accept it as http? */

			if (!lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) {
				lwsl_warn("Missing URI in HTTP request\n");
				goto bail_nuke_ah;
			}

			lwsl_info("HTTP request for '%s'\n",
				lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI));

			if (libwebsocket_ensure_user_space(wsi))
				goto bail_nuke_ah;

			/*
			 * Hm we still need the headers so the
			 * callback can look at leaders like the URI, but we
			 * need to transition to http union state.... hold a
			 * copy of u.hdr.ah and deallocate afterwards
			 */

			ah = wsi->u.hdr.ah;
			uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
			uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);

			/* union transition */
			memset(&wsi->u, 0, sizeof(wsi->u));

			wsi->mode = LWS_CONNMODE_HTTP_SERVING_ACCEPTED;
			wsi->state = WSI_STATE_HTTP;
			n = 0;
			if (wsi->protocol->callback)
				n = wsi->protocol->callback(context, wsi,
				    LWS_CALLBACK_HTTP,
				    wsi->user_space, uri_ptr, uri_len);

			/* now drop the header info we kept a pointer to */
			if (ah)
				free(ah);

			if (n) {
				lwsl_info("LWS_CALLBACK_HTTP closing\n");
				goto bail; /* struct ah ptr already nuked */
			}

			return 0;
		}

		if (!wsi->protocol)
			lwsl_err("NULL protocol at libwebsocket_read\n");

		/*
		 * It's websocket
		 *
		 * Make sure user side is happy about protocol
		 */

		while (wsi->protocol->callback) {

			if (!lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) {
				if (wsi->protocol->name == NULL)
					break;
			} else
				if (wsi->protocol->name && strcmp(
					lws_hdr_simple_ptr(wsi,
						WSI_TOKEN_PROTOCOL),
						      wsi->protocol->name) == 0)
					break;

			wsi->protocol++;
		}

		/* we didn't find a protocol he wanted? */

		if (wsi->protocol->callback == NULL) {
			if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL) ==
									 NULL) {
				lwsl_info("no protocol -> prot 0 handler\n");
				wsi->protocol = &context->protocols[0];
			} else {
				lwsl_err("Req protocol %s not supported\n",
				   lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL));
				goto bail_nuke_ah;
			}
		}

		/* allocate wsi->user storage */
		if (libwebsocket_ensure_user_space(wsi))
				goto bail_nuke_ah;

		/*
		 * Give the user code a chance to study the request and
		 * have the opportunity to deny it
		 */

		if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
				LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
				wsi->user_space,
			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
			lwsl_warn("User code denied connection\n");
			goto bail_nuke_ah;
		}


		/*
		 * Perform the handshake according to the protocol version the
		 * client announced
		 */

		switch (wsi->ietf_spec_revision) {
		case 13:
			lwsl_parser("lws_parse calling handshake_04\n");
			if (handshake_0405(context, wsi)) {
				lwsl_info("hs0405 has failed the connection\n");
				goto bail_nuke_ah;
			}
			break;

		default:
			lwsl_warn("Unknown client spec version %d\n",
						       wsi->ietf_spec_revision);
			goto bail_nuke_ah;
		}

		/* drop the header info -- no bail_nuke_ah after this */

		if (wsi->u.hdr.ah)
			free(wsi->u.hdr.ah);

		wsi->mode = LWS_CONNMODE_WS_SERVING;

		/* union transition */
		memset(&wsi->u, 0, sizeof(wsi->u));
		wsi->u.ws.rxflow_change_to = LWS_RXFLOW_ALLOW;

		/*
		 * create the frame buffer for this connection according to the
		 * size mentioned in the protocol definition.  If 0 there, use
		 * a big default for compatibility
		 */

		n = wsi->protocol->rx_buffer_size;
		if (!n)
			n = LWS_MAX_SOCKET_IO_BUF;
		n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
		wsi->u.ws.rx_user_buffer = malloc(n);
		if (!wsi->u.ws.rx_user_buffer) {
			lwsl_err("Out of Mem allocating rx buffer %d\n", n);
			goto bail;
		}
		lwsl_info("Allocating RX buffer %d\n", n);

		if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, (const void*) &n, sizeof n)) {
			lwsl_warn("Failed to set SNDBUF to %d", n);
			goto bail;
		}

		lwsl_parser("accepted v%02d connection\n",
						       wsi->ietf_spec_revision);
#endif
		break;

	case WSI_STATE_AWAITING_CLOSE_ACK:
	case WSI_STATE_ESTABLISHED:
#ifndef LWS_NO_CLIENT
		switch (wsi->mode) {
		case LWS_CONNMODE_WS_CLIENT:
			for (n = 0; n < len; n++)
				if (libwebsocket_client_rx_sm(
							     wsi, *buf++) < 0) {
					lwsl_info("client rx has bailed\n");
					goto bail;
				}

			return 0;
		default:
			break;
		}
#endif
#ifndef LWS_NO_SERVER
		/* LWS_CONNMODE_WS_SERVING */

		if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) {
			lwsl_info("interpret_incoming_packet has bailed\n");
			goto bail;
		}
#endif
		break;
	default:
		lwsl_err("libwebsocket_read: Unhandled state\n");
		break;
	}

	return 0;

bail_nuke_ah:
	/* drop the header info */
	if (wsi->u.hdr.ah)
		free(wsi->u.hdr.ah);

bail:
	lwsl_info("closing connection at libwebsocket_read bail:\n");

	libwebsocket_close_and_free_session(context, wsi,
						     LWS_CLOSE_STATUS_NOSTATUS);

	return -1;
}
示例#6
0
int lws_handshake_server(struct libwebsocket_context *context,
		struct libwebsocket *wsi, unsigned char **buf, size_t len)
{
	struct allocated_headers *ah;
	int protocol_len;
	char protocol_list[128];
	char protocol_name[32];
	char *p;
	int n, hit;

	/* LWS_CONNMODE_WS_SERVING */

	while (len--) {
		if (libwebsocket_parse(context, wsi, *(*buf)++)) {
			lwsl_info("libwebsocket_parse failed\n");
			goto bail_nuke_ah;
		}

		if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
			continue;

		lwsl_parser("libwebsocket_parse sees parsing complete\n");

		wsi->mode = LWS_CONNMODE_PRE_WS_SERVING_ACCEPT;
		libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

		/* is this websocket protocol or normal http 1.0? */

		if (!lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) ||
			     !lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
			
			ah = wsi->u.hdr.ah;
			
			/* union transition */
			memset(&wsi->u, 0, sizeof(wsi->u));
			wsi->mode = LWS_CONNMODE_HTTP_SERVING_ACCEPTED;
			wsi->state = WSI_STATE_HTTP;
			wsi->u.http.fd = LWS_INVALID_FILE;

			/* expose it at the same offset as u.hdr */
			wsi->u.http.ah = ah;
			
			n = lws_http_action(context, wsi);

			return n;
		}
		
		lwsl_err(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE));

		if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
								"websocket"))
			goto upgrade_ws;
#ifdef LWS_USE_HTTP2
		if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
								"h2c-14"))
			goto upgrade_h2c;
#endif
		/* dunno what he wanted to upgrade to */
		goto bail_nuke_ah;

#ifdef LWS_USE_HTTP2
upgrade_h2c:
		if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
			lwsl_err("missing http2_settings\n");
			goto bail_nuke_ah;
		}

		lwsl_err("h2c upgrade...\n");

		p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
		/* convert the peer's HTTP-Settings */
		n = lws_b64_decode_string(p, protocol_list, sizeof(protocol_list));
		if (n < 0) {
			lwsl_parser("HTTP2_SETTINGS too long\n");
			return 1;
		}

		/* adopt the header info */

		ah = wsi->u.hdr.ah;

		wsi->mode = LWS_CONNMODE_HTTP2_SERVING;

		/* union transition */
		memset(&wsi->u, 0, sizeof(wsi->u));
		
		/* http2 union member has http union struct at start */
		wsi->u.http.ah = ah;
		
		lws_http2_init(&wsi->u.http2.peer_settings);
		lws_http2_init(&wsi->u.http2.my_settings);
		
		/* HTTP2 union */
		
		lws_http2_interpret_settings_payload(&wsi->u.http2.peer_settings, (unsigned char *)protocol_list, n);

		strcpy(protocol_list,
		       "HTTP/1.1 101 Switching Protocols\x0d\x0a"
		      "Connection: Upgrade\x0d\x0a"
		      "Upgrade: h2c\x0d\x0a\x0d\x0a");
		n = lws_issue_raw(wsi, (unsigned char *)wsi->protocol->name,
					strlen(wsi->protocol->name));
		if (n != strlen(wsi->protocol->name)) {
			lwsl_debug("http2 switch: ERROR writing to socket\n");
			return 1;
		}
		
		wsi->state = WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE;
		
		return 0;
#endif

upgrade_ws:
		if (!wsi->protocol)
			lwsl_err("NULL protocol at libwebsocket_read\n");

		/*
		 * It's websocket
		 *
		 * Select the first protocol we support from the list
		 * the client sent us.
		 *
		 * Copy it to remove header fragmentation
		 */

		if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
				 WSI_TOKEN_PROTOCOL) < 0) {
			lwsl_err("protocol list too long");
			goto bail_nuke_ah;
		}

		protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
		protocol_list[protocol_len] = '\0';
		p = protocol_list;
		hit = 0;

		while (*p && !hit) {
			n = 0;
			while (n < sizeof(protocol_name) - 1 && *p && *p !=',')
				protocol_name[n++] = *p++;
			protocol_name[n] = '\0';
			if (*p)
				p++;
			while (*p == ' ')
				p++;

			lwsl_info("checking %s\n", protocol_name);

			n = 0;
			while (context->protocols[n].callback) {
				if (!wsi->protocol->name) {
					n++;
					continue;
				}
				if (!strcmp(context->protocols[n].name,
					    protocol_name)) {
					lwsl_info("prot match %d\n", n);
					wsi->protocol = &context->protocols[n];
					hit = 1;
					break;
				}

				n++;
			}
		}

		/* we didn't find a protocol he wanted? */

		if (!hit) {
			if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL) ==
									 NULL) {
				/*
				 * some clients only have one protocol and
				 * do not sent the protocol list header...
				 * allow it and match to protocol 0
				 */
				lwsl_info("defaulting to prot 0 handler\n");
				wsi->protocol = &context->protocols[0];
			} else {
				lwsl_err("No protocol from list \"%s\" supported\n",
					 protocol_list);
				goto bail_nuke_ah;
			}
		}

		/* allocate wsi->user storage */
		if (libwebsocket_ensure_user_space(wsi))
			goto bail_nuke_ah;

		/*
		 * Give the user code a chance to study the request and
		 * have the opportunity to deny it
		 */

		if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
				LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
				wsi->user_space,
			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
			lwsl_warn("User code denied connection\n");
			goto bail_nuke_ah;
		}


		/*
		 * Perform the handshake according to the protocol version the
		 * client announced
		 */

		switch (wsi->ietf_spec_revision) {
		case 13:
			lwsl_parser("lws_parse calling handshake_04\n");
			if (handshake_0405(context, wsi)) {
				lwsl_info("hs0405 has failed the connection\n");
				goto bail_nuke_ah;
			}
			break;

		default:
			lwsl_warn("Unknown client spec version %d\n",
						       wsi->ietf_spec_revision);
			goto bail_nuke_ah;
		}

		/* drop the header info -- no bail_nuke_ah after this */

		if (wsi->u.hdr.ah)
			free(wsi->u.hdr.ah);

		wsi->mode = LWS_CONNMODE_WS_SERVING;

		/* union transition */
		memset(&wsi->u, 0, sizeof(wsi->u));

		/*
		 * create the frame buffer for this connection according to the
		 * size mentioned in the protocol definition.  If 0 there, use
		 * a big default for compatibility
		 */

		n = wsi->protocol->rx_buffer_size;
		if (!n)
			n = LWS_MAX_SOCKET_IO_BUF;
		n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
		wsi->u.ws.rx_user_buffer = malloc(n);
		if (!wsi->u.ws.rx_user_buffer) {
			lwsl_err("Out of Mem allocating rx buffer %d\n", n);
			return 1;
		}
		lwsl_info("Allocating RX buffer %d\n", n);

		if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, (const char *)&n, sizeof n)) {
			lwsl_warn("Failed to set SNDBUF to %d", n);
			return 1;
		}

		lwsl_parser("accepted v%02d connection\n",
						       wsi->ietf_spec_revision);
	} /* while all chars are handled */

	return 0;

bail_nuke_ah:
	/* drop the header info */
	if (wsi->u.hdr.ah)
		free(wsi->u.hdr.ah);
	return 1;
}
示例#7
0
int
lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
{
	struct lws_context *context = lws_get_context(wsi);
	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
	struct _lws_header_related hdr;
	struct allocated_headers *ah;
	int protocol_len, n, hit;
	char protocol_list[128];
	char protocol_name[32];
	char *p;

	assert(len < 10000000);
	assert(wsi->u.hdr.ah);

	while (len--) {
		wsi->more_rx_waiting = !!len;

		if (wsi->mode != LWSCM_HTTP_SERVING &&
		    wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED) {
			lwsl_err("%s: bad wsi mode %d\n", __func__, wsi->mode);
			goto bail_nuke_ah;
		}

		if (lws_parse(wsi, *(*buf)++)) {
			lwsl_info("lws_parse failed\n");
			goto bail_nuke_ah;
		}

		if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
			continue;

		lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
		lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__,
				wsi->more_rx_waiting);

		/* select vhost */

		if (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)
				wsi->vhost = vhost;
		}

		wsi->vhost->trans++;
		if (!wsi->conn_stat_done) {
			wsi->vhost->conn++;
			wsi->conn_stat_done = 1;
		}

		wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT;
		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

		/* is this websocket protocol or normal http 1.0? */

		if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
			if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
					"websocket")) {
				wsi->vhost->ws_upgrades++;
				lwsl_info("Upgrade to ws\n");
				goto upgrade_ws;
			}
#ifdef LWS_USE_HTTP2
			if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
					"h2c")) {
				wsi->vhost->http2_upgrades++;
				lwsl_info("Upgrade to h2c\n");
				goto upgrade_h2c;
			}
#endif
			lwsl_err("Unknown upgrade\n");
			/* dunno what he wanted to upgrade to */
			goto bail_nuke_ah;
		}

		/* no upgrade ack... he remained as HTTP */

		lwsl_info("No upgrade\n");
		ah = wsi->u.hdr.ah;

		lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED);
		wsi->state = LWSS_HTTP;
		wsi->u.http.fd = LWS_INVALID_FILE;

		/* expose it at the same offset as u.hdr */
		wsi->u.http.ah = ah;
		lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
			   (void *)wsi->u.hdr.ah);

		n = lws_http_action(wsi);

		return n;

#ifdef LWS_USE_HTTP2
upgrade_h2c:
		if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
			lwsl_err("missing http2_settings\n");
			goto bail_nuke_ah;
		}

		lwsl_err("h2c upgrade...\n");

		p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
		/* convert the peer's HTTP-Settings */
		n = lws_b64_decode_string(p, protocol_list,
					  sizeof(protocol_list));
		if (n < 0) {
			lwsl_parser("HTTP2_SETTINGS too long\n");
			return 1;
		}

		/* adopt the header info */

		ah = wsi->u.hdr.ah;

		lws_union_transition(wsi, LWSCM_HTTP2_SERVING);

		/* http2 union member has http union struct at start */
		wsi->u.http.ah = ah;

		lws_http2_init(&wsi->u.http2.peer_settings);
		lws_http2_init(&wsi->u.http2.my_settings);

		/* HTTP2 union */

		lws_http2_interpret_settings_payload(&wsi->u.http2.peer_settings,
				(unsigned char *)protocol_list, n);

		strcpy(protocol_list,
		       "HTTP/1.1 101 Switching Protocols\x0d\x0a"
		      "Connection: Upgrade\x0d\x0a"
		      "Upgrade: h2c\x0d\x0a\x0d\x0a");
		n = lws_issue_raw(wsi, (unsigned char *)protocol_list,
					strlen(protocol_list));
		if (n != strlen(protocol_list)) {
			lwsl_debug("http2 switch: ERROR writing to socket\n");
			return 1;
		}

		wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE;

		return 0;
#endif

upgrade_ws:
		if (!wsi->protocol)
			lwsl_err("NULL protocol at lws_read\n");

		/*
		 * It's websocket
		 *
		 * Select the first protocol we support from the list
		 * the client sent us.
		 *
		 * Copy it to remove header fragmentation
		 */

		if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
				 WSI_TOKEN_PROTOCOL) < 0) {
			lwsl_err("protocol list too long");
			goto bail_nuke_ah;
		}

		protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
		protocol_list[protocol_len] = '\0';
		p = protocol_list;
		hit = 0;

		while (*p && !hit) {
			n = 0;
			while (n < sizeof(protocol_name) - 1 && *p && *p !=',')
				protocol_name[n++] = *p++;
			protocol_name[n] = '\0';
			if (*p)
				p++;

			lwsl_info("checking %s\n", protocol_name);

			n = 0;
			while (wsi->vhost->protocols[n].callback) {
				if (wsi->vhost->protocols[n].name &&
				    !strcmp(wsi->vhost->protocols[n].name,
					    protocol_name)) {
					wsi->protocol = &wsi->vhost->protocols[n];
					hit = 1;
					break;
				}

				n++;
			}
		}

		/* we didn't find a protocol he wanted? */

		if (!hit) {
			if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
				lwsl_err("No protocol from \"%s\" supported\n",
					 protocol_list);
				goto bail_nuke_ah;
			}
			/*
			 * some clients only have one protocol and
			 * do not sent the protocol list header...
			 * allow it and match to protocol 0
			 */
			lwsl_info("defaulting to prot 0 handler\n");
			n = 0;
			wsi->protocol = &wsi->vhost->protocols[0];
		}

		/* allocate wsi->user storage */
		if (lws_ensure_user_space(wsi))
			goto bail_nuke_ah;

		/*
		 * Give the user code a chance to study the request and
		 * have the opportunity to deny it
		 */
		if ((wsi->protocol->callback)(wsi,
				LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
				wsi->user_space,
			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
			lwsl_warn("User code denied connection\n");
			goto bail_nuke_ah;
		}

		/*
		 * stitch protocol choice into the vh protocol linked list
		 */
		wsi->same_vh_protocol_prev = /* guy who points to us */
			&wsi->vhost->same_vh_protocol_list[n];
		wsi->same_vh_protocol_next = /* old first guy is our next */
				wsi->vhost->same_vh_protocol_list[n];
		/* we become the new first guy */
		wsi->vhost->same_vh_protocol_list[n] = wsi;

		/*
		 * Perform the handshake according to the protocol version the
		 * client announced
		 */

		switch (wsi->ietf_spec_revision) {
		case 13:
			lwsl_parser("lws_parse calling handshake_04\n");
			if (handshake_0405(context, wsi)) {
				lwsl_info("hs0405 has failed the connection\n");
				goto bail_nuke_ah;
			}
			break;

		default:
			lwsl_warn("Unknown client spec version %d\n",
				  wsi->ietf_spec_revision);
			goto bail_nuke_ah;
		}

		/* we are upgrading to ws, so http/1.1 and keepalive +
		 * pipelined header considerations about keeping the ah around
		 * no longer apply.  However it's common for the first ws
		 * protocol data to have been coalesced with the browser
		 * upgrade request and to already be in the ah rx buffer.
		 */

		lwsl_info("%s: %p: inheriting ah in ws mode (rxpos:%d, rxlen:%d)\n",
			  __func__, wsi, wsi->u.hdr.ah->rxpos,
			  wsi->u.hdr.ah->rxlen);
		lws_pt_lock(pt);
		hdr = wsi->u.hdr;

		lws_union_transition(wsi, LWSCM_WS_SERVING);
		/*
		 * first service is WS mode will notice this, use the RX and
		 * then detach the ah (caution: we are not in u.hdr union
		 * mode any more then... ah_temp member is at start the same
		 * though)
		 *
		 * Because rxpos/rxlen shows something in the ah, we will get
		 * service guaranteed next time around the event loop
		 *
		 * All union members begin with hdr, so we can use it even
		 * though we transitioned to ws union mode (the ah detach
		 * code uses it anyway).
		 */
		wsi->u.hdr = hdr;
		lws_pt_unlock(pt);

		/*
		 * create the frame buffer for this connection according to the
		 * size mentioned in the protocol definition.  If 0 there, use
		 * a big default for compatibility
		 */

		n = wsi->protocol->rx_buffer_size;
		if (!n)
			n = LWS_MAX_SOCKET_IO_BUF;
		n += LWS_PRE;
		wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */);
		if (!wsi->u.ws.rx_ubuf) {
			lwsl_err("Out of Mem allocating rx buffer %d\n", n);
			return 1;
		}
		wsi->u.ws.rx_ubuf_alloc = n;
		lwsl_info("Allocating RX buffer %d\n", n);
#if LWS_POSIX
		if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF,
			       (const char *)&n, sizeof n)) {
			lwsl_warn("Failed to set SNDBUF to %d", n);
			return 1;
		}
#endif
		lwsl_parser("accepted v%02d connection\n",
			    wsi->ietf_spec_revision);

		return 0;
	} /* while all chars are handled */

	return 0;

bail_nuke_ah:
	/* drop the header info */
	/* we're closing, losing some rx is OK */
	wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
	lws_header_table_detach(wsi, 1);

	return 1;
}
示例#8
0
int
lws_context_init_server(struct lws_context_creation_info *info,
			struct lws_context *context)
{
#ifdef LWS_POSIX
	int n, opt = 1, limit = 1;
#endif
	lws_sockfd_type sockfd;
	struct lws *wsi;
	int m = 0;

	/* set up our external listening socket we serve on */

	if (info->port == CONTEXT_PORT_NO_LISTEN)
		return 0;

#if LWS_POSIX
#if defined(__linux__)
	limit = context->count_threads;
#endif

	for (m = 0; m < limit; m++) {
#ifdef LWS_USE_IPV6
	if (LWS_IPV6_ENABLED(context))
		sockfd = socket(AF_INET6, SOCK_STREAM, 0);
	else
#endif
		sockfd = socket(AF_INET, SOCK_STREAM, 0);

	if (sockfd == -1) {
#else
	sockfd = mbed3_create_tcp_stream_socket();
	if (!lws_sockfd_valid(sockfd)) {
#endif
		lwsl_err("ERROR opening socket\n");
		return 1;
	}

#if LWS_POSIX
	/*
	 * allow us to restart even if old sockets in TIME_WAIT
	 */
	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
		       (const void *)&opt, sizeof(opt)) < 0) {
		compatible_close(sockfd);
		return 1;
	}
#if defined(__linux__) && defined(SO_REUSEPORT) && LWS_MAX_SMP > 1
	if (context->count_threads > 1)
		if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,
				(const void *)&opt, sizeof(opt)) < 0) {
			compatible_close(sockfd);
			return 1;
		}
#endif
#endif
	lws_plat_set_socket_options(context, sockfd);

#if LWS_POSIX
	n = lws_socket_bind(context, sockfd, info->port, info->iface);
	if (n < 0)
		goto bail;
	info->port = n;
#endif
	context->listen_port = info->port;

	wsi = lws_zalloc(sizeof(struct lws));
	if (wsi == NULL) {
		lwsl_err("Out of mem\n");
		goto bail;
	}
	wsi->context = context;
	wsi->sock = sockfd;
	wsi->mode = LWSCM_SERVER_LISTENER;
	wsi->protocol = context->protocols;
	wsi->tsi = m;

	context->pt[m].wsi_listening = wsi;
	if (insert_wsi_socket_into_fds(context, wsi))
		goto bail;

	context->count_wsi_allocated++;
	context->pt[m].lserv_fd = sockfd;

#if LWS_POSIX
	listen(wsi->sock, LWS_SOMAXCONN);
	} /* for each thread able to independently lister */
#else
	mbed3_tcp_stream_bind(wsi->sock, info->port, wsi);
#endif
	lwsl_notice(" Listening on port %d\n", info->port);

	return 0;

bail:
	compatible_close(sockfd);

	return 1;
}

int
_lws_server_listen_accept_flow_control(struct lws *twsi, int on)
{
	struct lws_context_per_thread *pt = &twsi->context->pt[(int)twsi->tsi];
	struct lws *wsi = pt->wsi_listening;
	int n;

	if (!wsi || twsi->context->being_destroyed)
		return 0;

	lwsl_debug("%s: Thr %d: LISTEN wsi %p: state %d\n",
		   __func__, twsi->tsi, (void *)wsi, on);

	if (on)
		n = lws_change_pollfd(wsi, 0, LWS_POLLIN);
	else
		n = lws_change_pollfd(wsi, LWS_POLLIN, 0);

	return n;
}

int
lws_http_action(struct lws *wsi)
{
	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
	enum http_connection_type connection_type;
	enum http_version request_version;
	char content_length_str[32];
	unsigned int n, count = 0;
	char http_version_str[10];
	char http_conn_str[20];
	int http_version_len;
	char *uri_ptr = NULL;
	int uri_len = 0;

	static const unsigned char methods[] = {
		WSI_TOKEN_GET_URI,
		WSI_TOKEN_POST_URI,
		WSI_TOKEN_OPTIONS_URI,
		WSI_TOKEN_PUT_URI,
		WSI_TOKEN_PATCH_URI,
		WSI_TOKEN_DELETE_URI,
#ifdef LWS_USE_HTTP2
		WSI_TOKEN_HTTP_COLON_PATH,
#endif
	};
#ifdef _DEBUG
	static const char * const method_names[] = {
		"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE",
#ifdef LWS_USE_HTTP2
		":path",
#endif
	};
#endif

	/* it's not websocket.... shall we accept it as http? */

	for (n = 0; n < ARRAY_SIZE(methods); n++)
		if (lws_hdr_total_length(wsi, methods[n]))
			count++;
	if (!count) {
		lwsl_warn("Missing URI in HTTP request\n");
		goto bail_nuke_ah;
	}

	if (count != 1) {
		lwsl_warn("multiple methods?\n");
		goto bail_nuke_ah;
	}

	if (lws_ensure_user_space(wsi))
		goto bail_nuke_ah;

	for (n = 0; n < ARRAY_SIZE(methods); n++)
		if (lws_hdr_total_length(wsi, methods[n])) {
			uri_ptr = lws_hdr_simple_ptr(wsi, methods[n]);
			uri_len = lws_hdr_total_length(wsi, methods[n]);
			lwsl_info("Method: %s request for '%s'\n",
				  	method_names[n], uri_ptr);
			break;
		}

	/* HTTP header had a content length? */

	wsi->u.http.content_length = 0;
	if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||
		lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||
		lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI))
		wsi->u.http.content_length = 100 * 1024 * 1024;

	if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
		lws_hdr_copy(wsi, content_length_str,
			     sizeof(content_length_str) - 1,
			     WSI_TOKEN_HTTP_CONTENT_LENGTH);
		wsi->u.http.content_length = atoi(content_length_str);
	}

	/* http_version? Default to 1.0, override with token: */
	request_version = HTTP_VERSION_1_0;

	/* Works for single digit HTTP versions. : */
	http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
	if (http_version_len > 7) {
		lws_hdr_copy(wsi, http_version_str,
				sizeof(http_version_str) - 1, WSI_TOKEN_HTTP);
		if (http_version_str[5] == '1' && http_version_str[7] == '1')
			request_version = HTTP_VERSION_1_1;
	}
	wsi->u.http.request_version = request_version;

	/* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
	if (request_version == HTTP_VERSION_1_1)
		connection_type = HTTP_CONNECTION_KEEP_ALIVE;
	else
		connection_type = HTTP_CONNECTION_CLOSE;

	/* Override default if http "Connection:" header: */
	if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
		lws_hdr_copy(wsi, http_conn_str, sizeof(http_conn_str) - 1,
			     WSI_TOKEN_CONNECTION);
		http_conn_str[sizeof(http_conn_str) - 1] = '\0';
		if (!strcasecmp(http_conn_str, "keep-alive"))
			connection_type = HTTP_CONNECTION_KEEP_ALIVE;
		else
			if (!strcasecmp(http_conn_str, "close"))
				connection_type = HTTP_CONNECTION_CLOSE;
	}
	wsi->u.http.connection_type = connection_type;

	n = wsi->protocol->callback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION,
				    wsi->user_space, uri_ptr, uri_len);
	if (n) {
		lwsl_info("LWS_CALLBACK_HTTP closing\n");

		return 1;
	}
	/*
	 * if there is content supposed to be coming,
	 * put a timeout on it having arrived
	 */
	lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
			wsi->context->timeout_secs);

	if (wsi->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 + 512;

		if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST))
			goto bail_nuke_ah;
		if (lws_add_http_header_status(wsi, 301, &p, end))
			goto bail_nuke_ah;
		n = sprintf((char *)end, "https://%s/",
			    lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
		if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION,
				end, n, &p, end))
			goto bail_nuke_ah;
		if (lws_finalize_http_header(wsi, &p, end))
			goto bail_nuke_ah;
		n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
		if (n < 0)
			goto bail_nuke_ah;

		return lws_http_transaction_completed(wsi);
	}

	n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
				    wsi->user_space, uri_ptr, uri_len);
	if (n) {
		lwsl_info("LWS_CALLBACK_HTTP closing\n");

		return 1;
	}

	/*
	 * 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 (wsi->state != LWSS_HTTP_ISSUING_FILE)
		/* Prepare to read body if we have a content length: */
		if (wsi->u.http.content_length > 0)
			wsi->state = LWSS_HTTP_BODY;

	return 0;

bail_nuke_ah:
	/* we're closing, losing some rx is OK */
	wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
	lws_header_table_detach(wsi, 1);

	return 1;
}


int
lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
{
	struct lws_context *context = lws_get_context(wsi);
	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
	struct _lws_header_related hdr;
	struct allocated_headers *ah;
	int protocol_len, n, hit;
	char protocol_list[128];
	char protocol_name[32];
	char *p;

	assert(len < 10000000);
	assert(wsi->u.hdr.ah);

	while (len--) {
		wsi->more_rx_waiting = !!len;

		assert(wsi->mode == LWSCM_HTTP_SERVING);

		if (lws_parse(wsi, *(*buf)++)) {
			lwsl_info("lws_parse failed\n");
			goto bail_nuke_ah;
		}

		if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
			continue;

		lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
		lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__,
				wsi->more_rx_waiting);

		wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT;
		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);

		/* is this websocket protocol or normal http 1.0? */

		if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
			if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
					"websocket")) {
				lwsl_info("Upgrade to ws\n");
				goto upgrade_ws;
			}
#ifdef LWS_USE_HTTP2
			if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
					"h2c-14")) {
				lwsl_info("Upgrade to h2c-14\n");
				goto upgrade_h2c;
			}
#endif
			lwsl_err("Unknown upgrade\n");
			/* dunno what he wanted to upgrade to */
			goto bail_nuke_ah;
		}

		/* no upgrade ack... he remained as HTTP */

		lwsl_info("No upgrade\n");
		ah = wsi->u.hdr.ah;

		lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED);
		wsi->state = LWSS_HTTP;
		wsi->u.http.fd = LWS_INVALID_FILE;

		/* expose it at the same offset as u.hdr */
		wsi->u.http.ah = ah;
		lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi, (void *)wsi->u.hdr.ah);

		n = lws_http_action(wsi);

		return n;

#ifdef LWS_USE_HTTP2
upgrade_h2c:
		if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
			lwsl_err("missing http2_settings\n");
			goto bail_nuke_ah;
		}

		lwsl_err("h2c upgrade...\n");

		p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
		/* convert the peer's HTTP-Settings */
		n = lws_b64_decode_string(p, protocol_list,
					  sizeof(protocol_list));
		if (n < 0) {
			lwsl_parser("HTTP2_SETTINGS too long\n");
			return 1;
		}

		/* adopt the header info */

		ah = wsi->u.hdr.ah;

		lws_union_transition(wsi, LWSCM_HTTP2_SERVING);

		/* http2 union member has http union struct at start */
		wsi->u.http.ah = ah;

		lws_http2_init(&wsi->u.http2.peer_settings);
		lws_http2_init(&wsi->u.http2.my_settings);

		/* HTTP2 union */

		lws_http2_interpret_settings_payload(&wsi->u.http2.peer_settings,
				(unsigned char *)protocol_list, n);

		strcpy(protocol_list,
		       "HTTP/1.1 101 Switching Protocols\x0d\x0a"
		      "Connection: Upgrade\x0d\x0a"
		      "Upgrade: h2c\x0d\x0a\x0d\x0a");
		n = lws_issue_raw(wsi, (unsigned char *)protocol_list,
					strlen(protocol_list));
		if (n != strlen(protocol_list)) {
			lwsl_debug("http2 switch: ERROR writing to socket\n");
			return 1;
		}

		wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE;

		return 0;
#endif

upgrade_ws:
		if (!wsi->protocol)
			lwsl_err("NULL protocol at lws_read\n");

		/*
		 * It's websocket
		 *
		 * Select the first protocol we support from the list
		 * the client sent us.
		 *
		 * Copy it to remove header fragmentation
		 */

		if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
				 WSI_TOKEN_PROTOCOL) < 0) {
			lwsl_err("protocol list too long");
			goto bail_nuke_ah;
		}

		protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
		protocol_list[protocol_len] = '\0';
		p = protocol_list;
		hit = 0;

		while (*p && !hit) {
			unsigned int n = 0;
			while (n < sizeof(protocol_name) - 1 && *p && *p !=',')
				protocol_name[n++] = *p++;
			protocol_name[n] = '\0';
			if (*p)
				p++;

			lwsl_info("checking %s\n", protocol_name);

			n = 0;
			while (context->protocols[n].callback) {
				if (context->protocols[n].name &&
				    !strcmp(context->protocols[n].name,
					    protocol_name)) {
					lwsl_info("prot match %d\n", n);
					wsi->protocol = &context->protocols[n];
					hit = 1;
					break;
				}

				n++;
			}
		}

		/* we didn't find a protocol he wanted? */

		if (!hit) {
			if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
				lwsl_err("No protocol from \"%s\" supported\n",
					 protocol_list);
				goto bail_nuke_ah;
			}
			/*
			 * some clients only have one protocol and
			 * do not sent the protocol list header...
			 * allow it and match to protocol 0
			 */
			lwsl_info("defaulting to prot 0 handler\n");
			wsi->protocol = &context->protocols[0];
		}

		/* allocate wsi->user storage */
		if (lws_ensure_user_space(wsi))
			goto bail_nuke_ah;

		/*
		 * Give the user code a chance to study the request and
		 * have the opportunity to deny it
		 */

		if ((wsi->protocol->callback)(wsi,
				LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
				wsi->user_space,
			      lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
			lwsl_warn("User code denied connection\n");
			goto bail_nuke_ah;
		}

		/*
		 * Perform the handshake according to the protocol version the
		 * client announced
		 */

		switch (wsi->ietf_spec_revision) {
		case 13:
			lwsl_parser("lws_parse calling handshake_04\n");
			if (handshake_0405(context, wsi)) {
				lwsl_info("hs0405 has failed the connection\n");
				goto bail_nuke_ah;
			}
			break;

		default:
			lwsl_warn("Unknown client spec version %d\n",
				  wsi->ietf_spec_revision);
			goto bail_nuke_ah;
		}

		/* we are upgrading to ws, so http/1.1 and keepalive +
		 * pipelined header considerations about keeping the ah around
		 * no longer apply.  However it's common for the first ws
		 * protocol data to have been coalesced with the browser
		 * upgrade request and to already be in the ah rx buffer.
		 */

		lwsl_info("%s: %p: inheriting ah in ws mode (rxpos:%d, rxlen:%d)\n",
			  __func__, wsi, wsi->u.hdr.ah->rxpos,
			  wsi->u.hdr.ah->rxlen);
		lws_pt_lock(pt);
		hdr = wsi->u.hdr;

		lws_union_transition(wsi, LWSCM_WS_SERVING);
		/*
		 * first service is WS mode will notice this, use the RX and
		 * then detach the ah (caution: we are not in u.hdr union
		 * mode any more then... ah_temp member is at start the same
		 * though)
		 *
		 * Because rxpos/rxlen shows something in the ah, we will get
		 * service guaranteed next time around the event loop
		 *
		 * All union members begin with hdr, so we can use it even
		 * though we transitioned to ws union mode (the ah detach
		 * code uses it anyway).
		 */
		wsi->u.hdr = hdr;
		lws_pt_unlock(pt);

		/*
		 * create the frame buffer for this connection according to the
		 * size mentioned in the protocol definition.  If 0 there, use
		 * a big default for compatibility
		 */

		n = wsi->protocol->rx_buffer_size;
		if (!n)
			n = LWS_MAX_SOCKET_IO_BUF;
		n += LWS_PRE;
		wsi->u.ws.rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */);
		if (!wsi->u.ws.rx_ubuf) {
			lwsl_err("Out of Mem allocating rx buffer %d\n", n);
			return 1;
		}
		wsi->u.ws.rx_ubuf_alloc = n;
		lwsl_info("Allocating RX buffer %d\n", n);
#if LWS_POSIX
		if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF,
			       (const char *)&n, sizeof n)) {
			lwsl_warn("Failed to set SNDBUF to %d", n);
			return 1;
		}
#endif
		lwsl_parser("accepted v%02d connection\n",
			    wsi->ietf_spec_revision);

		return 0;
	} /* while all chars are handled */

	return 0;

bail_nuke_ah:
	/* drop the header info */
	/* we're closing, losing some rx is OK */
	wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
	lws_header_table_detach(wsi, 1);

	return 1;
}

static int
lws_get_idlest_tsi(struct lws_context *context)
{
	unsigned int lowest = ~0;
	int n = 0, hit = -1;

	for (; n < context->count_threads; n++) {
		if ((unsigned int)context->pt[n].fds_count !=
		    context->fd_limit_per_thread - 1 &&
		    (unsigned int)context->pt[n].fds_count < lowest) {
			lowest = context->pt[n].fds_count;
			hit = n;
		}
	}

	return hit;
}
示例#9
0
LWS_VISIBLE int
libwebsocket_read(struct libwebsocket_context *context,
		     struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
	size_t n;
	struct allocated_headers *ah;
	char *uri_ptr = NULL;
	int uri_len = 0;
	char content_length_str[32];

	switch (wsi->state) {

	case WSI_STATE_HTTP_BODY:
http_postbody:
		while (len--) {

			if (wsi->u.http.content_length_seen >= wsi->u.http.content_length)
				break;

			wsi->u.http.post_buffer[wsi->u.http.body_index++] = *buf++;
			wsi->u.http.content_length_seen++;
			n = wsi->protocol->rx_buffer_size;
			if (!n)
				n = LWS_MAX_SOCKET_IO_BUF;

			if (wsi->u.http.body_index != n &&
			    wsi->u.http.content_length_seen != wsi->u.http.content_length)
				continue;

			if (wsi->protocol->callback) {
				n = wsi->protocol->callback(
					wsi->protocol->owning_server, wsi,
					    LWS_CALLBACK_HTTP_BODY,
					    wsi->user_space, wsi->u.http.post_buffer,
							wsi->u.http.body_index);
				wsi->u.http.body_index = 0;
				if (n)
					goto bail;
			}

			if (wsi->u.http.content_length_seen == wsi->u.http.content_length) {
				/* he sent the content in time */
				libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
				n = wsi->protocol->callback(
					wsi->protocol->owning_server, wsi,
					    LWS_CALLBACK_HTTP_BODY_COMPLETION,
					    wsi->user_space, NULL, 0);
				wsi->u.http.body_index = 0;
				if (n)
					goto bail;
			}

		}

		/*
		 * we need to spill here so everything is seen in the case
		 * there is no content-length
		 */
		if (wsi->u.http.body_index && wsi->protocol->callback) {
			n = wsi->protocol->callback(
				wsi->protocol->owning_server, wsi,
				    LWS_CALLBACK_HTTP_BODY,
				    wsi->user_space, wsi->u.http.post_buffer,
						wsi->u.http.body_index);
			wsi->u.http.body_index = 0;
			if (n)
				goto bail;
		}
		break;

	case WSI_STATE_HTTP_ISSUING_FILE:
	case WSI_STATE_HTTP:
		wsi->state = WSI_STATE_HTTP_HEADERS;
		wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
		wsi->u.hdr.lextable_pos = 0;
		/* fallthru */
	case WSI_STATE_HTTP_HEADERS:

		lwsl_parser("issuing %d bytes to parser\n", (int)len);

#ifndef LWS_NO_SERVER
		/* LWS_CONNMODE_WS_SERVING */

		while (len--) {
			if (libwebsocket_parse(wsi, *buf++)) {
				lwsl_info("libwebsocket_parse failed\n");
				goto bail_nuke_ah;
			}

			if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
				continue;

			lwsl_parser("libwebsocket_parse sees parsing complete\n");

			wsi->mode = LWS_CONNMODE_PRE_WS_SERVING_ACCEPT;

			/* is this websocket protocol or normal http 1.0? */

			if (!lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) ||
				     !lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {

				/* it's not websocket.... shall we accept it as http? */

				if (!lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) &&
				    !lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
					lwsl_warn("Missing URI in HTTP request\n");
					goto bail_nuke_ah;
				}

				if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) &&
				    lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
					lwsl_warn("GET and POST methods?\n");
					goto bail_nuke_ah;
				}

				if (libwebsocket_ensure_user_space(wsi))
					goto bail_nuke_ah;

				if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) {
					uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
					uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
					lwsl_info("HTTP GET request for '%s'\n",
					    lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI));

				}
				if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
					lwsl_info("HTTP POST request for '%s'\n",
					   lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI));
					uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI);
					uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI);
				}

				/*
				 * Hm we still need the headers so the
				 * callback can look at leaders like the URI, but we
				 * need to transition to http union state.... hold a
				 * copy of u.hdr.ah and deallocate afterwards
				 */
				ah = wsi->u.hdr.ah;

				/* union transition */
				memset(&wsi->u, 0, sizeof(wsi->u));
				wsi->mode = LWS_CONNMODE_HTTP_SERVING_ACCEPTED;
				wsi->state = WSI_STATE_HTTP;
				wsi->u.http.fd = -1;

				/* expose it at the same offset as u.hdr */
				wsi->u.http.ah = ah;

				/* HTTP header had a content length? */

				wsi->u.http.content_length = 0;
				if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
					wsi->u.http.content_length = 100 * 1024 * 1024;

				if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
					lws_hdr_copy(wsi, content_length_str,
							sizeof(content_length_str) - 1,
									WSI_TOKEN_HTTP_CONTENT_LENGTH);
					wsi->u.http.content_length = atoi(content_length_str);
				}

				if (wsi->u.http.content_length > 0) {
					wsi->u.http.body_index = 0;
					n = wsi->protocol->rx_buffer_size;
					if (!n)
						n = LWS_MAX_SOCKET_IO_BUF;
					wsi->u.http.post_buffer = malloc(n);
					if (!wsi->u.http.post_buffer) {
						lwsl_err("Unable to allocate post buffer\n");
						n = -1;
						goto leave;
					}
				}

				n = 0;
				if (wsi->protocol->callback)
					n = wsi->protocol->callback(context, wsi,
						LWS_CALLBACK_FILTER_HTTP_CONNECTION,
						     wsi->user_space, uri_ptr, uri_len);

				if (!n && wsi->protocol->callback)
					n = wsi->protocol->callback(context, wsi,
					    LWS_CALLBACK_HTTP,
					    wsi->user_space, uri_ptr, uri_len);

leave:
				/* now drop the header info we kept a pointer to */
				if (ah)
					free(ah);
				/* not possible to continue to use past here */
				wsi->u.http.ah = NULL;

				if (n) {
					lwsl_info("LWS_CALLBACK_HTTP closing\n");
					goto bail; /* struct ah ptr already nuked */
				}

				/*
				 * if there is content supposed to be coming,
				 * put a timeout on it having arrived
				 */
				libwebsocket_set_timeout(wsi,
					PENDING_TIMEOUT_HTTP_CONTENT,
							      AWAITING_TIMEOUT);

				/*
				 * (if callback didn't start sending a file)
				 * deal with anything else as body, whether
				 * there was a content-length or not
				 */

				if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE)
					wsi->state = WSI_STATE_HTTP_BODY;
				goto http_postbody;
			}

			if (!wsi->protocol)
				lwsl_err("NULL protocol at libwebsocket_read\n");

			/*
			 * It's websocket
			 *
			 * Make sure user side is happy about protocol
			 */

			while (wsi->protocol->callback) {

				if (!lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) {
					if (wsi->protocol->name == NULL)
						break;
				} else
					if (wsi->protocol->name && strcmp(
						lws_hdr_simple_ptr(wsi,
							WSI_TOKEN_PROTOCOL),
							      wsi->protocol->name) == 0)
						break;

				wsi->protocol++;
			}

			/* we didn't find a protocol he wanted? */

			if (wsi->protocol->callback == NULL) {
				if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL) ==
										 NULL) {
					lwsl_info("no protocol -> prot 0 handler\n");
					wsi->protocol = &context->protocols[0];
				} else {
					lwsl_err("Req protocol %s not supported\n",
					   lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL));
					goto bail_nuke_ah;
				}
			}

			/* allocate wsi->user storage */
			if (libwebsocket_ensure_user_space(wsi))
				goto bail_nuke_ah;

			/*
			 * Give the user code a chance to study the request and
			 * have the opportunity to deny it
			 */

			if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
					LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
					wsi->user_space,
				      lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
				lwsl_warn("User code denied connection\n");
				goto bail_nuke_ah;
			}


			/*
			 * Perform the handshake according to the protocol version the
			 * client announced
			 */

			switch (wsi->ietf_spec_revision) {
			case 13:
				lwsl_parser("lws_parse calling handshake_04\n");
				if (handshake_0405(context, wsi)) {
					lwsl_info("hs0405 has failed the connection\n");
					goto bail_nuke_ah;
				}
				break;

			default:
				lwsl_warn("Unknown client spec version %d\n",
							       wsi->ietf_spec_revision);
				goto bail_nuke_ah;
			}

			/* drop the header info -- no bail_nuke_ah after this */

			if (wsi->u.hdr.ah)
				free(wsi->u.hdr.ah);

			wsi->mode = LWS_CONNMODE_WS_SERVING;

			/* union transition */
			memset(&wsi->u, 0, sizeof(wsi->u));
			wsi->u.ws.rxflow_change_to = LWS_RXFLOW_ALLOW;

			/*
			 * create the frame buffer for this connection according to the
			 * size mentioned in the protocol definition.  If 0 there, use
			 * a big default for compatibility
			 */

			n = wsi->protocol->rx_buffer_size;
			if (!n)
				n = LWS_MAX_SOCKET_IO_BUF;
			n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
			wsi->u.ws.rx_user_buffer = malloc(n);
			if (!wsi->u.ws.rx_user_buffer) {
				lwsl_err("Out of Mem allocating rx buffer %d\n", n);
				goto bail;
			}
			lwsl_info("Allocating RX buffer %d\n", n);

			if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF,  &n, sizeof n)) {
				lwsl_warn("Failed to set SNDBUF to %d", n);
				goto bail;
			}

			lwsl_parser("accepted v%02d connection\n",
							       wsi->ietf_spec_revision);
#endif
		} /* while all chars are handled */
		break;

	case WSI_STATE_AWAITING_CLOSE_ACK:
	case WSI_STATE_ESTABLISHED:
#ifndef LWS_NO_SERVER
		/* LWS_CONNMODE_WS_SERVING */

		if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) {
			lwsl_info("interpret_incoming_packet has bailed\n");
			goto bail;
		}
#endif
		break;
	default:
		lwsl_err("libwebsocket_read: Unhandled state\n");
		break;
	}

	return 0;

bail_nuke_ah:
	/* drop the header info */
	if (wsi->u.hdr.ah)
		free(wsi->u.hdr.ah);

bail:
	lwsl_info("closing connection at libwebsocket_read bail:\n");

	libwebsocket_close_and_free_session(context, wsi,
						     LWS_CLOSE_STATUS_NOSTATUS);

	return -1;
}