add-basic-http-fileserving.patch
Signed-off-by: Andy Green <andy@warmcat.com>
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..c0cc2e3
--- /dev/null
+++ b/favicon.ico
Binary files differ
diff --git a/libwebsockets.c b/libwebsockets.c
index fc67be7..7fcf4e1 100644
--- a/libwebsockets.c
+++ b/libwebsockets.c
@@ -4,6 +4,9 @@
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -48,8 +51,8 @@
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 @@
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 @@
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 @@
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 @@
fprintf(stderr, "ERROR on accept");
continue;
}
+
+// fcntl(newsockfd, F_SETFL, O_NONBLOCK);
/* fork off a new server instance */
@@ -247,6 +252,13 @@
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 @@
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 @@
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 @@
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 @@
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 @@
*/
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 @@
/* 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 @@
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 @@
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 0264957..6ecccd1 100644
--- a/libwebsockets.h
+++ b/libwebsockets.h
@@ -4,6 +4,13 @@
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 @@
/*
* 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 @@
*
* 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 1717c4d..5bbda6f 100644
--- a/test-server.c
+++ b/test-server.c
@@ -2,6 +2,8 @@
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
+#include <string.h>
+
#include "libwebsockets.h"
@@ -19,14 +21,16 @@
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 @@
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);
@@ -51,6 +55,33 @@
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;
}