blob: 065cb0180a7c97972f9ea5e2b7d4619497f4cadf [file] [log] [blame]
Andy Green05a0a7b2010-10-31 17:51:39 +00001/*
2 * libwebsockets Copyright 2010 Andy Green <andy@warmcat.com>
3 * licensed under GPL2
4 */
5
Andy Greenff95d7a2010-10-28 22:36:01 +01006#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <ctype.h>
10#include <unistd.h>
Andy Green775c0dd2010-10-29 14:15:22 +010011#include <errno.h>
Andy Green5fd8a5e2010-10-31 11:57:17 +000012#include <sys/types.h>
13#include <sys/stat.h>
14#include <fcntl.h>
Andy Green251f6fa2010-11-03 11:13:06 +000015#include <signal.h>
Andy Greenff95d7a2010-10-28 22:36:01 +010016
17#include <sys/types.h>
18#include <sys/socket.h>
19#include <netinet/in.h>
20
21#include <poll.h>
22#include <sys/mman.h>
23
24#include "libwebsockets.h"
25
Andy Green69fa0722010-11-03 08:25:13 +000026#ifdef DEBUG
27#define debug(format, args...) \
28 fprintf(stderr, format , ## args)
29#else
30#define debug(format, args...)
31#endif
32
Andy Greenff95d7a2010-10-28 22:36:01 +010033void md5(const unsigned char *input, int ilen, unsigned char output[16]);
Andy Green251f6fa2010-11-03 11:13:06 +000034static int
35libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len);
Andy Greenff95d7a2010-10-28 22:36:01 +010036
Andy Green251f6fa2010-11-03 11:13:06 +000037#define MAX_CLIENTS 100
Andy Green775c0dd2010-10-29 14:15:22 +010038#define LWS_MAX_HEADER_NAME_LENGTH 64
39#define LWS_MAX_HEADER_LEN 4096
40#define LWS_INITIAL_HDR_ALLOC 256
41#define LWS_ADDITIONAL_HDR_ALLOC 64
42
43
44enum lws_connection_states {
Andy Green5fd8a5e2010-10-31 11:57:17 +000045 WSI_STATE_HTTP,
46 WSI_STATE_HTTP_HEADERS,
Andy Green775c0dd2010-10-29 14:15:22 +010047 WSI_STATE_DEAD_SOCKET,
48 WSI_STATE_ESTABLISHED
49};
50
51enum lws_token_indexes {
52 WSI_TOKEN_GET_URI,
53 WSI_TOKEN_HOST,
54 WSI_TOKEN_CONNECTION,
55 WSI_TOKEN_KEY1,
56 WSI_TOKEN_KEY2,
57 WSI_TOKEN_PROTOCOL,
58 WSI_TOKEN_UPGRADE,
59 WSI_TOKEN_ORIGIN,
60 WSI_TOKEN_CHALLENGE,
61
62 /* always last real token index*/
63 WSI_TOKEN_COUNT,
64 /* parser state additions */
65 WSI_TOKEN_NAME_PART,
66 WSI_TOKEN_SKIPPING,
67 WSI_TOKEN_SKIPPING_SAW_CR,
68 WSI_PARSING_COMPLETE
69};
70
Andy Green4ea60062010-10-30 12:15:07 +010071enum lws_rx_parse_state {
72 LWS_RXPS_NEW,
73
74 LWS_RXPS_SEEN_76_FF,
75 LWS_RXPS_PULLING_76_LENGTH,
76
77 LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED
78};
79
Andy Green775c0dd2010-10-29 14:15:22 +010080
81struct lws_tokens {
82 char * token;
83 int token_len;
84};
85
86
87/*
88 * This is totally opaque to code using the library. It's exported as a
Andy Greenab990e42010-10-31 12:42:52 +000089 * forward-reference pointer-only declaration; the user can use the pointer with
90 * other APIs to get information out of it.
Andy Green775c0dd2010-10-29 14:15:22 +010091 */
92
93struct libwebsocket {
94 int (*callback)(struct libwebsocket *,
Andy Green251f6fa2010-11-03 11:13:06 +000095 enum libwebsocket_callback_reasons reason, void *, void *, size_t);
Andy Green775c0dd2010-10-29 14:15:22 +010096
97 enum lws_connection_states state;
98
99 char name_buffer[LWS_MAX_HEADER_NAME_LENGTH];
100 int name_buffer_pos;
101 int current_alloc_len;
102 enum lws_token_indexes parser_state;
103 struct lws_tokens utf8_token[WSI_TOKEN_COUNT];
104 int ietf_spec_revision;
105
106 int sock;
Andy Green4ea60062010-10-30 12:15:07 +0100107
108 enum lws_rx_parse_state lws_rx_parse_state;
109 size_t rx_packet_length;
Andy Green251f6fa2010-11-03 11:13:06 +0000110
111 /* last */
112 char user_space[0];
Andy Green775c0dd2010-10-29 14:15:22 +0100113};
114
Andy Greenff95d7a2010-10-28 22:36:01 +0100115
116const struct lws_tokens lws_tokens[WSI_TOKEN_COUNT] = {
117 { "GET ", 4 },
118 { "Host:", 5 },
119 { "Connection:", 11 },
120 { "Sec-WebSocket-Key1:", 19 },
121 { "Sec-WebSocket-Key2:", 19 },
122 { "Sec-WebSocket-Protocol:", 23 },
123 { "Upgrade:", 8 },
124 { "Origin:", 7 },
125 { "\x0d\x0a", 2 },
126};
127
Andy Green251f6fa2010-11-03 11:13:06 +0000128static void
129libwebsocket_close_and_free_session(struct libwebsocket *wsi)
130{
131 int n = wsi->state;
132
133 wsi->state = WSI_STATE_DEAD_SOCKET;
134
135 if (wsi->callback && n == WSI_STATE_ESTABLISHED)
136 wsi->callback(wsi, LWS_CALLBACK_CLOSED, &wsi->user_space[0],
137 NULL, 0);
138
139 for (n = 0; n < WSI_TOKEN_COUNT; n++)
140 if (wsi->utf8_token[n].token)
141 free(wsi->utf8_token[n].token);
142
143// fprintf(stderr, "closing fd=%d\n", wsi->sock);
144
145 shutdown(wsi->sock, SHUT_RDWR);
146 close(wsi->sock);
147 free(wsi);
148}
149
Andy Greenab990e42010-10-31 12:42:52 +0000150/**
151 * libwebsocket_create_server() - Create the listening websockets server
152 * @port: Port to listen on
153 * @callback: The callback in user code to perform actual serving
154 * @protocol: Which version of the websockets protocol (currently 76)
Andy Green251f6fa2010-11-03 11:13:06 +0000155 * @user_area_size: How much memory to allocate per connection session
156 * which will be used by the user application to store
157 * per-session data. A pointer to this space is given
158 * when the user callback is called.
Andy Greenab990e42010-10-31 12:42:52 +0000159 *
160 * This function forks to create the listening socket and takes care
161 * of all initialization in one step.
162 *
163 * The callback function is called for a handful of events including
164 * http requests coming in, websocket connections becoming
165 * established, and data arriving; it's also called periodically to allow
166 * async transmission.
167 *
168 * The server created is a simple http server by default; part of the
169 * websocket standard is upgrading this http connection to a websocket one.
170 *
171 * This allows the same server to provide files like scripts and favicon /
172 * images or whatever over http and dynamic data over websockets all in
173 * one place; they're all handled in the user callback.
174 */
Andy Green4ea60062010-10-30 12:15:07 +0100175
Andy Greenea71ed12010-10-31 07:40:33 +0000176int libwebsocket_create_server(int port,
177 int (*callback)(struct libwebsocket *,
Andy Green251f6fa2010-11-03 11:13:06 +0000178 enum libwebsocket_callback_reasons,
179 void *, void *, size_t),
180 int protocol, size_t user_area_size)
Andy Greenff95d7a2010-10-28 22:36:01 +0100181{
182 int n;
Andy Green251f6fa2010-11-03 11:13:06 +0000183 int client;
Andy Green69fa0722010-11-03 08:25:13 +0000184 int sockfd;
Andy Green251f6fa2010-11-03 11:13:06 +0000185 int fd;
Andy Greenff95d7a2010-10-28 22:36:01 +0100186 unsigned int clilen;
187 struct sockaddr_in serv_addr, cli_addr;
Andy Green251f6fa2010-11-03 11:13:06 +0000188 struct libwebsocket *wsi[MAX_CLIENTS + 1];
189 struct pollfd fds[MAX_CLIENTS + 1];
190 int fds_count = 0;
191 unsigned char buf[256];
192 int opt = 1;
Andy Greenff95d7a2010-10-28 22:36:01 +0100193
Andy Green251f6fa2010-11-03 11:13:06 +0000194 /* sanity check */
195
Andy Greenea71ed12010-10-31 07:40:33 +0000196 switch (protocol) {
197 case 0:
198 case 2:
199 case 76:
Andy Green5fd8a5e2010-10-31 11:57:17 +0000200 fprintf(stderr, " Using protocol v%d\n", protocol);
Andy Greenea71ed12010-10-31 07:40:33 +0000201 break;
202 default:
203 fprintf(stderr, "protocol %d not supported (try 0 2 or 76)\n",
204 protocol);
205 return -1;
206 }
Andy Green251f6fa2010-11-03 11:13:06 +0000207
208 if (!callback) {
209 fprintf(stderr, "callback is not optional!\n");
210 return -1;
211 }
Andy Greenff95d7a2010-10-28 22:36:01 +0100212
Andy Green775c0dd2010-10-29 14:15:22 +0100213 /* sit there listening for connects, accept and spawn session servers */
214
215 sockfd = socket(AF_INET, SOCK_STREAM, 0);
216 if (sockfd < 0) {
217 fprintf(stderr, "ERROR opening socket");
Andy Green251f6fa2010-11-03 11:13:06 +0000218 return -1;
Andy Green775c0dd2010-10-29 14:15:22 +0100219 }
Andy Green251f6fa2010-11-03 11:13:06 +0000220
221 /* allow us to restart even if old sockets in TIME_WAIT */
222 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
Andy Green775c0dd2010-10-29 14:15:22 +0100223
Andy Green251f6fa2010-11-03 11:13:06 +0000224 bzero((char *) &serv_addr, sizeof(serv_addr));
Andy Green775c0dd2010-10-29 14:15:22 +0100225 serv_addr.sin_family = AF_INET;
226 serv_addr.sin_addr.s_addr = INADDR_ANY;
227 serv_addr.sin_port = htons(port);
228 n = bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
229 if (n < 0) {
Andy Greenea71ed12010-10-31 07:40:33 +0000230 fprintf(stderr, "ERROR on binding to port %d (%d %d)\n", port, n,
231 errno);
Andy Green775c0dd2010-10-29 14:15:22 +0100232 return -1;
233 }
234
235 /* fork off a master server for this websocket server */
Andy Greenff95d7a2010-10-28 22:36:01 +0100236
237 n = fork();
238 if (n < 0) {
239 fprintf(stderr, "Failed on forking server thread: %d\n", n);
Andy Green251f6fa2010-11-03 11:13:06 +0000240 return -1;
Andy Greenff95d7a2010-10-28 22:36:01 +0100241 }
242
243 /* we are done as far as the caller is concerned */
244
245 if (n)
Andy Green251f6fa2010-11-03 11:13:06 +0000246 return sockfd;
Andy Greenff95d7a2010-10-28 22:36:01 +0100247
Andy Green251f6fa2010-11-03 11:13:06 +0000248 /* we are running in a forked subprocess now */
Andy Greenea71ed12010-10-31 07:40:33 +0000249
Andy Greenff95d7a2010-10-28 22:36:01 +0100250 listen(sockfd, 5);
Andy Green251f6fa2010-11-03 11:13:06 +0000251 fprintf(stderr, " Listening on port %d\n", port);
252
253 fds[0].fd = sockfd;
254 fds_count = 1;
255 fds[0].events = POLLIN;
Andy Greenff95d7a2010-10-28 22:36:01 +0100256
257 while (1) {
Andy Greenff95d7a2010-10-28 22:36:01 +0100258
Andy Green251f6fa2010-11-03 11:13:06 +0000259 n = poll(fds, fds_count, 50);
260 if (n < 0 || fds[0].revents & (POLLERR | POLLHUP)) {
261// fprintf(stderr, "Listen Socket dead\n");
262 goto fatal;
Andy Green775c0dd2010-10-29 14:15:22 +0100263 }
Andy Green251f6fa2010-11-03 11:13:06 +0000264 if (n == 0) /* poll timeout */
265 goto poll_out;
266
267 if (fds[0].revents & POLLIN) {
268
269 /* listen socket got a new connection... */
270
271 clilen = sizeof(cli_addr);
272 fd = accept(sockfd, (struct sockaddr *)&cli_addr,
273 &clilen);
274 if (fd < 0) {
275 fprintf(stderr, "ERROR on accept");
276 continue;
277 }
Andy Greenff95d7a2010-10-28 22:36:01 +0100278
Andy Green251f6fa2010-11-03 11:13:06 +0000279 if (fds_count >= MAX_CLIENTS) {
280 fprintf(stderr, "too busy");
281 close(fd);
282 continue;
283 }
Andy Greenff95d7a2010-10-28 22:36:01 +0100284
Andy Green251f6fa2010-11-03 11:13:06 +0000285// fprintf(stderr, "accepted new conn port %u on fd=%d\n",
286// ntohs(cli_addr.sin_port), fd);
287
288 /* intialize the instance struct */
289
290 wsi[fds_count] = malloc(sizeof(struct libwebsocket) +
291 user_area_size);
292 if (!wsi[fds_count])
293 return -1;
294
295 wsi[fds_count]->sock = fd;
296 wsi[fds_count]->state = WSI_STATE_HTTP;
297 wsi[fds_count]->name_buffer_pos = 0;
298
299 for (n = 0; n < WSI_TOKEN_COUNT; n++) {
300 wsi[fds_count]->utf8_token[n].token = NULL;
301 wsi[fds_count]->utf8_token[n].token_len = 0;
302 }
303
304 wsi[fds_count]->callback = callback;
305 wsi[fds_count]->ietf_spec_revision = protocol;
306
307 fds[fds_count].events = POLLIN;
308 fds[fds_count++].fd = fd;
Andy Green775c0dd2010-10-29 14:15:22 +0100309 }
310
Andy Green251f6fa2010-11-03 11:13:06 +0000311 /* check for activity on client sockets */
312
313 for (client = 1; client < fds_count; client++) {
314
315 /* handle session socket closed */
316
317 if (fds[client].revents & (POLLERR | POLLHUP)) {
318
319 fprintf(stderr, "Session Socket dead\n");
320
321 libwebsocket_close_and_free_session(wsi[client]);
322 goto nuke_this;
323 }
324
325 /* any incoming data ready? */
326
327 if (!(fds[client].revents & POLLIN))
328 continue;
329
330// fprintf(stderr, "POLLIN\n");
331
332 n = recv(fds[client].fd, buf, sizeof(buf), 0);
333 if (n < 0) {
334 fprintf(stderr, "Socket read returned %d\n", n);
335 continue;
336 }
337 if (!n) {
338// fprintf(stderr, "POLLIN with 0 len waiting\n");
339 libwebsocket_close_and_free_session(wsi[client]);
340 goto nuke_this;
341 }
342
343 /* service incoming data */
344
345 if (libwebsocket_read(wsi[client], buf, n) >= 0)
346 continue;
347
348 /* it closed and nuked wsi[client] */
349nuke_this:
350 for (n = client; n < fds_count - 1; n++) {
351 fds[n] = fds[n + 1];
352 wsi[n] = wsi[n + 1];
353 }
354 fds_count--;
355 client--;
Andy Green775c0dd2010-10-29 14:15:22 +0100356 }
357
Andy Green251f6fa2010-11-03 11:13:06 +0000358poll_out:
359 for (client = 1; client < fds_count; client++) {
Andy Green775c0dd2010-10-29 14:15:22 +0100360
Andy Green251f6fa2010-11-03 11:13:06 +0000361 if (wsi[client]->state != WSI_STATE_ESTABLISHED)
362 continue;
363
364 if (!wsi[client]->callback)
365 continue;
Andy Green69fa0722010-11-03 08:25:13 +0000366
Andy Green251f6fa2010-11-03 11:13:06 +0000367 wsi[client]->callback(wsi[client], LWS_CALLBACK_SEND,
368 &wsi[client]->user_space[0], NULL, 0);
369 }
370
371 continue;
Andy Greenff95d7a2010-10-28 22:36:01 +0100372 }
Andy Green251f6fa2010-11-03 11:13:06 +0000373
374fatal:
375 close(fds[0].fd);
376 for (client = 1; client < fds_count; client++)
377 libwebsocket_close_and_free_session(wsi[client]);
Andy Greenff95d7a2010-10-28 22:36:01 +0100378
Andy Green251f6fa2010-11-03 11:13:06 +0000379 kill(0, SIGTERM);
380
381 return 0;
Andy Greenff95d7a2010-10-28 22:36:01 +0100382}
383
Andy Greenab990e42010-10-31 12:42:52 +0000384/**
385 * libwebsocket_get_uri() - Return the URI path being requested
386 * @wsi: Websocket instance
387 *
388 * The user code can find out the local path being opened from this
389 * call, it's valid on HTTP or established websocket connections.
390 * If the client opened the connection with "http://127.0.0.1/xyz/abc.d"
391 * then this call will return a pointer to "/xyz/abc.d"
392 */
393
Andy Green5fd8a5e2010-10-31 11:57:17 +0000394const char * libwebsocket_get_uri(struct libwebsocket *wsi)
395{
396 if (wsi->utf8_token[WSI_TOKEN_GET_URI].token)
397 return wsi->utf8_token[WSI_TOKEN_GET_URI].token;
398
399 return NULL;
400}
Andy Greenff95d7a2010-10-28 22:36:01 +0100401
402static int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
403{
404 int n;
405
406 switch (wsi->parser_state) {
407 case WSI_TOKEN_GET_URI:
408 case WSI_TOKEN_HOST:
409 case WSI_TOKEN_CONNECTION:
410 case WSI_TOKEN_KEY1:
411 case WSI_TOKEN_KEY2:
412 case WSI_TOKEN_PROTOCOL:
413 case WSI_TOKEN_UPGRADE:
414 case WSI_TOKEN_ORIGIN:
415 case WSI_TOKEN_CHALLENGE:
416
Andy Green69fa0722010-11-03 08:25:13 +0000417 debug("WSI_TOKEN_(%d) '%c'\n", wsi->parser_state, c);
Andy Greenff95d7a2010-10-28 22:36:01 +0100418
419 /* collect into malloc'd buffers */
420 /* optional space swallow */
421 if (!wsi->utf8_token[wsi->parser_state].token_len && c == ' ')
422 break;
423
424 /* special case space terminator for get-uri */
425 if (wsi->parser_state == WSI_TOKEN_GET_URI && c == ' ') {
426 wsi->utf8_token[wsi->parser_state].token[
427 wsi->utf8_token[wsi->parser_state].token_len] = '\0';
428 wsi->parser_state = WSI_TOKEN_SKIPPING;
429 break;
430 }
431
432 /* allocate appropriate memory */
Andy Green4ea60062010-10-30 12:15:07 +0100433 if (wsi->utf8_token[wsi->parser_state].token_len ==
434 wsi->current_alloc_len - 1) {
Andy Greenff95d7a2010-10-28 22:36:01 +0100435 /* need to extend */
436 wsi->current_alloc_len += LWS_ADDITIONAL_HDR_ALLOC;
437 if (wsi->current_alloc_len >= LWS_MAX_HEADER_LEN) {
438 /* it's waaay to much payload, fail it */
439 strcpy(wsi->utf8_token[wsi->parser_state].token,
Andy Green4ea60062010-10-30 12:15:07 +0100440 "!!! Length exceeded maximum supported !!!");
Andy Greenff95d7a2010-10-28 22:36:01 +0100441 wsi->parser_state = WSI_TOKEN_SKIPPING;
442 break;
443 }
444 wsi->utf8_token[wsi->parser_state].token =
445 realloc(wsi->utf8_token[wsi->parser_state].token,
446 wsi->current_alloc_len);
447 }
448
449 /* bail at EOL */
450 if (wsi->parser_state != WSI_TOKEN_CHALLENGE && c == '\x0d') {
451 wsi->utf8_token[wsi->parser_state].token[
452 wsi->utf8_token[wsi->parser_state].token_len] = '\0';
453 wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
454 break;
455 }
456
457 wsi->utf8_token[wsi->parser_state].token[
458 wsi->utf8_token[wsi->parser_state].token_len++] = c;
459
460 /* special payload limiting */
Andy Green775c0dd2010-10-29 14:15:22 +0100461 if (wsi->parser_state == WSI_TOKEN_CHALLENGE &&
462 wsi->utf8_token[wsi->parser_state].token_len == 8) {
Andy Green69fa0722010-11-03 08:25:13 +0000463 debug("Setting WSI_PARSING_COMPLETE\n");
Andy Green775c0dd2010-10-29 14:15:22 +0100464 wsi->parser_state = WSI_PARSING_COMPLETE;
465 break;
466 }
Andy Greenff95d7a2010-10-28 22:36:01 +0100467
468 break;
469
470 /* collecting and checking a name part */
471 case WSI_TOKEN_NAME_PART:
Andy Green69fa0722010-11-03 08:25:13 +0000472 debug("WSI_TOKEN_NAME_PART '%c'\n", c);
Andy Greenff95d7a2010-10-28 22:36:01 +0100473
474 if (wsi->name_buffer_pos == sizeof(wsi->name_buffer) - 1) {
475 /* name bigger than we can handle, skip until next */
476 wsi->parser_state = WSI_TOKEN_SKIPPING;
477 break;
478 }
479 wsi->name_buffer[wsi->name_buffer_pos++] = c;
480 wsi->name_buffer[wsi->name_buffer_pos] = '\0';
481
482 for (n = 0; n < WSI_TOKEN_COUNT; n++) {
483 if (wsi->name_buffer_pos != lws_tokens[n].token_len)
484 continue;
485 if (strcmp(lws_tokens[n].token, wsi->name_buffer))
486 continue;
Andy Green69fa0722010-11-03 08:25:13 +0000487 debug("known hdr '%s'\n", wsi->name_buffer);
Andy Greenff95d7a2010-10-28 22:36:01 +0100488 wsi->parser_state = WSI_TOKEN_GET_URI + n;
489 wsi->current_alloc_len = LWS_INITIAL_HDR_ALLOC;
490 wsi->utf8_token[wsi->parser_state].token =
491 malloc(wsi->current_alloc_len);
492 wsi->utf8_token[wsi->parser_state].token_len = 0;
493 n = WSI_TOKEN_COUNT;
494 }
Andy Green5fd8a5e2010-10-31 11:57:17 +0000495
496 /* colon delimiter means we just don't know this name */
497
498 if (wsi->parser_state == WSI_TOKEN_NAME_PART && c == ':') {
Andy Green69fa0722010-11-03 08:25:13 +0000499 debug("skipping unknown header '%s'\n",
500 wsi->name_buffer);
Andy Green5fd8a5e2010-10-31 11:57:17 +0000501 wsi->parser_state = WSI_TOKEN_SKIPPING;
Andy Greenff95d7a2010-10-28 22:36:01 +0100502 break;
Andy Green5fd8a5e2010-10-31 11:57:17 +0000503 }
504
505 /* don't look for payload when it can just be http headers */
506
507 if (wsi->parser_state == WSI_TOKEN_CHALLENGE &&
508 !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len) {
509 /* they're HTTP headers, not websocket upgrade! */
Andy Green69fa0722010-11-03 08:25:13 +0000510 debug("Setting WSI_PARSING_COMPLETE "
511 "from http headers\n");
Andy Green5fd8a5e2010-10-31 11:57:17 +0000512 wsi->parser_state = WSI_PARSING_COMPLETE;
513 }
Andy Greenff95d7a2010-10-28 22:36:01 +0100514 break;
515
516 /* skipping arg part of a name we didn't recognize */
517 case WSI_TOKEN_SKIPPING:
Andy Green69fa0722010-11-03 08:25:13 +0000518 debug("WSI_TOKEN_SKIPPING '%c'\n", c);
Andy Greenff95d7a2010-10-28 22:36:01 +0100519 if (c == '\x0d')
520 wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
521 break;
522 case WSI_TOKEN_SKIPPING_SAW_CR:
Andy Green69fa0722010-11-03 08:25:13 +0000523 debug("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
Andy Greenff95d7a2010-10-28 22:36:01 +0100524 if (c == '\x0a')
525 wsi->parser_state = WSI_TOKEN_NAME_PART;
526 else
527 wsi->parser_state = WSI_TOKEN_SKIPPING;
528 wsi->name_buffer_pos = 0;
529 break;
530 /* we're done, ignore anything else */
531 case WSI_PARSING_COMPLETE:
Andy Green69fa0722010-11-03 08:25:13 +0000532 debug("WSI_PARSING_COMPLETE '%c'\n", c);
Andy Greenff95d7a2010-10-28 22:36:01 +0100533 break;
534
535 default: /* keep gcc happy */
536 break;
537 }
538
539 return 0;
540}
541
542static int interpret_key(const char *key, unsigned int *result)
543{
544 char digits[20];
545 int digit_pos = 0;
546 const char *p = key;
547 int spaces = 0;
548
549 while (*p) {
550 if (isdigit(*p)) {
551 if (digit_pos == sizeof(digits) - 1)
552 return -1;
553 digits[digit_pos++] = *p;
554 }
555 p++;
556 }
557 digits[digit_pos] = '\0';
558 if (!digit_pos)
559 return -2;
560
561 while (*key) {
562 if (*key == ' ')
563 spaces++;
564 key++;
565 }
566
567 if (!spaces)
568 return -3;
569
570 *result = atol(digits) / spaces;
571
572 return 0;
573}
574
Andy Green4ea60062010-10-30 12:15:07 +0100575static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c)
576{
577 int n;
578 unsigned char buf[2];
579
580 switch (wsi->lws_rx_parse_state) {
581 case LWS_RXPS_NEW:
582
583 switch (wsi->ietf_spec_revision) {
584 /* Firefox 4.0b6 likes this as of 30 Oct */
585 case 76:
586 if (c == 0xff)
587 wsi->lws_rx_parse_state = LWS_RXPS_SEEN_76_FF;
588 break;
589 case 0:
590 break;
591 }
592 break;
593 case LWS_RXPS_SEEN_76_FF:
Andy Green69fa0722010-11-03 08:25:13 +0000594 if (c)
Andy Green4ea60062010-10-30 12:15:07 +0100595 break;
Andy Green4ea60062010-10-30 12:15:07 +0100596
Andy Green69fa0722010-11-03 08:25:13 +0000597 debug("Seen that client is requesting "
Andy Greene5eafd32010-10-31 13:11:57 +0000598 "a v76 close, sending ack\n");
Andy Green4ea60062010-10-30 12:15:07 +0100599 buf[0] = 0xff;
600 buf[1] = 0;
601 n = write(wsi->sock, buf, 2);
602 if (n < 0) {
603 fprintf(stderr, "ERROR writing to socket");
604 return -1;
605 }
Andy Green69fa0722010-11-03 08:25:13 +0000606 debug(" v76 close ack sent, server closing skt\n");
Andy Green4ea60062010-10-30 12:15:07 +0100607 /* returning < 0 will get it closed in parent */
608 return -1;
609
610 case LWS_RXPS_PULLING_76_LENGTH:
611 break;
612 case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
613 break;
614 }
615
616 return 0;
617}
618
619static int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi,
620 unsigned char *buf, size_t len)
621{
622 int n;
623
624 fprintf(stderr, "received %d byte packet\n", (int)len);
625 for (n = 0; n < len; n++)
626 fprintf(stderr, "%02X ", buf[n]);
627 fprintf(stderr, "\n");
628
629 /* let the rx protocol state machine have as much as it needs */
630
631 n = 0;
632 while (wsi->lws_rx_parse_state !=
633 LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED && n < len)
634 if (libwebsocket_rx_sm(wsi, buf[n++]) < 0)
635 return -1;
636
Andy Greene5eafd32010-10-31 13:11:57 +0000637 if (n != len && wsi->callback)
Andy Green251f6fa2010-11-03 11:13:06 +0000638 wsi->callback(wsi, LWS_CALLBACK_RECEIVE, &wsi->user_space[0],
639 &buf[n], len - n);
Andy Green4ea60062010-10-30 12:15:07 +0100640
641 return -0;
642}
643
Andy Greenff95d7a2010-10-28 22:36:01 +0100644
645/*
646 * We have to take care about parsing because the headers may be split
647 * into multiple fragments. They may contain unknown headers with arbitrary
Andy Green775c0dd2010-10-29 14:15:22 +0100648 * argument lengths. So, we parse using a single-character at a time state
649 * machine that is completely independent of packet size.
Andy Greenff95d7a2010-10-28 22:36:01 +0100650 */
651
Andy Greenab990e42010-10-31 12:42:52 +0000652static int
653libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len)
Andy Greenff95d7a2010-10-28 22:36:01 +0100654{
655 size_t n;
656 char *p;
657 unsigned int key1, key2;
658 unsigned char sum[16];
Andy Green775c0dd2010-10-29 14:15:22 +0100659 char *response;
Andy Greenff95d7a2010-10-28 22:36:01 +0100660
661 switch (wsi->state) {
Andy Green5fd8a5e2010-10-31 11:57:17 +0000662 case WSI_STATE_HTTP:
663 wsi->state = WSI_STATE_HTTP_HEADERS;
Andy Greenff95d7a2010-10-28 22:36:01 +0100664 wsi->parser_state = WSI_TOKEN_NAME_PART;
Andy Green775c0dd2010-10-29 14:15:22 +0100665 /* fallthru */
Andy Green5fd8a5e2010-10-31 11:57:17 +0000666 case WSI_STATE_HTTP_HEADERS:
Andy Greenff95d7a2010-10-28 22:36:01 +0100667
Andy Green69fa0722010-11-03 08:25:13 +0000668 debug("issuing %d bytes to parser\n", (int)len);
669#ifdef DEBUG
670 fwrite(buf, 1, len, stderr);
671#endif
Andy Greenff95d7a2010-10-28 22:36:01 +0100672 for (n = 0; n< len; n++)
673 libwebsocket_parse(wsi, *buf++);
674
675 if (wsi->parser_state != WSI_PARSING_COMPLETE)
676 break;
Andy Green5fd8a5e2010-10-31 11:57:17 +0000677
678 /* is this websocket protocol or normal http 1.0? */
679
680 if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
681 !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len) {
682 if (wsi->callback)
Andy Greene5eafd32010-10-31 13:11:57 +0000683 (wsi->callback)(wsi, LWS_CALLBACK_HTTP,
Andy Green251f6fa2010-11-03 11:13:06 +0000684 &wsi->user_space[0],
Andy Greene5eafd32010-10-31 13:11:57 +0000685 NULL, 0);
Andy Green5fd8a5e2010-10-31 11:57:17 +0000686 wsi->state = WSI_STATE_HTTP;
687 return 0;
688 }
689
Andy Green5fd8a5e2010-10-31 11:57:17 +0000690 /* Websocket - confirm we have all the necessary pieces */
Andy Greenff95d7a2010-10-28 22:36:01 +0100691
Andy Green5fd8a5e2010-10-31 11:57:17 +0000692 if (!wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len ||
Andy Greenff95d7a2010-10-28 22:36:01 +0100693 !wsi->utf8_token[WSI_TOKEN_HOST].token_len ||
694 !wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len ||
695 !wsi->utf8_token[WSI_TOKEN_KEY1].token_len ||
Andy Green5fd8a5e2010-10-31 11:57:17 +0000696 !wsi->utf8_token[WSI_TOKEN_KEY2].token_len)
Andy Greenff95d7a2010-10-28 22:36:01 +0100697 /* completed header processing, but missing some bits */
698 goto bail;
Andy Greenff95d7a2010-10-28 22:36:01 +0100699
700 /* create the response packet */
701
Andy Green4ea60062010-10-30 12:15:07 +0100702 /* make a buffer big enough for everything */
703
Andy Green775c0dd2010-10-29 14:15:22 +0100704 response = malloc(256 +
Andy Greenff95d7a2010-10-28 22:36:01 +0100705 wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len +
706 wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len +
707 wsi->utf8_token[WSI_TOKEN_HOST].token_len +
708 wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len +
709 wsi->utf8_token[WSI_TOKEN_GET_URI].token_len +
710 wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len);
Andy Green4ea60062010-10-30 12:15:07 +0100711 if (!response) {
712 fprintf(stderr, "Out of memory for response buffer\n");
713 goto bail;
714 }
Andy Greenff95d7a2010-10-28 22:36:01 +0100715
Andy Green775c0dd2010-10-29 14:15:22 +0100716 p = response;
Andy Green4ea60062010-10-30 12:15:07 +0100717 strcpy(p, "HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a"
718 "Upgrade: WebSocket\x0d\x0a");
719 p += strlen("HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a"
720 "Upgrade: WebSocket\x0d\x0a");
Andy Greene5eafd32010-10-31 13:11:57 +0000721 strcpy(p, "Connection: Upgrade\x0d\x0a"
722 "Sec-WebSocket-Origin: ");
723 p += strlen("Connection: Upgrade\x0d\x0a"
724 "Sec-WebSocket-Origin: ");
Andy Greenff95d7a2010-10-28 22:36:01 +0100725 strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token);
726 p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len;
727 strcpy(p, "\x0d\x0aSec-WebSocket-Location: ws://");
728 p += strlen("\x0d\x0aSec-WebSocket-Location: ws://");
729 strcpy(p, wsi->utf8_token[WSI_TOKEN_HOST].token);
730 p += wsi->utf8_token[WSI_TOKEN_HOST].token_len;
731 strcpy(p, wsi->utf8_token[WSI_TOKEN_GET_URI].token);
732 p += wsi->utf8_token[WSI_TOKEN_GET_URI].token_len;
Andy Greenea71ed12010-10-31 07:40:33 +0000733
Andy Greenff95d7a2010-10-28 22:36:01 +0100734 if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
Andy Greenea71ed12010-10-31 07:40:33 +0000735 strcpy(p, "\x0d\x0aSec-WebSocket-Protocol: ");
736 p += strlen("\x0d\x0aSec-WebSocket-Protocol: ");
Andy Greenff95d7a2010-10-28 22:36:01 +0100737 strcpy(p, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
738 p += wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len;
739 }
Andy Greenea71ed12010-10-31 07:40:33 +0000740
Andy Greenff95d7a2010-10-28 22:36:01 +0100741 strcpy(p, "\x0d\x0a\x0d\x0a");
742 p += strlen("\x0d\x0a\x0d\x0a");
743
Andy Green775c0dd2010-10-29 14:15:22 +0100744 /* convert the two keys into 32-bit integers */
745
Andy Greenff95d7a2010-10-28 22:36:01 +0100746 if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY1].token, &key1))
747 goto bail;
Andy Greenff95d7a2010-10-28 22:36:01 +0100748 if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY2].token, &key2))
749 goto bail;
Andy Green775c0dd2010-10-29 14:15:22 +0100750
751 /* lay them out in network byte order (MSB first */
Andy Greenff95d7a2010-10-28 22:36:01 +0100752
753 sum[0] = key1 >> 24;
754 sum[1] = key1 >> 16;
755 sum[2] = key1 >> 8;
756 sum[3] = key1;
757 sum[4] = key2 >> 24;
758 sum[5] = key2 >> 16;
759 sum[6] = key2 >> 8;
760 sum[7] = key2;
Andy Green775c0dd2010-10-29 14:15:22 +0100761
762 /* follow them with the challenge token we were sent */
763
Andy Greenff95d7a2010-10-28 22:36:01 +0100764 memcpy(&sum[8], wsi->utf8_token[WSI_TOKEN_CHALLENGE].token, 8);
765
Andy Green775c0dd2010-10-29 14:15:22 +0100766 /*
767 * compute the md5sum of that 16-byte series and use as our
768 * payload after our headers
769 */
770
Andy Greenff95d7a2010-10-28 22:36:01 +0100771 md5(sum, 16, (unsigned char *)p);
772 p += 16;
773
Andy Green4ea60062010-10-30 12:15:07 +0100774 /* it's complete: go ahead and send it */
Andy Greenff95d7a2010-10-28 22:36:01 +0100775
Andy Green69fa0722010-11-03 08:25:13 +0000776 debug("issuing response packet %d len\n",
777 (int)(p - response));
778#ifdef DEBUG
779 fwrite(response, 1, p - response, stderr);
780#endif
Andy Green775c0dd2010-10-29 14:15:22 +0100781 n = write(wsi->sock, response, p - response);
782 if (n < 0) {
783 fprintf(stderr, "ERROR writing to socket");
784 goto bail;
785 }
Andy Green4ea60062010-10-30 12:15:07 +0100786
787 /* alright clean up and set ourselves into established state */
Andy Green775c0dd2010-10-29 14:15:22 +0100788
789 free(response);
790 wsi->state = WSI_STATE_ESTABLISHED;
Andy Green4ea60062010-10-30 12:15:07 +0100791 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
Andy Green775c0dd2010-10-29 14:15:22 +0100792
793 /* notify user code that we're ready to roll */
794
795 if (wsi->callback)
Andy Green251f6fa2010-11-03 11:13:06 +0000796 wsi->callback(wsi, LWS_CALLBACK_ESTABLISHED,
797 &wsi->user_space[0], NULL, 0);
Andy Greenff95d7a2010-10-28 22:36:01 +0100798 break;
799
800 case WSI_STATE_ESTABLISHED:
Andy Green4ea60062010-10-30 12:15:07 +0100801 if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0)
802 goto bail;
Andy Greenff95d7a2010-10-28 22:36:01 +0100803 break;
804 default:
805 break;
806 }
807
808 return 0;
809
810bail:
Andy Green251f6fa2010-11-03 11:13:06 +0000811 libwebsocket_close_and_free_session(wsi);
Andy Greenff95d7a2010-10-28 22:36:01 +0100812 return -1;
813}
814
Andy Greenab990e42010-10-31 12:42:52 +0000815/**
816 * libwebsocket_write() - Apply protocol then write data to client
817 * @wsi: Websocket instance (available from user callback)
818 * @buf: The data to send. For data being sent on a websocket
819 * connection (ie, not default http), this buffer MUST have
820 * LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE the pointer
821 * and an additional LWS_SEND_BUFFER_POST_PADDING bytes valid
822 * in the buffer after (buf + len). This is so the protocol
823 * header and trailer data can be added in-situ.
824 * @len: Count of the data bytes in the payload starting from buf
825 * @protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one
826 * of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate
827 * data on a websockets connection. Remember to allow the extra
828 * bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT
829 * are used.
830 *
831 * This function provides the way to issue data back to the client
832 * for both http and websocket protocols.
Andy Green4ea60062010-10-30 12:15:07 +0100833 *
Andy Greenab990e42010-10-31 12:42:52 +0000834 * In the case of sending using websocket protocol, be sure to allocate
835 * valid storage before and after buf as explained above. This scheme
836 * allows maximum efficiency of sending data and protocol in a single
837 * packet while not burdening the user code with any protocol knowledge.
Andy Green4ea60062010-10-30 12:15:07 +0100838 */
839
840int libwebsocket_write(struct libwebsocket * wsi, unsigned char *buf,
Andy Green5fd8a5e2010-10-31 11:57:17 +0000841 size_t len, enum libwebsocket_write_protocol protocol)
Andy Green775c0dd2010-10-29 14:15:22 +0100842{
843 int n;
Andy Green4ea60062010-10-30 12:15:07 +0100844 int pre = 0;
845 int post = 0;
846 unsigned int shift = 7;
Andy Green775c0dd2010-10-29 14:15:22 +0100847
Andy Green5fd8a5e2010-10-31 11:57:17 +0000848 if (protocol == LWS_WRITE_HTTP)
849 goto send_raw;
850
851 /* websocket protocol, either binary or text */
852
Andy Green775c0dd2010-10-29 14:15:22 +0100853 if (wsi->state != WSI_STATE_ESTABLISHED)
854 return -1;
Andy Greenff95d7a2010-10-28 22:36:01 +0100855
Andy Green775c0dd2010-10-29 14:15:22 +0100856 switch (wsi->ietf_spec_revision) {
Andy Green69fa0722010-11-03 08:25:13 +0000857 /* chrome likes this as of 30 Oct */
Andy Green4ea60062010-10-30 12:15:07 +0100858 /* Firefox 4.0b6 likes this as of 30 Oct */
859 case 76:
Andy Green5fd8a5e2010-10-31 11:57:17 +0000860 if (protocol == LWS_WRITE_BINARY) {
Andy Green4ea60062010-10-30 12:15:07 +0100861 /* in binary mode we send 7-bit used length blocks */
862 pre = 1;
863 while (len & (127 << shift)) {
864 pre++;
865 shift += 7;
866 }
867 n = 0;
868 shift -= 7;
869 while (shift >= 0) {
870 if (shift)
871 buf[0 - pre + n] =
872 ((len >> shift) & 127) | 0x80;
873 else
874 buf[0 - pre + n] =
875 ((len >> shift) & 127);
876 n++;
877 shift -= 7;
878 }
879 break;
Andy Green775c0dd2010-10-29 14:15:22 +0100880 }
Andy Green4ea60062010-10-30 12:15:07 +0100881
882 /* frame type = text, length-free spam mode */
883
884 buf[-1] = 0;
885 buf[len] = 0xff; /* EOT marker */
886 pre = 1;
887 post = 1;
Andy Green775c0dd2010-10-29 14:15:22 +0100888 break;
Andy Green4ea60062010-10-30 12:15:07 +0100889
Andy Green4ea60062010-10-30 12:15:07 +0100890 case 0:
891 buf[-9] = 0xff;
892#if defined __LP64__
893 buf[-8] = len >> 56;
894 buf[-7] = len >> 48;
895 buf[-6] = len >> 40;
896 buf[-5] = len >> 32;
897#else
898 buf[-8] = 0;
899 buf[-7] = 0;
900 buf[-6] = 0;
901 buf[-5] = 0;
902#endif
903 buf[-4] = len >> 24;
904 buf[-3] = len >> 16;
905 buf[-2] = len >> 8;
906 buf[-1] = len;
907 pre = 9;
908 break;
909
Andy Green775c0dd2010-10-29 14:15:22 +0100910 /* just an unimplemented spec right now apparently */
911 case 2:
Andy Green4ea60062010-10-30 12:15:07 +0100912 n = 4; /* text */
Andy Green5fd8a5e2010-10-31 11:57:17 +0000913 if (protocol == LWS_WRITE_BINARY)
Andy Green4ea60062010-10-30 12:15:07 +0100914 n = 5; /* binary */
Andy Green775c0dd2010-10-29 14:15:22 +0100915 if (len < 126) {
Andy Green4ea60062010-10-30 12:15:07 +0100916 buf[-2] = n;
917 buf[-1] = len;
918 pre = 2;
Andy Green775c0dd2010-10-29 14:15:22 +0100919 } else {
920 if (len < 65536) {
Andy Green4ea60062010-10-30 12:15:07 +0100921 buf[-4] = n;
922 buf[-3] = 126;
923 buf[-2] = len >> 8;
924 buf[-1] = len;
925 pre = 4;
Andy Green775c0dd2010-10-29 14:15:22 +0100926 } else {
Andy Green4ea60062010-10-30 12:15:07 +0100927 buf[-10] = n;
928 buf[-9] = 127;
929#if defined __LP64__
930 buf[-8] = (len >> 56) & 0x7f;
931 buf[-7] = len >> 48;
932 buf[-6] = len >> 40;
933 buf[-5] = len >> 32;
934#else
935 buf[-8] = 0;
936 buf[-7] = 0;
937 buf[-6] = 0;
938 buf[-5] = 0;
939#endif
940 buf[-4] = len >> 24;
941 buf[-3] = len >> 16;
942 buf[-2] = len >> 8;
943 buf[-1] = len;
944 pre = 10;
Andy Green775c0dd2010-10-29 14:15:22 +0100945 }
946 }
Andy Green775c0dd2010-10-29 14:15:22 +0100947 break;
948 }
Andy Green251f6fa2010-11-03 11:13:06 +0000949
950#if 0
951 for (n = 0; n < (len + pre + post); n++)
952 fprintf(stderr, "%02X ", buf[n - pre]);
953
954 fprintf(stderr, "\n");
955#endif
Andy Green775c0dd2010-10-29 14:15:22 +0100956
Andy Green5fd8a5e2010-10-31 11:57:17 +0000957send_raw:
958
959 n = send(wsi->sock, buf - pre, len + pre + post, 0);
Andy Green775c0dd2010-10-29 14:15:22 +0100960 if (n < 0) {
961 fprintf(stderr, "ERROR writing to socket");
962 return -1;
963 }
Andy Green4ea60062010-10-30 12:15:07 +0100964
Andy Green5fd8a5e2010-10-31 11:57:17 +0000965// fprintf(stderr, "written %d bytes to client\n", (int)len);
Andy Green775c0dd2010-10-29 14:15:22 +0100966
967 return 0;
968}
969
Andy Greenff95d7a2010-10-28 22:36:01 +0100970
Andy Greenab990e42010-10-31 12:42:52 +0000971/**
972 * libwebsockets_serve_http_file() - Send a file back to the client using http
973 * @wsi: Websocket instance (available from user callback)
974 * @file: The file to issue over http
975 * @content_type: The http content type, eg, text/html
976 *
977 * This function is intended to be called from the callback in response
978 * to http requests from the client. It allows the callback to issue
979 * local files down the http link in a single step.
980 */
981
Andy Green5fd8a5e2010-10-31 11:57:17 +0000982int libwebsockets_serve_http_file(struct libwebsocket *wsi, const char * file,
983 const char * content_type)
984{
985 int fd;
986 struct stat stat;
987 char buf[512];
988 char *p = buf;
989 int n;
990
991 fd = open(file, O_RDONLY);
992 if (fd < 1) {
993 p += sprintf(p, "HTTP/1.0 400 Bad\x0d\x0a"
994 "Server: libwebsockets\x0d\x0a"
995 "\x0d\x0a"
996 );
Andy Greene5eafd32010-10-31 13:11:57 +0000997 libwebsocket_write(wsi, (unsigned char *)buf, p - buf,
998 LWS_WRITE_HTTP);
Andy Green5fd8a5e2010-10-31 11:57:17 +0000999
1000 return -1;
1001 }
1002
1003 fstat(fd, &stat);
1004 p += sprintf(p, "HTTP/1.0 200 OK\x0d\x0a"
1005 "Server: libwebsockets\x0d\x0a"
1006 "Content-Type: %s\x0d\x0a"
1007 "Content-Length: %u\x0d\x0a"
Andy Greene5eafd32010-10-31 13:11:57 +00001008 "\x0d\x0a", content_type, (unsigned int)stat.st_size);
Andy Green5fd8a5e2010-10-31 11:57:17 +00001009
1010 libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP);
1011
1012 n = 1;
1013 while (n > 0) {
1014 n = read(fd, buf, 512);
Andy Greene5eafd32010-10-31 13:11:57 +00001015 libwebsocket_write(wsi, (unsigned char *)buf, n,
1016 LWS_WRITE_HTTP);
Andy Green5fd8a5e2010-10-31 11:57:17 +00001017 }
1018
1019 close(fd);
1020
1021 return 0;
1022}