Andy Green | 7c212cc | 2010-11-08 20:20:42 +0000 | [diff] [blame] | 1 | /* |
| 2 | * libwebsockets - small server side websockets and web server implementation |
Andy Green | e77ddd8 | 2010-11-13 10:03:47 +0000 | [diff] [blame] | 3 | * |
Andy Green | 1cc0388 | 2015-12-06 08:00:03 +0800 | [diff] [blame] | 4 | * Copyright (C) 2010-2015 Andy Green <andy@warmcat.com> |
Andy Green | 7c212cc | 2010-11-08 20:20:42 +0000 | [diff] [blame] | 5 | * |
| 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. |
| 10 | * |
| 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. |
| 15 | * |
| 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, |
| 19 | * MA 02110-1301 USA |
| 20 | */ |
| 21 | |
| 22 | #include "private-libwebsockets.h" |
| 23 | |
Andy Green | 7c212cc | 2010-11-08 20:20:42 +0000 | [diff] [blame] | 24 | /* |
Andy Green | d1b11e3 | 2011-01-18 15:39:02 +0000 | [diff] [blame] | 25 | * -04 of the protocol (actually the 80th version) has a radically different |
| 26 | * handshake. The 04 spec gives the following idea |
| 27 | * |
| 28 | * The handshake from the client looks as follows: |
| 29 | * |
| 30 | * GET /chat HTTP/1.1 |
| 31 | * Host: server.example.com |
| 32 | * Upgrade: websocket |
| 33 | * Connection: Upgrade |
| 34 | * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== |
| 35 | * Sec-WebSocket-Origin: http://example.com |
| 36 | * Sec-WebSocket-Protocol: chat, superchat |
Andy Green | e252217 | 2011-01-18 17:14:03 +0000 | [diff] [blame] | 37 | * Sec-WebSocket-Version: 4 |
Andy Green | d1b11e3 | 2011-01-18 15:39:02 +0000 | [diff] [blame] | 38 | * |
| 39 | * The handshake from the server looks as follows: |
| 40 | * |
| 41 | * HTTP/1.1 101 Switching Protocols |
| 42 | * Upgrade: websocket |
| 43 | * Connection: Upgrade |
| 44 | * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= |
| 45 | * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== |
| 46 | * Sec-WebSocket-Protocol: chat |
| 47 | */ |
| 48 | |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 49 | #ifndef min |
| 50 | #define min(a, b) ((a) < (b) ? (a) : (b)) |
| 51 | #endif |
Andy Green | 1cc0388 | 2015-12-06 08:00:03 +0800 | [diff] [blame] | 52 | |
Andy Green | d1b11e3 | 2011-01-18 15:39:02 +0000 | [diff] [blame] | 53 | /* |
Andy Green | 7c212cc | 2010-11-08 20:20:42 +0000 | [diff] [blame] | 54 | * We have to take care about parsing because the headers may be split |
| 55 | * into multiple fragments. They may contain unknown headers with arbitrary |
| 56 | * argument lengths. So, we parse using a single-character at a time state |
| 57 | * machine that is completely independent of packet size. |
Andy Green | 2c218e7 | 2016-02-15 12:37:04 +0800 | [diff] [blame] | 58 | * |
| 59 | * Returns <0 for error or length of chars consumed from buf (up to len) |
Andy Green | 7c212cc | 2010-11-08 20:20:42 +0000 | [diff] [blame] | 60 | */ |
| 61 | |
Peter Pentchev | 9a4fef7 | 2013-03-30 09:52:21 +0800 | [diff] [blame] | 62 | LWS_VISIBLE int |
Andy Green | 11c05bf | 2015-12-16 18:19:08 +0800 | [diff] [blame] | 63 | lws_read(struct lws *wsi, unsigned char *buf, size_t len) |
Andy Green | 7c212cc | 2010-11-08 20:20:42 +0000 | [diff] [blame] | 64 | { |
Andy Green | 2c218e7 | 2016-02-15 12:37:04 +0800 | [diff] [blame] | 65 | unsigned char *last_char, *oldbuf = buf; |
Andy Green | 1cc0388 | 2015-12-06 08:00:03 +0800 | [diff] [blame] | 66 | int body_chunk_len; |
| 67 | size_t n; |
Andy Green | e77ddd8 | 2010-11-13 10:03:47 +0000 | [diff] [blame] | 68 | |
Andy Green | 6711266 | 2016-01-11 11:34:01 +0800 | [diff] [blame] | 69 | lwsl_debug("%s: incoming len %d\n", __func__, (int)len); |
| 70 | |
Andy Green | 7c212cc | 2010-11-08 20:20:42 +0000 | [diff] [blame] | 71 | switch (wsi->state) { |
Andy Green | 095d303 | 2014-10-08 12:15:15 +0800 | [diff] [blame] | 72 | #ifdef LWS_USE_HTTP2 |
Andy Green | 54806b1 | 2015-12-17 17:03:59 +0800 | [diff] [blame] | 73 | case LWSS_HTTP2_AWAIT_CLIENT_PREFACE: |
| 74 | case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS: |
| 75 | case LWSS_HTTP2_ESTABLISHED: |
Andy Green | 024eb6c | 2014-10-08 12:00:53 +0800 | [diff] [blame] | 76 | n = 0; |
| 77 | while (n < len) { |
| 78 | /* |
| 79 | * we were accepting input but now we stopped doing so |
| 80 | */ |
| 81 | if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) { |
| 82 | lws_rxflow_cache(wsi, buf, n, len); |
| 83 | |
| 84 | return 1; |
| 85 | } |
| 86 | |
| 87 | /* account for what we're using in rxflow buffer */ |
| 88 | if (wsi->rxflow_buffer) |
| 89 | wsi->rxflow_pos++; |
Andy Green | 11c05bf | 2015-12-16 18:19:08 +0800 | [diff] [blame] | 90 | if (lws_http2_parser(wsi, buf[n++])) |
Andy Green | 024eb6c | 2014-10-08 12:00:53 +0800 | [diff] [blame] | 91 | goto bail; |
| 92 | } |
| 93 | break; |
Andy Green | 095d303 | 2014-10-08 12:15:15 +0800 | [diff] [blame] | 94 | #endif |
Andy Green | 2c218e7 | 2016-02-15 12:37:04 +0800 | [diff] [blame] | 95 | |
Andy Green | 54806b1 | 2015-12-17 17:03:59 +0800 | [diff] [blame] | 96 | case LWSS_HTTP: |
Andrew Canaday | e917345 | 2014-08-06 08:58:23 -0400 | [diff] [blame] | 97 | wsi->hdr_parsing_completed = 0; |
| 98 | /* fallthru */ |
Andy Green | 54806b1 | 2015-12-17 17:03:59 +0800 | [diff] [blame] | 99 | case LWSS_HTTP_ISSUING_FILE: |
| 100 | wsi->state = LWSS_HTTP_HEADERS; |
Andy Green | 623a98d | 2013-01-21 11:04:23 +0800 | [diff] [blame] | 101 | wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; |
| 102 | wsi->u.hdr.lextable_pos = 0; |
Andy Green | 7c212cc | 2010-11-08 20:20:42 +0000 | [diff] [blame] | 103 | /* fallthru */ |
Andy Green | 54806b1 | 2015-12-17 17:03:59 +0800 | [diff] [blame] | 104 | case LWSS_HTTP_HEADERS: |
Andy Green | 8c1f602 | 2016-01-26 20:56:56 +0800 | [diff] [blame] | 105 | assert(wsi->u.hdr.ah); |
Andy Green | 43db045 | 2013-01-10 19:50:35 +0800 | [diff] [blame] | 106 | lwsl_parser("issuing %d bytes to parser\n", (int)len); |
Andy Green | 4739e5c | 2011-01-22 12:51:57 +0000 | [diff] [blame] | 107 | |
Andy Green | aad2eac | 2014-04-03 09:03:37 +0800 | [diff] [blame] | 108 | if (lws_handshake_client(wsi, &buf, len)) |
| 109 | goto bail; |
| 110 | |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 111 | last_char = buf; |
Andy Green | 11c05bf | 2015-12-16 18:19:08 +0800 | [diff] [blame] | 112 | if (lws_handshake_server(wsi, &buf, len)) |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 113 | /* Handshake indicates this session is done. */ |
| 114 | goto bail; |
| 115 | |
Andy Green | 44e0b08 | 2015-12-28 14:24:49 +0800 | [diff] [blame] | 116 | /* |
| 117 | * It's possible that we've exhausted our data already, or |
| 118 | * rx flow control has stopped us dealing with this early, |
| 119 | * but lws_handshake_server doesn't update len for us. |
Andy Green | 54806b1 | 2015-12-17 17:03:59 +0800 | [diff] [blame] | 120 | * Figure out how much was read, so that we can proceed |
| 121 | * appropriately: |
| 122 | */ |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 123 | len -= (buf - last_char); |
Andy Green | 44e0b08 | 2015-12-28 14:24:49 +0800 | [diff] [blame] | 124 | lwsl_debug("%s: thinks we have used %d\n", __func__, len); |
Andrew Canaday | c7dbad6 | 2014-07-19 07:00:52 +0800 | [diff] [blame] | 125 | |
| 126 | if (!wsi->hdr_parsing_completed) |
| 127 | /* More header content on the way */ |
| 128 | goto read_ok; |
| 129 | |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 130 | switch (wsi->state) { |
Andy Green | 54806b1 | 2015-12-17 17:03:59 +0800 | [diff] [blame] | 131 | case LWSS_HTTP: |
| 132 | case LWSS_HTTP_HEADERS: |
Andy Green | dbfbbb4 | 2016-02-24 20:58:19 +0800 | [diff] [blame] | 133 | goto read_ok; |
Andy Green | 54806b1 | 2015-12-17 17:03:59 +0800 | [diff] [blame] | 134 | case LWSS_HTTP_ISSUING_FILE: |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 135 | goto read_ok; |
Andy Green | 54806b1 | 2015-12-17 17:03:59 +0800 | [diff] [blame] | 136 | case LWSS_HTTP_BODY: |
Andy Green | 6ab6ee2 | 2015-12-06 06:39:51 +0800 | [diff] [blame] | 137 | wsi->u.http.content_remain = |
| 138 | wsi->u.http.content_length; |
| 139 | if (wsi->u.http.content_remain) |
| 140 | goto http_postbody; |
| 141 | |
| 142 | /* there is no POST content */ |
| 143 | goto postbody_completion; |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 144 | default: |
| 145 | break; |
| 146 | } |
| 147 | break; |
| 148 | |
Andy Green | 54806b1 | 2015-12-17 17:03:59 +0800 | [diff] [blame] | 149 | case LWSS_HTTP_BODY: |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 150 | http_postbody: |
| 151 | while (len && wsi->u.http.content_remain) { |
| 152 | /* Copy as much as possible, up to the limit of: |
| 153 | * what we have in the read buffer (len) |
| 154 | * remaining portion of the POST body (content_remain) |
| 155 | */ |
| 156 | body_chunk_len = min(wsi->u.http.content_remain,len); |
| 157 | wsi->u.http.content_remain -= body_chunk_len; |
| 158 | len -= body_chunk_len; |
Andy Green | 6a8099b | 2016-02-21 21:25:48 +0800 | [diff] [blame] | 159 | #ifdef LWS_WITH_CGI |
| 160 | if (wsi->cgi) { |
| 161 | struct lws_cgi_args args; |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 162 | |
Andy Green | 6a8099b | 2016-02-21 21:25:48 +0800 | [diff] [blame] | 163 | args.ch = LWS_STDIN; |
| 164 | args.stdwsi = &wsi->cgi->stdwsi[0]; |
| 165 | args.data = buf; |
| 166 | args.len = body_chunk_len; |
Andy Green | 6ab6ee2 | 2015-12-06 06:39:51 +0800 | [diff] [blame] | 167 | |
Andy Green | 6a8099b | 2016-02-21 21:25:48 +0800 | [diff] [blame] | 168 | /* returns how much used */ |
| 169 | n = user_callback_handle_rxflow( |
| 170 | wsi->protocol->callback, |
| 171 | wsi, LWS_CALLBACK_CGI_STDIN_DATA, |
| 172 | wsi->user_space, |
| 173 | (void *)&args, 0); |
| 174 | if (n < 0) |
| 175 | goto bail; |
| 176 | } else { |
| 177 | #endif |
| 178 | n = wsi->protocol->callback(wsi, |
| 179 | LWS_CALLBACK_HTTP_BODY, wsi->user_space, |
| 180 | buf, body_chunk_len); |
| 181 | if (n) |
| 182 | goto bail; |
| 183 | n = body_chunk_len; |
| 184 | #ifdef LWS_WITH_CGI |
| 185 | } |
| 186 | #endif |
| 187 | buf += n; |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 188 | |
Andy Green | 6ab6ee2 | 2015-12-06 06:39:51 +0800 | [diff] [blame] | 189 | if (wsi->u.http.content_remain) { |
| 190 | lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, |
Andy Green | 200a6a2 | 2016-02-15 20:36:02 +0800 | [diff] [blame] | 191 | wsi->context->timeout_secs); |
Andy Green | 6ab6ee2 | 2015-12-06 06:39:51 +0800 | [diff] [blame] | 192 | break; |
| 193 | } |
| 194 | /* he sent all the content in time */ |
| 195 | postbody_completion: |
| 196 | lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); |
Andy Green | 6a8099b | 2016-02-21 21:25:48 +0800 | [diff] [blame] | 197 | #ifdef LWS_WITH_CGI |
| 198 | if (!wsi->cgi) |
| 199 | #endif |
| 200 | { |
| 201 | n = wsi->protocol->callback(wsi, |
| 202 | LWS_CALLBACK_HTTP_BODY_COMPLETION, |
| 203 | wsi->user_space, NULL, 0); |
| 204 | if (n) |
| 205 | goto bail; |
| 206 | } |
Andy Green | 6ab6ee2 | 2015-12-06 06:39:51 +0800 | [diff] [blame] | 207 | |
| 208 | goto http_complete; |
Andy Green | 4739e5c | 2011-01-22 12:51:57 +0000 | [diff] [blame] | 209 | } |
Andy Green | 7c212cc | 2010-11-08 20:20:42 +0000 | [diff] [blame] | 210 | break; |
| 211 | |
Andy Green | 54806b1 | 2015-12-17 17:03:59 +0800 | [diff] [blame] | 212 | case LWSS_ESTABLISHED: |
| 213 | case LWSS_AWAITING_CLOSE_ACK: |
Andy Green | 8c1f602 | 2016-01-26 20:56:56 +0800 | [diff] [blame] | 214 | case LWSS_SHUTDOWN: |
Andy Green | d7340c1 | 2014-04-10 14:08:10 +0800 | [diff] [blame] | 215 | if (lws_handshake_client(wsi, &buf, len)) |
| 216 | goto bail; |
Andy Green | f3d3b40 | 2011-02-09 07:16:34 +0000 | [diff] [blame] | 217 | switch (wsi->mode) { |
Andy Green | 54806b1 | 2015-12-17 17:03:59 +0800 | [diff] [blame] | 218 | case LWSCM_WS_SERVING: |
Andy Green | 4739e5c | 2011-01-22 12:51:57 +0000 | [diff] [blame] | 219 | |
Andy Green | 6711266 | 2016-01-11 11:34:01 +0800 | [diff] [blame] | 220 | if (lws_interpret_incoming_packet(wsi, &buf, len) < 0) { |
Andy Green | d7340c1 | 2014-04-10 14:08:10 +0800 | [diff] [blame] | 221 | lwsl_info("interpret_incoming_packet has bailed\n"); |
| 222 | goto bail; |
| 223 | } |
Andy Green | f3d3b40 | 2011-02-09 07:16:34 +0000 | [diff] [blame] | 224 | break; |
Andy Green | 4739e5c | 2011-01-22 12:51:57 +0000 | [diff] [blame] | 225 | } |
Andy Green | 7c212cc | 2010-11-08 20:20:42 +0000 | [diff] [blame] | 226 | break; |
| 227 | default: |
Andy Green | 11c05bf | 2015-12-16 18:19:08 +0800 | [diff] [blame] | 228 | lwsl_err("%s: Unhandled state\n", __func__); |
Andy Green | 7c212cc | 2010-11-08 20:20:42 +0000 | [diff] [blame] | 229 | break; |
| 230 | } |
Andy Green | e77ddd8 | 2010-11-13 10:03:47 +0000 | [diff] [blame] | 231 | |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 232 | read_ok: |
Andy Green | 6ab6ee2 | 2015-12-06 06:39:51 +0800 | [diff] [blame] | 233 | /* Nothing more to do for now */ |
Andy Green | 2c218e7 | 2016-02-15 12:37:04 +0800 | [diff] [blame] | 234 | lwsl_info("%s: read_ok, used %d\n", __func__, buf - oldbuf); |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 235 | |
Andy Green | 2c218e7 | 2016-02-15 12:37:04 +0800 | [diff] [blame] | 236 | return buf - oldbuf; |
Andy Green | e77ddd8 | 2010-11-13 10:03:47 +0000 | [diff] [blame] | 237 | |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 238 | http_complete: |
Andy Green | 11c05bf | 2015-12-16 18:19:08 +0800 | [diff] [blame] | 239 | lwsl_debug("%s: http_complete\n", __func__); |
Carl Stehle | 66d466a | 2014-08-16 10:17:59 +0800 | [diff] [blame] | 240 | |
Andy Green | 2721e3c | 2015-10-21 09:23:21 +0800 | [diff] [blame] | 241 | #ifndef LWS_NO_SERVER |
Carl Stehle | 66d466a | 2014-08-16 10:17:59 +0800 | [diff] [blame] | 242 | /* Did the client want to keep the HTTP connection going? */ |
Andy Green | 2627148 | 2015-10-21 08:16:34 +0800 | [diff] [blame] | 243 | if (lws_http_transaction_completed(wsi)) |
| 244 | goto bail; |
Andy Green | 2721e3c | 2015-10-21 09:23:21 +0800 | [diff] [blame] | 245 | #endif |
Andy Green | 2c218e7 | 2016-02-15 12:37:04 +0800 | [diff] [blame] | 246 | /* we may have next header set already, but return to event loop first |
| 247 | * so a heaily-pipelined http/1.1 connection cannot monopolize the |
| 248 | * service thread with GET hugefile.bin GET hugefile.bin etc |
| 249 | */ |
| 250 | goto read_ok; |
Andrew Canaday | afe26cf | 2014-07-13 01:07:36 -0400 | [diff] [blame] | 251 | |
Andy Green | 7c212cc | 2010-11-08 20:20:42 +0000 | [diff] [blame] | 252 | bail: |
Andy Green | 6230476 | 2015-12-04 08:43:54 +0800 | [diff] [blame] | 253 | lwsl_debug("closing connection at lws_read bail:\n"); |
Andy Green | 6b5de70 | 2015-12-15 21:15:58 +0800 | [diff] [blame] | 254 | lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); |
Andy Green | 6964bb5 | 2011-01-23 16:50:33 +0000 | [diff] [blame] | 255 | |
Andy Green | 7c212cc | 2010-11-08 20:20:42 +0000 | [diff] [blame] | 256 | return -1; |
| 257 | } |