diff --git a/lib/handshake.c b/lib/handshake.c index 12c2de70..c53d1d01 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -549,6 +549,7 @@ libwebsocket_read(struct libwebsocket_context *context, size_t n; switch (wsi->state) { + case WSI_STATE_HTTP_ISSUING_FILE: case WSI_STATE_HTTP: wsi->state = WSI_STATE_HTTP_HEADERS; wsi->parser_state = WSI_TOKEN_NAME_PART; @@ -692,6 +693,8 @@ libwebsocket_read(struct libwebsocket_context *context, goto bail; } + wsi->mode = LWS_CONNMODE_WS_SERVING; + lwsl_parser("accepted v%02d connection\n", wsi->ietf_spec_revision); @@ -717,6 +720,7 @@ libwebsocket_read(struct libwebsocket_context *context, break; default: + lwsl_err("libwebsocket_read: Unhandled state\n"); break; } diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 63b10ffc..12359ba0 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -770,7 +770,7 @@ libwebsocket_create_new_server_wsi(struct libwebsocket_context *context) new_wsi->state = WSI_STATE_HTTP; new_wsi->name_buffer_pos = 0; - new_wsi->mode = LWS_CONNMODE_WS_SERVING; + new_wsi->mode = LWS_CONNMODE_HTTP_SERVING; for (n = 0; n < WSI_TOKEN_COUNT; n++) { new_wsi->utf8_token[n].token = NULL; @@ -1536,10 +1536,68 @@ libwebsocket_service_fd(struct libwebsocket_context *context, wsi = wsi_from_fd(context, pollfd->fd); - if (wsi == NULL) + if (wsi == NULL) { + lwsl_debug("hm fd %d has NULL wsi\n", pollfd->fd); return 0; + } switch (wsi->mode) { + + case LWS_CONNMODE_HTTP_SERVING: + + /* handle http headers coming in */ + + /* any incoming data ready? */ + + if (pollfd->revents & POLLIN) { + + #ifdef LWS_OPENSSL_SUPPORT + if (wsi->ssl) + len = SSL_read(wsi->ssl, buf, sizeof buf); + else + #endif + len = recv(pollfd->fd, buf, sizeof buf, 0); + + if (len < 0) { + lwsl_debug("Socket read returned %d\n", len); + if (errno != EINTR && errno != EAGAIN) + libwebsocket_close_and_free_session(context, + wsi, LWS_CLOSE_STATUS_NOSTATUS); + return 1; + } + if (!len) { + libwebsocket_close_and_free_session(context, wsi, + LWS_CLOSE_STATUS_NOSTATUS); + return 0; + } + + n = libwebsocket_read(context, wsi, buf, len); + if (n < 0) + /* we closed wsi */ + return 1; + } + + /* this handles POLLOUT for http serving fragments */ + + if (!(pollfd->revents & POLLOUT)) + break; + + /* one shot */ + pollfd->events &= ~POLLOUT; + + if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) + break; + + if (libwebsockets_serve_http_file_fragment(context, wsi) < 0) + libwebsocket_close_and_free_session(context, wsi, + LWS_CLOSE_STATUS_NOSTATUS); + else + if (wsi->state == WSI_STATE_HTTP && wsi->protocol->callback) + if (wsi->protocol->callback(context, wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION, wsi->user_space, + wsi->filepath, wsi->filepos)) + libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); + break; + case LWS_CONNMODE_SERVER_LISTENER: /* pollin means a client has connected to us then */ @@ -2258,6 +2316,8 @@ libwebsocket_service(struct libwebsocket_context *context, int timeout_ms) if (n == 0) /* poll timeout */ return 0; + + if (n < 0) { /* lwsl_err("Listen Socket dead\n"); diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 0c7c2eca..83d7b3d3 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -92,6 +92,7 @@ enum libwebsocket_callback_reasons { LWS_CALLBACK_CLIENT_WRITEABLE, LWS_CALLBACK_SERVER_WRITEABLE, LWS_CALLBACK_HTTP, + LWS_CALLBACK_HTTP_FILE_COMPLETION, LWS_CALLBACK_BROADCAST, LWS_CALLBACK_FILTER_NETWORK_CONNECTION, LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION, @@ -378,6 +379,9 @@ struct libwebsocket_extension; * total number of client connections allowed set * by MAX_CLIENTS. * + * LWS_CALLBACK_HTTP_FILE_COMPLETION: a file requested to be send down + * http link has completed. + * * LWS_CALLBACK_CLIENT_WRITEABLE: * LWS_CALLBACK_SERVER_WRITEABLE: If you call * libwebsocket_callback_on_writable() on a connection, you will @@ -730,8 +734,12 @@ libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, size_t len, enum libwebsocket_write_protocol protocol); LWS_EXTERN int -libwebsockets_serve_http_file(struct libwebsocket *wsi, const char *file, +libwebsockets_serve_http_file(struct libwebsocket_context *context, + struct libwebsocket *wsi, const char *file, const char *content_type); +LWS_EXTERN int +libwebsockets_serve_http_file_fragment(struct libwebsocket_context *context, + struct libwebsocket *wsi); /* notice - you need the pre- and post- padding allocation for buf below */ diff --git a/lib/parsers.c b/lib/parsers.c index 87d4f735..952aee20 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -1988,19 +1988,22 @@ send_raw: * local files down the http link in a single step. */ -int libwebsockets_serve_http_file(struct libwebsocket *wsi, const char *file, +int libwebsockets_serve_http_file(struct libwebsocket_context *context, + struct libwebsocket *wsi, const char *file, const char *content_type) { int fd; struct stat stat_buf; char buf[512]; char *p = buf; - int n; + + strncpy(wsi->filepath, file, sizeof wsi->filepath); + wsi->filepath[sizeof(wsi->filepath) - 1] = '\0'; #ifdef WIN32 - fd = open(file, O_RDONLY | _O_BINARY); + fd = open(wsi->filepath, O_RDONLY | _O_BINARY); #else - fd = open(file, O_RDONLY); + fd = open(wsi->filepath, O_RDONLY); #endif if (fd < 1) { p += sprintf(p, "HTTP/1.0 400 Bad\x0d\x0a" @@ -2014,6 +2017,7 @@ int libwebsockets_serve_http_file(struct libwebsocket *wsi, const char *file, } fstat(fd, &stat_buf); + wsi->filelen = stat_buf.st_size; p += sprintf(p, "HTTP/1.0 200 OK\x0d\x0a" "Server: libwebsockets\x0d\x0a" "Content-Type: %s\x0d\x0a" @@ -2023,20 +2027,53 @@ int libwebsockets_serve_http_file(struct libwebsocket *wsi, const char *file, libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP); - n = 1; - while (n > 0) { - n = read(fd, buf, 512); - if (n <= 0) - continue; - libwebsocket_write(wsi, (unsigned char *)buf, n, - LWS_WRITE_HTTP); - } + wsi->filepos = 0; + libwebsocket_callback_on_writable(context, wsi); + wsi->state = WSI_STATE_HTTP_ISSUING_FILE; close(fd); return 0; } +int libwebsockets_serve_http_file_fragment(struct libwebsocket_context *context, + struct libwebsocket *wsi) +{ + int fd; + int ret = 0; + char buf[512]; + int n; + +#ifdef WIN32 + fd = open(wsi->filepath, O_RDONLY | _O_BINARY); +#else + fd = open(wsi->filepath, O_RDONLY); +#endif + if (fd < 1) + return -1; + + lseek(fd, wsi->filepos, SEEK_SET); + + n = read(fd, buf, 512); + if (n > 0) { + libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP); + wsi->filepos += n; + } + + if (n < 0) + ret = -1; + + if (n < 512 || wsi->filepos == wsi->filelen) + wsi->state = WSI_STATE_HTTP; + else + if (!ret) + libwebsocket_callback_on_writable(context, wsi); + + close(fd); + + return ret; +} + /** * libwebsockets_remaining_packet_payload() - Bytes to come before "overall" diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 212bbb1a..48bdc1a6 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -203,6 +203,7 @@ enum lws_websocket_opcodes_07 { enum lws_connection_states { WSI_STATE_HTTP, + WSI_STATE_HTTP_ISSUING_FILE, WSI_STATE_HTTP_HEADERS, WSI_STATE_DEAD_SOCKET, WSI_STATE_ESTABLISHED, @@ -245,6 +246,8 @@ enum lws_rx_parse_state { enum connection_mode { + LWS_CONNMODE_HTTP_SERVING, + LWS_CONNMODE_WS_SERVING, LWS_CONNMODE_WS_CLIENT, @@ -385,6 +388,11 @@ struct libwebsocket { int use_ssl; #endif + /* http send file */ + char filepath[PATH_MAX]; + unsigned long filepos; + unsigned long filelen; + void *user_space; }; diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html index 00d38e1c..ed43e095 100644 --- a/libwebsockets-api-doc.html +++ b/libwebsockets-api-doc.html @@ -411,7 +411,8 @@ packet while not burdening the user code with any protocol knowledge.
+a file requested to be send down +http link has completed. +
If you call diff --git a/test-server/test-server.c b/test-server/test-server.c index 75935463..a9db6eba 100644 --- a/test-server/test-server.c +++ b/test-server/test-server.c @@ -85,10 +85,10 @@ static int callback_http(struct libwebsocket_context *context, switch (reason) { case LWS_CALLBACK_HTTP: - fprintf(stderr, "serving HTTP URI %s\n", (char *)in); +// fprintf(stderr, "serving HTTP URI %s\n", (char *)in); if (in && strcmp(in, "/favicon.ico") == 0) { - if (libwebsockets_serve_http_file(wsi, + if (libwebsockets_serve_http_file(context, wsi, LOCAL_RESOURCE_PATH"/favicon.ico", "image/x-icon")) fprintf(stderr, "Failed to send favicon\n"); break; @@ -96,11 +96,21 @@ static int callback_http(struct libwebsocket_context *context, /* send the script... when it runs it'll start websockets */ - if (libwebsockets_serve_http_file(wsi, + if (libwebsockets_serve_http_file(context, wsi, LOCAL_RESOURCE_PATH"/test.html", "text/html")) fprintf(stderr, "Failed to send HTTP file\n"); - /* we are done with this http connection */ + /* + * notice that the sending of the file completes asynchronously, + * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when + * it's done + */ + + return 0; + + case LWS_CALLBACK_HTTP_FILE_COMPLETION: +// fprintf(stderr, "LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n"); + /* kill the connection after we sent one file */ return 1; /* @@ -116,8 +126,8 @@ static int callback_http(struct libwebsocket_context *context, libwebsockets_get_peer_addresses((int)(long)user, client_name, sizeof(client_name), client_ip, sizeof(client_ip)); - fprintf(stderr, "Received network connect from %s (%s)\n", - client_name, client_ip); +// fprintf(stderr, "Received network connect from %s (%s)\n", +// client_name, client_ip); /* if we returned non-zero from here, we kill the connection */ break; @@ -573,6 +583,7 @@ int main(int argc, char **argv) if (n < 0) continue; + if (n) for (n = 0; n < count_pollfds; n++) if (pollfds[n].revents) @@ -584,13 +595,12 @@ int main(int argc, char **argv) if (libwebsocket_service_fd(context, &pollfds[n]) < 0) goto done; - #else n = libwebsocket_service(context, 50); #endif } -#else +#else /* !LWS_NO_FORK */ /* * This example shows how to work with the forked websocket service loop