diff --git a/include/libwebsockets/lws-write.h b/include/libwebsockets/lws-write.h index b73e51c78..6e0be6b34 100644 --- a/include/libwebsockets/lws-write.h +++ b/include/libwebsockets/lws-write.h @@ -123,6 +123,7 @@ struct lws_write_passthru { /** * lws_write() - Apply protocol then write data to client + * * \param wsi: Websocket instance (available from user callback) * \param buf: The data to send. For data being sent on a websocket * connection (ie, not default http), this buffer MUST have @@ -216,7 +217,17 @@ lws_write(struct lws *wsi, unsigned char *buf, size_t len, #define lws_write_http(wsi, buf, len) \ lws_write(wsi, (unsigned char *)(buf), len, LWS_WRITE_HTTP) -/* helper for multi-frame ws message flags */ +/** + * lws_write_ws_flags() - Helper for multi-frame ws message flags + * + * \param initial: the lws_write flag to use for the start fragment, eg, + * LWS_WRITE_TEXT + * \param is_start: nonzero if this is the first fragment of the message + * \param is_end: nonzero if this is the last fragment of the message + * + * Returns the correct LWS_WRITE_ flag to use for each fragment of a message + * in turn. + */ static LWS_INLINE int lws_write_ws_flags(int initial, int is_start, int is_end) { @@ -232,4 +243,21 @@ lws_write_ws_flags(int initial, int is_start, int is_end) return r; } + +/** + * lws_raw_transaction_completed() - Helper for flushing before close + * + * \param wsi: the struct lws to operate on + * + * Returns -1 if the wsi can close now. However if there is buffered, unsent + * data, the wsi is marked as to be closed when the output buffer data is + * drained, and it returns 0. + * + * For raw cases where the transaction completed without failure, + * `return lws_raw_transaction_completed(wsi)` should better be used than + * return -1. + */ +LWS_VISIBLE int LWS_WARN_UNUSED_RESULT +lws_raw_transaction_completed(struct lws *wsi); + ///@} diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c index 661493c51..35218a385 100644 --- a/lib/core/libwebsockets.c +++ b/lib/core/libwebsockets.c @@ -1437,6 +1437,29 @@ lws_get_network_wsi(struct lws *wsi) return wsi; } + +LWS_VISIBLE int LWS_WARN_UNUSED_RESULT +lws_raw_transaction_completed(struct lws *wsi) +{ + if (lws_has_buffered_out(wsi)) { + /* + * ...so he tried to send something large, but it went out + * as a partial, but he immediately called us to say he wants + * to close the connection. + * + * Defer the close until the last part of the partial is sent. + * + */ + lwsl_debug("%s: %p: deferring due to partial\n", __func__, wsi); + wsi->close_when_buffered_out_drained = 1; + lws_callback_on_writable(wsi); + + return 0; + } + + return -1; +} + LWS_VISIBLE LWS_EXTERN const struct lws_protocols * lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name) { diff --git a/lib/core/output.c b/lib/core/output.c index 49d289db4..e58aa45b2 100644 --- a/lib/core/output.c +++ b/lib/core/output.c @@ -150,6 +150,11 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len) return -1; /* retry closing now */ } + if (wsi->close_when_buffered_out_drained) { + wsi->close_when_buffered_out_drained = 0; + return -1; + } + #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) #if !defined(LWS_WITHOUT_SERVER) if (wsi->http.deferred_transaction_completed) { diff --git a/lib/core/private.h b/lib/core/private.h index 295f27d51..9a8dc8684 100644 --- a/lib/core/private.h +++ b/lib/core/private.h @@ -980,6 +980,7 @@ struct lws { unsigned int handling_404:1; unsigned int protocol_bind_balance:1; unsigned int unix_skt:1; + unsigned int close_when_buffered_out_drained:1; unsigned int could_have_pending:1; /* detect back-to-back writes */ unsigned int outer_will_close:1;