Merge branch 'sample-http-server'
authorAzat Khuzhin <a3at.mail@gmail.com>
Tue, 13 Nov 2018 08:10:25 +0000 (11:10 +0300)
committerAzat Khuzhin <azat@libevent.org>
Sat, 2 Feb 2019 12:18:03 +0000 (15:18 +0300)
Some improvements for http-server sample:
- getopt
- persistent port via -p option
- IOCP for win32 via -I
- disable buffering
- enable debug logging via -v/EVENT_DEBUG_LOGGING_ALL
- cleanup (by signal and separate error path on errors)

* sample-http-server:
  s/http-server: graceful cleanup
  s/http-server: enable debug logging if EVENT_DEBUG_LOGGING_ALL env isset
  s/http-server: turn off buffering (otherwise do output on win32)
  s/http-server: add an option to use IOCP
  s/http-server: add options (for persistent port)

Refs: #709
(cherry picked from commit 9a4b8ec1b64fab27544f154076261afdf1efac07)

CMakeLists.txt
sample/http-server.c

index a82d6f57ed1b309bee0c213e17ee31d0d59d3f48..d865160930bd8c0cd822a55331a3e287433707cd 100644 (file)
@@ -888,7 +888,6 @@ if (NOT EVENT__DISABLE_SAMPLES)
         event-read-fifo
         hello-world
         signal-test
-        http-server
         http-connect
         time-test)
 
@@ -905,7 +904,13 @@ if (NOT EVENT__DISABLE_SAMPLES)
                         sample/le-proxy.c)
     endif()
 
-    add_sample_prog(OFF dns-example sample/dns-example.c ${WIN32_GETOPT})
+    set(SAMPLES_WOPT
+        dns-example
+        http-server
+    )
+    foreach (SAMPLE ${SAMPLES_WOPT})
+        add_sample_prog(OFF ${SAMPLE} sample/${SAMPLE}.c ${WIN32_GETOPT})
+    endforeach()
 endif()
 
 #
index 579feea6e3e17816bc9829de31d94fbb88989167..bb067fb89bc739de2b97a6b134511a62e826e5cc 100644 (file)
@@ -20,6 +20,7 @@
 #include <winsock2.h>
 #include <ws2tcpip.h>
 #include <windows.h>
+#include <getopt.h>
 #include <io.h>
 #include <fcntl.h>
 #ifndef S_ISDIR
 #else
 #include <sys/stat.h>
 #include <sys/socket.h>
-#include <signal.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <dirent.h>
 #endif
+#include <signal.h>
 
 #include <event2/event.h>
 #include <event2/http.h>
 #include <event2/util.h>
 #include <event2/keyvalq_struct.h>
 
+#ifdef _WIN32
+#include <event2/thread.h>
+#endif
+
 #ifdef EVENT__HAVE_NETINET_IN_H
 #include <netinet/in.h>
 # ifdef _XOPEN_SOURCE_EXTENDED
@@ -86,6 +91,13 @@ static const struct table_entry {
        { NULL, NULL },
 };
 
+struct options
+{
+       int port;
+       int iocp;
+       int verbose;
+};
+
 /* Try to guess a good content-type for 'path' */
 static const char *
 guess_content_type(const char *path)
@@ -320,43 +332,93 @@ done:
                evbuffer_free(evb);
 }
 
+static struct options
+parse_opts(int argc, char **argv)
+{
+       struct options o;
+       int opt;
+
+       memset(&o, 0, sizeof(o));
+
+       while ((opt = getopt(argc, argv, "p:Iv")) != -1) {
+               switch (opt) {
+                       case 'p': o.port = atoi(optarg); break;
+                       case 'I': o.iocp = 1; break;
+                       case 'v': ++o.verbose; break;
+                       default : fprintf(stderr, "Unknown option %c\n", opt); break;
+               }
+       }
+
+       if (optind >= argc || (argc-optind) > 1) {
+               fprintf(stdout, "Syntax: %s <docroot>\n", argv[0]);
+               exit(1);
+       }
+
+       return o;
+}
+
 static void
