1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

close add api to control sent close frame contents

This adds an api lws_close_reason() which lets you control what will
be sent in the close frame when the connection is closed by returning
nonzero from the user callback.

The test server demo is extended to prove it works in both directions.

With this, we should have nice close support.

https://github.com/warmcat/libwebsockets/issues/196

Signed-off-by: Andy Green <andy.green@linaro.org>
This commit is contained in:
Andy Green 2015-12-26 17:20:34 +08:00
parent 066a7a1801
commit 1fb95e8084
8 changed files with 137 additions and 39 deletions

View file

@ -66,6 +66,39 @@ lwsts[15714]: 3: 0x79
lwsts[15714]: 4: 0x65
lwsts[15714]: 5: 0x21
3) There is a new API to allow the user code to control the content of the
close frame sent when about to return nonzero from the user callback to
indicate the connection should close.
/**
* lws_close_reason - Set reason and aux data to send with Close packet
* If you are going to return nonzero from the callback
* requesting the connection to close, you can optionally
* call this to set the reason the peer will be told if
* possible.
*
* @wsi: The websocket connection to set the close reason on
* @status: A valid close status from websocket standard
* @buf: NULL or buffer containing up to 124 bytes of auxiliary data
* @len: Length of data in @buf to send
*/
LWS_VISIBLE LWS_EXTERN void
lws_close_reason(struct lws *wsi, enum lws_close_status status,
unsigned char *buf, size_t len);
An extra button is added to the "open and close" test server page that requests
that the test server close the connection from his end.
The test server code will do so by
lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
(unsigned char *)"seeya", 5);
return -1;
The browser shows the close code and reason he received
websocket connection CLOSED, code: 1001, reason: seeya
User api changes
----------------
@ -84,6 +117,11 @@ extra 2 bytes space at the end of your buffer. This ONLY applies to
LWS_WRITE_CLOSE, which you normally don't send directly, but cause by returning
nonzero from a callback letting the library actually send it.
2) Because of lws_close_reason() formalizing handling close frames,
LWS_WRITE_CLOSE is removed from libwebsockets.h. It was only of use to send
close frames...close frame content should be managed using lws_close_reason()
now.
v1.6.0-chrome48-firefox42

View file

