add-basic-http-fileserving.patch
Signed-off-by: Andy Green <andy@warmcat.com>
This commit is contained in:
parent
ea71ed1c21
commit
5fd8a5e580
4 changed files with 195 additions and 50 deletions
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
173
libwebsockets.c
173
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 @@ 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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#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;
|
||||
|
|
Loading…
Add table
Reference in a new issue