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"
28 int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c)
31 int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
33 struct lws_tokens eff_buf;
34 #ifndef LWS_NO_EXTENSIONS
38 // lwsl_parser(" CRX: %02X %d\n", c, wsi->lws_rx_parse_state);
40 switch (wsi->lws_rx_parse_state) {
43 switch (wsi->ietf_spec_revision) {
46 wsi->u.ws.opcode = c & 0xf;
47 wsi->u.ws.rsv = (c & 0x70);
48 wsi->u.ws.final = !!((c >> 7) & 1);
49 switch (wsi->u.ws.opcode) {
50 case LWS_WS_OPCODE_07__TEXT_FRAME:
51 case LWS_WS_OPCODE_07__BINARY_FRAME:
52 wsi->u.ws.frame_is_binary = wsi->u.ws.opcode == LWS_WS_OPCODE_07__BINARY_FRAME;
55 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
59 lwsl_err("client_rx_sm doesn't know how "
60 "to handle spec version %02d\n",
61 wsi->ietf_spec_revision);
67 case LWS_RXPS_04_FRAME_HDR_LEN:
69 wsi->u.ws.this_frame_masked = !!(c & 0x80);
73 /* control frames are not allowed to have big lengths */
74 if (wsi->u.ws.opcode & 8)
75 goto illegal_ctl_length;
76 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
79 /* control frames are not allowed to have big lengths */
80 if (wsi->u.ws.opcode & 8)
81 goto illegal_ctl_length;
82 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
85 wsi->u.ws.rx_packet_length = c;
86 if (wsi->u.ws.this_frame_masked)
87 wsi->lws_rx_parse_state =
88 LWS_RXPS_07_COLLECT_FRAME_KEY_1;
91 wsi->lws_rx_parse_state =
92 LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
94 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
102 case LWS_RXPS_04_FRAME_HDR_LEN16_2:
103 wsi->u.ws.rx_packet_length = c << 8;
104 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
107 case LWS_RXPS_04_FRAME_HDR_LEN16_1:
108 wsi->u.ws.rx_packet_length |= c;
109 if (wsi->u.ws.this_frame_masked)
110 wsi->lws_rx_parse_state =
111 LWS_RXPS_07_COLLECT_FRAME_KEY_1;
113 if (wsi->u.ws.rx_packet_length)
114 wsi->lws_rx_parse_state =
115 LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
117 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
123 case LWS_RXPS_04_FRAME_HDR_LEN64_8:
125 lwsl_warn("b63 of length must be zero\n");
126 /* kill the connection */
130 wsi->u.ws.rx_packet_length = ((size_t)c) << 56;
132 wsi->u.ws.rx_packet_length = 0;
134 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
137 case LWS_RXPS_04_FRAME_HDR_LEN64_7:
139 wsi->u.ws.rx_packet_length |= ((size_t)c) << 48;
141 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
144 case LWS_RXPS_04_FRAME_HDR_LEN64_6:
146 wsi->u.ws.rx_packet_length |= ((size_t)c) << 40;
148 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
151 case LWS_RXPS_04_FRAME_HDR_LEN64_5:
153 wsi->u.ws.rx_packet_length |= ((size_t)c) << 32;
155 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
158 case LWS_RXPS_04_FRAME_HDR_LEN64_4:
159 wsi->u.ws.rx_packet_length |= ((size_t)c) << 24;
160 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
163 case LWS_RXPS_04_FRAME_HDR_LEN64_3:
164 wsi->u.ws.rx_packet_length |= ((size_t)c) << 16;
165 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
168 case LWS_RXPS_04_FRAME_HDR_LEN64_2:
169 wsi->u.ws.rx_packet_length |= ((size_t)c) << 8;
170 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
173 case LWS_RXPS_04_FRAME_HDR_LEN64_1:
174 wsi->u.ws.rx_packet_length |= (size_t)c;
175 if (wsi->u.ws.this_frame_masked)
176 wsi->lws_rx_parse_state =
177 LWS_RXPS_07_COLLECT_FRAME_KEY_1;
179 if (wsi->u.ws.rx_packet_length)
180 wsi->lws_rx_parse_state =
181 LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
183 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
189 case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
190 wsi->u.ws.frame_masking_nonce_04[0] = c;
192 wsi->u.ws.all_zero_nonce = 0;
193 wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
196 case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
197 wsi->u.ws.frame_masking_nonce_04[1] = c;
199 wsi->u.ws.all_zero_nonce = 0;
200 wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
203 case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
204 wsi->u.ws.frame_masking_nonce_04[2] = c;
206 wsi->u.ws.all_zero_nonce = 0;
207 wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
210 case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
211 wsi->u.ws.frame_masking_nonce_04[3] = c;
213 wsi->u.ws.all_zero_nonce = 0;
215 if (wsi->u.ws.rx_packet_length)
216 wsi->lws_rx_parse_state =
217 LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
219 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
224 case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
225 if ((!wsi->u.ws.this_frame_masked) || wsi->u.ws.all_zero_nonce)
226 wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
227 (wsi->u.ws.rx_user_buffer_head++)] = c;
229 wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
230 (wsi->u.ws.rx_user_buffer_head++)] =
231 c ^ wsi->u.ws.frame_masking_nonce_04[(wsi->u.ws.frame_mask_index++) & 3];
233 if (--wsi->u.ws.rx_packet_length == 0) {
234 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
237 if (wsi->u.ws.rx_user_buffer_head != MAX_USER_RX_BUFFER)
244 * is this frame a control packet we should take care of at this
245 * layer? If so service it and hide it from the user callback
248 switch (wsi->u.ws.opcode) {
249 case LWS_WS_OPCODE_07__CLOSE:
250 /* is this an acknowledgement of our close? */
251 if (wsi->state == WSI_STATE_AWAITING_CLOSE_ACK) {
253 * fine he has told us he is closing too, let's
256 lwsl_parser("seen server's close ack\n");
259 lwsl_parser("client sees server close packet len = %d\n", wsi->u.ws.rx_user_buffer_head);
260 /* parrot the close packet payload back */
261 n = libwebsocket_write(wsi, (unsigned char *)
262 &wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
263 wsi->u.ws.rx_user_buffer_head, LWS_WRITE_CLOSE);
264 lwsl_parser("client writing close ack returned %d\n", n);
265 wsi->state = WSI_STATE_RETURNED_CLOSE_ALREADY;
266 /* close the connection */
269 case LWS_WS_OPCODE_07__PING:
270 lwsl_info("client received ping, doing pong\n");
271 /* parrot the ping packet payload back as a pong*/
272 n = libwebsocket_write(wsi, (unsigned char *)
273 &wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
274 wsi->u.ws.rx_user_buffer_head, LWS_WRITE_PONG);
278 case LWS_WS_OPCODE_07__PONG:
279 lwsl_info("client receied pong\n");
280 lwsl_hexdump(&wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
281 wsi->u.ws.rx_user_buffer_head);
282 /* keep the statistics... */
283 wsi->u.ws.pings_vs_pongs--;
286 callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG;
289 case LWS_WS_OPCODE_07__CONTINUATION:
290 case LWS_WS_OPCODE_07__TEXT_FRAME:
291 case LWS_WS_OPCODE_07__BINARY_FRAME:
296 lwsl_parser("Reserved opcode 0x%2X\n", wsi->u.ws.opcode);
297 #ifndef LWS_NO_EXTENSIONS
299 * It's something special we can't understand here.
300 * Pass the payload up to the extension's parsing
304 eff_buf.token = &wsi->u.ws.rx_user_buffer[
305 LWS_SEND_BUFFER_PRE_PADDING];
306 eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
308 for (n = 0; n < wsi->count_active_extensions; n++) {
309 m = wsi->active_extensions[n]->callback(
310 wsi->protocol->owning_server,
311 wsi->active_extensions[n], wsi,
312 LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX,
313 wsi->active_extensions_user[n],
323 lwsl_ext("Unhandled extended opcode "
324 "0x%x - ignoring frame\n", wsi->u.ws.opcode);
325 wsi->u.ws.rx_user_buffer_head = 0;
334 * No it's real payload, pass it up to the user callback.
335 * It's nicely buffered with the pre-padding taken care of
336 * so it can be sent straight out again using libwebsocket_write
341 eff_buf.token = &wsi->u.ws.rx_user_buffer[
342 LWS_SEND_BUFFER_PRE_PADDING];
343 eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
344 #ifndef LWS_NO_EXTENSIONS
345 for (n = 0; n < wsi->count_active_extensions; n++) {
346 m = wsi->active_extensions[n]->callback(
347 wsi->protocol->owning_server,
348 wsi->active_extensions[n], wsi,
349 LWS_EXT_CALLBACK_PAYLOAD_RX,
350 wsi->active_extensions_user[n],
354 "Extension '%s' failed to handle payload!\n",
355 wsi->active_extensions[n]->name);
360 if (eff_buf.token_len > 0) {
361 eff_buf.token[eff_buf.token_len] = '\0';
363 if (wsi->protocol->callback) {
364 if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
365 lwsl_info("Client doing pong callback\n");
366 wsi->protocol->callback(
367 wsi->protocol->owning_server,
369 (enum libwebsocket_callback_reasons)callback_action,
376 wsi->u.ws.rx_user_buffer_head = 0;
379 lwsl_err("client rx illegal state\n");
387 lwsl_warn("Control frame asking for "
388 "extended length is illegal\n");
389 /* kill the connection */