forked from Imagelibrary/rtems
Enable WebSocket support in the Mongoose HTTP server
This commit is contained in:
committed by
Sebastian Huber
parent
3acc6196a4
commit
f42d4292cb
@@ -7,7 +7,7 @@ include_mghttpddir = $(includedir)/mghttpd
|
|||||||
project_lib_LIBRARIES = libmghttpd.a
|
project_lib_LIBRARIES = libmghttpd.a
|
||||||
libmghttpd_a_CPPFLAGS = $(AM_CPPFLAGS)
|
libmghttpd_a_CPPFLAGS = $(AM_CPPFLAGS)
|
||||||
# libmghttpd_a_CPPFLAGS += -DHAVE_MD5
|
# libmghttpd_a_CPPFLAGS += -DHAVE_MD5
|
||||||
libmghttpd_a_CPPFLAGS += -DNO_SSL -DNO_POPEN -DNO_CGI
|
libmghttpd_a_CPPFLAGS += -DNO_SSL -DNO_POPEN -DNO_CGI -DUSE_WEBSOCKET
|
||||||
|
|
||||||
libmghttpd_a_SOURCES = mongoose.c mongoose.h
|
libmghttpd_a_SOURCES = mongoose.c mongoose.h
|
||||||
include_mghttpd_HEADERS = mongoose.h
|
include_mghttpd_HEADERS = mongoose.h
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <rtems/rtems_bsdnet.h>
|
#include <rtems/rtems_bsdnet.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include <mghttpd/mongoose.h>
|
#include <mghttpd/mongoose.h>
|
||||||
|
|
||||||
#include <rtems/imfs.h>
|
#include <rtems/imfs.h>
|
||||||
@@ -43,6 +44,9 @@ const char rtems_test_name[] = "MGHTTPD 1";
|
|||||||
"\r\n" \
|
"\r\n" \
|
||||||
"This is a message from the callback function.\r\n"
|
"This is a message from the callback function.\r\n"
|
||||||
|
|
||||||
|
#define WSTEST_REQ "Test request"
|
||||||
|
#define WSTEST_RESP "This is a message from the WebSocket callback function."
|
||||||
|
|
||||||
#define INDEX_HTML "HTTP/1.1 200 OK\r\n" \
|
#define INDEX_HTML "HTTP/1.1 200 OK\r\n" \
|
||||||
"Date: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n" \
|
"Date: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n" \
|
||||||
"Last-Modified: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n" \
|
"Last-Modified: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n" \
|
||||||
@@ -96,6 +100,22 @@ static int callback(struct mg_connection *conn)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int callback_websocket(struct mg_connection *connection,
|
||||||
|
int bits,
|
||||||
|
char *data,
|
||||||
|
size_t data_len)
|
||||||
|
{
|
||||||
|
if (data_len == strlen(WSTEST_REQ) && strncmp(data, WSTEST_REQ, data_len) == 0)
|
||||||
|
{
|
||||||
|
mg_websocket_write(connection, WEBSOCKET_OPCODE_TEXT, WSTEST_RESP, strlen(WSTEST_RESP));
|
||||||
|
|
||||||
|
/* Don't close the WebSocket */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void test_mg_index_html(void)
|
static void test_mg_index_html(void)
|
||||||
{
|
{
|
||||||
httpc_context httpc_ctx;
|
httpc_context httpc_ctx;
|
||||||
@@ -164,10 +184,41 @@ static void test_mg_callback(void)
|
|||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_mg_websocket(void)
|
||||||
|
{
|
||||||
|
httpc_context httpc_ctx;
|
||||||
|
char *buffer = malloc(BUFFERSIZE);
|
||||||
|
bool brv = false;
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
|
rtems_test_assert(buffer != NULL);
|
||||||
|
|
||||||
|
puts("=== Get a WebSocket response generated from a callback function" \
|
||||||
|
" from first Mongoose instance:");
|
||||||
|
|
||||||
|
httpc_init_context(&httpc_ctx);
|
||||||
|
brv = httpc_open_connection(&httpc_ctx, "127.0.0.1", 80);
|
||||||
|
rtems_test_assert(brv);
|
||||||
|
brv = httpc_ws_open_connection(&httpc_ctx);
|
||||||
|
rtems_test_assert(brv);
|
||||||
|
brv = httpc_ws_send_request(&httpc_ctx, WSTEST_REQ, buffer, BUFFERSIZE);
|
||||||
|
rtems_test_assert(brv);
|
||||||
|
brv = httpc_close_connection(&httpc_ctx);
|
||||||
|
rtems_test_assert(brv);
|
||||||
|
puts(buffer);
|
||||||
|
rv = strcmp(buffer, WSTEST_RESP);
|
||||||
|
rtems_test_assert(rv == 0);
|
||||||
|
|
||||||
|
puts("=== OK");
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_mongoose(void)
|
static void test_mongoose(void)
|
||||||
{
|
{
|
||||||
const struct mg_callbacks callbacks = {
|
const struct mg_callbacks callbacks = {
|
||||||
.begin_request = callback
|
.begin_request = callback,
|
||||||
|
.websocket_data = callback_websocket
|
||||||
};
|
};
|
||||||
const char *options[] = {
|
const char *options[] = {
|
||||||
"listening_ports", "80",
|
"listening_ports", "80",
|
||||||
@@ -192,6 +243,7 @@ static void test_mongoose(void)
|
|||||||
|
|
||||||
test_mg_index_html();
|
test_mg_index_html();
|
||||||
test_mg_callback();
|
test_mg_callback();
|
||||||
|
test_mg_websocket();
|
||||||
|
|
||||||
mg_stop(mg1);
|
mg_stop(mg1);
|
||||||
mg_stop(mg2);
|
mg_stop(mg2);
|
||||||
|
|||||||
@@ -18,11 +18,34 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <tmacros.h>
|
||||||
|
|
||||||
#include "test-http-client.h"
|
#include "test-http-client.h"
|
||||||
|
|
||||||
|
#define HTTPC_WS_CONN_REQ "GET / HTTP/1.1\r\n" \
|
||||||
|
"Host: localhost\r\n" \
|
||||||
|
"Upgrade: websocket\r\n" \
|
||||||
|
"Connection: Upgrade\r\n" \
|
||||||
|
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" \
|
||||||
|
"Sec-WebSocket-Version: 13\r\n" \
|
||||||
|
"\r\n"
|
||||||
|
#define HTTPC_WS_CONN_RESP "HTTP/1.1 101 Switching Protocols\r\n" \
|
||||||
|
"Upgrade: websocket\r\n" \
|
||||||
|
"Connection: Upgrade\r\n" \
|
||||||
|
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" \
|
||||||
|
"\r\n"
|
||||||
|
|
||||||
|
static ssize_t httpc_read_full(
|
||||||
|
const httpc_context *ctx,
|
||||||
|
void *response,
|
||||||
|
size_t responsesize
|
||||||
|
);
|
||||||
|
|
||||||
void httpc_init_context(
|
void httpc_init_context(
|
||||||
httpc_context *ctx
|
httpc_context *ctx
|
||||||
)
|
)
|
||||||
@@ -33,7 +56,7 @@ void httpc_init_context(
|
|||||||
|
|
||||||
bool httpc_open_connection(
|
bool httpc_open_connection(
|
||||||
httpc_context *ctx,
|
httpc_context *ctx,
|
||||||
char *targethost,
|
const char *targethost,
|
||||||
int targetport
|
int targetport
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -76,21 +99,126 @@ bool httpc_close_connection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool httpc_send_request(
|
bool httpc_send_request(
|
||||||
httpc_context *ctx,
|
const httpc_context *ctx,
|
||||||
char *request,
|
const char *request,
|
||||||
char *response,
|
char *response,
|
||||||
int responsesize
|
int responsesize
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
int size = strlen(request);
|
rtems_test_assert(ctx != NULL);
|
||||||
char lineend[] = " HTTP/1.1\r\n\r\n";
|
rtems_test_assert(ctx->socket >= 0);
|
||||||
|
rtems_test_assert(request != NULL);
|
||||||
|
rtems_test_assert(response != NULL);
|
||||||
|
rtems_test_assert(responsesize > 1);
|
||||||
|
|
||||||
write(ctx->socket, request, size);
|
static const char * const lineend = " HTTP/1.1\r\n\r\n";
|
||||||
write(ctx->socket, lineend, sizeof(lineend));
|
|
||||||
|
|
||||||
size = read(ctx->socket, response, responsesize-1);
|
write(ctx->socket, request, strlen(request));
|
||||||
response[size] = '\0';
|
write(ctx->socket, lineend, strlen(lineend));
|
||||||
|
|
||||||
|
ssize_t size;
|
||||||
|
if((size = httpc_read_full(ctx, response, responsesize - 1)) == -1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*(response + size) = '\0';
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool httpc_ws_open_connection(
|
||||||
|
const httpc_context *ctx
|
||||||
|
)
|
||||||
|
{
|
||||||
|
rtems_test_assert(ctx != NULL);
|
||||||
|
rtems_test_assert(ctx->socket >= 0);
|
||||||
|
|
||||||
|
write(ctx->socket, HTTPC_WS_CONN_REQ, strlen(HTTPC_WS_CONN_REQ));
|
||||||
|
|
||||||
|
char response[strlen(HTTPC_WS_CONN_RESP)];
|
||||||
|
if(httpc_read_full(ctx, response, sizeof(response)) != sizeof(response))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(strncmp(response, HTTPC_WS_CONN_RESP, sizeof(response)) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool httpc_ws_send_request(
|
||||||
|
const httpc_context *ctx,
|
||||||
|
const char *request,
|
||||||
|
char *response,
|
||||||
|
int responsesize
|
||||||
|
)
|
||||||
|
{
|
||||||
|
rtems_test_assert(ctx != NULL);
|
||||||
|
rtems_test_assert(ctx->socket >= 0);
|
||||||
|
rtems_test_assert(request != NULL);
|
||||||
|
rtems_test_assert(response != NULL);
|
||||||
|
rtems_test_assert(responsesize > 0);
|
||||||
|
|
||||||
|
static const uint16_t ws_header_fin = 1U << 15;
|
||||||
|
static const uint16_t ws_header_text = 1U << 8;
|
||||||
|
static const uint16_t ws_header_size = 0x7FU;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't support sending WebSocket messages which require multiple
|
||||||
|
* chunks
|
||||||
|
*/
|
||||||
|
if(strlen(request) > ws_header_size) { return false; }
|
||||||
|
|
||||||
|
uint16_t header = htons(ws_header_fin | ws_header_text | strlen(request));
|
||||||
|
|
||||||
|
write(ctx->socket, &header, sizeof(header));
|
||||||
|
write(ctx->socket, request, strlen(request));
|
||||||
|
|
||||||
|
if (httpc_read_full(ctx, &header, sizeof(header)) != sizeof(header))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
header = ntohs(header);
|
||||||
|
if (!(header & ws_header_fin)) { return false; }
|
||||||
|
if (!(header & ws_header_text)) { return false; }
|
||||||
|
if (responsesize < (header & ws_header_size) + 1) { return false; }
|
||||||
|
|
||||||
|
responsesize = header & ws_header_size;
|
||||||
|
if (httpc_read_full(ctx, response, responsesize) != responsesize)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*(response + responsesize) = '\0';
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ssize_t httpc_read_full(
|
||||||
|
const httpc_context *ctx,
|
||||||
|
void *response,
|
||||||
|
size_t responsesize
|
||||||
|
)
|
||||||
|
{
|
||||||
|
rtems_test_assert(ctx != NULL);
|
||||||
|
rtems_test_assert(ctx->socket >= 0);
|
||||||
|
rtems_test_assert(response != NULL);
|
||||||
|
rtems_test_assert(responsesize > 0);
|
||||||
|
|
||||||
|
if (responsesize > SSIZE_MAX) { return -1; }
|
||||||
|
|
||||||
|
unsigned char *pos = response;
|
||||||
|
|
||||||
|
while(pos < (unsigned char *)response + responsesize)
|
||||||
|
{
|
||||||
|
ssize_t size =
|
||||||
|
read(ctx->socket, pos, (unsigned char *)response + responsesize - pos);
|
||||||
|
if (size == -1) { return -1; }
|
||||||
|
if (size == 0) { break; }
|
||||||
|
pos += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (pos - (unsigned char *)response);
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ void httpc_init_context(
|
|||||||
|
|
||||||
bool httpc_open_connection(
|
bool httpc_open_connection(
|
||||||
httpc_context *ctx,
|
httpc_context *ctx,
|
||||||
char *targethost,
|
const char *targethost,
|
||||||
int targetport
|
int targetport
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -45,8 +45,19 @@ bool httpc_close_connection(
|
|||||||
);
|
);
|
||||||
|
|
||||||
bool httpc_send_request(
|
bool httpc_send_request(
|
||||||
httpc_context *ctx,
|
const httpc_context *ctx,
|
||||||
char *request,
|
const char *request,
|
||||||
|
char *response,
|
||||||
|
int responsesize
|
||||||
|
);
|
||||||
|
|
||||||
|
bool httpc_ws_open_connection(
|
||||||
|
const httpc_context *ctx
|
||||||
|
);
|
||||||
|
|
||||||
|
bool httpc_ws_send_request(
|
||||||
|
const httpc_context *ctx,
|
||||||
|
const char *request,
|
||||||
char *response,
|
char *response,
|
||||||
int responsesize
|
int responsesize
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user