Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/gwsocket.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "settings.h"
#include "websocket.h"
#include "wsauth.h"
#include "util.h"
#include "xmalloc.h"

/* Allocate memory for a new GWSReader instance.
Expand Down Expand Up @@ -488,6 +489,7 @@ start_server (void *ptr_data) {
/* Read and set the WebSocket config options. */
static void
set_ws_opts (void) {
const char *html_filename;
ws_set_config_strict (1);
if (conf.addr)
ws_set_config_host (conf.addr);
Expand All @@ -505,6 +507,8 @@ set_ws_opts (void) {
ws_set_config_sslcert (conf.sslcert);
if (conf.sslkey)
ws_set_config_sslkey (conf.sslkey);
if (find_output_type((char**)&html_filename, "html", 0) == 0)
ws_set_config_html_path(html_filename);
#ifdef HAVE_LIBSSL
if (conf.ws_auth_secret) {
ws_set_config_auth_secret (conf.ws_auth_secret);
Expand Down
2 changes: 1 addition & 1 deletion src/output.c
Original file line number Diff line number Diff line change
Expand Up @@ -1260,7 +1260,7 @@ print_json_defs (FILE *fp) {
fprintf (fp, external_assets ? "\n" : "</script>");
}

static char *
char *
get_asset_filepath (const char *filename, const char *asset_fname) {
char *fname = NULL, *path = NULL, *s = NULL;

Expand Down
1 change: 1 addition & 0 deletions src/output.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,5 @@ typedef struct GDefMetric_ {

void output_html (GHolder * holder, const char *filename);

char *get_asset_filepath (const char *filename, const char *asset_fname);
#endif
5 changes: 4 additions & 1 deletion src/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -462,8 +462,11 @@ find_output_type (char **filename, const char *ext, int alloc) {
return 0;

if ((dot = strrchr (conf.output_formats[i], '.')) != NULL && strcmp (dot + 1, ext) == 0) {
if (alloc)
if (alloc && filename) {
*filename = xstrdup (conf.output_formats[i]);
} else if (filename) {
*filename = conf.output_formats[i];
}
return 0;
}
}
Expand Down
185 changes: 155 additions & 30 deletions src/websocket.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include "error.h"
#include "gslist.h"
#include "sha1.h"
#include "output.h"
#include "util.h"
#include "xmalloc.h"

Expand Down Expand Up @@ -625,6 +626,13 @@ ws_stop (WSServer *server) {
ws_ssl_cleanup (server);
#endif

if (server->file_html)
free (server->file_html);
if (server->file_css)
free (server->file_css);
if (server->file_js)
free (server->file_js);

free (server);
free (fdstate);
fdstate = NULL;
Expand Down Expand Up @@ -1579,6 +1587,39 @@ ws_set_handshake_headers (WSHeaders *headers) {
free (s);
}

static char*
ws_load_file(char **file_buffer, const char *file_name, off_t *size)
{
FILE *fp = NULL;
char *buf = NULL;
int read_len = -1;
if (*file_buffer != NULL) {
return *file_buffer;
}
fp = fopen (file_name, "r");
if (!fp) {
LOG (("Failed to open %s: %s\n", file_name, strerror (errno)));
return NULL;
}
*file_buffer = NULL;
*size = file_size (file_name);
buf = xmalloc (*size);
if (!buf) {
LOG (("Failed to allocate buffer for %s\n", file_name));
goto fail;
}
read_len = fread (buf, *size, 1, fp);
if (read_len < 1) {
LOG (("Failed to read whole file %s (read %d/%llu)\n", file_name, read_len, *size));
free (buf);
goto fail;
}
*file_buffer = buf;
fail:
fclose (fp);
return *file_buffer;
}

/* Send the websocket handshake headers to the given client.
*
* On success, the number of sent bytes is returned. */
Expand Down Expand Up @@ -1608,8 +1649,111 @@ ws_send_handshake_headers (WSClient *client, WSHeaders *headers) {
return bytes;
}

/* Given the HTTP connection headers, attempt to parse the web socket
* handshake headers.
/* Upgrade to a WebSocket and send the headers to the client.
*
* Returns the number of bytes written for the response. */
static int
ws_send_websocket_response (WSClient *client, WSServer *server, int bytes)
{
/* Ensure we can authenticate the connection */
if (wsconfig.auth_secret && wsconfig.auth (client->headers->jwt, wsconfig.auth_secret) != 1) {
LOG (("Unable to authenticate connection %d [%s]...\n", client->listener, client->remote_ip));
http_error (client, WS_UNAUTHORIZED_STR);
return ws_set_status (client, WS_CLOSE, bytes);
}

ws_set_handshake_headers (client->headers);

/* handshake response */
ws_send_handshake_headers (client, client->headers);

/* upon success, call onopen() callback */
if (server->onopen && wsconfig.strict && !wsconfig.echomode)
server->onopen (server->pipeout, client);
client->headers->reading = 0;

/* do access logging */
gettimeofday (&client->end_proc, NULL);
if (wsconfig.accesslog)
access_log (client, 101);
LOG (("Active: %d\n", list_count (server->colist)));

return ws_set_status (client, WS_OK, bytes);
}

/* Serves a static resource to the client (the HTML report).
*
* Returns the number of bytes written for the response. */
static int
ws_send_http_response (WSClient *client, WSServer *server, int bytes, const char *buf, off_t size, const char *mime)
{
char *str = NULL, *html_size_str = NULL;

html_size_str = u642str (size, 0);

client->headers->ws_resp = xstrdup (WS_OK_STR);

/* send headers */
str = xstrdup ("");

ws_append_str (&str, client->headers->ws_resp);
ws_append_str (&str, CRLF);

ws_append_str (&str, "Content-Length: ");
ws_append_str (&str, html_size_str);
ws_append_str (&str, CRLF);

ws_append_str (&str, "Content-Type: ");
ws_append_str (&str, mime);
ws_append_str (&str, CRLF CRLF);
bytes += ws_respond (client, str, strlen (str));
free (str);
free (html_size_str);

/* report html */
bytes += ws_respond (client, buf, size);

/* do access logging */
gettimeofday (&client->end_proc, NULL);
if (wsconfig.accesslog)
access_log (client, 200);
LOG (("Provided %s, %d bytes, %lld file size, %lld header size\n", mime, bytes, size, bytes - size));

return ws_set_status (client, WS_CLOSE, bytes);
}

/* Serve a static resource: HTML report, CSS, or JS. */
static int
ws_serve_resource (WSClient *client, WSServer *server, int bytes) {
char *resource_name = NULL;
if (strcmp(client->headers->path, "/") == 0
&& wsconfig.html_path
&& ws_load_file (&server->file_html, wsconfig.html_path, &server->size_html)) {
return ws_send_http_response (client, server, bytes, server->file_html, server->size_html, "text/html; charset=utf-8");
}
else if (strcmp(client->headers->path, "/" FILENAME_CSS) == 0
&& wsconfig.html_path
&& (resource_name = get_asset_filepath (wsconfig.html_path, FILENAME_CSS))
&& ws_load_file (&server->file_css, resource_name, &server->size_css)) {
free (resource_name);
return ws_send_http_response (client, server, bytes, server->file_css, server->size_css, "text/css");
}
else if (strcmp(client->headers->path, "/" FILENAME_JS) == 0
&& wsconfig.html_path
&& (resource_name = get_asset_filepath (wsconfig.html_path, FILENAME_JS))
&& ws_load_file (&server->file_js, resource_name, &server->size_js)) {
free (resource_name);
return ws_send_http_response (client, server, bytes, server->file_js, server->size_js, "text/javascript");
}
LOG (("Missing headers for handshake %d [%s]...\n", client->listener, client->remote_ip));
if (resource_name)
free (resource_name);
http_error (client, WS_BAD_REQUEST_STR);
return ws_set_status (client, WS_CLOSE, bytes);
}

/* Given the HTTP connection headers, attempt to parse the passed headers and
* come up with an appropriate response.
*
* On success, the number of sent bytes is returned. */
static int
Expand Down Expand Up @@ -1654,37 +1798,13 @@ ws_get_handshake (WSClient *client, WSServer *server) {
return ws_set_status (client, WS_CLOSE, bytes);
}

/* Ensure we have the required headers */
/* Ensure we have the required headers for WebSocket */
if (ws_verify_req_headers (client->headers) != 0) {
LOG (("Missing headers for handshake %d [%s]...\n", client->listener, client->remote_ip));
http_error (client, WS_BAD_REQUEST_STR);
return ws_set_status (client, WS_CLOSE, bytes);
}

/* Ensure we can authenticate the connection */
if (wsconfig.auth_secret && wsconfig.auth (client->headers->jwt, wsconfig.auth_secret) != 1) {
LOG (("Unable to authenticate connection %d [%s]...\n", client->listener, client->remote_ip));
http_error (client, WS_UNAUTHORIZED_STR);
return ws_set_status (client, WS_CLOSE, bytes);
/* If not, check if it's a rquest for the HTML report, otherwise fail */
return ws_serve_resource (client, server, bytes);
}

ws_set_handshake_headers (client->headers);

/* handshake response */
ws_send_handshake_headers (client, client->headers);

/* upon success, call onopen() callback */
if (server->onopen && wsconfig.strict && !wsconfig.echomode)
server->onopen (server->pipeout, client);
client->headers->reading = 0;

/* do access logging */
gettimeofday (&client->end_proc, NULL);
if (wsconfig.accesslog)
access_log (client, 101);
LOG (("Active: %d\n", list_count (server->colist)));

return ws_set_status (client, WS_OK, bytes);
return ws_send_websocket_response (client, server, bytes);
}

/* Send a data message to the given client.
Expand Down Expand Up @@ -3021,6 +3141,11 @@ ws_set_config_auth_cb (int (*auth_cb) (const char *jwt, const char *secret)) {
wsconfig.auth = auth_cb;
}

void
ws_set_config_html_path (const char *html_path) {
wsconfig.html_path = html_path;
}

/* Create a new websocket server context. */
WSServer *
ws_init (const char *host, const char *port, void (*initopts) (void)) {
Expand Down
12 changes: 12 additions & 0 deletions src/websocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,10 @@
#define MAX(a,b) (((a)>(b))?(a):(b))
#include "gslist.h"

#define WS_OK_STR "HTTP/1.1 200 OK"
#define WS_BAD_REQUEST_STR "HTTP/1.1 400 Invalid Request\r\n\r\n"
#define WS_UNAUTHORIZED_STR "HTTP/1.1 401 Unauthorized\r\n\r\n"
#define WS_NOT_FOUND_STR "HTTP/1.1 404 Not Found\n\r\n"
#define WS_SWITCH_PROTO_STR "HTTP/1.1 101 Switching Protocols"
#define WS_TOO_BUSY_STR "HTTP/1.1 503 Service Unavailable\r\n\r\n"

Expand Down Expand Up @@ -275,6 +277,7 @@ typedef struct WSConfig_ {
const char *sslkey;
const char *unix_socket;
const char *auth_secret;
const char *html_path;

/* Function pointer for JWT verification */
int (*auth) (const char *jwt, const char *secret);
Expand Down Expand Up @@ -304,6 +307,14 @@ typedef struct WSServer_ {
/* Connected Clients */
GSLList *colist;

/* Cached Report Resources */
char *file_html;
off_t size_html;
char *file_css;
off_t size_css;
char *file_js;
off_t size_js;

#ifdef HAVE_LIBSSL
SSL_CTX *ctx;
#endif
Expand Down Expand Up @@ -331,6 +342,7 @@ void ws_set_config_sslkey (const char *sslkey);
void ws_set_config_strict (int strict);
void ws_set_config_auth_secret (const char *auth_secret);
void ws_set_config_auth_cb (int (*auth_cb) (const char *jwt, const char *secret));
void ws_set_config_html_path (const char *html_path);
void ws_start (WSServer * server);
void ws_stop (WSServer * server);
WSServer *ws_init (const char *host, const char *port, void (*initopts) (void));
Expand Down
Loading