Skip to content

Commit 480c212

Browse files
committed
extmod/modwebsocket: Handle CLOSE control frame.
This fixes situation when clients hangs waiting for disconnect and does so only on timeout.
1 parent 351ec6d commit 480c212

1 file changed

Lines changed: 52 additions & 12 deletions

File tree

extmod/modwebsocket.c

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737

3838
#if MICROPY_PY_WEBSOCKET
3939

40-
enum { FRAME_HEADER, FRAME_OPT, PAYLOAD };
40+
enum { FRAME_HEADER, FRAME_OPT, PAYLOAD, CONTROL };
4141

4242
enum { BLOCKING_WRITE = 0x80 };
4343

@@ -52,10 +52,14 @@ typedef struct _mp_obj_websocket_t {
5252
byte buf_pos;
5353
byte buf[6];
5454
byte opts;
55-
// Copy of current frame's flags
55+
// Copy of last data frame flags
5656
byte ws_flags;
57+
// Copy of current frame flags
58+
byte last_flags;
5759
} mp_obj_websocket_t;
5860

61+
STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode);
62+
5963
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) {
6064
mp_arg_check_num(n_args, n_kw, 1, 2, false);
6165
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
97101
// "Control frames MAY be injected in the middle of a fragmented message."
98102
// So, they must be processed before data frames (and not alter
99103
// self->ws_flags)
100-
if ((self->buf[0] & FRAME_OPCODE_MASK) >= FRAME_CLOSE) {
101-
// TODO: implement
102-
assert(0);
103-
}
104+
byte frame_type = self->buf[0];
105+
self->last_flags = frame_type;
106+
frame_type &= FRAME_OPCODE_MASK;
104107

105108
if ((self->buf[0] & FRAME_OPCODE_MASK) == FRAME_CONT) {
106109
// 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
119122
// Msg size is next 2 bytes
120123
to_recv += 2;
121124
} else if (sz == 127) {
122-
// Msg size is next 2 bytes
125+
// Msg size is next 8 bytes
123126
assert(0);
124127
}
125128
if (self->buf[1] & 0x80) {
@@ -133,7 +136,11 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
133136
if (to_recv != 0) {
134137
self->state = FRAME_OPT;
135138
} else {
136-
self->state = PAYLOAD;
139+
if (frame_type >= FRAME_CLOSE) {
140+
self->state = CONTROL;
141+
} else {
142+
self->state = PAYLOAD;
143+
}
137144
}
138145
continue;
139146
}
@@ -148,13 +155,24 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
148155
memcpy(self->mask, self->buf + self->buf_pos - 4, 4);
149156
}
150157
self->buf_pos = 0;
151-
self->state = PAYLOAD;
158+
if ((self->last_flags & FRAME_OPCODE_MASK) >= FRAME_CLOSE) {
159+
self->state = CONTROL;
160+
} else {
161+
self->state = PAYLOAD;
162+
}
152163
continue;
153164
}
154165

155-
case PAYLOAD: {
166+
case PAYLOAD:
167+
case CONTROL: {
168+
mp_uint_t out_sz = 0;
169+
if (self->msg_sz == 0) {
170+
// In case message had zero payload
171+
goto no_payload;
172+
}
173+
156174
size_t sz = MIN(size, self->msg_sz);
157-
mp_uint_t out_sz = stream_p->read(self->sock, buf, sz, errcode);
175+
out_sz = stream_p->read(self->sock, buf, sz, errcode);
158176
if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
159177
return out_sz;
160178
}
@@ -166,12 +184,34 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
166184

167185
self->msg_sz -= out_sz;
168186
if (self->msg_sz == 0) {
187+
byte last_state;
188+
no_payload:
189+
last_state = self->state;
169190
self->state = FRAME_HEADER;
170191
self->to_recv = 2;
171192
self->mask_pos = 0;
172193
self->buf_pos = 0;
194+
195+
// Handle control frame
196+
if (last_state == CONTROL) {
197+
byte frame_type = self->last_flags & FRAME_OPCODE_MASK;
198+
if (frame_type == FRAME_CLOSE) {
199+
static char close_resp[2] = {0x88, 0};
200+
int err;
201+
websocket_write(self_in, close_resp, sizeof(close_resp), &err);
202+
return 0;
203+
}
204+
205+
//DEBUG_printf("Finished receiving ctrl message %x, ignoring\n", self->last_flags);
206+
continue;
207+
}
173208
}
174-
return out_sz;
209+
210+
if (out_sz != 0) {
211+
return out_sz;
212+
}
213+
// Empty (data) frame received is not EOF
214+
continue;
175215
}
176216

177217
}

0 commit comments

Comments
 (0)