Skip to content
Snippets Groups Projects
Commit 480c2120 authored by Paul Sokolovsky's avatar Paul Sokolovsky
Browse files

extmod/modwebsocket: Handle CLOSE control frame.

This fixes situation when clients hangs waiting for disconnect and does
so only on timeout.
parent 351ec6d4
Branches
No related tags found
No related merge requests found
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
#if MICROPY_PY_WEBSOCKET #if MICROPY_PY_WEBSOCKET
enum { FRAME_HEADER, FRAME_OPT, PAYLOAD }; enum { FRAME_HEADER, FRAME_OPT, PAYLOAD, CONTROL };
enum { BLOCKING_WRITE = 0x80 }; enum { BLOCKING_WRITE = 0x80 };
...@@ -52,10 +52,14 @@ typedef struct _mp_obj_websocket_t { ...@@ -52,10 +52,14 @@ typedef struct _mp_obj_websocket_t {
byte buf_pos; byte buf_pos;
byte buf[6]; byte buf[6];
byte opts; byte opts;
// Copy of current frame's flags // Copy of last data frame flags
byte ws_flags; byte ws_flags;
// Copy of current frame flags
byte last_flags;
} mp_obj_websocket_t; } mp_obj_websocket_t;
STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode);
STATIC mp_obj_t websocket_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { STATIC mp_obj_t websocket_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, 2, false); mp_arg_check_num(n_args, n_kw, 1, 2, false);
mp_obj_websocket_t *o = m_new_obj(mp_obj_websocket_t); mp_obj_websocket_t *o = m_new_obj(mp_obj_websocket_t);
...@@ -97,10 +101,9 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int ...@@ -97,10 +101,9 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
// "Control frames MAY be injected in the middle of a fragmented message." // "Control frames MAY be injected in the middle of a fragmented message."
// So, they must be processed before data frames (and not alter // So, they must be processed before data frames (and not alter
// self->ws_flags) // self->ws_flags)
if ((self->buf[0] & FRAME_OPCODE_MASK) >= FRAME_CLOSE) { byte frame_type = self->buf[0];
// TODO: implement self->last_flags = frame_type;
assert(0); frame_type &= FRAME_OPCODE_MASK;
}
if ((self->buf[0] & FRAME_OPCODE_MASK) == FRAME_CONT) { if ((self->buf[0] & FRAME_OPCODE_MASK) == FRAME_CONT) {
// Preserve previous frame type // Preserve previous frame type
...@@ -119,7 +122,7 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int ...@@ -119,7 +122,7 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
// Msg size is next 2 bytes // Msg size is next 2 bytes
to_recv += 2; to_recv += 2;
} else if (sz == 127) { } else if (sz == 127) {
// Msg size is next 2 bytes // Msg size is next 8 bytes
assert(0); assert(0);
} }
if (self->buf[1] & 0x80) { if (self->buf[1] & 0x80) {
...@@ -132,9 +135,13 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int ...@@ -132,9 +135,13 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
self->msg_sz = sz; // May be overriden by FRAME_OPT self->msg_sz = sz; // May be overriden by FRAME_OPT
if (to_recv != 0) { if (to_recv != 0) {
self->state = FRAME_OPT; self->state = FRAME_OPT;
} else {
if (frame_type >= FRAME_CLOSE) {
self->state = CONTROL;
} else { } else {
self->state = PAYLOAD; self->state = PAYLOAD;
} }
}
continue; continue;
} }
...@@ -148,13 +155,24 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int ...@@ -148,13 +155,24 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
memcpy(self->mask, self->buf + self->buf_pos - 4, 4); memcpy(self->mask, self->buf + self->buf_pos - 4, 4);
} }
self->buf_pos = 0; self->buf_pos = 0;
if ((self->last_flags & FRAME_OPCODE_MASK) >= FRAME_CLOSE) {
self->state = CONTROL;
} else {
self->state = PAYLOAD; self->state = PAYLOAD;
}
continue; continue;
} }
case PAYLOAD: { case PAYLOAD:
case CONTROL: {
mp_uint_t out_sz = 0;
if (self->msg_sz == 0) {
// In case message had zero payload
goto no_payload;
}
size_t sz = MIN(size, self->msg_sz); size_t sz = MIN(size, self->msg_sz);
mp_uint_t out_sz = stream_p->read(self->sock, buf, sz, errcode); out_sz = stream_p->read(self->sock, buf, sz, errcode);
if (out_sz == 0 || out_sz == MP_STREAM_ERROR) { if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
return out_sz; return out_sz;
} }
...@@ -166,13 +184,35 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int ...@@ -166,13 +184,35 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
self->msg_sz -= out_sz; self->msg_sz -= out_sz;
if (self->msg_sz == 0) { if (self->msg_sz == 0) {
byte last_state;
no_payload:
last_state = self->state;
self->state = FRAME_HEADER; self->state = FRAME_HEADER;
self->to_recv = 2; self->to_recv = 2;
self->mask_pos = 0; self->mask_pos = 0;
self->buf_pos = 0; self->buf_pos = 0;
// Handle control frame
if (last_state == CONTROL) {
byte frame_type = self->last_flags & FRAME_OPCODE_MASK;
if (frame_type == FRAME_CLOSE) {
static char close_resp[2] = {0x88, 0};
int err;
websocket_write(self_in, close_resp, sizeof(close_resp), &err);
return 0;
}
//DEBUG_printf("Finished receiving ctrl message %x, ignoring\n", self->last_flags);
continue;
} }
}
if (out_sz != 0) {
return out_sz; return out_sz;
} }
// Empty (data) frame received is not EOF
continue;
}
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment