blob: dbee05f300d4bf0b8aac317ebd49bd1e154c67a3 [file] [log] [blame]
Andy Green7c212cc2010-11-08 20:20:42 +00001/*
2 * libwebsockets - small server side websockets and web server implementation
Andy Greene77ddd82010-11-13 10:03:47 +00003 *
Andy Green7c212cc2010-11-08 20:20:42 +00004 * Copyright (C) 2010 Andy Green <andy@warmcat.com>
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
24const struct lws_tokens lws_tokens[WSI_TOKEN_COUNT] = {
Andy Green4b23c722010-12-20 09:19:37 +000025 [WSI_TOKEN_GET_URI] = { "GET ", 4 },
26 [WSI_TOKEN_HOST] = { "Host:", 5 },
27 [WSI_TOKEN_CONNECTION] = { "Connection:", 11 },
28 [WSI_TOKEN_KEY1] = { "Sec-WebSocket-Key1:", 19 },
29 [WSI_TOKEN_KEY2] = { "Sec-WebSocket-Key2:", 19 },
30 [WSI_TOKEN_PROTOCOL] = { "Sec-WebSocket-Protocol:", 23 },
31 [WSI_TOKEN_UPGRADE] = { "Upgrade:", 8 },
32 [WSI_TOKEN_ORIGIN] = { "Origin:", 7 },
33 [WSI_TOKEN_DRAFT] = { "Sec-WebSocket-Draft:", 20 },
34 [WSI_TOKEN_CHALLENGE] = { "\x0d\x0a", 2 },
Andy Greend1b11e32011-01-18 15:39:02 +000035
36 [WSI_TOKEN_KEY] = { "Sec-WebSocket-Key:", 18 },
37 [WSI_TOKEN_VERSION] = { "Sec-WebSocket-Version:", 22 },
38
Andy Green7c212cc2010-11-08 20:20:42 +000039};
40
Andy Green7c212cc2010-11-08 20:20:42 +000041int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
42{
43 int n;
44
45 switch (wsi->parser_state) {
46 case WSI_TOKEN_GET_URI:
47 case WSI_TOKEN_HOST:
48 case WSI_TOKEN_CONNECTION:
49 case WSI_TOKEN_KEY1:
50 case WSI_TOKEN_KEY2:
51 case WSI_TOKEN_PROTOCOL:
52 case WSI_TOKEN_UPGRADE:
53 case WSI_TOKEN_ORIGIN:
Andy Greence510c62010-11-11 12:48:13 +000054 case WSI_TOKEN_DRAFT:
Andy Green7c212cc2010-11-08 20:20:42 +000055 case WSI_TOKEN_CHALLENGE:
Andy Greend1b11e32011-01-18 15:39:02 +000056 case WSI_TOKEN_KEY:
57 case WSI_TOKEN_VERSION:
Andy Green7c212cc2010-11-08 20:20:42 +000058 debug("WSI_TOKEN_(%d) '%c'\n", wsi->parser_state, c);
59
60 /* collect into malloc'd buffers */
61 /* optional space swallow */
62 if (!wsi->utf8_token[wsi->parser_state].token_len && c == ' ')
63 break;
Andy Greene77ddd82010-11-13 10:03:47 +000064
Andy Green7c212cc2010-11-08 20:20:42 +000065 /* special case space terminator for get-uri */
66 if (wsi->parser_state == WSI_TOKEN_GET_URI && c == ' ') {
67 wsi->utf8_token[wsi->parser_state].token[
68 wsi->utf8_token[wsi->parser_state].token_len] = '\0';
69 wsi->parser_state = WSI_TOKEN_SKIPPING;
70 break;
71 }
72
73 /* allocate appropriate memory */
74 if (wsi->utf8_token[wsi->parser_state].token_len ==
75 wsi->current_alloc_len - 1) {
76 /* need to extend */
77 wsi->current_alloc_len += LWS_ADDITIONAL_HDR_ALLOC;
78 if (wsi->current_alloc_len >= LWS_MAX_HEADER_LEN) {
79 /* it's waaay to much payload, fail it */
80 strcpy(wsi->utf8_token[wsi->parser_state].token,
81 "!!! Length exceeded maximum supported !!!");
82 wsi->parser_state = WSI_TOKEN_SKIPPING;
83 break;
84 }
85 wsi->utf8_token[wsi->parser_state].token =
86 realloc(wsi->utf8_token[wsi->parser_state].token,
87 wsi->current_alloc_len);
88 }
89
90 /* bail at EOL */
91 if (wsi->parser_state != WSI_TOKEN_CHALLENGE && c == '\x0d') {
92 wsi->utf8_token[wsi->parser_state].token[
93 wsi->utf8_token[wsi->parser_state].token_len] = '\0';
94 wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
95 break;
96 }
97
98 wsi->utf8_token[wsi->parser_state].token[
99 wsi->utf8_token[wsi->parser_state].token_len++] = c;
100
Andy Greend1b11e32011-01-18 15:39:02 +0000101 /* per-protocol end of headers management */
Andy Greene77ddd82010-11-13 10:03:47 +0000102
Andy Greend1b11e32011-01-18 15:39:02 +0000103 if (wsi->parser_state != WSI_TOKEN_CHALLENGE)
104 break;
105
106 /* -76 has no version header */
107 if (!wsi->utf8_token[WSI_TOKEN_VERSION].token_len &&
108 wsi->utf8_token[wsi->parser_state].token_len != 8)
109 break;
110
111 /* <= 03 has old handshake with version header */
112 if (wsi->utf8_token[WSI_TOKEN_VERSION].token_len &&
113 atoi(wsi->utf8_token[WSI_TOKEN_VERSION].token) < 4 &&
114 wsi->utf8_token[wsi->parser_state].token_len != 8)
115 break;
116
117 /* For any supported protocol we have enough payload */
118
119 debug("Setting WSI_PARSING_COMPLETE\n");
120 wsi->parser_state = WSI_PARSING_COMPLETE;
Andy Green7c212cc2010-11-08 20:20:42 +0000121 break;
122
123 /* collecting and checking a name part */
124 case WSI_TOKEN_NAME_PART:
125 debug("WSI_TOKEN_NAME_PART '%c'\n", c);
126
127 if (wsi->name_buffer_pos == sizeof(wsi->name_buffer) - 1) {
128 /* name bigger than we can handle, skip until next */
129 wsi->parser_state = WSI_TOKEN_SKIPPING;
130 break;
131 }
132 wsi->name_buffer[wsi->name_buffer_pos++] = c;
133 wsi->name_buffer[wsi->name_buffer_pos] = '\0';
Andy Greene77ddd82010-11-13 10:03:47 +0000134
Andy Green7c212cc2010-11-08 20:20:42 +0000135 for (n = 0; n < WSI_TOKEN_COUNT; n++) {
136 if (wsi->name_buffer_pos != lws_tokens[n].token_len)
137 continue;
138 if (strcmp(lws_tokens[n].token, wsi->name_buffer))
139 continue;
140 debug("known hdr '%s'\n", wsi->name_buffer);
141 wsi->parser_state = WSI_TOKEN_GET_URI + n;
142 wsi->current_alloc_len = LWS_INITIAL_HDR_ALLOC;
143 wsi->utf8_token[wsi->parser_state].token =
144 malloc(wsi->current_alloc_len);
145 wsi->utf8_token[wsi->parser_state].token_len = 0;
146 n = WSI_TOKEN_COUNT;
147 }
148
149 /* colon delimiter means we just don't know this name */
150
151 if (wsi->parser_state == WSI_TOKEN_NAME_PART && c == ':') {
152 debug("skipping unknown header '%s'\n",
153 wsi->name_buffer);
154 wsi->parser_state = WSI_TOKEN_SKIPPING;
155 break;
156 }
Andy Greene77ddd82010-11-13 10:03:47 +0000157
Andy Green7c212cc2010-11-08 20:20:42 +0000158 /* don't look for payload when it can just be http headers */
Andy Greene77ddd82010-11-13 10:03:47 +0000159
Andy Green7c212cc2010-11-08 20:20:42 +0000160 if (wsi->parser_state == WSI_TOKEN_CHALLENGE &&
161 !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len) {
162 /* they're HTTP headers, not websocket upgrade! */
163 debug("Setting WSI_PARSING_COMPLETE "
164 "from http headers\n");
165 wsi->parser_state = WSI_PARSING_COMPLETE;
166 }
167 break;
Andy Greene77ddd82010-11-13 10:03:47 +0000168
Andy Green7c212cc2010-11-08 20:20:42 +0000169 /* skipping arg part of a name we didn't recognize */
170 case WSI_TOKEN_SKIPPING:
171 debug("WSI_TOKEN_SKIPPING '%c'\n", c);
172 if (c == '\x0d')
173 wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
174 break;
175 case WSI_TOKEN_SKIPPING_SAW_CR:
176 debug("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
177 if (c == '\x0a')
178 wsi->parser_state = WSI_TOKEN_NAME_PART;
179 else
180 wsi->parser_state = WSI_TOKEN_SKIPPING;
181 wsi->name_buffer_pos = 0;
182 break;
183 /* we're done, ignore anything else */
184 case WSI_PARSING_COMPLETE:
185 debug("WSI_PARSING_COMPLETE '%c'\n", c);
186 break;
Andy Greene77ddd82010-11-13 10:03:47 +0000187
Andy Green7c212cc2010-11-08 20:20:42 +0000188 default: /* keep gcc happy */
189 break;
190 }
Andy Greene77ddd82010-11-13 10:03:47 +0000191
Andy Green7c212cc2010-11-08 20:20:42 +0000192 return 0;
193}
194
Andy Green3e5eb782011-01-18 18:14:26 +0000195static unsigned char inline
196unmask(struct libwebsocket *wsi, unsigned char c)
197{
198 c ^= wsi->masking_key_04[wsi->frame_mask_index++];
199 if (wsi->frame_mask_index == 20)
200 wsi->frame_mask_index = 0;
201
202 return c;
203}
204
Andy Green7c212cc2010-11-08 20:20:42 +0000205
206static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c)
207{
208 int n;
Andy Green3e5eb782011-01-18 18:14:26 +0000209 unsigned char buf[20 + 4];
Andy Green7c212cc2010-11-08 20:20:42 +0000210
211 switch (wsi->lws_rx_parse_state) {
212 case LWS_RXPS_NEW:
Andy Greene77ddd82010-11-13 10:03:47 +0000213
Andy Green7c212cc2010-11-08 20:20:42 +0000214 switch (wsi->ietf_spec_revision) {
215 /* Firefox 4.0b6 likes this as of 30 Oct */
Andy Greene2522172011-01-18 17:14:03 +0000216 case 0:
Andy Green7c212cc2010-11-08 20:20:42 +0000217 if (c == 0xff)
218 wsi->lws_rx_parse_state = LWS_RXPS_SEEN_76_FF;
219 break;
Andy Greene2522172011-01-18 17:14:03 +0000220 case 4:
Andy Green3e5eb782011-01-18 18:14:26 +0000221 wsi->frame_masking_nonce_04[0] = c;
222 wsi->lws_rx_parse_state = LWS_RXPS_04_MASK_NONCE_1;
Andy Green7c212cc2010-11-08 20:20:42 +0000223 break;
224 }
Andy Green6452f1e2010-11-11 09:22:22 +0000225
226 if (c == 0) {
227 wsi->lws_rx_parse_state = LWS_RXPS_EAT_UNTIL_76_FF;
228 wsi->rx_user_buffer_head = 0;
229 }
230 break;
Andy Green3e5eb782011-01-18 18:14:26 +0000231 case LWS_RXPS_04_MASK_NONCE_1:
232 wsi->frame_masking_nonce_04[1] = c;
233 wsi->lws_rx_parse_state = LWS_RXPS_04_MASK_NONCE_2;
234 break;
235 case LWS_RXPS_04_MASK_NONCE_2:
236 wsi->frame_masking_nonce_04[2] = c;
237 wsi->lws_rx_parse_state = LWS_RXPS_04_MASK_NONCE_3;
238 break;
239 case LWS_RXPS_04_MASK_NONCE_3:
240 wsi->frame_masking_nonce_04[3] = c;
241
242 /*
243 * we are able to compute the frame key now
244 * it's a SHA1 of ( frame nonce we were just sent, concatenated
245 * with the connection masking key we computed at handshake
246 * time ) -- yeah every frame from the client invokes a SHA1
247 * for no real reason so much for lightweight.
248 */
249
250 buf[0] = wsi->frame_masking_nonce_04[0];
251 buf[1] = wsi->frame_masking_nonce_04[1];
252 buf[2] = wsi->frame_masking_nonce_04[2];
253 buf[3] = wsi->frame_masking_nonce_04[3];
254
255 memcpy(buf + 4, wsi->masking_key_04, 20);
256
257 /*
258 * wsi->frame_mask_04 is our recirculating 20-byte XOR key
259 * for this frame
260 */
261
262 SHA1((unsigned char *)buf, 4 + 20, wsi->frame_mask_04);
263
264 /*
265 * start from the zero'th byte in the XOR key buffer since
266 * this is the start of a frame with a new key
267 */
268
269 wsi->frame_mask_index = 0;
270
271 wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1;
272 break;
Andy Green6452f1e2010-11-11 09:22:22 +0000273 case LWS_RXPS_EAT_UNTIL_76_FF:
274 if (c == 0xff) {
275 wsi->lws_rx_parse_state = LWS_RXPS_NEW;
276 goto issue;
277 }
278 wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
279 (wsi->rx_user_buffer_head++)] = c;
280
281 if (wsi->rx_user_buffer_head != MAX_USER_RX_BUFFER)
282 break;
283issue:
Andy Green4f3943a2010-11-12 10:44:16 +0000284 if (wsi->protocol->callback)
285 wsi->protocol->callback(wsi, LWS_CALLBACK_RECEIVE,
Andy Green47943ae2010-11-12 11:15:49 +0000286 wsi->user_space,
Andy Green6452f1e2010-11-11 09:22:22 +0000287 &wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
288 wsi->rx_user_buffer_head);
289 wsi->rx_user_buffer_head = 0;
Andy Green7c212cc2010-11-08 20:20:42 +0000290 break;
291 case LWS_RXPS_SEEN_76_FF:
292 if (c)
293 break;
294
295 debug("Seen that client is requesting "
296 "a v76 close, sending ack\n");
297 buf[0] = 0xff;
298 buf[1] = 0;
299 n = libwebsocket_write(wsi, buf, 2, LWS_WRITE_HTTP);
300 if (n < 0) {
301 fprintf(stderr, "ERROR writing to socket");
302 return -1;
303 }
304 debug(" v76 close ack sent, server closing skt\n");
305 /* returning < 0 will get it closed in parent */
306 return -1;
307
308 case LWS_RXPS_PULLING_76_LENGTH:
309 break;
310 case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
311 break;
312 }
313
314 return 0;
315}
316
317int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi,
318 unsigned char *buf, size_t len)
319{
320 int n;
321
Andy Greenab7d9332010-11-11 13:19:19 +0000322#ifdef DEBUG
Andy Green7c212cc2010-11-08 20:20:42 +0000323 fprintf(stderr, "received %d byte packet\n", (int)len);
324 for (n = 0; n < len; n++)
325 fprintf(stderr, "%02X ", buf[n]);
326 fprintf(stderr, "\n");
Andy Greenab7d9332010-11-11 13:19:19 +0000327#endif
Andy Green7c212cc2010-11-08 20:20:42 +0000328 /* let the rx protocol state machine have as much as it needs */
Andy Greene77ddd82010-11-13 10:03:47 +0000329
Andy Green7c212cc2010-11-08 20:20:42 +0000330 n = 0;
331 while (wsi->lws_rx_parse_state !=
332 LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED && n < len)
333 if (libwebsocket_rx_sm(wsi, buf[n++]) < 0)
334 return -1;
Andy Greene77ddd82010-11-13 10:03:47 +0000335
Andy Green7c212cc2010-11-08 20:20:42 +0000336 return -0;
337}
338
339
340
341/**
342 * libwebsocket_write() - Apply protocol then write data to client
343 * @wsi: Websocket instance (available from user callback)
344 * @buf: The data to send. For data being sent on a websocket
Andy Green8f037e42010-12-19 22:13:26 +0000345 * connection (ie, not default http), this buffer MUST have
346 * LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE the pointer
347 * and an additional LWS_SEND_BUFFER_POST_PADDING bytes valid
348 * in the buffer after (buf + len). This is so the protocol
349 * header and trailer data can be added in-situ.
Andy Green7c212cc2010-11-08 20:20:42 +0000350 * @len: Count of the data bytes in the payload starting from buf
351 * @protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one
Andy Green8f037e42010-12-19 22:13:26 +0000352 * of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate
353 * data on a websockets connection. Remember to allow the extra
354 * bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT
355 * are used.
Andy Green7c212cc2010-11-08 20:20:42 +0000356 *
Andy Green8f037e42010-12-19 22:13:26 +0000357 * This function provides the way to issue data back to the client
358 * for both http and websocket protocols.
Andy Greene77ddd82010-11-13 10:03:47 +0000359 *
Andy Green8f037e42010-12-19 22:13:26 +0000360 * In the case of sending using websocket protocol, be sure to allocate
361 * valid storage before and after buf as explained above. This scheme
362 * allows maximum efficiency of sending data and protocol in a single
363 * packet while not burdening the user code with any protocol knowledge.
Andy Green7c212cc2010-11-08 20:20:42 +0000364 */
365
Andy Greene77ddd82010-11-13 10:03:47 +0000366int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
Andy Green7c212cc2010-11-08 20:20:42 +0000367 size_t len, enum libwebsocket_write_protocol protocol)
368{
369 int n;
370 int pre = 0;
371 int post = 0;
372 unsigned int shift = 7;
Andy Greene77ddd82010-11-13 10:03:47 +0000373
Andy Green7c212cc2010-11-08 20:20:42 +0000374 if (protocol == LWS_WRITE_HTTP)
375 goto send_raw;
Andy Greene77ddd82010-11-13 10:03:47 +0000376
Andy Green7c212cc2010-11-08 20:20:42 +0000377 /* websocket protocol, either binary or text */
Andy Greene77ddd82010-11-13 10:03:47 +0000378
Andy Green7c212cc2010-11-08 20:20:42 +0000379 if (wsi->state != WSI_STATE_ESTABLISHED)
380 return -1;
381
382 switch (wsi->ietf_spec_revision) {
383 /* chrome likes this as of 30 Oct */
384 /* Firefox 4.0b6 likes this as of 30 Oct */
385 case 76:
386 if (protocol == LWS_WRITE_BINARY) {
387 /* in binary mode we send 7-bit used length blocks */
388 pre = 1;
389 while (len & (127 << shift)) {
390 pre++;
391 shift += 7;
392 }
393 n = 0;
394 shift -= 7;
395 while (shift >= 0) {
396 if (shift)
397 buf[0 - pre + n] =
398 ((len >> shift) & 127) | 0x80;
399 else
400 buf[0 - pre + n] =
401 ((len >> shift) & 127);
402 n++;
403 shift -= 7;
404 }
405 break;
406 }
407
408 /* frame type = text, length-free spam mode */
409
410 buf[-1] = 0;
411 buf[len] = 0xff; /* EOT marker */
412 pre = 1;
413 post = 1;
414 break;
415
416 case 0:
417 buf[-9] = 0xff;
418#if defined __LP64__
419 buf[-8] = len >> 56;
420 buf[-7] = len >> 48;
421 buf[-6] = len >> 40;
422 buf[-5] = len >> 32;
423#else
424 buf[-8] = 0;
425 buf[-7] = 0;
426 buf[-6] = 0;
427 buf[-5] = 0;
428#endif
429 buf[-4] = len >> 24;
430 buf[-3] = len >> 16;
431 buf[-2] = len >> 8;
432 buf[-1] = len;
433 pre = 9;
434 break;
Andy Greene77ddd82010-11-13 10:03:47 +0000435
Andy Green7c212cc2010-11-08 20:20:42 +0000436 /* just an unimplemented spec right now apparently */
Andy Green4b23c722010-12-20 09:19:37 +0000437 case 3:
Andy Green7c212cc2010-11-08 20:20:42 +0000438 n = 4; /* text */
439 if (protocol == LWS_WRITE_BINARY)
440 n = 5; /* binary */
441 if (len < 126) {
442 buf[-2] = n;
443 buf[-1] = len;
444 pre = 2;
445 } else {
446 if (len < 65536) {
447 buf[-4] = n;
448 buf[-3] = 126;
449 buf[-2] = len >> 8;
450 buf[-1] = len;
451 pre = 4;
452 } else {
453 buf[-10] = n;
454 buf[-9] = 127;
455#if defined __LP64__
456 buf[-8] = (len >> 56) & 0x7f;
457 buf[-7] = len >> 48;
458 buf[-6] = len >> 40;
459 buf[-5] = len >> 32;
460#else
461 buf[-8] = 0;
462 buf[-7] = 0;
463 buf[-6] = 0;
464 buf[-5] = 0;
465#endif
466 buf[-4] = len >> 24;
467 buf[-3] = len >> 16;
468 buf[-2] = len >> 8;
469 buf[-1] = len;
470 pre = 10;
471 }
472 }
473 break;
474 }
475
476#if 0
477 for (n = 0; n < (len + pre + post); n++)
478 fprintf(stderr, "%02X ", buf[n - pre]);
Andy Greene77ddd82010-11-13 10:03:47 +0000479
Andy Green7c212cc2010-11-08 20:20:42 +0000480 fprintf(stderr, "\n");
481#endif
482
483send_raw:
484#ifdef LWS_OPENSSL_SUPPORT
485 if (use_ssl) {
486 n = SSL_write(wsi->ssl, buf - pre, len + pre + post);
487 if (n < 0) {
488 fprintf(stderr, "ERROR writing to socket");
489 return -1;
490 }
491 } else {
492#endif
493 n = send(wsi->sock, buf - pre, len + pre + post, 0);
494 if (n < 0) {
495 fprintf(stderr, "ERROR writing to socket");
496 return -1;
497 }
498#ifdef LWS_OPENSSL_SUPPORT
499 }
500#endif
Andy Greene77ddd82010-11-13 10:03:47 +0000501 debug("written %d bytes to client\n", (int)len);
502
Andy Green7c212cc2010-11-08 20:20:42 +0000503 return 0;
504}
505
506
507/**
508 * libwebsockets_serve_http_file() - Send a file back to the client using http
509 * @wsi: Websocket instance (available from user callback)
510 * @file: The file to issue over http
511 * @content_type: The http content type, eg, text/html
Andy Greene77ddd82010-11-13 10:03:47 +0000512 *
Andy Green8f037e42010-12-19 22:13:26 +0000513 * This function is intended to be called from the callback in response
514 * to http requests from the client. It allows the callback to issue
515 * local files down the http link in a single step.
Andy Green7c212cc2010-11-08 20:20:42 +0000516 */
517
Andy Greene77ddd82010-11-13 10:03:47 +0000518int libwebsockets_serve_http_file(struct libwebsocket *wsi, const char *file,
519 const char *content_type)
Andy Green7c212cc2010-11-08 20:20:42 +0000520{
521 int fd;
522 struct stat stat;
523 char buf[512];
524 char *p = buf;
525 int n;
526
527 fd = open(file, O_RDONLY);
528 if (fd < 1) {
529 p += sprintf(p, "HTTP/1.0 400 Bad\x0d\x0a"
530 "Server: libwebsockets\x0d\x0a"
531 "\x0d\x0a"
532 );
533 libwebsocket_write(wsi, (unsigned char *)buf, p - buf,
534 LWS_WRITE_HTTP);
Andy Greene77ddd82010-11-13 10:03:47 +0000535
Andy Green7c212cc2010-11-08 20:20:42 +0000536 return -1;
537 }
538
539 fstat(fd, &stat);
540 p += sprintf(p, "HTTP/1.0 200 OK\x0d\x0a"
541 "Server: libwebsockets\x0d\x0a"
542 "Content-Type: %s\x0d\x0a"
543 "Content-Length: %u\x0d\x0a"
544 "\x0d\x0a", content_type, (unsigned int)stat.st_size);
Andy Greene77ddd82010-11-13 10:03:47 +0000545
Andy Green7c212cc2010-11-08 20:20:42 +0000546 libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP);
547
548 n = 1;
549 while (n > 0) {
550 n = read(fd, buf, 512);
551 libwebsocket_write(wsi, (unsigned char *)buf, n,
552 LWS_WRITE_HTTP);
553 }
Andy Greene77ddd82010-11-13 10:03:47 +0000554
Andy Green7c212cc2010-11-08 20:20:42 +0000555 close(fd);
Andy Greene77ddd82010-11-13 10:03:47 +0000556
Andy Green7c212cc2010-11-08 20:20:42 +0000557 return 0;
558}