@ -270,6 +270,14 @@ spill:
}
lwsl_parser("client sees server close len = %d\n",
wsi->u.ws.rx_user_buffer_head);
if (user_callback_handle_rxflow(
wsi->protocol->callback, wsi,
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
wsi->user_space,
&wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING],
wsi->u.ws.rx_user_buffer_head))
return -1;
/*
* parrot the close packet payload back
* we do not care about how it went, we are closing
@ -287,6 +295,10 @@ spill:
lwsl_info("received %d byte ping, sending pong\n",
wsi->u.ws.rx_user_buffer_head);
/* he set a close reason on this guy, ignore PING */
if (wsi->u.ws.close_in_ping_buffer_len)
goto ping_drop;
if (wsi->u.ws.ping_pending_flag) {
/*
* there is already a pending ping payload

View file

@ -74,7 +74,6 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
struct lws_context *context = wsi->context;
int n, m, ret, old_state;
struct lws_tokens eff_buf;
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 4];
if (!wsi)
return;
@ -118,8 +117,6 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
break;
}
wsi->u.ws.close_reason = reason;
if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT ||
wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE)
goto just_kill_connection;
@ -188,13 +185,24 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
*/
if (old_state == LWSS_ESTABLISHED &&
reason != LWS_CLOSE_STATUS_NOSTATUS &&
reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY) {
(wsi->u.ws.close_in_ping_buffer_len || /* already a reason */
(reason != LWS_CLOSE_STATUS_NOSTATUS &&
(reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) {
lwsl_debug("sending close indication...\n");
/* make valgrind happy */
memset(buf, 0, sizeof(buf));
n = lws_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING + 2],
0, LWS_WRITE_CLOSE);
/* if no prepared close reason, use 1000 and no aux data */
if (!wsi->u.ws.close_in_ping_buffer_len) {
wsi->u.ws.close_in_ping_buffer_len = 2;
wsi->u.ws.ping_payload_buf[LWS_SEND_BUFFER_PRE_PADDING] =
(reason >> 16) & 0xff;
wsi->u.ws.ping_payload_buf[LWS_SEND_BUFFER_PRE_PADDING + 1] =
reason & 0xff;
}
n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[
LWS_SEND_BUFFER_PRE_PADDING],
wsi->u.ws.close_in_ping_buffer_len,
LWS_WRITE_CLOSE);
if (n >= 0) {
/*
* we have sent a nice protocol level indication we
@ -944,3 +952,25 @@ lws_wsi_user(struct lws *wsi)
{
return wsi->user_space;
}
LWS_VISIBLE LWS_EXTERN void
lws_close_reason(struct lws *wsi, enum lws_close_status status,
unsigned char *buf, size_t len)
{
unsigned char *p, *start;
int budget = sizeof(wsi->u.ws.ping_payload_buf) -
LWS_SEND_BUFFER_PRE_PADDING;
assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT);
start = p = &wsi->u.ws.ping_payload_buf[LWS_SEND_BUFFER_PRE_PADDING];
*p++ = (((int)status) >> 8) & 0xff;
*p++ = ((int)status) & 0xff;
if (buf)
while (len-- && p < start + budget)
*p++ = *buf++;
wsi->u.ws.close_in_ping_buffer_len = p - start;
}

View file

@ -453,7 +453,7 @@ enum lws_write_protocol {
/* special 04+ opcodes */
LWS_WRITE_CLOSE = 4,
/* LWS_WRITE_CLOSE is handled by lws_close_reason() */
LWS_WRITE_PING = 5,
LWS_WRITE_PONG = 6,
@ -1409,7 +1409,6 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
* LWS_WRITE_TEXT,
* LWS_WRITE_BINARY,
* LWS_WRITE_CONTINUATION,
* LWS_WRITE_CLOSE,
* LWS_WRITE_PING,
* LWS_WRITE_PONG
*
@ -1428,13 +1427,6 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
* memset(&buf[LWS_SEND_BUFFER_PRE_PADDING], 0, 128);
*
* lws_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], 128, LWS_WRITE_TEXT);
*
* When sending
*
* LWS_WRITE_CLOSE
*
* only, you must allow your buffer to be 2 bytes longer than otherwise
* needed.
*
* When sending HTTP, with
*
@ -1476,6 +1468,22 @@ LWS_VISIBLE LWS_EXTERN int
lws_write(struct lws *wsi, unsigned char *buf, size_t len,
enum lws_write_protocol protocol);
/**
* lws_close_reason - Set reason and aux data to send with Close packet
* If you are going to return nonzero from the callback
* requesting the connection to close, you can optionally
* call this to set the reason the peer will be told if
* possible.
*
* @wsi: The websocket connection to set the close reason on
* @status: A valid close status from websocket standard
* @buf: NULL or buffer containing up to 124 bytes of auxiliary data
* @len: Length of data in @buf to send
*/
LWS_VISIBLE LWS_EXTERN void
lws_close_reason(struct lws *wsi, enum lws_close_status status,
unsigned char *buf, size_t len);
/* helper for case where buffer may be const */
#define lws_write_http(wsi, buf, len) \
lws_write(wsi, (unsigned char *)(buf), len, LWS_WRITE_HTTP)

View file

@ -278,7 +278,7 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf,
eff_buf.token = (char *)buf;
eff_buf.token_len = len;
switch (protocol) {
switch ((int)protocol) {
case LWS_WRITE_PING:
case LWS_WRITE_PONG:
case LWS_WRITE_CLOSE:
@ -325,18 +325,6 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf,
case LWS_WRITE_CLOSE:
n = LWSWSOPC_CLOSE;
/*
* 06+ has a 2-byte status code in network order
* we can do this because we demand post-buf
*/
if (wsi->u.ws.close_reason) {
/* reason codes count as data bytes */
buf[0] = (unsigned char)(wsi->u.ws.close_reason >> 8);
buf[1] = (unsigned char)wsi->u.ws.close_reason;
len += 2;
}
break;
case LWS_WRITE_PING:
n = LWSWSOPC_PING;
@ -416,7 +404,7 @@ do_more_inside_frame:
}
send_raw:
switch (protocol) {
switch ((int)protocol) {
case LWS_WRITE_CLOSE:
/* lwsl_hexdump(&buf[-pre], len); */
case LWS_WRITE_HTTP:

View file

@ -431,6 +431,9 @@ enum {
LWS_RXFLOW_PENDING_CHANGE = (1 << 1),
};
/* this is not usable directly by user code any more, lws_close_reason() */
#define LWS_WRITE_CLOSE 4
struct lws_protocols;
struct lws;
@ -835,12 +838,15 @@ struct _lws_websocket_related {
size_t rx_packet_length;
unsigned int rx_user_buffer_head;
unsigned char mask_nonce[4];
unsigned char ping_payload_buf[128 - 4 + LWS_SEND_BUFFER_PRE_PADDING]; /* control opc == < 124 */
short close_reason; /* enum lws_close_status */
/* Also used for close content... control opcode == < 128 */
unsigned char ping_payload_buf[128 - 4 + LWS_SEND_BUFFER_PRE_PADDING];
unsigned char ping_payload_len;
unsigned char frame_mask_index;
unsigned char opcode;
unsigned char rsv;
/* zero if no info, or length including 2-byte close code */
unsigned char close_in_ping_buffer_len;
unsigned int final:1;
unsigned int frame_is_binary:1;

View file

@ -56,6 +56,12 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
break;
if (strcmp((const char *)in, "reset\n") == 0)
pss->number = 0;
if (strcmp((const char *)in, "closeme\n") == 0) {
lwsl_notice("dumb_inc: closing as requested\n");
lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
(unsigned char *)"seeya", 5);
return -1;
}
break;
/*
* this just demonstrates how to use the protocol filter. If you won't

View file

@ -84,13 +84,15 @@ run.
<tr>
<td align=center><input type=button id=ot_open_btn value="Open" onclick="ot_open();" ></td>
<td align=center><input type=button id=ot_close_btn disabled value="Close" onclick="ot_close();" ></td>
<td align=center><input type=button id=ot_req_close_btn disabled value="Request Server Close" onclick="ot_req_close();" ></td>
</tr>
<tr><td colspan="2" id=ot_statustd align=center class="explain"><div id=ot_status>Not initialized</div></td></tr>
<tr><td colspan="3" id=ot_statustd align=center class="explain"><div id=ot_status>Not initialized</div></td></tr>
</tr>
</table>
</td><td class="explain">
To help with open and close testing, you can open and close a connection by hand using
the buttons.
the buttons. "Request Server Close" sends a message asking the server to
initiate the close.
</td></tr></table>
</section>
<br>
@ -310,24 +312,32 @@ function ot_open() {
document.getElementById("ot_status").textContent = " websocket connection opened ";
document.getElementById("ot_open_btn").disabled = true;
document.getElementById("ot_close_btn").disabled = false;
document.getElementById("ot_req_close_btn").disabled = false;
}
socket_ot.onclose = function(){
socket_ot.onclose = function(e){
document.getElementById("ot_statustd").style.backgroundColor = "#ff4040";
document.getElementById("ot_status").textContent = " websocket connection CLOSED ";
document.getElementById("ot_status").textContent = " websocket connection CLOSED, code: " + e.code +
", reason: " + e.reason;
document.getElementById("ot_open_btn").disabled = false;
document.getElementById("ot_close_btn").disabled = true;
document.getElementById("ot_req_close_btn").disabled = true;
}
} catch(exception) {
alert('<p>Error' + exception);
}
}
/* browser will close the ws in a controlled way */
function ot_close() {
socket_ot.close(3000, "Bye!");
}
/* we ask the server to close the ws in a controlled way */
function ot_req_close() {
socket_ot.send("closeme\n");
}
/* lws-mirror protocol */
var down = 0;