proxy_server.c
Go to the documentation of this file.
00001 // Copyright (c) 2014 Cesanta Software Limited
00002 // All rights reserved
00003 //
00004 // To build and run this example:
00005 //    git clone https://github.com/cesanta/net_skeleton.git
00006 //    git clone https://github.com/cesanta/mongoose.git
00007 //    cd mongoose/examples
00008 //    make proxy
00009 //    ./proxy
00010 //
00011 //  Configure your browser to use localhost:2014 as a proxy for all protocols
00012 //  Then, navigate to https://cesanta.com
00013 
00014 #include <sys/stat.h>
00015 #include <signal.h>
00016 #include <stdarg.h>
00017 #include <stdio.h>
00018 #include <stdlib.h>
00019 #include <string.h>
00020 #include <time.h>
00021 
00022 #ifdef _WIN32
00023 #define sleep(x) Sleep((x) * 1000)
00024 #else
00025 #include <unistd.h>
00026 #endif
00027 
00028 #include "mongoose.h"
00029 
00030 static int s_received_signal = 0;
00031 static struct mg_server *s_server = NULL;
00032 
00033 #define SSE_CONNECTION              ((void *) 1)
00034 
00035 static void elog(int do_exit, const char *fmt, ...) {
00036   va_list ap;
00037   va_start(ap, fmt);
00038   vfprintf(stderr, fmt, ap);
00039   va_end(ap);
00040   fputc('\n', stderr);
00041   if (do_exit) exit(EXIT_FAILURE);
00042 }
00043 
00044 static void signal_handler(int sig_num) {
00045   signal(sig_num, signal_handler);
00046   s_received_signal = sig_num;
00047 }
00048 
00049 static int sse_push(struct mg_connection *conn, enum mg_event ev) {
00050   if (ev == MG_POLL && conn->connection_param == SSE_CONNECTION) {
00051     mg_printf(conn, "data: %s\r\n\r\n", (const char *) conn->callback_param);
00052   }
00053   return MG_TRUE;
00054 }
00055 
00056 static void *sse_pusher_thread_func(void *param) {
00057   while (s_received_signal == 0) {
00058     mg_wakeup_server_ex(s_server, sse_push, "%lu %s",
00059                         (unsigned long) time(NULL), (const char *) param);
00060     sleep(1);
00061   }
00062   return NULL;
00063 }
00064 
00065 // Return: 1 if regular file, 2 if directory, 0 if not found
00066 static int exists(const char *path) {
00067   struct stat st;
00068   return stat(path, &st) != 0 ? 0 : S_ISDIR(st.st_mode)  == 0 ? 1 : 2;
00069 }
00070 
00071 // Return: 1 if regular file, 2 if directory, 0 if not found
00072 static int is_local_file(const char *uri, char *path, size_t path_len) {
00073   snprintf(path, path_len, "%s/%s",
00074            mg_get_option(s_server, "document_root"), uri);
00075   return exists(path);
00076 }
00077 
00078 static int try_to_serve_locally(struct mg_connection *conn) {
00079   char path[500], buf[2000];
00080   int n, res;
00081   FILE *fp = NULL;
00082 
00083   if ((res = is_local_file(conn->uri, path, sizeof(path))) == 2) {
00084     strncat(path, "/index.html", sizeof(path) - strlen(path) - 1);
00085     res = exists(path);
00086     printf("PATH: [%s]\n", path);
00087   }
00088   if (res == 0) return MG_FALSE;
00089 
00090   if ((fp = fopen(path, "rb")) != NULL) {
00091     printf("Serving [%s] locally \n", path);
00092     mg_send_header(conn, "Connection", "close");
00093     mg_send_header(conn, "Content-Type", mg_get_mime_type(path, "text/plain"));
00094     while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
00095       mg_send_data(conn, buf, n);
00096     }
00097     mg_send_data(conn, "", 0);
00098     fclose(fp);
00099   }
00100   return fp == NULL ? MG_FALSE : MG_TRUE;
00101 }
00102 
00103 static int is_resource_present_locally(const char *uri) {
00104   char path[500];
00105   return is_local_file(uri, path, sizeof(path)) || strcmp(uri, "/api/sse") == 0;
00106 }
00107 
00108 static int proxy_event_handler(struct mg_connection *conn, enum mg_event ev) {
00109   static const char target_url[] = "http://cesanta.com";
00110   static int target_url_size = sizeof(target_url) - 1;
00111   const char *host;
00112 
00113   switch (ev) {
00114     case MG_REQUEST:
00115       host = mg_get_header(conn, "Host");
00116       printf("[%s] [%s] [%s]\n", conn->request_method, conn->uri,
00117              host == NULL ? "" : host);
00118       if (strstr(conn->uri, "/qqq") != NULL) s_received_signal = SIGTERM;
00119 
00120       // Proxied HTTPS requests use "CONNECT foo.com:443"
00121       // Proxied HTTP requests use "GET http://..... "
00122       // Serve requests for target_url from the local FS.
00123       if (memcmp(conn->uri, target_url, target_url_size) == 0 &&
00124           is_resource_present_locally(conn->uri + target_url_size)) {
00125         conn->uri += target_url_size;   // Leave only path in the URI
00126       }
00127 
00128       if (strcmp(conn->uri, "/api/sse") == 0) {
00129         conn->connection_param = SSE_CONNECTION;
00130         mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n"
00131                   "Content-Type: text/event-stream\r\n"
00132                   "Cache-Control: no-cache\r\n\r\n");
00133         return MG_MORE;
00134       }
00135 
00136       if (host != NULL && strstr(host, "cesanta") != NULL) {
00137         return try_to_serve_locally(conn);
00138       }
00139 
00140       // Enable man-in-the-middle SSL mode for oracle.com
00141       if (!strcmp(conn->request_method, "CONNECT") &&
00142           !strcmp(host, "oracle.com")) {
00143         mg_terminate_ssl(conn, "ssl_cert.pem");  // MUST return MG_MORE after
00144         return MG_MORE;
00145       }
00146 
00147       return MG_FALSE;
00148     case MG_AUTH:
00149       return MG_TRUE;
00150     default:
00151       return MG_FALSE;
00152   }
00153 }
00154 
00155 static void setopt(struct mg_server *s, const char *opt, const char *val) {
00156   const char *err_msg = mg_set_option(s, opt, val);
00157   if (err_msg != NULL) {
00158     elog(1, "Error setting [%s]: [%s]", opt, err_msg);
00159   }
00160 }
00161 
00162 int main(int argc, char *argv[]) {
00163   const char *port = "2014", *dump = NULL, *root = "proxy_web_root";
00164   int i;
00165 
00166   // Parse command line options
00167   for (i = 1; i < argc; i++) {
00168     if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) {
00169       port = argv[++i];
00170     } else if (strcmp(argv[i], "-root") == 0 && i + 1 < argc) {
00171       root = argv[++i];
00172     } else if (strcmp(argv[i], "-dump") == 0 && i + 1 < argc) {
00173       dump = argv[++i];
00174     } else {
00175       elog(1, "Usage: %s [-cert FILE] [-ca_cert FILE] [-port PORT]", argv[0]);
00176     }
00177   }
00178 
00179   signal(SIGTERM, signal_handler);
00180   signal(SIGINT, signal_handler);
00181 
00182   // Create and configure proxy server
00183   s_server = mg_create_server(NULL, &proxy_event_handler);
00184   setopt(s_server, "enable_proxy",        "yes");
00185   setopt(s_server, "document_root",       root);
00186   setopt(s_server, "listening_port",      port);
00187   setopt(s_server, "hexdump_file",        dump);
00188 
00189   // Start two SSE pushing threads
00190   mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_1");
00191   mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_2");
00192 
00193   // Start serving in the main thread
00194   printf("Starting on port %s\n", mg_get_option(s_server, "listening_port"));
00195   while (s_received_signal == 0) {
00196     mg_poll_server(s_server, 1000);
00197   }
00198   printf("Existing on signal %d\n", s_received_signal);
00199   mg_destroy_server(&s_server);
00200 
00201   return EXIT_SUCCESS;
00202 }


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:06