Skip to content
Snippets Groups Projects
proxy.c 4.5 KiB
Newer Older
// 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);
}

static void setopt(struct mg_server *s, const char *opt, const char *val) {
  const char *err_msg = mg_set_option(s, opt, val);
  if (err_msg != NULL) {
    elog(1, "Error setting [%s]: [%s]", opt, err_msg);
  }
}

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);
  setopt(s_proxy_server, "listening_port", s_proxy_port);
  setopt(s_proxy_server, "ssl_certificate", s_cert);
  setopt(s_proxy_server, "ssl_ca_certificate", s_ca_cert);
  setopt(s_proxy_server, "hexdump_file", "/dev/stdout");
  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);
  setopt(s_sse_server, "listening_port", s_sse_port);
  setopt(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;