diff --git a/changelog b/changelog index 97c0f647..f0278dc9 100644 --- a/changelog +++ b/changelog @@ -23,6 +23,11 @@ User api changes LWS_CALLBACK_FILTER_NETWORK_CONNECTION also has the socket descriptor delivered by @in now instead of @user. + - libwebsocket_write() now returns -1 for error, or the amount of data + actually accepted for send. Under load, the OS may signal it is + ready to send new data on the socket, but have only a restricted + amount of memory to buffer the packet compared to usual. + User api removal ---------------- diff --git a/lib/client-parser.c b/lib/client-parser.c index 4125efd9..e548b4f0 100644 --- a/lib/client-parser.c +++ b/lib/client-parser.c @@ -273,7 +273,11 @@ spill: } lwsl_parser("client sees server close len = %d\n", wsi->u.ws.rx_user_buffer_head); - /* parrot the close packet payload back */ + /* + * parrot the close packet payload back + * we do not care about how it went, we are closing + * immediately afterwards + */ libwebsocket_write(wsi, (unsigned char *) &wsi->u.ws.rx_user_buffer[ LWS_SEND_BUFFER_PRE_PADDING], @@ -284,7 +288,11 @@ spill: case LWS_WS_OPCODE_07__PING: lwsl_info("client received ping, doing pong\n"); - /* parrot the ping packet payload back as a pong*/ + /* + * parrot the ping packet payload back as a pong + * !!! this may block or have partial write or fail + * !!! very unlikely if the ping size is small + */ libwebsocket_write(wsi, (unsigned char *) &wsi->u.ws.rx_user_buffer[ LWS_SEND_BUFFER_PRE_PADDING], diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 7414fe74..0c85f0af 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -276,7 +276,7 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context, if (eff_buf.token_len) if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token, - eff_buf.token_len)) { + eff_buf.token_len) != eff_buf.token_len) { lwsl_debug("close: ext spill failed\n"); goto just_kill_connection; } @@ -305,7 +305,7 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context, n = libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING + 2], 0, LWS_WRITE_CLOSE); - if (!n) { + if (n >= 0) { /* * we have sent a nice protocol level indication we * now wish to close, we should not send anything more @@ -698,9 +698,18 @@ lws_handle_POLLOUT_event(struct libwebsocket_context *context, /* assuming they gave us something to send, send it */ if (eff_buf.token_len) { - if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token, - eff_buf.token_len)) + n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len); + if (n < 0) return -1; + /* + * Keep amount spilled small to minimize chance of this + */ + if (n != eff_buf.token_len) { + lwsl_err("Unable to spill ext %d vs %s\n", + eff_buf.token_len, n); + return -1; + } } else continue; diff --git a/lib/output.c b/lib/output.c index 35389c23..2d8c3694 100644 --- a/lib/output.c +++ b/lib/output.c @@ -90,6 +90,10 @@ void lwsl_hexdump(void *vbuf, size_t len) #endif +/* + * notice this returns number of bytes sent, or -1 + */ + int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len) { struct libwebsocket_context *context = wsi->protocol->owning_server; @@ -117,7 +121,7 @@ int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len) } if (m) /* handled */ { /* lwsl_ext("ext sent it\n"); */ - return 0; + return m; } } #endif @@ -146,14 +150,14 @@ int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len) #endif n = send(wsi->sock, buf, len, MSG_NOSIGNAL); lws_latency(context, wsi, "send lws_issue_raw", n, n == len); - if (n != len) { + if (n < 0) { lwsl_debug("ERROR writing len %d to skt %d\n", len, n); return -1; } #ifdef LWS_OPENSSL_SUPPORT } #endif - return 0; + return n; } #ifdef LWS_NO_EXTENSIONS @@ -210,10 +214,21 @@ lws_issue_raw_ext_access(struct libwebsocket *wsi, /* assuming they left us something to send, send it */ - if (eff_buf.token_len) - if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token, - eff_buf.token_len)) + if (eff_buf.token_len) { + n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len); + if (n < 0) return -1; + /* + * Keep amount spilled small to minimize chance of this + */ + if (n != eff_buf.token_len) { + lwsl_err("Unable to spill ext %d vs %s\n", + eff_buf.token_len, n); + return -1; + } + + } lwsl_parser("written %d bytes to client\n", eff_buf.token_len); @@ -247,7 +262,7 @@ lws_issue_raw_ext_access(struct libwebsocket *wsi, ret = 0; } - return 0; + return len; } #endif @@ -274,6 +289,11 @@ lws_issue_raw_ext_access(struct libwebsocket *wsi, * valid storage before and after buf as explained above. This scheme * allows maximum efficiency of sending data and protocol in a single * packet while not burdening the user code with any protocol knowledge. + * + * Return may be -1 for a fatal error needing connection close, or a + * positive number reflecting the amount of bytes actually sent. This + * can be less than the requested number of bytes due to OS memory + * pressure at any given time. */ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, @@ -285,6 +305,7 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, int masked7 = wsi->mode == LWS_CONNMODE_WS_CLIENT; unsigned char *dropmask = NULL; unsigned char is_masked_bit = 0; + size_t orig_len = len; #ifndef LWS_NO_EXTENSIONS struct lws_tokens eff_buf; int m; @@ -423,7 +444,7 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, if (libwebsocket_0405_frame_mask_generate(wsi)) { lwsl_err("lws_write: frame mask generation failed\n"); - return 1; + return -1; } /* @@ -453,11 +474,8 @@ send_raw: case LWS_WRITE_HTTP: case LWS_WRITE_PONG: case LWS_WRITE_PING: - if (lws_issue_raw(wsi, (unsigned char *)buf - pre, - len + pre + post)) - return -1; - - return 0; + return lws_issue_raw(wsi, (unsigned char *)buf - pre, + len + pre + post); default: break; } @@ -476,26 +494,36 @@ send_raw: * callback returns 1 in case it wants to spill more buffers */ - return lws_issue_raw_ext_access(wsi, buf - pre, len + pre + post); + n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre + post); + if (n < 0) + return n; + + return orig_len - ((len - pre + post) -n ); } int libwebsockets_serve_http_file_fragment( struct libwebsocket_context *context, struct libwebsocket *wsi) { int ret = 0; - int n; + int n, m; while (!lws_send_pipe_choked(wsi)) { n = read(wsi->u.http.fd, context->service_buffer, sizeof(context->service_buffer)); if (n > 0) { - libwebsocket_write(wsi, context->service_buffer, n, + m = libwebsocket_write(wsi, context->service_buffer, n, LWS_WRITE_HTTP); + if (m < 0) + return -1; + wsi->u.http.filepos += n; + if (m != n) + /* adjust for what was not sent */ + lseek(wsi->u.http.fd, m - n, SEEK_CUR); } if (n < 0) - return 1; /* caller will close */ + return -1; /* caller will close */ if (n < sizeof(context->service_buffer) || wsi->u.http.filepos == wsi->u.http.filelen) { @@ -552,6 +580,7 @@ int libwebsockets_serve_http_file(struct libwebsocket_context *context, "HTTP/1.0 400 Bad\x0d\x0aServer: libwebsockets\x0d\x0a\x0d\x0a" ); wsi->u.http.fd = 0; + /* too small to care about partial, closing anyway */ libwebsocket_write(wsi, context->service_buffer, p - context->service_buffer, LWS_WRITE_HTTP); @@ -569,8 +598,10 @@ int libwebsockets_serve_http_file(struct libwebsocket_context *context, ret = libwebsocket_write(wsi, context->service_buffer, p - context->service_buffer, LWS_WRITE_HTTP); - if (ret) + if (ret != (p - context->service_buffer)) { + lwsl_err("_write returned %d from %d\n", ret, (p - context->service_buffer)); return -1; + } wsi->u.http.filepos = 0; wsi->state = WSI_STATE_HTTP_ISSUING_FILE; diff --git a/lib/parsers.c b/lib/parsers.c index e6b0d241..804ec9e3 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -935,7 +935,7 @@ spill: LWS_SEND_BUFFER_PRE_PADDING], wsi->u.ws.rx_user_buffer_head, LWS_WRITE_CLOSE); - if (n) + if (n < 0) lwsl_info("write of close ack failed %d\n", n); wsi->state = WSI_STATE_RETURNED_CLOSE_ALREADY; /* close the connection */ @@ -951,6 +951,8 @@ spill: n = libwebsocket_write(wsi, (unsigned char *) &wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING], wsi->u.ws.rx_user_buffer_head, LWS_WRITE_PONG); + if (n < 0) + return -1; /* ... then just drop it */ wsi->u.ws.rx_user_buffer_head = 0; return 0; diff --git a/lib/server-handshake.c b/lib/server-handshake.c index 75938fe9..53da4e7a 100644 --- a/lib/server-handshake.c +++ b/lib/server-handshake.c @@ -241,7 +241,7 @@ handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi) #endif n = libwebsocket_write(wsi, (unsigned char *)response, p - response, LWS_WRITE_HTTP); - if (n < 0) { + if (n != (p - response)) { lwsl_debug("handshake_0405: ERROR writing to socket\n"); goto bail; } diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html index a38145cd..e4ad60bb 100644 --- a/libwebsockets-api-doc.html +++ b/libwebsockets-api-doc.html @@ -438,6 +438,11 @@ In the case of sending using websocket protocol, be sure to allocate valid storage before and after buf as explained above. This scheme allows maximum efficiency of sending data and protocol in a single packet while not burdening the user code with any protocol knowledge. +
+Return may be -1 for a fatal error needing connection close, or a +positive number reflecting the amount of bytes actually sent. This +can be less than the requested number of bytes due to OS memory +pressure at any given time.