2 * libwebsockets - small server side websockets and web server implementation
4 * Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation:
9 * version 2.1 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 #include "private-libwebsockets.h"
24 int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c)
27 int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
29 struct lws_tokens eff_buf;
30 #ifndef LWS_NO_EXTENSIONS
34 // lwsl_parser(" CRX: %02X %d\n", c, wsi->lws_rx_parse_state);
36 switch (wsi->lws_rx_parse_state) {
39 switch (wsi->ietf_spec_revision) {
42 wsi->u.ws.opcode = c & 0xf;
43 wsi->u.ws.rsv = (c & 0x70);
44 wsi->u.ws.final = !!((c >> 7) & 1);
45 switch (wsi->u.ws.opcode) {
46 case LWS_WS_OPCODE_07__TEXT_FRAME:
47 case LWS_WS_OPCODE_07__BINARY_FRAME:
48 wsi->u.ws.frame_is_binary = wsi->u.ws.opcode == LWS_WS_OPCODE_07__BINARY_FRAME;
51 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
55 lwsl_err("client_rx_sm doesn't know how "
56 "to handle spec version %02d\n",
57 wsi->ietf_spec_revision);
63 case LWS_RXPS_04_FRAME_HDR_LEN:
65 wsi->u.ws.this_frame_masked = !!(c & 0x80);
69 /* control frames are not allowed to have big lengths */
70 if (wsi->u.ws.opcode & 8)
71 goto illegal_ctl_length;
72 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
75 /* control frames are not allowed to have big lengths */
76 if (wsi->u.ws.opcode & 8)
77 goto illegal_ctl_length;
78 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
81 wsi->u.ws.rx_packet_length = c;
82 if (wsi->u.ws.this_frame_masked)
83 wsi->lws_rx_parse_state =
84 LWS_RXPS_07_COLLECT_FRAME_KEY_1;
87 wsi->lws_rx_parse_state =
88 LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
90 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
98 case LWS_RXPS_04_FRAME_HDR_LEN16_2:
99 wsi->u.ws.rx_packet_length = c << 8;
100 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
103 case LWS_RXPS_04_FRAME_HDR_LEN16_1:
104 wsi->u.ws.rx_packet_length |= c;
105 if (wsi->u.ws.this_frame_masked)
106 wsi->lws_rx_parse_state =
107 LWS_RXPS_07_COLLECT_FRAME_KEY_1;
109 if (wsi->u.ws.rx_packet_length)
110 wsi->lws_rx_parse_state =
111 LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
113 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
119 case LWS_RXPS_04_FRAME_HDR_LEN64_8:
121 lwsl_warn("b63 of length must be zero\n");
122 /* kill the connection */
126 wsi->u.ws.rx_packet_length = ((size_t)c) << 56;
128 wsi->u.ws.rx_packet_length = 0;
130 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
133 case LWS_RXPS_04_FRAME_HDR_LEN64_7:
135 wsi->u.ws.rx_packet_length |= ((size_t)c) << 48;
137 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
140 case LWS_RXPS_04_FRAME_HDR_LEN64_6:
142 wsi->u.ws.rx_packet_length |= ((size_t)c) << 40;
144 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
147 case LWS_RXPS_04_FRAME_HDR_LEN64_5:
149 wsi->u.ws.rx_packet_length |= ((size_t)c) << 32;
151 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
154 case LWS_RXPS_04_FRAME_HDR_LEN64_4:
155 wsi->u.ws.rx_packet_length |= ((size_t)c) << 24;
156 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
159 case LWS_RXPS_04_FRAME_HDR_LEN64_3:
160 wsi->u.ws.rx_packet_length |= ((size_t)c) << 16;
161 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
164 case LWS_RXPS_04_FRAME_HDR_LEN64_2:
165 wsi->u.ws.rx_packet_length |= ((size_t)c) << 8;
166 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
169 case LWS_RXPS_04_FRAME_HDR_LEN64_1:
170 wsi->u.ws.rx_packet_length |= (size_t)c;
171 if (wsi->u.ws.this_frame_masked)
172 wsi->lws_rx_parse_state =
173 LWS_RXPS_07_COLLECT_FRAME_KEY_1;
175 if (wsi->u.ws.rx_packet_length)
176 wsi->lws_rx_parse_state =
177 LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
179 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
185 case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
186 wsi->u.ws.frame_masking_nonce_04[0] = c;
188 wsi->u.ws.all_zero_nonce = 0;
189 wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
192 case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
193 wsi->u.ws.frame_masking_nonce_04[1] = c;
195 wsi->u.ws.all_zero_nonce = 0;
196 wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
199 case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
200 wsi->u.ws.frame_masking_nonce_04[2] = c;
202 wsi->u.ws.all_zero_nonce = 0;
203 wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
206 case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
207 wsi->u.ws.frame_masking_nonce_04[3] = c;
209 wsi->u.ws.all_zero_nonce = 0;
211 if (wsi->u.ws.rx_packet_length)
212 wsi->lws_rx_parse_state =
213 LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
215 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
220 case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
221 if ((!wsi->u.ws.this_frame_masked) || wsi->u.ws.all_zero_nonce)
222 wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
223 (wsi->u.ws.rx_user_buffer_head++)] = c;
225 wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
226 (wsi->u.ws.rx_user_buffer_head++)] =
227 c ^ wsi->u.ws.frame_masking_nonce_04[(wsi->u.ws.frame_mask_index++) & 3];
229 if (--wsi->u.ws.rx_packet_length == 0) {
230 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
233 if (wsi->u.ws.rx_user_buffer_head != MAX_USER_RX_BUFFER)
240 * is this frame a control packet we should take care of at this
241 * layer? If so service it and hide it from the user callback
244 switch (wsi->u.ws.opcode) {
245 case LWS_WS_OPCODE_07__CLOSE:
246 /* is this an acknowledgement of our close? */
247 if (wsi->state == WSI_STATE_AWAITING_CLOSE_ACK) {
249 * fine he has told us he is closing too, let's
252 lwsl_parser("seen server's close ack\n");
255 lwsl_parser("client sees server close packet len = %d\n", wsi->u.ws.rx_user_buffer_head);
256 /* parrot the close packet payload back */
257 n = libwebsocket_write(wsi, (unsigned char *)
258 &wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
259 wsi->u.ws.rx_user_buffer_head, LWS_WRITE_CLOSE);
260 lwsl_parser("client writing close ack returned %d\n", n);
261 wsi->state = WSI_STATE_RETURNED_CLOSE_ALREADY;
262 /* close the connection */
265 case LWS_WS_OPCODE_07__PING:
266 lwsl_info("client received ping, doing pong\n");
267 /* parrot the ping packet payload back as a pong*/
268 n = libwebsocket_write(wsi, (unsigned char *)
269 &wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
270 wsi->u.ws.rx_user_buffer_head, LWS_WRITE_PONG);
274 case LWS_WS_OPCODE_07__PONG:
275 lwsl_info("client receied pong\n");
276 lwsl_hexdump(&wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
277 wsi->u.ws.rx_user_buffer_head);
278 /* keep the statistics... */
279 wsi->u.ws.pings_vs_pongs--;
282 callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG;
285 case LWS_WS_OPCODE_07__CONTINUATION:
286 case LWS_WS_OPCODE_07__TEXT_FRAME:
287 case LWS_WS_OPCODE_07__BINARY_FRAME:
292 lwsl_parser("Reserved opcode 0x%2X\n", wsi->u.ws.opcode);
293 #ifndef LWS_NO_EXTENSIONS
295 * It's something special we can't understand here.
296 * Pass the payload up to the extension's parsing
300 eff_buf.token = &wsi->u.ws.rx_user_buffer[
301 LWS_SEND_BUFFER_PRE_PADDING];
302 eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
304 for (n = 0; n < wsi->count_active_extensions; n++) {
305 m = wsi->active_extensions[n]->callback(
306 wsi->protocol->owning_server,
307 wsi->active_extensions[n], wsi,
308 LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX,
309 wsi->active_extensions_user[n],
319 lwsl_ext("Unhandled extended opcode "
320 "0x%x - ignoring frame\n", wsi->u.ws.opcode);
321 wsi->u.ws.rx_user_buffer_head = 0;
330 * No it's real payload, pass it up to the user callback.
331 * It's nicely buffered with the pre-padding taken care of
332 * so it can be sent straight out again using libwebsocket_write
337 eff_buf.token = &wsi->u.ws.rx_user_buffer[
338 LWS_SEND_BUFFER_PRE_PADDING];
339 eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
340 #ifndef LWS_NO_EXTENSIONS
341 for (n = 0; n < wsi->count_active_extensions; n++) {
342 m = wsi->active_extensions[n]->callback(
343 wsi->protocol->owning_server,
344 wsi->active_extensions[n], wsi,
345 LWS_EXT_CALLBACK_PAYLOAD_RX,
346 wsi->active_extensions_user[n],
350 "Extension '%s' failed to handle payload!\n",
351 wsi->active_extensions[n]->name);
356 if (eff_buf.token_len > 0) {
357 eff_buf.token[eff_buf.token_len] = '\0';
359 if (wsi->protocol->callback) {
360 if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
361 lwsl_info("Client doing pong callback\n");
362 wsi->protocol->callback(
363 wsi->protocol->owning_server,
365 (enum libwebsocket_callback_reasons)callback_action,
372 wsi->u.ws.rx_user_buffer_head = 0;
375 lwsl_err("client rx illegal state\n");
383 lwsl_warn("Control frame asking for "
384 "extended length is illegal\n");
385 /* kill the connection */