diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 00000000..c0cc2e3d Binary files /dev/null and b/favicon.ico differ diff --git a/libwebsockets.c b/libwebsockets.c index fc67be75..7fcf4e1f 100644 --- a/libwebsockets.c +++ b/libwebsockets.c @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include #include @@ -48,8 +51,8 @@ static void libwebsocket_service(struct libwebsocket *wsi, int sock); enum lws_connection_states { - WSI_STATE_CLOSED, - WSI_STATE_HANDSHAKE_RX, + WSI_STATE_HTTP, + WSI_STATE_HTTP_HEADERS, WSI_STATE_DEAD_SOCKET, WSI_STATE_ESTABLISHED }; @@ -143,7 +146,7 @@ int libwebsocket_create_server(int port, if (!wsi) return -1; - wsi->state = WSI_STATE_CLOSED; + wsi->state = WSI_STATE_HTTP; wsi->name_buffer_pos = 0; for (n = 0; n < WSI_TOKEN_COUNT; n++) { @@ -156,7 +159,7 @@ int libwebsocket_create_server(int port, case 0: case 2: case 76: - fprintf(stderr, "Using protocol v%d\n", protocol); + fprintf(stderr, " Using protocol v%d\n", protocol); wsi->ietf_spec_revision = protocol; break; default: @@ -196,7 +199,7 @@ int libwebsocket_create_server(int port, if (n) return 0; - fprintf(stderr, "Listening on port %d\n", port); + fprintf(stderr, " Listening on port %d\n", port); listen(sockfd, 5); @@ -208,6 +211,8 @@ int libwebsocket_create_server(int port, fprintf(stderr, "ERROR on accept"); continue; } + +// fcntl(newsockfd, F_SETFL, O_NONBLOCK); /* fork off a new server instance */ @@ -247,6 +252,13 @@ void libwebsocket_close(struct libwebsocket *wsi) free(wsi->utf8_token[n].token); } +const char * libwebsocket_get_uri(struct libwebsocket *wsi) +{ + if (wsi->utf8_token[WSI_TOKEN_GET_URI].token) + return wsi->utf8_token[WSI_TOKEN_GET_URI].token; + + return NULL; +} static int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) { @@ -333,6 +345,7 @@ static int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) continue; if (strcmp(lws_tokens[n].token, wsi->name_buffer)) continue; + fprintf(stderr, "known header '%s'\n", wsi->name_buffer); wsi->parser_state = WSI_TOKEN_GET_URI + n; wsi->current_alloc_len = LWS_INITIAL_HDR_ALLOC; wsi->utf8_token[wsi->parser_state].token = @@ -340,8 +353,23 @@ static int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) wsi->utf8_token[wsi->parser_state].token_len = 0; n = WSI_TOKEN_COUNT; } - if (wsi->parser_state != WSI_TOKEN_NAME_PART) + + /* colon delimiter means we just don't know this name */ + + if (wsi->parser_state == WSI_TOKEN_NAME_PART && c == ':') { + fprintf(stderr, "skipping unknown header '%s'\n", wsi->name_buffer); + wsi->parser_state = WSI_TOKEN_SKIPPING; break; + } + + /* don't look for payload when it can just be http headers */ + + if (wsi->parser_state == WSI_TOKEN_CHALLENGE && + !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len) { + /* they're HTTP headers, not websocket upgrade! */ + fprintf(stderr, "Setting WSI_PARSING_COMPLETE from http headers\n"); + wsi->parser_state = WSI_PARSING_COMPLETE; + } break; /* skipping arg part of a name we didn't recognize */ @@ -490,11 +518,11 @@ int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len) char *response; switch (wsi->state) { - case WSI_STATE_CLOSED: - wsi->state = WSI_STATE_HANDSHAKE_RX; + case WSI_STATE_HTTP: + wsi->state = WSI_STATE_HTTP_HEADERS; wsi->parser_state = WSI_TOKEN_NAME_PART; /* fallthru */ - case WSI_STATE_HANDSHAKE_RX: + case WSI_STATE_HTTP_HEADERS: fprintf(stderr, "issuing %d bytes to parser\n", (int)len); @@ -504,24 +532,29 @@ int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len) if (wsi->parser_state != WSI_PARSING_COMPLETE) break; + + /* is this websocket protocol or normal http 1.0? */ + + if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len || + !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len) { + if (wsi->callback) + (wsi->callback)(wsi, LWS_CALLBACK_HTTP, NULL, 0); + wsi->state = WSI_STATE_HTTP; + return 0; + } + fprintf(stderr, "Preparing return packet\n"); - - /* Confirm we have all the necessary pieces */ + /* Websocket - confirm we have all the necessary pieces */ - if ( - !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len || - !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len || - !wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len || + if (!wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len || !wsi->utf8_token[WSI_TOKEN_HOST].token_len || !wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len || !wsi->utf8_token[WSI_TOKEN_KEY1].token_len || - !wsi->utf8_token[WSI_TOKEN_KEY2].token_len) { - + !wsi->utf8_token[WSI_TOKEN_KEY2].token_len) /* completed header processing, but missing some bits */ goto bail; - } /* create the response packet */ @@ -645,20 +678,25 @@ bail: */ int libwebsocket_write(struct libwebsocket * wsi, unsigned char *buf, - size_t len, int is_binary) + size_t len, enum libwebsocket_write_protocol protocol) { int n; int pre = 0; int post = 0; unsigned int shift = 7; + if (protocol == LWS_WRITE_HTTP) + goto send_raw; + + /* websocket protocol, either binary or text */ + if (wsi->state != WSI_STATE_ESTABLISHED) return -1; switch (wsi->ietf_spec_revision) { /* Firefox 4.0b6 likes this as of 30 Oct */ case 76: - if (is_binary) { + if (protocol == LWS_WRITE_BINARY) { /* in binary mode we send 7-bit used length blocks */ pre = 1; while (len & (127 << shift)) { @@ -712,7 +750,7 @@ int libwebsocket_write(struct libwebsocket * wsi, unsigned char *buf, /* just an unimplemented spec right now apparently */ case 2: n = 4; /* text */ - if (is_binary) + if (protocol == LWS_WRITE_BINARY) n = 5; /* binary */ if (len < 126) { buf[-2] = n; @@ -749,18 +787,20 @@ int libwebsocket_write(struct libwebsocket * wsi, unsigned char *buf, break; } - for (n = 0; n < (len + pre + post); n++) - fprintf(stderr, "%02X ", buf[n - pre]); - - fprintf(stderr, "\n"); +// for (n = 0; n < (len + pre + post); n++) +// fprintf(stderr, "%02X ", buf[n - pre]); +// +// fprintf(stderr, "\n"); - n = write(wsi->sock, buf - pre, len + pre + post); +send_raw: + + n = send(wsi->sock, buf - pre, len + pre + post, 0); if (n < 0) { fprintf(stderr, "ERROR writing to socket"); return -1; } - fprintf(stderr, "written %d bytes to websocket\n", (int)len); +// fprintf(stderr, "written %d bytes to client\n", (int)len); return 0; } @@ -772,45 +812,102 @@ static void libwebsocket_service(struct libwebsocket *wsi, int sock) struct pollfd fds; wsi->sock = sock; - - fds.fd = sock; - fds.events = POLLIN | POLLOUT; - + while (1) { - - n = poll(&fds, 1, 10); + fds.fd = sock; + fds.events = POLLIN; + fds.revents = 0; + n = poll(&fds, 1, 50); + + /* + * we seem to get a ton of POLLINs coming when there's nothing + * to read (at least, 0 bytes read)... don't understand why yet + * but I stuck a usleep() in the 0 byte read case to regulate + * CPU + */ + + if (n < 0) { fprintf(stderr, "Socket dead (poll = %d)\n", n); return; } + if (n == 0) + goto pout; if (fds.revents & (POLLERR | POLLHUP)) { fprintf(stderr, "Socket dead\n"); return; } - if (wsi->state == WSI_STATE_DEAD_SOCKET) + if (wsi->state == WSI_STATE_DEAD_SOCKET) { + fprintf(stderr, "Seen socket dead, returning from service\n"); return; - + } if (fds.revents & POLLIN) { // fprintf(stderr, "POLLIN\n"); - n = read(sock, buf, sizeof(buf)); + n = recv(sock, buf, sizeof(buf), 0); if (n < 0) { fprintf(stderr, "Socket read returned %d\n", n); continue; } if (n) libwebsocket_read(wsi, buf, n); + else { +// fprintf(stderr, "POLLIN with zero length waiting\n"); + usleep(50000); + } } - +pout: if (wsi->state != WSI_STATE_ESTABLISHED) continue; - + +// fprintf(stderr, "POLLOUT\n"); + if (wsi->callback) wsi->callback(wsi, LWS_CALLBACK_SEND, NULL, 0); } } +int libwebsockets_serve_http_file(struct libwebsocket *wsi, const char * file, + const char * content_type) +{ + int fd; + struct stat stat; + char buf[512]; + char *p = buf; + int n; + + fd = open(file, O_RDONLY); + if (fd < 1) { + p += sprintf(p, "HTTP/1.0 400 Bad\x0d\x0a" + "Server: libwebsockets\x0d\x0a" + "\x0d\x0a" + ); + libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP); + + return -1; + } + + fstat(fd, &stat); + p += sprintf(p, "HTTP/1.0 200 OK\x0d\x0a" + "Server: libwebsockets\x0d\x0a" + "Content-Type: %s\x0d\x0a" + "Content-Length: %u\x0d\x0a" + "\x0d\x0a", content_type, (unsigned int)stat.st_size + ); + + libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP); + + n = 1; + while (n > 0) { + n = read(fd, buf, 512); + libwebsocket_write(wsi, (unsigned char *)buf, n, LWS_WRITE_HTTP); + } + + close(fd); + + return 0; +} diff --git a/libwebsockets.h b/libwebsockets.h index 0264957b..6ecccd15 100644 --- a/libwebsockets.h +++ b/libwebsockets.h @@ -4,6 +4,13 @@ enum libwebsocket_callback_reasons { LWS_CALLBACK_CLOSED, LWS_CALLBACK_SEND, LWS_CALLBACK_RECEIVE, + LWS_CALLBACK_HTTP +}; + +enum libwebsocket_write_protocol { + LWS_WRITE_TEXT, + LWS_WRITE_BINARY, + LWS_WRITE_HTTP }; struct libwebsocket; @@ -16,7 +23,8 @@ extern int libwebsocket_create_server(int port, /* * IMPORTANT NOTICE! * - * The send buffer has to have LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE + * When sending with websocket protocol (LWS_WRITE_TEXT or LWS_WRITE_BINARY) + * the send buffer has to have LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE * buf, and LWS_SEND_BUFFER_POST_PADDING bytes valid AFTER (buf + len). * * This allows us to add protocol info before and after the data, and send as @@ -32,9 +40,18 @@ extern int libwebsocket_create_server(int port, * * libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], 128); * + * When sending LWS_WRITE_HTTP, there is no protocol addition and you can just + * use the whole buffer without taking care of the above. */ #define LWS_SEND_BUFFER_PRE_PADDING 12 #define LWS_SEND_BUFFER_POST_PADDING 1 -extern int libwebsocket_write(struct libwebsocket *, unsigned char *buf, size_t len, int is_binary); +extern int +libwebsocket_write(struct libwebsocket *, unsigned char *buf, size_t len, + enum libwebsocket_write_protocol protocol); +extern const char * +libwebsocket_get_uri(struct libwebsocket *wsi); +extern int +libwebsockets_serve_http_file(struct libwebsocket *wsi, const char * file, + const char * content_type); diff --git a/test-server.c b/test-server.c index 1717c4de..5bbda6fb 100644 --- a/test-server.c +++ b/test-server.c @@ -2,6 +2,8 @@ #include #include #include +#include + #include "libwebsockets.h" @@ -19,14 +21,16 @@ static int websocket_callback(struct libwebsocket * wsi, enum libwebsocket_callback_reasons reason, void *in, size_t len) { int n; - char buf[LWS_SEND_BUFFER_PRE_PADDING + 256 + LWS_SEND_BUFFER_POST_PADDING]; + char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 + LWS_SEND_BUFFER_POST_PADDING]; static int bump; static int slow; + char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING]; + const char *uri; switch (reason) { case LWS_CALLBACK_ESTABLISHED: fprintf(stderr, "Websocket connection established\n"); - slow = 500; + slow = 100; break; case LWS_CALLBACK_CLOSED: @@ -34,14 +38,14 @@ static int websocket_callback(struct libwebsocket * wsi, break; case LWS_CALLBACK_SEND: - slow--; - if (slow) { - usleep(10000); - break; - } - slow = 100; - n = sprintf(&buf[LWS_SEND_BUFFER_PRE_PADDING], "%d", bump++); - n = libwebsocket_write(wsi, (unsigned char *)&buf[LWS_SEND_BUFFER_PRE_PADDING], n, 0); +// slow--; +// if (slow) { +// usleep(10000); +// break; +// } +// slow = 20; + n = sprintf(p, "%d", bump++); + n = libwebsocket_write(wsi, (unsigned char *)p, n, 0); if (n < 0) { fprintf(stderr, "ERROR writing to socket"); exit(1); @@ -50,6 +54,33 @@ static int websocket_callback(struct libwebsocket * wsi, case LWS_CALLBACK_RECEIVE: fprintf(stderr, "Received %d bytes payload\n", (int)len); + break; + case LWS_CALLBACK_HTTP: + /* + * The client has asked us for something in normal HTTP mode, + * not websockets mode. Normally it means we want to send + * our script / html to the client, and when that script runs + * it will start up separate websocket connections. + */ + uri = libwebsocket_get_uri(wsi); + if (uri == NULL) + fprintf(stderr, "LWS_CALLBACK_HTTP NULL URI\n"); + else + fprintf(stderr, "LWS_CALLBACK_HTTP '%s'\n", uri); + + if (uri && strcmp(uri, "/favicon.ico") == 0) { + if (libwebsockets_serve_http_file(wsi, "./favicon.ico", "image/x-icon")) { + fprintf(stderr, "Failed to send favicon file\n"); + } + break; + } + + /* send the script... when it runs it'll start websockets */ + + if (libwebsockets_serve_http_file(wsi, "./test.html", "text/html")) { + fprintf(stderr, "Failed to send HTTP file\n"); + } + break; } return 0;