blob: 0a6f9fb9a32ae573097f09f0640e0771bedb6b65 [file] [log] [blame] [raw]
/* -*- mode: c -*- */
#ifndef HTTP_WEBSOCKET_TEST
#include "http.h"
/*
A simple Hello World HTTP response emulation. Test with:
ab -n 1000000 -c 200 -k http://127.0.0.1:3000/
*/
static void http1_hello_on_request(http_request_s *request) {
static char hello_message[] = "HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n"
"Connection: keep-alive\r\n"
"Keep-Alive: 1;timeout=5\r\n"
"\r\n"
"Hello World!";
sock_write(request->metadata.fd, hello_message, sizeof(hello_message) - 1);
}
#include "websockets.h" // includes the "http.h" header
#include "bscrypt.h"
#include <stdio.h>
#include <stdlib.h>
/*****************************
A Websocket echo implementation
*/
static void ws_open(ws_s *ws) {
fprintf(stderr, "Opened a new websocket connection (%p)\n", (void *)ws);
}
static void ws_echo(ws_s *ws, char *data, size_t size, uint8_t is_text) {
// echos the data to the current websocket
websocket_write(ws, data, size, is_text);
if (memcmp(data, "bomb me", 7) == 0) {
char *msg = malloc(1024 * 1024);
for (char *pos = msg; pos < msg + (1024 * 1024 - 1); pos += 8) {
memcpy(pos, "bomb(!) ", 8);
}
websocket_write(ws, msg, 1024 * 1024, is_text);
free(msg);
}
}
static void ws_shutdown(ws_s *ws) {
websocket_write(ws, "Shutting Down", 13, 1);
}
static void ws_close(ws_s *ws) {
fprintf(stderr, "Closed websocket connection (%p)\n", (void *)ws);
}
/*****************************
A Websocket Broadcast implementation
*/
/* websocket broadcast data */
struct ws_data {
size_t size;
char data[];
};
/* free the websocket broadcast data */
static void free_wsdata(ws_s *ws, void *arg) {
free(arg);
(void)(ws);
}
/* the broadcast "task" performed by `Websocket.each` */
static void ws_get_broadcast(ws_s *ws, void *arg) {
struct ws_data *data = arg;
websocket_write(ws, data->data, data->size, 1); // echo
}
/* The websocket broadcast server's `on_message` callback */
static void ws_broadcast(ws_s *ws, char *data, size_t size, uint8_t is_text) {
// Copy the message to a broadcast data-packet
struct ws_data *msg = malloc(sizeof(*msg) + size);
msg->size = size;
memcpy(msg->data, data, size);
// Asynchronously calls `ws_get_broadcast` for each of the websockets
// (except this one)
// and calls `free_wsdata` once all the broadcasts were perfomed.
websocket_each(ws, ws_get_broadcast, msg, free_wsdata);
// echos the data to the current websocket
websocket_write(ws, data, size, is_text);
}
/*****************************
The HTTP implementation
*/
static void on_request(http_request_s *request) {
// to log we will start a response.
http_response_s response = http_response_init(request);
// http_response_log_start(&response);
// upgrade requests to broadcast will have the following properties:
if (request->upgrade && !strcmp(request->path, "/broadcast")) {
// Websocket upgrade will use our existing response (never leak responses).
websocket_upgrade(.request = request, .on_message = ws_broadcast,
.on_open = ws_open, .on_close = ws_close,
.on_shutdown = ws_shutdown, .response = &response);
return;
}
// other upgrade requests will have the following properties:
if (request->upgrade) {
websocket_upgrade(.request = request, .on_message = ws_echo,
.on_open = ws_open, .on_close = ws_close, .timeout = 4,
.on_shutdown = ws_shutdown, .response = &response);
return;
}
// file dumping
if (!strcmp(request->path, "/dump.jpg")) {
fdump_s *data = bscrypt_fdump("./public_www/bo.jpg", 0);
if (data == NULL) {
fprintf(stderr, "Couldn't read file\n");
http_response_write_body(&response, "Sorry, error!", 13);
http_response_finish(&response);
return;
}
http_response_write_body(&response, data->data, data->length);
http_response_finish(&response);
free(data);
return;
}
if (!strcmp(request->path, "/dump.mov")) {
fdump_s *data = bscrypt_fdump("./public_www/rolex.mov", 0);
if (data == NULL) {
fprintf(stderr, "Couldn't read file\n");
http_response_write_body(&response, "Sorry, error!", 13);
http_response_finish(&response);
return;
}
http_response_write_body(&response, data->data, data->length);
http_response_finish(&response);
free(data);
return;
}
// HTTP response
http_response_write_body(&response, "Hello World!", 12);
http_response_finish(&response);
}
/*****************************
Print to screen protocol
*/
struct prnt2scrn_protocol_s {
protocol_s protocol;
intptr_t uuid;
};
static void on_data(intptr_t uuid, protocol_s *protocol) {
(void)(protocol);
uint8_t buffer[1024];
ssize_t len;
while ((len = sock_read(uuid, buffer, 1024)) > 0) {
if (len > 0)
fprintf(stderr, "%.*s\n", (int)len, buffer);
}
fprintf(stderr, "returning from on_data\n");
// sock_write(uuid, "HTTP/1.1 100 Continue\r\n\r\n", 25);
}
static void on_close(protocol_s *protocol) {
fprintf(stderr, "Connection closed %p\n",
(void *)(((struct prnt2scrn_protocol_s *)protocol)->uuid));
free(protocol);
}
static protocol_s *on_open(intptr_t uuid, void *udata) {
(void)(udata);
struct prnt2scrn_protocol_s *prt = malloc(sizeof *prt);
*prt = (struct prnt2scrn_protocol_s){
.protocol.on_data = on_data, .protocol.on_close = on_close, .uuid = uuid};
fprintf(stderr, "New connection %p\n", (void *)uuid);
server_set_timeout(uuid, 10);
return (void *)prt;
}
/*****************************
non-http-dump
*/
static void htpdmp_on_data(intptr_t uuid, protocol_s *protocol) {
(void)(protocol);
uint8_t buffer[1024];
ssize_t len;
while ((len = sock_read(uuid, buffer, 1024)) > 0) {
sock_write(uuid, "HTTP/1.1 200 "
"OK\r\nContent-Length:11\r\nConnection:keep-"
"alive\r\n\r\nHello Dump!",
72);
}
// sock_write(uuid, "HTTP/1.1 100 Continue\r\n\r\n", 25);
}
static protocol_s *htpdmp_on_open(intptr_t uuid, void *udata) {
(void)(udata);
protocol_s *prt = malloc(sizeof *prt);
*prt = (protocol_s){.on_data = htpdmp_on_data,
.on_close = (void (*)(protocol_s *))free};
server_set_timeout(uuid, 10);
return (void *)prt;
}
/*****************************
Environment details
*/
#if defined(__linux__) // My Linux machine has a slow file system issue.
static const char *public_folder = NULL; // "./public_www";
#else
static const char *public_folder = "./public_www";
#endif
#ifndef THREAD_COUNT
#define THREAD_COUNT 8
#endif
/*****************************
The main function
*/
#define HTTP_WEBSOCKET_TEST() \
server_listen(.port = "5000", .on_open = htpdmp_on_open); \
if (http1_listen("3000", NULL, .on_request = on_request, \
.public_folder = public_folder, .log_static = 1)) \
perror("Couldn't initiate HTTP service"), exit(1); \
server_run(.threads = THREAD_COUNT);
#endif