blob: 749cec06c3cea807b652b1c814e257f004fa3d7a [file] [log] [blame]
Andy Greena1ce6be2013-01-18 11:43:21 +08001/*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010-2013 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
23#include "private-libwebsockets.h"
24
Andreas Pakulat68bd4bd2013-10-28 15:18:04 +010025#if defined(WIN32) || defined(_WIN32)
Andy Greena1ce6be2013-01-18 11:43:21 +080026#include <tchar.h>
Andy Greena1ce6be2013-01-18 11:43:21 +080027#else
28#ifdef LWS_BUILTIN_GETIFADDRS
29#include <getifaddrs.h>
30#else
31#include <ifaddrs.h>
32#endif
33#include <sys/un.h>
34#include <sys/socket.h>
35#include <netdb.h>
36#endif
37
38#ifdef LWS_OPENSSL_SUPPORT
Andy Greena1ce6be2013-01-18 11:43:21 +080039
40static void
41libwebsockets_decode_ssl_error(void)
42{
43 char buf[256];
44 u_long err;
45
46 while ((err = ERR_get_error()) != 0) {
47 ERR_error_string_n(err, buf, sizeof(buf));
Joakim Soderbergb82b0dd2013-02-22 09:28:15 +080048 lwsl_err("*** %lu %s\n", err, buf);
Andy Greena1ce6be2013-01-18 11:43:21 +080049 }
50}
51#endif
52
Andy Greena1ce6be2013-01-18 11:43:21 +080053struct libwebsocket *
54libwebsocket_create_new_server_wsi(struct libwebsocket_context *context)
55{
56 struct libwebsocket *new_wsi;
Andy Greena1ce6be2013-01-18 11:43:21 +080057
58 new_wsi = (struct libwebsocket *)malloc(sizeof(struct libwebsocket));
59 if (new_wsi == NULL) {
60 lwsl_err("Out of memory for new connection\n");
61 return NULL;
62 }
63
64 memset(new_wsi, 0, sizeof(struct libwebsocket));
Andy Green3182ece2013-01-20 17:08:31 +080065#ifndef LWS_NO_EXTENSIONS
Andy Greena1ce6be2013-01-18 11:43:21 +080066 new_wsi->count_active_extensions = 0;
Andy Green3182ece2013-01-20 17:08:31 +080067#endif
Andy Greena1ce6be2013-01-18 11:43:21 +080068 new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
69
70 /* intialize the instance struct */
71
72 new_wsi->state = WSI_STATE_HTTP;
Andy Greena1ce6be2013-01-18 11:43:21 +080073 new_wsi->mode = LWS_CONNMODE_HTTP_SERVING;
Andy Green224149a2013-02-11 21:43:41 +080074 new_wsi->hdr_parsing_completed = 0;
Andy Greena1ce6be2013-01-18 11:43:21 +080075
Andy Green16ab3182013-02-10 18:02:31 +080076 if (lws_allocate_header_table(new_wsi)) {
77 free(new_wsi);
78 return NULL;
Andy Greena1ce6be2013-01-18 11:43:21 +080079 }
80
81 /*
82 * these can only be set once the protocol is known
83 * we set an unestablished connection's protocol pointer
84 * to the start of the supported list, so it can look
85 * for matching ones during the handshake
86 */
87 new_wsi->protocol = context->protocols;
88 new_wsi->user_space = NULL;
Andy Greena1ce6be2013-01-18 11:43:21 +080089 new_wsi->ietf_spec_revision = 0;
90
Andy Green76b6ea12014-02-15 19:25:50 +080091 /*
92 * outermost create notification for wsi
93 * no user_space because no protocol selection
94 */
95 context->protocols[0].callback(context, new_wsi,
96 LWS_CALLBACK_WSI_CREATE, NULL, NULL, 0);
97
Andy Greena1ce6be2013-01-18 11:43:21 +080098 return new_wsi;
99}
100
101int lws_server_socket_service(struct libwebsocket_context *context,
102 struct libwebsocket *wsi, struct pollfd *pollfd)
103{
Andy Greena1ce6be2013-01-18 11:43:21 +0800104 struct libwebsocket *new_wsi;
105 int accept_fd;
Bob Robertsac049112013-04-25 09:16:30 +0800106 socklen_t clilen;
Andy Greena1ce6be2013-01-18 11:43:21 +0800107 struct sockaddr_in cli_addr;
108 int n;
Andy Greena1ce6be2013-01-18 11:43:21 +0800109 ssize_t len;
Edwin van den Oetelaar0794af92013-01-28 21:53:53 +0800110#ifdef LWS_OPENSSL_SUPPORT
111 int m;
Andy Green23c5f2e2013-02-06 15:43:00 +0900112#ifndef USE_CYASSL
Andy Green1167dd42013-01-28 17:45:34 +0800113 BIO *bio;
Edwin van den Oetelaar0794af92013-01-28 21:53:53 +0800114#endif
Andy Green23c5f2e2013-02-06 15:43:00 +0900115#endif
Andy Greena1ce6be2013-01-18 11:43:21 +0800116
117 switch (wsi->mode) {
118
119 case LWS_CONNMODE_HTTP_SERVING:
Andy Green7cf6cb02013-05-19 14:04:10 +0800120 case LWS_CONNMODE_HTTP_SERVING_ACCEPTED:
Andy Greena1ce6be2013-01-18 11:43:21 +0800121
122 /* handle http headers coming in */
123
Andy Green2764eba2013-12-09 14:16:17 +0800124 /* pending truncated sends have uber priority */
125
126 if (wsi->truncated_send_malloc) {
127 if (pollfd->revents & POLLOUT)
128 lws_issue_raw(wsi, wsi->truncated_send_malloc +
129 wsi->truncated_send_offset,
130 wsi->truncated_send_len);
131 /*
132 * we can't afford to allow input processing send
133 * something new, so spin around he event loop until
134 * he doesn't have any partials
135 */
136 break;
137 }
138
Andy Greena1ce6be2013-01-18 11:43:21 +0800139 /* any incoming data ready? */
140
141 if (pollfd->revents & POLLIN) {
142
143 #ifdef LWS_OPENSSL_SUPPORT
144 if (wsi->ssl)
Andy Greenb5b23192013-02-11 17:13:32 +0800145 len = SSL_read(wsi->ssl,
146 context->service_buffer,
147 sizeof(context->service_buffer));
Andy Greena1ce6be2013-01-18 11:43:21 +0800148 else
149 #endif
Andy Greenb5b23192013-02-11 17:13:32 +0800150 len = recv(pollfd->fd,
151 context->service_buffer,
152 sizeof(context->service_buffer), 0);
Andy Greena1ce6be2013-01-18 11:43:21 +0800153
154 if (len < 0) {
155 lwsl_debug("Socket read returned %d\n", len);
156 if (errno != EINTR && errno != EAGAIN)
Andy Greenb5b23192013-02-11 17:13:32 +0800157 libwebsocket_close_and_free_session(
158 context, wsi,
159 LWS_CLOSE_STATUS_NOSTATUS);
Andy Greena1ce6be2013-01-18 11:43:21 +0800160 return 0;
161 }
162 if (!len) {
Andy Green224149a2013-02-11 21:43:41 +0800163 lwsl_info("lws_server_skt_srv: read 0 len\n");
164 /* lwsl_info(" state=%d\n", wsi->state); */
165 if (!wsi->hdr_parsing_completed)
166 free(wsi->u.hdr.ah);
Andy Greenb5b23192013-02-11 17:13:32 +0800167 libwebsocket_close_and_free_session(
168 context, wsi, LWS_CLOSE_STATUS_NOSTATUS);
Andy Greena1ce6be2013-01-18 11:43:21 +0800169 return 0;
170 }
171
Andy Green2764eba2013-12-09 14:16:17 +0800172 /* hm this may want to send (via HTTP callback for example) */
173
Andy Greenb5b23192013-02-11 17:13:32 +0800174 n = libwebsocket_read(context, wsi,
175 context->service_buffer, len);
Andy Greena1ce6be2013-01-18 11:43:21 +0800176 if (n < 0)
177 /* we closed wsi */
178 return 0;
Andy Green2764eba2013-12-09 14:16:17 +0800179
180 /* hum he may have used up the writability above */
Andy Green2764eba2013-12-09 14:16:17 +0800181 break;
Andy Greena1ce6be2013-01-18 11:43:21 +0800182 }
183
184 /* this handles POLLOUT for http serving fragments */
185
186 if (!(pollfd->revents & POLLOUT))
187 break;
188
189 /* one shot */
Andy Green91f19d82013-12-21 11:18:34 +0800190 lws_change_pollfd(wsi, POLLOUT, 0);
Andy Green7a132792013-12-18 09:48:26 +0800191
Andy Green54cb3462013-02-14 22:23:54 +0800192 if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) {
193 n = user_callback_handle_rxflow(
194 wsi->protocol->callback,
195 wsi->protocol->owning_server,
196 wsi, LWS_CALLBACK_HTTP_WRITEABLE,
197 wsi->user_space,
198 NULL,
199 0);
200 if (n < 0)
201 libwebsocket_close_and_free_session(
202 context, wsi, LWS_CLOSE_STATUS_NOSTATUS);
Andy Greena1ce6be2013-01-18 11:43:21 +0800203 break;
Andy Green54cb3462013-02-14 22:23:54 +0800204 }
Andy Greena1ce6be2013-01-18 11:43:21 +0800205
Andy Greenb5b23192013-02-11 17:13:32 +0800206 /* nonzero for completion or error */
207 if (libwebsockets_serve_http_file_fragment(context, wsi))
Andy Greena1ce6be2013-01-18 11:43:21 +0800208 libwebsocket_close_and_free_session(context, wsi,
209 LWS_CLOSE_STATUS_NOSTATUS);
Andy Greena1ce6be2013-01-18 11:43:21 +0800210 break;
211
212 case LWS_CONNMODE_SERVER_LISTENER:
213
214 /* pollin means a client has connected to us then */
215
216 if (!(pollfd->revents & POLLIN))
217 break;
218
219 /* listen socket got an unencrypted connection... */
220
221 clilen = sizeof(cli_addr);
Andy Greene000a702013-01-29 12:37:35 +0800222 lws_latency_pre(context, wsi);
Andy Greena1ce6be2013-01-18 11:43:21 +0800223 accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr,
224 &clilen);
Andy Greenb5b23192013-02-11 17:13:32 +0800225 lws_latency(context, wsi,
226 "unencrypted accept LWS_CONNMODE_SERVER_LISTENER",
227 accept_fd, accept_fd >= 0);
Andy Greena1ce6be2013-01-18 11:43:21 +0800228 if (accept_fd < 0) {
Andy Greene2160712013-01-28 12:19:10 +0800229 if (errno == EAGAIN || errno == EWOULDBLOCK) {
230 lwsl_debug("accept asks to try again\n");
231 break;
232 }
Andy Greena1ce6be2013-01-18 11:43:21 +0800233 lwsl_warn("ERROR on accept: %s\n", strerror(errno));
234 break;
235 }
236
Andy Greena690cd02013-02-09 12:25:31 +0800237 lws_set_socket_options(context, accept_fd);
Andy Green6f047ee2013-01-28 11:23:52 +0800238
Andy Greena1ce6be2013-01-18 11:43:21 +0800239 /*
240 * look at who we connected to and give user code a chance
241 * to reject based on client IP. There's no protocol selected
242 * yet so we issue this to protocols[0]
243 */
244
245 if ((context->protocols[0].callback)(context, wsi,
246 LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
Edwin van den Oetelaar8c8a8e12013-02-20 20:56:59 +0800247 NULL, (void *)(long)accept_fd, 0)) {
Andy Greena1ce6be2013-01-18 11:43:21 +0800248 lwsl_debug("Callback denied network connection\n");
249 compatible_close(accept_fd);
250 break;
251 }
252
253 new_wsi = libwebsocket_create_new_server_wsi(context);
254 if (new_wsi == NULL) {
255 compatible_close(accept_fd);
256 break;
257 }
258
259 new_wsi->sock = accept_fd;
260
Andy Green176de272014-02-15 14:36:02 +0800261 /* the transport is accepted... give him time to negotiate */
262 libwebsocket_set_timeout(new_wsi,
263 PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
264 AWAITING_TIMEOUT);
265
Alexandre Erwin Ittnerd578f572014-02-06 23:15:51 -0200266 /*
267 * A new connection was accepted. Give the user a chance to
268 * set properties of the newly created wsi. There's no protocol
269 * selected yet so we issue this to protocols[0]
270 */
271
272 (context->protocols[0].callback)(context, new_wsi,
273 LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0);
274
275
Andy Greena1ce6be2013-01-18 11:43:21 +0800276#ifdef LWS_OPENSSL_SUPPORT
277 new_wsi->ssl = NULL;
Andy Greene2160712013-01-28 12:19:10 +0800278 if (!context->use_ssl) {
Andy Greena1ce6be2013-01-18 11:43:21 +0800279#endif
Andy Greene2160712013-01-28 12:19:10 +0800280
Andy Greena1ce6be2013-01-18 11:43:21 +0800281 lwsl_debug("accepted new conn port %u on fd=%d\n",
282 ntohs(cli_addr.sin_port), accept_fd);
283
Andy Greene2160712013-01-28 12:19:10 +0800284 insert_wsi_socket_into_fds(context, new_wsi);
285 break;
286#ifdef LWS_OPENSSL_SUPPORT
287 }
288
289 new_wsi->ssl = SSL_new(context->ssl_ctx);
290 if (new_wsi->ssl == NULL) {
291 lwsl_err("SSL_new failed: %s\n",
292 ERR_error_string(SSL_get_error(
293 new_wsi->ssl, 0), NULL));
294 libwebsockets_decode_ssl_error();
295 free(new_wsi);
296 compatible_close(accept_fd);
297 break;
298 }
299
300 SSL_set_ex_data(new_wsi->ssl,
301 openssl_websocket_private_data_index, context);
302
303 SSL_set_fd(new_wsi->ssl, accept_fd);
304
Joakim Soderbergb378ce92013-02-06 15:29:18 +0900305 #ifdef USE_CYASSL
306 CyaSSL_set_using_nonblock(new_wsi->ssl, 1);
307 #else
Andy Green1167dd42013-01-28 17:45:34 +0800308 bio = SSL_get_rbio(new_wsi->ssl);
309 if (bio)
310 BIO_set_nbio(bio, 1); /* nonblocking */
311 else
312 lwsl_notice("NULL rbio\n");
313 bio = SSL_get_wbio(new_wsi->ssl);
314 if (bio)
315 BIO_set_nbio(bio, 1); /* nonblocking */
316 else
317 lwsl_notice("NULL rbio\n");
Joakim Soderbergb378ce92013-02-06 15:29:18 +0900318 #endif
Andy Green1167dd42013-01-28 17:45:34 +0800319
Andy Greenb5b23192013-02-11 17:13:32 +0800320 /*
Andy Greene2160712013-01-28 12:19:10 +0800321 * we are not accepted yet, but we need to enter ourselves
322 * as a live connection. That way we can retry when more
323 * pieces come if we're not sorted yet
324 */
325
326 wsi = new_wsi;
327 wsi->mode = LWS_CONNMODE_SSL_ACK_PENDING;
328 insert_wsi_socket_into_fds(context, wsi);
329
Andy Greenba85a7d2013-01-28 17:20:41 +0800330 libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT,
331 AWAITING_TIMEOUT);
332
Andy Greenb5b23192013-02-11 17:13:32 +0800333 lwsl_info("inserted SSL accept into fds, trying SSL_accept\n");
Andy Greene2160712013-01-28 12:19:10 +0800334
335 /* fallthru */
336
337 case LWS_CONNMODE_SSL_ACK_PENDING:
338
Andy Green91f19d82013-12-21 11:18:34 +0800339 lws_change_pollfd(wsi, POLLOUT, 0);
Andy Green7a132792013-12-18 09:48:26 +0800340
Andy Greene000a702013-01-29 12:37:35 +0800341 lws_latency_pre(context, wsi);
James Devine5b34c972013-12-14 11:41:29 +0800342
343 n = recv(wsi->sock, context->service_buffer,
344 sizeof(context->service_buffer), MSG_PEEK);
345
346 /*
347 * optionally allow non-SSL connect on SSL listening socket
348 * This is disabled by default, if enabled it goes around any
349 * SSL-level access control (eg, client-side certs) so leave
350 * it disabled unless you know it's not a problem for you
351 */
352
353 if (context->allow_non_ssl_on_ssl_port && n >= 1 &&
354 context->service_buffer[0] >= ' ') {
355 /*
356 * TLS content-type for Handshake is 0x16
357 * TLS content-type for ChangeCipherSpec Record is 0x14
358 *
359 * A non-ssl session will start with the HTTP method in
360 * ASCII. If we see it's not a legit SSL handshake
361 * kill the SSL for this connection and try to handle
362 * as a HTTP connection upgrade directly.
363 */
364 wsi->use_ssl = 0;
365 SSL_shutdown(wsi->ssl);
366 SSL_free(wsi->ssl);
367 wsi->ssl = NULL;
368 goto accepted;
369 }
370
371 /* normal SSL connection processing path */
372
Andy Greene2160712013-01-28 12:19:10 +0800373 n = SSL_accept(wsi->ssl);
Andy Greenb5b23192013-02-11 17:13:32 +0800374 lws_latency(context, wsi,
375 "SSL_accept LWS_CONNMODE_SSL_ACK_PENDING\n", n, n == 1);
Andy Greene2160712013-01-28 12:19:10 +0800376
377 if (n != 1) {
378 m = SSL_get_error(wsi->ssl, n);
Andy Greenb5b23192013-02-11 17:13:32 +0800379 lwsl_debug("SSL_accept failed %d / %s\n",
380 m, ERR_error_string(m, NULL));
Andy Greene2160712013-01-28 12:19:10 +0800381
382 if (m == SSL_ERROR_WANT_READ) {
Andy Green91f19d82013-12-21 11:18:34 +0800383 lws_change_pollfd(wsi, 0, POLLIN);
Andy Greene2160712013-01-28 12:19:10 +0800384 lwsl_info("SSL_ERROR_WANT_READ\n");
385 break;
386 }
387 if (m == SSL_ERROR_WANT_WRITE) {
Andy Green91f19d82013-12-21 11:18:34 +0800388 lws_change_pollfd(wsi, 0, POLLOUT);
Andy Greene2160712013-01-28 12:19:10 +0800389 break;
390 }
391 lwsl_debug("SSL_accept failed skt %u: %s\n",
James Devine5b34c972013-12-14 11:41:29 +0800392 pollfd->fd,
393 ERR_error_string(m, NULL));
Andy Greenb5b23192013-02-11 17:13:32 +0800394 libwebsocket_close_and_free_session(context, wsi,
James Devine5b34c972013-12-14 11:41:29 +0800395 LWS_CLOSE_STATUS_NOSTATUS);
Andy Greene2160712013-01-28 12:19:10 +0800396 break;
397 }
398
James Devine5b34c972013-12-14 11:41:29 +0800399accepted:
Andy Green176de272014-02-15 14:36:02 +0800400 /* OK, we are accepted... give him some time to negotiate */
401 libwebsocket_set_timeout(wsi,
402 PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
403 AWAITING_TIMEOUT);
Andy Greenba85a7d2013-01-28 17:20:41 +0800404
Andy Greene2160712013-01-28 12:19:10 +0800405 wsi->mode = LWS_CONNMODE_HTTP_SERVING;
406
Andy Green35517092013-02-11 20:10:56 +0800407 lwsl_debug("accepted new SSL conn\n");
Andy Greena1ce6be2013-01-18 11:43:21 +0800408 break;
Andy Greene2160712013-01-28 12:19:10 +0800409#endif
410
Andy Greena1ce6be2013-01-18 11:43:21 +0800411 default:
412 break;
413 }
414 return 0;
415}
Joakim Soderberg63ff1202013-02-11 17:52:23 +0100416
Andy Green4e7a1332013-11-11 07:30:33 +0800417
418static const char *err400[] = {
419 "Bad Request",
420 "Unauthorized",
421 "Payment Required",
422 "Forbidden",
423 "Not Found",
424 "Method Not Allowed",
425 "Not Acceptable",
426 "Proxy Auth Required",
427 "Request Timeout",
428 "Conflict",
429 "Gone",
430 "Length Required",
431 "Precondition Failed",
432 "Request Entity Too Large",
433 "Request URI too Long",
434 "Unsupported Media Type",
435 "Requested Range Not Satisfiable",
436 "Expectation Failed"
437};
438
439static const char *err500[] = {
440 "Internal Server Error",
441 "Not Implemented",
442 "Bad Gateway",
443 "Service Unavailable",
444 "Gateway Timeout",
445 "HTTP Version Not Supported"
446};
447
448/**
449 * libwebsockets_return_http_status() - Return simple http status
450 * @context: libwebsockets context
451 * @wsi: Websocket instance (available from user callback)
452 * @code: Status index, eg, 404
453 * @html_body: User-readable HTML description, or NULL
454 *
455 * Helper to report HTTP errors back to the client cleanly and
456 * consistently
457 */
Andy Green83725d82014-02-27 07:19:21 +0800458LWS_VISIBLE int libwebsockets_return_http_status(
Andy Green4e7a1332013-11-11 07:30:33 +0800459 struct libwebsocket_context *context, struct libwebsocket *wsi,
460 unsigned int code, const char *html_body)
461{
462 int n, m;
463 const char *description = "";
464
465 if (!html_body)
466 html_body = "";
467
468 if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
469 description = err400[code - 400];
470 if (code >= 500 && code < (500 + ARRAY_SIZE(err500)))
471 description = err500[code - 500];
472
473 n = sprintf((char *)context->service_buffer,
474 "HTTP/1.0 %u %s\x0d\x0a"
475 "Server: libwebsockets\x0d\x0a"
Patrick Ganstererfce64cd2014-02-27 11:42:41 +0100476 "Content-Type: text/html\x0d\x0a\x0d\x0a"
Andy Green4e7a1332013-11-11 07:30:33 +0800477 "<h1>%u %s</h1>%s",
478 code, description, code, description, html_body);
479
480 lwsl_info((const char *)context->service_buffer);
481
482 m = libwebsocket_write(wsi, context->service_buffer, n, LWS_WRITE_HTTP);
483
484 return m;
485}
486
Patrick Gansterer81338aa2014-02-27 03:21:50 +0100487#if defined(WIN32) || defined(_WIN32)
488static inline HANDLE lws_open_file(const char* filename, unsigned long* filelen)
489{
Patrick Gansterercd9d6c52014-02-28 01:29:28 +0100490 HANDLE ret;
491 WCHAR buffer[MAX_PATH];
492
493 MultiByteToWideChar(CP_UTF8, 0, filename, -1, buffer,
494 sizeof(buffer) / sizeof(buffer[0]));
495 ret = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ,
496 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
Patrick Gansterer81338aa2014-02-27 03:21:50 +0100497
498 if (ret != LWS_INVALID_FILE)
499 *filelen = GetFileSize(ret, NULL);
500
501 return ret;
502}
503#else
504static inline int lws_open_file(const char* filename, unsigned long* filelen)
505{
506 struct stat stat_buf;
507 int ret = open(filename, O_RDONLY);
508
509 if (ret < 0)
510 return LWS_INVALID_FILE;
511
512 fstat(ret, &stat_buf);
513 *filelen = stat_buf.st_size;
514 return ret;
515}
516#endif
517
Andy Green4e7a1332013-11-11 07:30:33 +0800518/**
519 * libwebsockets_serve_http_file() - Send a file back to the client using http
520 * @context: libwebsockets context
521 * @wsi: Websocket instance (available from user callback)
522 * @file: The file to issue over http
523 * @content_type: The http content type, eg, text/html
524 * @other_headers: NULL or pointer to \0-terminated other header string
525 *
526 * This function is intended to be called from the callback in response
527 * to http requests from the client. It allows the callback to issue
528 * local files down the http link in a single step.
529 *
530 * Returning <0 indicates error and the wsi should be closed. Returning
531 * >0 indicates the file was completely sent and the wsi should be closed.
532 * ==0 indicates the file transfer is started and needs more service later,
533 * the wsi should be left alone.
534 */
535
536LWS_VISIBLE int libwebsockets_serve_http_file(
537 struct libwebsocket_context *context,
538 struct libwebsocket *wsi, const char *file,
539 const char *content_type, const char *other_headers)
540{
Andy Green4e7a1332013-11-11 07:30:33 +0800541 unsigned char *p = context->service_buffer;
542 int ret = 0;
543 int n;
544
Patrick Gansterer81338aa2014-02-27 03:21:50 +0100545 wsi->u.http.fd = lws_open_file(file, &wsi->u.http.filelen);
Andy Green4e7a1332013-11-11 07:30:33 +0800546
Patrick Gansterer81338aa2014-02-27 03:21:50 +0100547 if (wsi->u.http.fd == LWS_INVALID_FILE) {
Andy Green4e7a1332013-11-11 07:30:33 +0800548 lwsl_err("Unable to open '%s'\n", file);
549 libwebsockets_return_http_status(context, wsi,
550 HTTP_STATUS_NOT_FOUND, NULL);
Andy Green4e7a1332013-11-11 07:30:33 +0800551 return -1;
552 }
553
Andy Green4e7a1332013-11-11 07:30:33 +0800554 p += sprintf((char *)p,
555"HTTP/1.0 200 OK\x0d\x0aServer: libwebsockets\x0d\x0a""Content-Type: %s\x0d\x0a",
556 content_type);
557 if (other_headers) {
558 n = strlen(other_headers);
559 memcpy(p, other_headers, n);
560 p += n;
561 }
562 p += sprintf((char *)p,
Patrick Gansterer81338aa2014-02-27 03:21:50 +0100563 "Content-Length: %lu\x0d\x0a\x0d\x0a", wsi->u.http.filelen);
Andy Green4e7a1332013-11-11 07:30:33 +0800564
565 ret = libwebsocket_write(wsi, context->service_buffer,
566 p - context->service_buffer, LWS_WRITE_HTTP);
567 if (ret != (p - context->service_buffer)) {
568 lwsl_err("_write returned %d from %d\n", ret, (p - context->service_buffer));
569 return -1;
570 }
571
572 wsi->u.http.filepos = 0;
573 wsi->state = WSI_STATE_HTTP_ISSUING_FILE;
574
575 return libwebsockets_serve_http_file_fragment(context, wsi);
576}
577