3737
3838#if MICROPY_PY_WEBSOCKET
3939
40- enum { FRAME_HEADER , FRAME_OPT , PAYLOAD };
40+ enum { FRAME_HEADER , FRAME_OPT , PAYLOAD , CONTROL };
4141
4242enum { 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+
5963STATIC 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