-syntax(void)
+do_term(int sig, short events, void *arg)
 {
-       fprintf(stdout, "Syntax: http-server <docroot>\n");
+       struct event_base *base = arg;
+       event_base_loopbreak(base);
+       fprintf(stderr, "Got %i, Terminating\n", sig);
 }
 
 int
 main(int argc, char **argv)
 {
-       struct event_base *base;
-       struct evhttp *http;
-       struct evhttp_bound_socket *handle;
+       struct event_config *cfg = NULL;
+       struct event_base *base = NULL;
+       struct evhttp *http = NULL;
+       struct evhttp_bound_socket *handle = NULL;
+       struct event *term = NULL;
+       struct options o = parse_opts(argc, argv);
+       int ret = 0;
 
-       ev_uint16_t port = 0;
 #ifdef _WIN32
-       WSADATA WSAData;
-       WSAStartup(0x101, &WSAData);
+       {
+               WORD wVersionRequested;
+               WSADATA wsaData;
+               wVersionRequested = MAKEWORD(2, 2);
+               WSAStartup(wVersionRequested, &wsaData);
+       }
 #else
-       if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
-               return (1);
+       if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
+               ret = 1;
+               goto err;
+       }
 #endif
-       if (argc < 2) {
-               syntax();
-               return 1;
+
+       setbuf(stdout, NULL);
+       setbuf(stderr, NULL);
+
+       /** Read env like in regress" */
+       if (o.verbose || getenv("EVENT_DEBUG_LOGGING_ALL"))
+               event_enable_debug_logging(EVENT_DBG_ALL);
+
+       cfg = event_config_new();
+#ifdef _WIN32
+       if (o.iocp) {
+               evthread_use_windows_threads();
+               event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
+               event_config_set_num_cpus_hint(cfg, 8);
        }
+#endif
 
-       base = event_base_new();
+       base = event_base_new_with_config(cfg);
        if (!base) {
                fprintf(stderr, "Couldn't create an event_base: exiting\n");
-               return 1;
+               ret = 1;
        }
+       event_config_free(cfg);
+       cfg = NULL;
 
        /* Create a new evhttp object to handle requests. */
        http = evhttp_new(base);
        if (!http) {
                fprintf(stderr, "couldn't create evhttp. Exiting.\n");
-               return 1;
+               ret = 1;
        }
 
        /* The /dump URI will dump all requests to stdout and say 200 ok. */
@@ -367,11 +429,10 @@ main(int argc, char **argv)
        evhttp_set_gencb(http, send_document_cb, argv[1]);
 
        /* Now we tell the evhttp what port to listen on */
-       handle = evhttp_bind_socket_with_handle(http, "0.0.0.0", port);
+       handle = evhttp_bind_socket_with_handle(http, "0.0.0.0", o.port);
        if (!handle) {
-               fprintf(stderr, "couldn't bind to port %d. Exiting.\n",
-                   (int)port);
-               return 1;
+               fprintf(stderr, "couldn't bind to port %d. Exiting.\n", o.port);
+               ret = 1;
        }
 
        {
@@ -387,7 +448,7 @@ main(int argc, char **argv)
                memset(&ss, 0, sizeof(ss));
                if (getsockname(fd, (struct sockaddr *)&ss, &socklen)) {
                        perror("getsockname() failed");
-                       return 1;
+                       ret = 1;
                }
                if (ss.ss_family == AF_INET) {
                        got_port = ntohs(((struct sockaddr_in*)&ss)->sin_port);
@@ -398,7 +459,7 @@ main(int argc, char **argv)
                } else {
                        fprintf(stderr, "Weird address family %d\n",
                            ss.ss_family);
-                       return 1;
+                       ret = 1;
                }
                addr = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf,
                    sizeof(addrbuf));
@@ -408,11 +469,31 @@ main(int argc, char **argv)
                            "http://%s:%d",addr,got_port);
                } else {
                        fprintf(stderr, "evutil_inet_ntop failed\n");
-                       return 1;
+                       ret = 1;
                }
        }
 
+       term = evsignal_new(base, SIGINT, do_term, base);
+       if (!term)
+               goto err;
+       if (event_add(term, NULL))
+               goto err;
+
        event_base_dispatch(base);
 
-       return 0;
+#ifdef _WIN32
+       WSACleanup();
+#endif
+
+err:
+       if (cfg)
+               event_config_free(cfg);
+       if (http)
+               evhttp_free(http);
+       if (term)
+               event_free(term);
+       if (base)
+               event_base_free(base);
+
+       return ret;
 }