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; }
int lws_client_socket_service(struct libwebsocket_context *context, struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd) { int n; char *p = (char *)&context->service_buffer[0]; int len; unsigned char c; switch (wsi->mode) { case LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT: /* * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE * timeout protection set in client-handshake.c */ if (libwebsocket_client_connect_2(context, wsi) == NULL) { /* closed */ lwsl_client("closed\n"); return -1; } /* either still pending connection, or changed mode */ return 0; case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY: /* handle proxy hung up on us */ if (pollfd->revents & LWS_POLLHUP) { lwsl_warn("Proxy connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } n = recv(wsi->sock, context->service_buffer, sizeof(context->service_buffer), 0); if (n < 0) { if (LWS_ERRNO == LWS_EAGAIN) { lwsl_debug( "Proxy read returned EAGAIN... retrying\n"); return 0; } libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR reading from proxy socket\n"); return 0; } context->service_buffer[13] = '\0'; if (strcmp((char *)context->service_buffer, "HTTP/1.0 200 ")) { libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR proxy: %s\n", context->service_buffer); return 0; } /* clear his proxy connection timeout */ libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); /* fallthru */ case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE: /* * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE * timeout protection set in client-handshake.c */ /* * take care of our libwebsocket_callback_on_writable * happening at a time when there's no real connection yet */ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) return -1; #ifdef LWS_OPENSSL_SUPPORT /* we can retry this... just cook the SSL BIO the first time */ if (wsi->use_ssl && !wsi->ssl) { #if defined(CYASSL_SNI_HOST_NAME) || defined(SSL_CTRL_SET_TLSEXT_HOSTNAME) const char *hostname = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); #endif wsi->ssl = SSL_new(context->ssl_client_ctx); #ifndef USE_CYASSL SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); #endif /* * use server name indication (SNI), if supported, * when establishing connection */ #ifdef USE_CYASSL #ifdef CYASSL_SNI_HOST_NAME CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, hostname, strlen(hostname)); #endif #else #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME SSL_set_tlsext_host_name(wsi->ssl, hostname); #endif #endif #ifdef USE_CYASSL /* * CyaSSL does certificate verification differently * from OpenSSL. * If we should ignore the certificate, we need to set * this before SSL_new and SSL_connect is called. * Otherwise the connect will simply fail with error * code -155 */ if (wsi->use_ssl == 2) CyaSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL); #endif /* USE_CYASSL */ wsi->client_bio = BIO_new_socket(wsi->sock, BIO_NOCLOSE); SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio); #ifdef USE_CYASSL CyaSSL_set_using_nonblock(wsi->ssl, 1); #else BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */ #endif SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index, context); } if (wsi->use_ssl) { lws_latency_pre(context, wsi); n = SSL_connect(wsi->ssl); lws_latency(context, wsi, "SSL_connect LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE", n, n > 0); if (n < 0) { n = SSL_get_error(wsi->ssl, n); if (n == SSL_ERROR_WANT_READ || n == SSL_ERROR_WANT_WRITE) { /* * wants us to retry connect due to * state of the underlying ssl layer... * but since it may be stalled on * blocked write, no incoming data may * arrive to trigger the retry. * Force (possibly many times if the SSL * state persists in returning the * condition code, but other sockets * are getting serviced inbetweentimes) * us to get called back when writable. */ lwsl_info( "SSL_connect WANT_... retrying\n"); libwebsocket_callback_on_writable( context, wsi); wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SSL; return 0; /* no error */ } n = -1; } if (n <= 0) { /* * retry if new data comes until we * run into the connection timeout or win */ n = ERR_get_error(); if (n != SSL_ERROR_NONE) { lwsl_err("SSL connect error %lu: %s\n", n, ERR_error_string(n, (char *)context->service_buffer)); return 0; } } } else wsi->ssl = NULL; /* fallthru */ case LWS_CONNMODE_WS_CLIENT_WAITING_SSL: if (wsi->use_ssl) { if (wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_SSL) { lws_latency_pre(context, wsi); n = SSL_connect(wsi->ssl); lws_latency(context, wsi, "SSL_connect LWS_CONNMODE_WS_CLIENT_WAITING_SSL", n, n > 0); if (n < 0) { n = SSL_get_error(wsi->ssl, n); if (n == SSL_ERROR_WANT_READ || n == SSL_ERROR_WANT_WRITE) { /* * wants us to retry connect due to * state of the underlying ssl layer... * but since it may be stalled on * blocked write, no incoming data may * arrive to trigger the retry. * Force (possibly many times if the SSL * state persists in returning the * condition code, but other sockets * are getting serviced inbetweentimes) * us to get called back when writable. */ lwsl_info( "SSL_connect WANT_... retrying\n"); libwebsocket_callback_on_writable( context, wsi); wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SSL; return 0; /* no error */ } n = -1; } if (n <= 0) { /* * retry if new data comes until we * run into the connection timeout or win */ n = ERR_get_error(); if (n != SSL_ERROR_NONE) { lwsl_err("SSL connect error %lu: %s\n", n, ERR_error_string(n, (char *)context->service_buffer)); return 0; } } } #ifndef USE_CYASSL /* * See comment above about CyaSSL certificate * verification */ lws_latency_pre(context, wsi); n = SSL_get_verify_result(wsi->ssl); lws_latency(context, wsi, "SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0); if (n != X509_V_OK) { if((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) && wsi->use_ssl == 2) { lwsl_notice("accepting self-signed certificate\n"); } else { lwsl_err( "server's cert didn't look good %d\n", n); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } } #endif /* USE_CYASSL */ } else wsi->ssl = NULL; #endif wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE2; libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, AWAITING_TIMEOUT); /* fallthru */ case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE2: p = libwebsockets_generate_client_handshake(context, wsi, p); if (p == NULL) { lwsl_err("Failed to generate handshake for client\n"); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } /* send our request to the server */ lws_latency_pre(context, wsi); n = lws_ssl_capable_write(wsi, context->service_buffer, p - (char *)context->service_buffer); lws_latency(context, wsi, "send lws_issue_raw", n, n == p - (char *)context->service_buffer); switch (n) { case LWS_SSL_CAPABLE_ERROR: lwsl_debug("ERROR writing to client socket\n"); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; case LWS_SSL_CAPABLE_MORE_SERVICE: libwebsocket_callback_on_writable(context, wsi); break; } wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; wsi->u.hdr.lextable_pos = 0; wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY; libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, AWAITING_TIMEOUT); break; case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY: /* handle server hung up on us */ if (pollfd->revents & LWS_POLLHUP) { lwsl_debug("Server connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); goto bail3; } if (!(pollfd->revents & LWS_POLLIN)) break; /* interpret the server response */ /* * HTTP/1.1 101 Switching Protocols * Upgrade: websocket * Connection: Upgrade * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== * Sec-WebSocket-Protocol: chat */ /* * we have to take some care here to only take from the * socket bytewise. The browser may (and has been seen to * in the case that onopen() performs websocket traffic) * coalesce both handshake response and websocket traffic * in one packet, since at that point the connection is * definitively ready from browser pov. */ len = 1; while (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE && len > 0) { n = lws_ssl_capable_read(context, wsi, &c, 1); lws_latency(context, wsi, "send lws_issue_raw", n, n == 1); switch (n) { case LWS_SSL_CAPABLE_ERROR: goto bail3; case LWS_SSL_CAPABLE_MORE_SERVICE: return 0; } if (libwebsocket_parse(context, wsi, c)) { lwsl_warn("problems parsing header\n"); goto bail3; } } /* * hs may also be coming in multiple packets, there is a 5-sec * libwebsocket timeout still active here too, so if parsing did * not complete just wait for next packet coming in this state */ if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) break; /* * otherwise deal with the handshake. If there's any * packet traffic already arrived we'll trigger poll() again * right away and deal with it that way */ return lws_client_interpret_server_handshake(context, wsi); bail3: lwsl_info( "closing connection at LWS_CONNMODE...SERVER_REPLY\n"); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return -1; case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT: lwsl_ext("LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT\n"); break; case LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD: lwsl_ext("LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD\n"); break; default: break; } return 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; }
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; }
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; }