diff --git a/lib/client.c b/lib/client.c index f19d06754..d0265bfc7 100644 --- a/lib/client.c +++ b/lib/client.c @@ -21,8 +21,6 @@ #include "private-libwebsockets.h" - - int lws_handshake_client(struct libwebsocket *wsi, unsigned char **buf, size_t len) { int n; diff --git a/lib/handshake.c b/lib/handshake.c index fd1f5ec36..25dd3cc6f 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -142,29 +142,17 @@ http_postbody: case WSI_STATE_AWAITING_CLOSE_ACK: case WSI_STATE_ESTABLISHED: -#ifndef LWS_NO_CLIENT + if (lws_handshake_client(wsi, &buf, len)) + goto bail; switch (wsi->mode) { - case LWS_CONNMODE_WS_CLIENT: - for (n = 0; n < len; n++) - if (libwebsocket_client_rx_sm( - wsi, *buf++)) { - lwsl_debug("client rx has bailed\n"); - goto bail; - } + case LWS_CONNMODE_WS_SERVING: - return 0; - default: + if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) { + lwsl_info("interpret_incoming_packet has bailed\n"); + goto bail; + } 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"); diff --git a/lib/output.c b/lib/output.c index 7419b8e48..6c060c999 100644 --- a/lib/output.c +++ b/lib/output.c @@ -123,20 +123,21 @@ int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len) * nope, send it on the socket directly */ lws_latency_pre(context, wsi); - n = lws_ssl_capable_write(wsi, buf, len); lws_latency(context, wsi, "send lws_issue_raw", n, n == len); + switch (n) { case LWS_SSL_CAPABLE_ERROR: return -1; case LWS_SSL_CAPABLE_MORE_SERVICE: + /* nothing got sent, not fatal, retry the whole thing later */ n = 0; - goto handle_truncated_send; + break; } handle_truncated_send: /* - * already handling a truncated send? + * we were already handling a truncated send? */ if (wsi->truncated_send_len) { lwsl_info("***** %x partial send moved on by %d (vs %d)\n", @@ -156,54 +157,53 @@ handle_truncated_send: return n; } - if (n < real_len) { - if (n && wsi->u.ws.clean_buffer) - /* - * This buffer unaffected by extension rewriting. - * It means the user code is expected to deal with - * partial sends. (lws knows the header was already - * sent, so on next send will just resume sending - * payload) - */ - return n; + if (n == real_len) + /* what we just sent went out cleanly */ + return n; + if (n && wsi->u.ws.clean_buffer) /* - * Newly truncated send. Buffer the remainder (it will get - * first priority next time the socket is writable) + * This buffer unaffected by extension rewriting. + * It means the user code is expected to deal with + * partial sends. (lws knows the header was already + * sent, so on next send will just resume sending + * payload) */ - lwsl_info("***** %x new partial sent %d from %d total\n", - wsi, n, real_len); + return n; - /* - * - if we still have a suitable malloc lying around, use it - * - or, if too small, reallocate it - * - or, if no buffer, create it - */ - if (!wsi->truncated_send_malloc || - real_len - n > wsi->truncated_send_allocation) { - if (wsi->truncated_send_malloc) - free(wsi->truncated_send_malloc); + /* + * Newly truncated send. Buffer the remainder (it will get + * first priority next time the socket is writable) + */ + lwsl_info("***** %x new partial sent %d from %d total\n", + wsi, n, real_len); - wsi->truncated_send_allocation = real_len - n; - wsi->truncated_send_malloc = malloc(real_len - n); - if (!wsi->truncated_send_malloc) { - lwsl_err( - "truncated send: unable to malloc %d\n", - real_len - n); - return -1; - } + /* + * - if we still have a suitable malloc lying around, use it + * - or, if too small, reallocate it + * - or, if no buffer, create it + */ + if (!wsi->truncated_send_malloc || + real_len - n > wsi->truncated_send_allocation) { + if (wsi->truncated_send_malloc) + free(wsi->truncated_send_malloc); + + wsi->truncated_send_allocation = real_len - n; + wsi->truncated_send_malloc = malloc(real_len - n); + if (!wsi->truncated_send_malloc) { + lwsl_err("truncated send: unable to malloc %d\n", + real_len - n); + return -1; } - wsi->truncated_send_offset = 0; - wsi->truncated_send_len = real_len - n; - memcpy(wsi->truncated_send_malloc, buf + n, real_len - n); - - libwebsocket_callback_on_writable( - wsi->protocol->owning_server, wsi); - - return real_len; } + wsi->truncated_send_offset = 0; + wsi->truncated_send_len = real_len - n; + memcpy(wsi->truncated_send_malloc, buf + n, real_len - n); - return n; + /* since something buffered, force it to get another chance to send */ + libwebsocket_callback_on_writable(wsi->protocol->owning_server, wsi); + + return real_len; } /** @@ -531,12 +531,11 @@ lws_ssl_capable_read_no_ssl(struct libwebsocket *wsi, unsigned char *buf, int le int n; n = recv(wsi->sock, buf, len, 0); - if (n < 0) { - lwsl_warn("error on reading from skt\n"); - return LWS_SSL_CAPABLE_ERROR; - } - - return n; + if (n >= 0) + return n; + + lwsl_warn("error on reading from skt\n"); + return LWS_SSL_CAPABLE_ERROR; } LWS_VISIBLE int @@ -545,17 +544,17 @@ lws_ssl_capable_write_no_ssl(struct libwebsocket *wsi, unsigned char *buf, int l int n; n = send(wsi->sock, buf, len, 0); - if (n < 0) { - if (LWS_ERRNO == LWS_EAGAIN || - LWS_ERRNO == LWS_EWOULDBLOCK || - LWS_ERRNO == LWS_EINTR) { - if (LWS_ERRNO == LWS_EWOULDBLOCK) - lws_set_blocking_send(wsi); - return LWS_SSL_CAPABLE_MORE_SERVICE; - } - lwsl_debug("ERROR writing len %d to skt %d\n", len, n); - return LWS_SSL_CAPABLE_ERROR; - } + if (n >= 0) + return n; - return n; + if (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK || + LWS_ERRNO == LWS_EINTR) { + if (LWS_ERRNO == LWS_EWOULDBLOCK) + lws_set_blocking_send(wsi); + + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + lwsl_debug("ERROR writing len %d to skt %d\n", len, n); + return LWS_SSL_CAPABLE_ERROR; } diff --git a/lib/parsers.c b/lib/parsers.c index aeefcd898..00f77bb2c 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -949,56 +949,6 @@ illegal_ctl_length: } -int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi, - unsigned char *buf, size_t len) -{ - size_t n = 0; - int m; - -#if 0 - lwsl_parser("received %d byte packet\n", (int)len); - lwsl_hexdump(buf, len); -#endif - - /* let the rx protocol state machine have as much as it needs */ - - while (n < len) { - /* - * we were accepting input but now we stopped doing so - */ - if (!(wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW)) { - /* his RX is flowcontrolled, don't send remaining now */ - if (!wsi->u.ws.rxflow_buffer) { - /* a new rxflow, buffer it and warn caller */ - lwsl_info("new rxflow input buffer len %d\n", - len - n); - wsi->u.ws.rxflow_buffer = - (unsigned char *)malloc(len - n); - wsi->u.ws.rxflow_len = len - n; - wsi->u.ws.rxflow_pos = 0; - memcpy(wsi->u.ws.rxflow_buffer, - buf + n, len - n); - } else - /* rxflow while we were spilling prev rxflow */ - lwsl_info("stalling in existing rxflow buf\n"); - - return 1; - } - - /* account for what we're using in rxflow buffer */ - if (wsi->u.ws.rxflow_buffer) - wsi->u.ws.rxflow_pos++; - - /* process the byte */ - m = libwebsocket_rx_sm(wsi, buf[n++]); - if (m < 0) - return -1; - } - - return 0; -} - - /** * libwebsockets_remaining_packet_payload() - Bytes to come before "overall" * rx packet is complete diff --git a/lib/pollfd.c b/lib/pollfd.c index d2a6b3ac6..ee5bf9129 100644 --- a/lib/pollfd.c +++ b/lib/pollfd.c @@ -146,15 +146,14 @@ lws_change_pollfd(struct libwebsocket *wsi, int _and, int _or) pa.fd = wsi->sock; context->protocols[0].callback(context, wsi, - LWS_CALLBACK_LOCK_POLL, - wsi->user_space, (void *) &pa, 0); + LWS_CALLBACK_LOCK_POLL, wsi->user_space, (void *) &pa, 0); pa.prev_events = pfd->events; pa.events = pfd->events = (pfd->events & ~_and) | _or; context->protocols[0].callback(context, wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD, - wsi->user_space, (void *) &pa, 0); + wsi->user_space, (void *) &pa, 0); /* * if we changed something in this pollfd... @@ -178,8 +177,7 @@ lws_change_pollfd(struct libwebsocket *wsi, int _and, int _or) } context->protocols[0].callback(context, wsi, - LWS_CALLBACK_UNLOCK_POLL, - wsi->user_space, (void *) &pa, 0); + LWS_CALLBACK_UNLOCK_POLL, wsi->user_space, (void *) &pa, 0); return 0; } diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index ea92ce85d..be6c2d936 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -621,10 +621,6 @@ libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c); LWS_EXTERN int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c); -LWS_EXTERN int -libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi, - unsigned char *buf, size_t len); - LWS_EXTERN int lws_b64_selftest(void); @@ -723,8 +719,12 @@ int lws_context_init_server(struct lws_context_creation_info *info, struct libwebsocket_context *context); LWS_EXTERN int handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi); +LWS_EXTERN int +libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi, + unsigned char *buf, size_t len); #else #define lws_context_init_server(_a, _b) (0) +#define libwebsocket_interpret_incoming_packet(_a, _b, _c) (0) #endif #ifndef LWS_NO_DAEMONIZE diff --git a/lib/server-handshake.c b/lib/server-handshake.c index b4cdd6604..962b7ebd8 100644 --- a/lib/server-handshake.c +++ b/lib/server-handshake.c @@ -23,9 +23,140 @@ #define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); } -/* - * Perform the newer BASE64-encoded handshake scheme - */ +LWS_VISIBLE int +lws_extension_server_handshake(struct libwebsocket_context *context, + struct libwebsocket *wsi, char **p) +{ + int n; + char *c; + char ext_name[128]; + struct libwebsocket_extension *ext; + int ext_count = 0; + int more = 1; + + /* + * Figure out which extensions the client has that we want to + * enable on this connection, and give him back the list + */ + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) + return 0; + + /* + * break down the list of client extensions + * and go through them + */ + + if (lws_hdr_copy(wsi, (char *)context->service_buffer, + sizeof(context->service_buffer), + WSI_TOKEN_EXTENSIONS) < 0) + return 1; + + c = (char *)context->service_buffer; + lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c); + wsi->count_active_extensions = 0; + n = 0; + while (more) { + + if (*c && (*c != ',' && *c != ' ' && *c != '\t')) { + ext_name[n] = *c++; + if (n < sizeof(ext_name) - 1) + n++; + continue; + } + ext_name[n] = '\0'; + if (!*c) + more = 0; + else { + c++; + if (!n) + continue; + } + + /* check a client's extension against our support */ + + ext = wsi->protocol->owning_server->extensions; + + while (ext && ext->callback) { + + if (strcmp(ext_name, ext->name)) { + ext++; + continue; + } + + /* + * oh, we do support this one he + * asked for... but let's ask user + * code if it's OK to apply it on this + * particular connection + protocol + */ + + n = wsi->protocol->owning_server-> + protocols[0].callback( + wsi->protocol->owning_server, + wsi, + LWS_CALLBACK_CONFIRM_EXTENSION_OKAY, + wsi->user_space, ext_name, 0); + + /* + * zero return from callback means + * go ahead and allow the extension, + * it's what we get if the callback is + * unhandled + */ + + if (n) { + ext++; + continue; + } + + /* apply it */ + + if (ext_count) + *(*p)++ = ','; + else + LWS_CPYAPP(*p, + "\x0d\x0aSec-WebSocket-Extensions: "); + *p += sprintf(*p, "%s", ext_name); + ext_count++; + + /* instantiate the extension on this conn */ + + wsi->active_extensions_user[ + wsi->count_active_extensions] = + malloc(ext->per_session_data_size); + if (wsi->active_extensions_user[ + wsi->count_active_extensions] == NULL) { + lwsl_err("Out of mem\n"); + return 1; + } + memset(wsi->active_extensions_user[ + wsi->count_active_extensions], 0, + ext->per_session_data_size); + + wsi->active_extensions[ + wsi->count_active_extensions] = ext; + + /* allow him to construct his context */ + + ext->callback(wsi->protocol->owning_server, + ext, wsi, + LWS_EXT_CALLBACK_CONSTRUCT, + wsi->active_extensions_user[ + wsi->count_active_extensions], NULL, 0); + + wsi->count_active_extensions++; + lwsl_parser("count_active_extensions <- %d\n", + wsi->count_active_extensions); + + ext++; + } + + n = 0; + } + + return 0; +} int handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi) @@ -35,13 +166,6 @@ handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi) char *response; char *p; int accept_len; -#ifndef LWS_NO_EXTENSIONS - char *c; - char ext_name[128]; - struct libwebsocket_extension *ext; - int ext_count = 0; - int more = 1; -#endif if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) || !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) { @@ -99,129 +223,13 @@ handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi) p += n; } -#ifndef LWS_NO_EXTENSIONS /* * Figure out which extensions the client has that we want to * enable on this connection, and give him back the list */ + if (lws_extension_server_handshake(context, wsi, &p)) + goto bail; - if (lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) { - - /* - * break down the list of client extensions - * and go through them - */ - - if (lws_hdr_copy(wsi, (char *)context->service_buffer, - sizeof(context->service_buffer), - WSI_TOKEN_EXTENSIONS) < 0) - goto bail; - - c = (char *)context->service_buffer; - lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c); - wsi->count_active_extensions = 0; - n = 0; - while (more) { - - if (*c && (*c != ',' && *c != ' ' && *c != '\t')) { - ext_name[n] = *c++; - if (n < sizeof(ext_name) - 1) - n++; - continue; - } - ext_name[n] = '\0'; - if (!*c) - more = 0; - else { - c++; - if (!n) - continue; - } - - /* check a client's extension against our support */ - - ext = wsi->protocol->owning_server->extensions; - - while (ext && ext->callback) { - - if (strcmp(ext_name, ext->name)) { - ext++; - continue; - } - - /* - * oh, we do support this one he - * asked for... but let's ask user - * code if it's OK to apply it on this - * particular connection + protocol - */ - - n = wsi->protocol->owning_server-> - protocols[0].callback( - wsi->protocol->owning_server, - wsi, - LWS_CALLBACK_CONFIRM_EXTENSION_OKAY, - wsi->user_space, ext_name, 0); - - /* - * zero return from callback means - * go ahead and allow the extension, - * it's what we get if the callback is - * unhandled - */ - - if (n) { - ext++; - continue; - } - - /* apply it */ - - if (ext_count) - *p++ = ','; - else - LWS_CPYAPP(p, - "\x0d\x0aSec-WebSocket-Extensions: "); - p += sprintf(p, "%s", ext_name); - ext_count++; - - /* instantiate the extension on this conn */ - - wsi->active_extensions_user[ - wsi->count_active_extensions] = - malloc(ext->per_session_data_size); - if (wsi->active_extensions_user[ - wsi->count_active_extensions] == NULL) { - lwsl_err("Out of mem\n"); - free(response); - goto bail; - } - memset(wsi->active_extensions_user[ - wsi->count_active_extensions], 0, - ext->per_session_data_size); - - wsi->active_extensions[ - wsi->count_active_extensions] = ext; - - /* allow him to construct his context */ - - ext->callback(wsi->protocol->owning_server, - ext, wsi, - LWS_EXT_CALLBACK_CONSTRUCT, - wsi->active_extensions_user[ - wsi->count_active_extensions], NULL, 0); - - wsi->count_active_extensions++; - lwsl_parser("count_active_extensions <- %d\n", - wsi->count_active_extensions); - - ext++; - } - - n = 0; - } - } -#endif /* end of response packet */ LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a"); @@ -233,9 +241,9 @@ handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi) /* okay send the handshake response accepting the connection */ lwsl_parser("issuing resp pkt %d len\n", (int)(p - response)); - #ifdef DEBUG +#ifdef DEBUG fwrite(response, 1, p - response, stderr); - #endif +#endif n = libwebsocket_write(wsi, (unsigned char *)response, p - response, LWS_WRITE_HTTP); if (n != (p - response)) { diff --git a/lib/server.c b/lib/server.c index cf0d68d29..ed2dd202e 100644 --- a/lib/server.c +++ b/lib/server.c @@ -959,3 +959,52 @@ LWS_VISIBLE int libwebsockets_serve_http_file( return libwebsockets_serve_http_file_fragment(context, wsi); } + +int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi, + unsigned char *buf, size_t len) +{ + size_t n = 0; + int m; + +#if 0 + lwsl_parser("received %d byte packet\n", (int)len); + lwsl_hexdump(buf, len); +#endif + + /* let the rx protocol state machine have as much as it needs */ + + while (n < len) { + /* + * we were accepting input but now we stopped doing so + */ + if (!(wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW)) { + /* his RX is flowcontrolled, don't send remaining now */ + if (!wsi->u.ws.rxflow_buffer) { + /* a new rxflow, buffer it and warn caller */ + lwsl_info("new rxflow input buffer len %d\n", + len - n); + wsi->u.ws.rxflow_buffer = + (unsigned char *)malloc(len - n); + wsi->u.ws.rxflow_len = len - n; + wsi->u.ws.rxflow_pos = 0; + memcpy(wsi->u.ws.rxflow_buffer, + buf + n, len - n); + } else + /* rxflow while we were spilling prev rxflow */ + lwsl_info("stalling in existing rxflow buf\n"); + + return 1; + } + + /* account for what we're using in rxflow buffer */ + if (wsi->u.ws.rxflow_buffer) + wsi->u.ws.rxflow_pos++; + + /* process the byte */ + m = libwebsocket_rx_sm(wsi, buf[n++]); + if (m < 0) + return -1; + } + + return 0; +} \ No newline at end of file