mirror of
https://github.com/lammertb/libhttp.git
synced 2025-04-19 11:02:13 +03:00
269 lines
8.6 KiB
C
269 lines
8.6 KiB
C
// Copyright (c) 2004-2012 Sergey Lyubka
|
|
// This file is a part of civetweb project, http://github.com/bel2125/civetweb
|
|
//
|
|
// v 0.1 Contributed by William Greathouse 9-Sep-2013
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "civetweb.h"
|
|
|
|
// simple structure for keeping track of websocket connection
|
|
struct ws_connection {
|
|
struct httplib_connection *conn;
|
|
int update;
|
|
int closing;
|
|
};
|
|
|
|
// time base and structure periodic updates to client for demo
|
|
#define BASETIME 100000 /* 0.1 seconds */
|
|
struct progress {
|
|
int limit;
|
|
int increment;
|
|
int period;
|
|
int value;
|
|
};
|
|
|
|
// up to 16 independent client connections
|
|
#define CONNECTIONS 16
|
|
static struct ws_connection ws_conn[CONNECTIONS];
|
|
|
|
|
|
// ws_server_thread()
|
|
// Simple demo server thread. Sends periodic updates to connected clients
|
|
static void *ws_server_thread(void *parm)
|
|
{
|
|
int wsd = (long)parm;
|
|
struct httplib_connection *conn = ws_conn[wsd].conn;
|
|
int timer = 0;
|
|
char tstr[32];
|
|
int i;
|
|
struct progress meter[] = {
|
|
/* first meter 0 to 1000, by 5 every 0.1 second */
|
|
{ 1000, 5, 1, 0 },
|
|
/* second meter 0 to 500, by 10 every 0.5 second */
|
|
{ 500, 10, 5, 0 },
|
|
/* third meter 0 to 100, by 10 every 1.0 second */
|
|
{ 100, 10, 10, 0},
|
|
/* end of list */
|
|
{ 0, 0, 0, 0}
|
|
};
|
|
|
|
fprintf(stderr, "ws_server_thread %d\n", wsd);
|
|
|
|
/* Send initial meter updates */
|
|
for (i=0; meter[i].period != 0; i++) {
|
|
if (meter[i].value >= meter[i].limit)
|
|
meter[i].value = 0;
|
|
if (meter[i].value >= meter[i].limit)
|
|
meter[i].value = meter[i].limit;
|
|
sprintf(tstr, "meter%d:%d,%d", i+1,
|
|
meter[i].value, meter[i].limit);
|
|
httplib_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, tstr, strlen(tstr));
|
|
}
|
|
|
|
/* While the connection is open, send periodic updates */
|
|
while(!ws_conn[wsd].closing) {
|
|
usleep(100000); /* 0.1 second */
|
|
timer++;
|
|
|
|
/* Send meter updates */
|
|
if (ws_conn[wsd].update) {
|
|
for (i=0; meter[i].period != 0; i++) {
|
|
if (timer%meter[i].period == 0) {
|
|
if (meter[i].value >= meter[i].limit)
|
|
meter[i].value = 0;
|
|
else
|
|
meter[i].value += meter[i].increment;
|
|
if (meter[i].value >= meter[i].limit)
|
|
meter[i].value = meter[i].limit;
|
|
// if we are closing, server should not send new data
|
|
if (!ws_conn[wsd].closing) {
|
|
sprintf(tstr, "meter%d:%d,%d", i+1,
|
|
meter[i].value, meter[i].limit);
|
|
httplib_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, tstr, strlen(tstr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Send periodic PING to assure websocket remains connected, except if we are closing */
|
|
if (timer%100 == 0 && !ws_conn[wsd].closing)
|
|
httplib_websocket_write(conn, WEBSOCKET_OPCODE_PING, NULL, 0);
|
|
}
|
|
|
|
fprintf(stderr, "ws_server_thread %d exiting\n", wsd);
|
|
|
|
// reset connection information to allow reuse by new client
|
|
ws_conn[wsd].conn = NULL;
|
|
ws_conn[wsd].update = 0;
|
|
ws_conn[wsd].closing = 2;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// websocket_connect_handler()
|
|
// On new client connection, find next available server connection and store
|
|
// new connection information. If no more server connections are available
|
|
// tell civetweb to not accept the client request.
|
|
static int websocket_connect_handler(const struct httplib_connection *conn)
|
|
{
|
|
int i;
|
|
|
|
fprintf(stderr, "connect handler\n");
|
|
|
|
for(i=0; i < CONNECTIONS; ++i) {
|
|
if (ws_conn[i].conn == NULL) {
|
|
fprintf(stderr, "...prep for server %d\n", i);
|
|
ws_conn[i].conn = (struct httplib_connection *)conn;
|
|
ws_conn[i].closing = 0;
|
|
ws_conn[i].update = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (i >= CONNECTIONS) {
|
|
fprintf(stderr, "Refused connection: Max connections exceeded\n");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// websocket_ready_handler()
|
|
// Once websocket negotiation is complete, start a server for the connection
|
|
static void websocket_ready_handler(struct httplib_connection *conn)
|
|
{
|
|
int i;
|
|
|
|
fprintf(stderr, "ready handler\n");
|
|
|
|
for(i=0; i < CONNECTIONS; ++i) {
|
|
if (ws_conn[i].conn == conn) {
|
|
fprintf(stderr, "...start server %d\n", i);
|
|
httplib_start_thread(ws_server_thread, (void *)(long)i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// websocket_close_handler()
|
|
// When websocket is closed, tell the associated server to shut down
|
|
static void websocket_close_handler(struct httplib_connection *conn)
|
|
{
|
|
int i;
|
|
|
|
//fprintf(stderr, "close handler\n"); /* called for every close, not just websockets */
|
|
|
|
for(i=0; i < CONNECTIONS; ++i) {
|
|
if (ws_conn[i].conn == conn) {
|
|
fprintf(stderr, "...close server %d\n", i);
|
|
ws_conn[i].closing = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Arguments:
|
|
// flags: first byte of websocket frame, see websocket RFC,
|
|
// http://tools.ietf.org/html/rfc6455, section 5.2
|
|
// data, data_len: payload data. Mask, if any, is already applied.
|
|
static int websocket_data_handler(struct httplib_connection *conn, int flags, char *data, size_t data_len)
|
|
{
|
|
int i;
|
|
int wsd;
|
|
|
|
for(i=0; i < CONNECTIONS; ++i) {
|
|
if (ws_conn[i].conn == conn) {
|
|
wsd = i;
|
|
break;
|
|
}
|
|
}
|
|
if (i >= CONNECTIONS) {
|
|
fprintf(stderr, "Received websocket data from unknown connection\n");
|
|
return 1;
|
|
}
|
|
|
|
if (flags & 0x80) {
|
|
flags &= 0x7f;
|
|
switch (flags) {
|
|
case WEBSOCKET_OPCODE_CONTINUATION:
|
|
fprintf(stderr, "CONTINUATION...\n");
|
|
break;
|
|
case WEBSOCKET_OPCODE_TEXT:
|
|
fprintf(stderr, "TEXT: %-.*s\n", (int)data_len, data);
|
|
/*** interpret data as commands here ***/
|
|
if (strncmp("update on", data, data_len)== 0) {
|
|
/* turn on updates */
|
|
ws_conn[wsd].update = 1;
|
|
/* echo back */
|
|
httplib_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
|
|
} else if (strncmp("update off", data, data_len)== 0) {
|
|
/* turn off updates */
|
|
ws_conn[wsd].update = 0;
|
|
/* echo back */
|
|
httplib_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
|
|
}
|
|
break;
|
|
case WEBSOCKET_OPCODE_BINARY:
|
|
fprintf(stderr, "BINARY...\n");
|
|
break;
|
|
case WEBSOCKET_OPCODE_CONNECTION_CLOSE:
|
|
fprintf(stderr, "CLOSE...\n");
|
|
/* If client initiated close, respond with close message in acknowlegement */
|
|
if (!ws_conn[wsd].closing) {
|
|
httplib_websocket_write(conn, WEBSOCKET_OPCODE_CONNECTION_CLOSE, data, data_len);
|
|
ws_conn[wsd].closing = 1; /* we should not send addional messages when close requested/acknowledged */
|
|
}
|
|
return 0; /* time to close the connection */
|
|
break;
|
|
case WEBSOCKET_OPCODE_PING:
|
|
/* client sent PING, respond with PONG */
|
|
httplib_websocket_write(conn, WEBSOCKET_OPCODE_PONG, data, data_len);
|
|
break;
|
|
case WEBSOCKET_OPCODE_PONG:
|
|
/* received PONG to our PING, no action */
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown flags: %02x\n", flags);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1; /* keep connection open */
|
|
}
|
|
|
|
|
|
int main(void)
|
|
{
|
|
char server_name[40];
|
|
struct httplib_context *ctx;
|
|
struct httplib_callbacks callbacks;
|
|
const char *options[] = {
|
|
"listening_ports", "8080",
|
|
"document_root", "docroot",
|
|
NULL
|
|
};
|
|
|
|
/* get simple greeting for the web server */
|
|
snprintf(server_name, sizeof(server_name), "Civetweb websocket server v. %s", httplib_version());
|
|
|
|
memset(&callbacks, 0, sizeof(callbacks));
|
|
callbacks.websocket_connect = websocket_connect_handler;
|
|
callbacks.websocket_ready = websocket_ready_handler;
|
|
callbacks.websocket_data = websocket_data_handler;
|
|
callbacks.connection_close = websocket_close_handler;
|
|
|
|
ctx = httplib_start(&callbacks, NULL, options);
|
|
|
|
/* show the greeting and some basic information */
|
|
printf("%s started on port(s) %s with web root [%s]\n",
|
|
server_name, httplib_get_option(ctx, "listening_ports"),
|
|
httplib_get_option(ctx, "document_root"));
|
|
|
|
getchar(); // Wait until user hits "enter"
|
|
httplib_stop(ctx);
|
|
|
|
return 0;
|
|
}
|