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
----------------
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
}
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
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
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;
break;
}
- wsi->u.ws.close_reason = reason;
-
if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT ||
wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE)
goto just_kill_connection;
*/
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
{
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;
+}
/* special 04+ opcodes */
- LWS_WRITE_CLOSE = 4,
+ /* LWS_WRITE_CLOSE is handled by lws_close_reason() */
LWS_WRITE_PING = 5,
LWS_WRITE_PONG = 6,
* LWS_WRITE_TEXT,
* LWS_WRITE_BINARY,
* LWS_WRITE_CONTINUATION,
- * LWS_WRITE_CLOSE,
* LWS_WRITE_PING,
* LWS_WRITE_PONG
*
* 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
*
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)
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:
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;
}
send_raw:
- switch (protocol) {
+ switch ((int)protocol) {
case LWS_WRITE_CLOSE:
/* lwsl_hexdump(&buf[-pre], len); */
case LWS_WRITE_HTTP:
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;
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;
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
<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>
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;