diff --git a/examples/proxy.c b/examples/proxy.c new file mode 100644 index 0000000000000000000000000000000000000000..7d93248ce327942b820717c16e615d6ab43f0873 --- /dev/null +++ b/examples/proxy.c @@ -0,0 +1,140 @@ +// Copyright (c) 2014 Cesanta Software Limited +// All rights reserved +// +// To build and run this example: +// git clone https://github.com/cesanta/net_skeleton.git +// git clone https://github.com/cesanta/mongoose.git +// cd mongoose/examples +// make proxy +// ./proxy # (then point your browser at http://localhost:2014) + +#include "net_skeleton.h" +#include "mongoose.h" + +static int s_received_signal = 0; +static const char *s_ca_cert = NULL; +static const char *s_cert = NULL; +static const char *s_sse_port = "2014"; +static const char *s_proxy_port = "2015"; +static struct mg_server *s_sse_server = NULL; +static struct mg_server *s_proxy_server = NULL; + +static void elog(int do_exit, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); + if (do_exit) exit(EXIT_FAILURE); +} + +static void signal_handler(int sig_num) { + signal(sig_num, signal_handler); + s_received_signal = sig_num; +} + +static int sse_push(struct mg_connection *conn, enum mg_event ev) { + const char *str = (const char *) conn->connection_param; + if (ev == MG_POLL && str != NULL && strcmp(str, "sse") == 0) { + mg_printf(conn, "data: %s\r\n\r\n", (const char *) conn->callback_param); + } + return MG_TRUE; +} + +static void *sse_pusher_thread_func(void *param) { + while (s_received_signal == 0) { + mg_wakeup_server_ex(s_sse_server, sse_push, "%lu %s", + (unsigned long) time(NULL), (const char *) param); + sleep(1); + } + return NULL; +} + +static int sse_handler(struct mg_connection *conn, enum mg_event ev) { + switch (ev) { + case MG_REQUEST: + if (strcmp(conn->uri, "/") == 0) { + mg_printf(conn, "%s", "HTTP/1.1 302 Moved\r\n" + "Location: /sse.html\r\n\r\n"); + return MG_TRUE; + } + if (strcmp(conn->uri, "/api/sse") == 0) { + conn->connection_param = (void *) "sse"; + mg_printf(conn, "%s", + "HTTP/1.0 200 OK\r\n" + "Content-Type: text/event-stream\r\n" + "Cache-Control: no-cache\r\n" + "\r\n"); + return MG_MORE; + } + return MG_FALSE; + case MG_AUTH: + return MG_TRUE; + case MG_POLL: + return MG_FALSE; + default: + return MG_FALSE; + } +} + +static void *serve_thread_func(void *param) { + struct mg_server *server = (struct mg_server *) param; + printf("Starting on port %s\n", mg_get_option(server, "listening_port")); + + while (s_received_signal == 0) { + mg_poll_server(server, 1000); + } + + return NULL; +} + +static void show_usage_and_exit(const char *prog) { + elog(1, "Usage: %s [-ca_cert FILE] [-sse_port PORT] [-proxy_port PORT]", + prog); + exit(EXIT_FAILURE); +} + +int main(int argc, char *argv[]) { + int i; + + // Parse command line options + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-ca_cert") == 0 && i + 1 < argc) { + s_ca_cert = argv[++i]; + } else if (strcmp(argv[i], "-cert") == 0 && i + 1 < argc) { + s_cert = argv[++i]; + } else if (strcmp(argv[i], "-sse_port") == 0 && i + 1 < argc) { + s_sse_port = argv[++i]; + } else if (strcmp(argv[i], "-proxy_port") == 0 && i + 1 < argc) { + s_proxy_port = argv[++i]; + } else { + show_usage_and_exit(argv[0]); + } + } + + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); + + // Create, configure and start proxy server in a separate thread + s_proxy_server = mg_create_server(NULL, &sse_handler); + mg_set_option(s_proxy_server, "listening_port", s_proxy_port); + mg_set_option(s_proxy_server, "ssl_certificate", s_cert); + mg_set_option(s_proxy_server, "ssl_ca_certificate", s_ca_cert); + mg_start_thread(serve_thread_func, s_proxy_server); + + // Create, configure and start SSE server and SSE pusher threads + // Start two SSE pushing threads + // Serve SSE server in the main thread + s_sse_server = mg_create_server(NULL, &sse_handler); + mg_set_option(s_sse_server, "listening_port", s_sse_port); + mg_set_option(s_sse_server, "document_root", "."); + mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_1"); + mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_2"); + serve_thread_func(s_sse_server); + + printf("Existing on signal %d\n", s_received_signal); + mg_destroy_server(&s_sse_server); + mg_destroy_server(&s_proxy_server); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/sse.html b/examples/sse.html new file mode 100644 index 0000000000000000000000000000000000000000..f4f4affbe91bc601026529e966342416c665adec --- /dev/null +++ b/examples/sse.html @@ -0,0 +1,21 @@ +<html> + <head> + <script type="text/javascript"> + window.onload = function() { + var es = new EventSource("/api/sse"); + var div = document.getElementById('events'); + es.onmessage = function(ev) { + var el = document.createElement('div'); + el.innerHTML = 'sse message: ' + ev.data; + div.appendChild(el); + // Keep only last 5 messages in the list + while (div.childNodes.length > 5) div.removeChild(div.firstChild); + }; + }; + </script> + </head> + <body> + <h1>SSE pushes, done by separate threads at random times:</h1> + <div id="events"></div> + </body> +</html> \ No newline at end of file