xref: /libevent-2.1.12/sample/http-server.c (revision 4a6f1ccf)
14e794d5dSNick Mathewson /*
24e794d5dSNick Mathewson   A trivial static http webserver using Libevent's evhttp.
34e794d5dSNick Mathewson 
44e794d5dSNick Mathewson   This is not the best code in the world, and it does some fairly stupid stuff
54e794d5dSNick Mathewson   that you would never want to do in a production webserver. Caveat hackor!
64e794d5dSNick Mathewson 
74e794d5dSNick Mathewson  */
84e794d5dSNick Mathewson 
995e2455cSNick Mathewson /* Compatibility for possible missing IPv6 declarations */
1095e2455cSNick Mathewson #include "../util-internal.h"
1195e2455cSNick Mathewson 
124e794d5dSNick Mathewson #include <stdio.h>
134e794d5dSNick Mathewson #include <stdlib.h>
144e794d5dSNick Mathewson #include <string.h>
154e794d5dSNick Mathewson 
164e794d5dSNick Mathewson #include <sys/types.h>
1718d03276SNick Mathewson #include <sys/stat.h>
184e794d5dSNick Mathewson 
199f560bfaSNick Mathewson #ifdef _WIN32
204e794d5dSNick Mathewson #include <winsock2.h>
2118d03276SNick Mathewson #include <ws2tcpip.h>
224e794d5dSNick Mathewson #include <windows.h>
23b6309bccSAzat Khuzhin #include <getopt.h>
2418d03276SNick Mathewson #include <io.h>
2518d03276SNick Mathewson #include <fcntl.h>
2670be7d17SPeter Rosin #ifndef S_ISDIR
2770be7d17SPeter Rosin #define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
2870be7d17SPeter Rosin #endif
297c4da937SAzat Khuzhin #else /* !_WIN32 */
304e794d5dSNick Mathewson #include <sys/stat.h>
314e794d5dSNick Mathewson #include <sys/socket.h>
324e794d5dSNick Mathewson #include <fcntl.h>
334e794d5dSNick Mathewson #include <unistd.h>
344e794d5dSNick Mathewson #include <dirent.h>
357c4da937SAzat Khuzhin #endif /* _WIN32 */
36b6309bccSAzat Khuzhin #include <signal.h>
374e794d5dSNick Mathewson 
387c4da937SAzat Khuzhin #ifdef EVENT__HAVE_SYS_UN_H
397c4da937SAzat Khuzhin #include <sys/un.h>
407c4da937SAzat Khuzhin #endif
417c4da937SAzat Khuzhin #ifdef EVENT__HAVE_AFUNIX_H
427c4da937SAzat Khuzhin #include <afunix.h>
437c4da937SAzat Khuzhin #endif
447c4da937SAzat Khuzhin 
454e794d5dSNick Mathewson #include <event2/event.h>
464e794d5dSNick Mathewson #include <event2/http.h>
477c4da937SAzat Khuzhin #include <event2/listener.h>
484e794d5dSNick Mathewson #include <event2/buffer.h>
494e794d5dSNick Mathewson #include <event2/util.h>
504e794d5dSNick Mathewson #include <event2/keyvalq_struct.h>
514e794d5dSNick Mathewson 
52b6309bccSAzat Khuzhin #ifdef _WIN32
53b6309bccSAzat Khuzhin #include <event2/thread.h>
547c4da937SAzat Khuzhin #endif /* _WIN32 */
55b6309bccSAzat Khuzhin 
5668120d9bSNick Mathewson #ifdef EVENT__HAVE_NETINET_IN_H
57ad66dfd0SNick Mathewson #include <netinet/in.h>
5810c834c4SHarlan Stenn # ifdef _XOPEN_SOURCE_EXTENDED
5910c834c4SHarlan Stenn #  include <arpa/inet.h>
6010c834c4SHarlan Stenn # endif
61ad66dfd0SNick Mathewson #endif
62ad66dfd0SNick Mathewson 
639f560bfaSNick Mathewson #ifdef _WIN32
646810908aSNick Mathewson #ifndef stat
6518d03276SNick Mathewson #define stat _stat
666810908aSNick Mathewson #endif
676810908aSNick Mathewson #ifndef fstat
6818d03276SNick Mathewson #define fstat _fstat
696810908aSNick Mathewson #endif
706810908aSNick Mathewson #ifndef open
7118d03276SNick Mathewson #define open _open
726810908aSNick Mathewson #endif
736810908aSNick Mathewson #ifndef close
7418d03276SNick Mathewson #define close _close
756810908aSNick Mathewson #endif
766810908aSNick Mathewson #ifndef O_RDONLY
7718d03276SNick Mathewson #define O_RDONLY _O_RDONLY
7818d03276SNick Mathewson #endif
797c4da937SAzat Khuzhin #endif /* _WIN32 */
8018d03276SNick Mathewson 
814e794d5dSNick Mathewson char uri_root[512];
824e794d5dSNick Mathewson 
834e794d5dSNick Mathewson static const struct table_entry {
844e794d5dSNick Mathewson 	const char *extension;
854e794d5dSNick Mathewson 	const char *content_type;
864e794d5dSNick Mathewson } content_type_table[] = {
874e794d5dSNick Mathewson 	{ "txt", "text/plain" },
884e794d5dSNick Mathewson 	{ "c", "text/plain" },
894e794d5dSNick Mathewson 	{ "h", "text/plain" },
904e794d5dSNick Mathewson 	{ "html", "text/html" },
914e794d5dSNick Mathewson 	{ "htm", "text/htm" },
924e794d5dSNick Mathewson 	{ "css", "text/css" },
934e794d5dSNick Mathewson 	{ "gif", "image/gif" },
944e794d5dSNick Mathewson 	{ "jpg", "image/jpeg" },
954e794d5dSNick Mathewson 	{ "jpeg", "image/jpeg" },
964e794d5dSNick Mathewson 	{ "png", "image/png" },
974e794d5dSNick Mathewson 	{ "pdf", "application/pdf" },
9823f9a20eSjohnsonlee 	{ "ps", "application/postscript" },
994e794d5dSNick Mathewson 	{ NULL, NULL },
1004e794d5dSNick Mathewson };
1014e794d5dSNick Mathewson 
102*4a6f1ccfSDavid Disseldorp struct options {
103b6309bccSAzat Khuzhin 	int port;
104b6309bccSAzat Khuzhin 	int iocp;
105b6309bccSAzat Khuzhin 	int verbose;
1067c4da937SAzat Khuzhin 
1077c4da937SAzat Khuzhin 	int unlink;
1087c4da937SAzat Khuzhin 	const char *unixsock;
109*4a6f1ccfSDavid Disseldorp 	const char *docroot;
110b6309bccSAzat Khuzhin };
111b6309bccSAzat Khuzhin 
1124e794d5dSNick Mathewson /* Try to guess a good content-type for 'path' */
1134e794d5dSNick Mathewson static const char *
guess_content_type(const char * path)1144e794d5dSNick Mathewson guess_content_type(const char *path)
1154e794d5dSNick Mathewson {
1164e794d5dSNick Mathewson 	const char *last_period, *extension;
1174e794d5dSNick Mathewson 	const struct table_entry *ent;
1184e794d5dSNick Mathewson 	last_period = strrchr(path, '.');
1194e794d5dSNick Mathewson 	if (!last_period || strchr(last_period, '/'))
1204e794d5dSNick Mathewson 		goto not_found; /* no exension */
1214e794d5dSNick Mathewson 	extension = last_period + 1;
1224e794d5dSNick Mathewson 	for (ent = &content_type_table[0]; ent->extension; ++ent) {
1234e794d5dSNick Mathewson 		if (!evutil_ascii_strcasecmp(ent->extension, extension))
1244e794d5dSNick Mathewson 			return ent->content_type;
1254e794d5dSNick Mathewson 	}
1264e794d5dSNick Mathewson 
1274e794d5dSNick Mathewson not_found:
1284e794d5dSNick Mathewson 	return "application/misc";
1294e794d5dSNick Mathewson }
1304e794d5dSNick Mathewson 
1314e794d5dSNick Mathewson /* Callback used for the /dump URI, and for every non-GET request:
1324e794d5dSNick Mathewson  * dumps all information to stdout and gives back a trivial 200 ok */
1334e794d5dSNick Mathewson static void
dump_request_cb(struct evhttp_request * req,void * arg)1344e794d5dSNick Mathewson dump_request_cb(struct evhttp_request *req, void *arg)
1354e794d5dSNick Mathewson {
1364e794d5dSNick Mathewson 	const char *cmdtype;
1374e794d5dSNick Mathewson 	struct evkeyvalq *headers;
1384e794d5dSNick Mathewson 	struct evkeyval *header;
1394e794d5dSNick Mathewson 	struct evbuffer *buf;
1404e794d5dSNick Mathewson 
1414e794d5dSNick Mathewson 	switch (evhttp_request_get_command(req)) {
1424e794d5dSNick Mathewson 	case EVHTTP_REQ_GET: cmdtype = "GET"; break;
1434e794d5dSNick Mathewson 	case EVHTTP_REQ_POST: cmdtype = "POST"; break;
1444e794d5dSNick Mathewson 	case EVHTTP_REQ_HEAD: cmdtype = "HEAD"; break;
1454e794d5dSNick Mathewson 	case EVHTTP_REQ_PUT: cmdtype = "PUT"; break;
1464e794d5dSNick Mathewson 	case EVHTTP_REQ_DELETE: cmdtype = "DELETE"; break;
1474e794d5dSNick Mathewson 	case EVHTTP_REQ_OPTIONS: cmdtype = "OPTIONS"; break;
1484e794d5dSNick Mathewson 	case EVHTTP_REQ_TRACE: cmdtype = "TRACE"; break;
1494e794d5dSNick Mathewson 	case EVHTTP_REQ_CONNECT: cmdtype = "CONNECT"; break;
1504e794d5dSNick Mathewson 	case EVHTTP_REQ_PATCH: cmdtype = "PATCH"; break;
1514e794d5dSNick Mathewson 	default: cmdtype = "unknown"; break;
1524e794d5dSNick Mathewson 	}
1534e794d5dSNick Mathewson 
1544e794d5dSNick Mathewson 	printf("Received a %s request for %s\nHeaders:\n",
1554e794d5dSNick Mathewson 	    cmdtype, evhttp_request_get_uri(req));
1564e794d5dSNick Mathewson 
1574e794d5dSNick Mathewson 	headers = evhttp_request_get_input_headers(req);
1584e794d5dSNick Mathewson 	for (header = headers->tqh_first; header;
1594e794d5dSNick Mathewson 	    header = header->next.tqe_next) {
1604e794d5dSNick Mathewson 		printf("  %s: %s\n", header->key, header->value);
1614e794d5dSNick Mathewson 	}
1624e794d5dSNick Mathewson 
1634e794d5dSNick Mathewson 	buf = evhttp_request_get_input_buffer(req);
1644e794d5dSNick Mathewson 	puts("Input data: <<<");
1654e794d5dSNick Mathewson 	while (evbuffer_get_length(buf)) {
1664e794d5dSNick Mathewson 		int n;
1674e794d5dSNick Mathewson 		char cbuf[128];
168c322c207SGyepi Sam 		n = evbuffer_remove(buf, cbuf, sizeof(cbuf));
1697c11e51eSHarlan Stenn 		if (n > 0)
1707206e8cdSNick Mathewson 			(void) fwrite(cbuf, 1, n, stdout);
1714e794d5dSNick Mathewson 	}
1724e794d5dSNick Mathewson 	puts(">>>");
1734e794d5dSNick Mathewson 
1744e794d5dSNick Mathewson 	evhttp_send_reply(req, 200, "OK", NULL);
1754e794d5dSNick Mathewson }
1764e794d5dSNick Mathewson 
1774e794d5dSNick Mathewson /* This callback gets invoked when we get any http request that doesn't match
1784e794d5dSNick Mathewson  * any other callback.  Like any evhttp server callback, it has a simple job:
1794e794d5dSNick Mathewson  * it must eventually call evhttp_send_error() or evhttp_send_reply().
1804e794d5dSNick Mathewson  */
1814e794d5dSNick Mathewson static void
send_document_cb(struct evhttp_request * req,void * arg)1824e794d5dSNick Mathewson send_document_cb(struct evhttp_request *req, void *arg)
1834e794d5dSNick Mathewson {
1842e9f6655SNick Mathewson 	struct evbuffer *evb = NULL;
185*4a6f1ccfSDavid Disseldorp 	struct options *o = arg;
1864e794d5dSNick Mathewson 	const char *uri = evhttp_request_get_uri(req);
1874e794d5dSNick Mathewson 	struct evhttp_uri *decoded = NULL;
1884e794d5dSNick Mathewson 	const char *path;
1894e794d5dSNick Mathewson 	char *decoded_path;
1904e794d5dSNick Mathewson 	char *whole_path = NULL;
1914e794d5dSNick Mathewson 	size_t len;
1924e794d5dSNick Mathewson 	int fd = -1;
1934e794d5dSNick Mathewson 	struct stat st;
1944e794d5dSNick Mathewson 
1954e794d5dSNick Mathewson 	if (evhttp_request_get_command(req) != EVHTTP_REQ_GET) {
1964e794d5dSNick Mathewson 		dump_request_cb(req, arg);
1974e794d5dSNick Mathewson 		return;
1984e794d5dSNick Mathewson 	}
1994e794d5dSNick Mathewson 
2004e794d5dSNick Mathewson 	printf("Got a GET request for <%s>\n",  uri);
2014e794d5dSNick Mathewson 
2024e794d5dSNick Mathewson 	/* Decode the URI */
2034e794d5dSNick Mathewson 	decoded = evhttp_uri_parse(uri);
2044e794d5dSNick Mathewson 	if (!decoded) {
2054e794d5dSNick Mathewson 		printf("It's not a good URI. Sending BADREQUEST\n");
2064e794d5dSNick Mathewson 		evhttp_send_error(req, HTTP_BADREQUEST, 0);
2074e794d5dSNick Mathewson 		return;
2084e794d5dSNick Mathewson 	}
2094e794d5dSNick Mathewson 
2104e794d5dSNick Mathewson 	/* Let's see what path the user asked for. */
2114e794d5dSNick Mathewson 	path = evhttp_uri_get_path(decoded);
2124e794d5dSNick Mathewson 	if (!path) path = "/";
2134e794d5dSNick Mathewson 
2144e794d5dSNick Mathewson 	/* We need to decode it, to see what path the user really wanted. */
2154e794d5dSNick Mathewson 	decoded_path = evhttp_uridecode(path, 0, NULL);
2167c11e51eSHarlan Stenn 	if (decoded_path == NULL)
2177c11e51eSHarlan Stenn 		goto err;
2184e794d5dSNick Mathewson 	/* Don't allow any ".."s in the path, to avoid exposing stuff outside
2194e794d5dSNick Mathewson 	 * of the docroot.  This test is both overzealous and underzealous:
2204e794d5dSNick Mathewson 	 * it forbids aceptable paths like "/this/one..here", but it doesn't
2214e794d5dSNick Mathewson 	 * do anything to prevent symlink following." */
2224e794d5dSNick Mathewson 	if (strstr(decoded_path, ".."))
2234e794d5dSNick Mathewson 		goto err;
2244e794d5dSNick Mathewson 
225*4a6f1ccfSDavid Disseldorp 	len = strlen(decoded_path)+strlen(o->docroot)+2;
2264e794d5dSNick Mathewson 	if (!(whole_path = malloc(len))) {
2274e794d5dSNick Mathewson 		perror("malloc");
2284e794d5dSNick Mathewson 		goto err;
2294e794d5dSNick Mathewson 	}
230*4a6f1ccfSDavid Disseldorp 	evutil_snprintf(whole_path, len, "%s/%s", o->docroot, decoded_path);
2314e794d5dSNick Mathewson 
2324e794d5dSNick Mathewson 	if (stat(whole_path, &st)<0) {
2334e794d5dSNick Mathewson 		goto err;
2344e794d5dSNick Mathewson 	}
2354e794d5dSNick Mathewson 
2364e794d5dSNick Mathewson 	/* This holds the content we're sending. */
2374e794d5dSNick Mathewson 	evb = evbuffer_new();
2384e794d5dSNick Mathewson 
2394e794d5dSNick Mathewson 	if (S_ISDIR(st.st_mode)) {
2404e794d5dSNick Mathewson 		/* If it's a directory, read the comments and make a little
2414e794d5dSNick Mathewson 		 * index page */
2429f560bfaSNick Mathewson #ifdef _WIN32
24318d03276SNick Mathewson 		HANDLE d;
24418d03276SNick Mathewson 		WIN32_FIND_DATAA ent;
24518d03276SNick Mathewson 		char *pattern;
24618d03276SNick Mathewson 		size_t dirlen;
24718d03276SNick Mathewson #else
2484e794d5dSNick Mathewson 		DIR *d;
2494e794d5dSNick Mathewson 		struct dirent *ent;
25018d03276SNick Mathewson #endif
2514e794d5dSNick Mathewson 		const char *trailing_slash = "";
2524e794d5dSNick Mathewson 
2534e794d5dSNick Mathewson 		if (!strlen(path) || path[strlen(path)-1] != '/')
2544e794d5dSNick Mathewson 			trailing_slash = "/";
2554e794d5dSNick Mathewson 
2569f560bfaSNick Mathewson #ifdef _WIN32
25718d03276SNick Mathewson 		dirlen = strlen(whole_path);
25818d03276SNick Mathewson 		pattern = malloc(dirlen+3);
25918d03276SNick Mathewson 		memcpy(pattern, whole_path, dirlen);
26018d03276SNick Mathewson 		pattern[dirlen] = '\\';
26118d03276SNick Mathewson 		pattern[dirlen+1] = '*';
26218d03276SNick Mathewson 		pattern[dirlen+2] = '\0';
26318d03276SNick Mathewson 		d = FindFirstFileA(pattern, &ent);
26418d03276SNick Mathewson 		free(pattern);
26518d03276SNick Mathewson 		if (d == INVALID_HANDLE_VALUE)
26618d03276SNick Mathewson 			goto err;
26718d03276SNick Mathewson #else
2684e794d5dSNick Mathewson 		if (!(d = opendir(whole_path)))
2694e794d5dSNick Mathewson 			goto err;
27018d03276SNick Mathewson #endif
2714e794d5dSNick Mathewson 
2726d72bdcaSNick Mathewson 		evbuffer_add_printf(evb,
2736d72bdcaSNick Mathewson                     "<!DOCTYPE html>\n"
2746d72bdcaSNick Mathewson                     "<html>\n <head>\n"
2756d72bdcaSNick Mathewson                     "  <meta charset='utf-8'>\n"
2764e794d5dSNick Mathewson 		    "  <title>%s</title>\n"
2776171e1c2SAzat Khuzhin 		    "  <base href='%s%s'>\n"
2784e794d5dSNick Mathewson 		    " </head>\n"
2794e794d5dSNick Mathewson 		    " <body>\n"
2804e794d5dSNick Mathewson 		    "  <h1>%s</h1>\n"
2814e794d5dSNick Mathewson 		    "  <ul>\n",
2824e794d5dSNick Mathewson 		    decoded_path, /* XXX html-escape this. */
2836171e1c2SAzat Khuzhin 		    path, /* XXX html-escape this? */
2844e794d5dSNick Mathewson 		    trailing_slash,
2854e794d5dSNick Mathewson 		    decoded_path /* XXX html-escape this */);
2869f560bfaSNick Mathewson #ifdef _WIN32
28718d03276SNick Mathewson 		do {
28818d03276SNick Mathewson 			const char *name = ent.cFileName;
28918d03276SNick Mathewson #else
2904e794d5dSNick Mathewson 		while ((ent = readdir(d))) {
29118d03276SNick Mathewson 			const char *name = ent->d_name;
29218d03276SNick Mathewson #endif
2934e794d5dSNick Mathewson 			evbuffer_add_printf(evb,
2944e794d5dSNick Mathewson 			    "    <li><a href=\"%s\">%s</a>\n",
29518d03276SNick Mathewson 			    name, name);/* XXX escape this */
2969f560bfaSNick Mathewson #ifdef _WIN32
29713fd242dSNick Mathewson 		} while (FindNextFileA(d, &ent));
29818d03276SNick Mathewson #else
2994e794d5dSNick Mathewson 		}
30018d03276SNick Mathewson #endif
3014e794d5dSNick Mathewson 		evbuffer_add_printf(evb, "</ul></body></html>\n");
3029f560bfaSNick Mathewson #ifdef _WIN32
3036466e88aSNick Mathewson 		FindClose(d);
30418d03276SNick Mathewson #else
3054e794d5dSNick Mathewson 		closedir(d);
30618d03276SNick Mathewson #endif
3074e794d5dSNick Mathewson 		evhttp_add_header(evhttp_request_get_output_headers(req),
3084e794d5dSNick Mathewson 		    "Content-Type", "text/html");
3094e794d5dSNick Mathewson 	} else {
3104e794d5dSNick Mathewson 		/* Otherwise it's a file; add it to the buffer to get
3114e794d5dSNick Mathewson 		 * sent via sendfile */
3124e794d5dSNick Mathewson 		const char *type = guess_content_type(decoded_path);
3134881778cSNick Mathewson 		if ((fd = open(whole_path, O_RDONLY)) < 0) {
3144e794d5dSNick Mathewson 			perror("open");
3154e794d5dSNick Mathewson 			goto err;
3164e794d5dSNick Mathewson 		}
3174e794d5dSNick Mathewson 
3184e794d5dSNick Mathewson 		if (fstat(fd, &st)<0) {
3194e794d5dSNick Mathewson 			/* Make sure the length still matches, now that we
3204e794d5dSNick Mathewson 			 * opened the file :/ */
3214e794d5dSNick Mathewson 			perror("fstat");
3224e794d5dSNick Mathewson 			goto err;
3234e794d5dSNick Mathewson 		}
3244e794d5dSNick Mathewson 		evhttp_add_header(evhttp_request_get_output_headers(req),
3254e794d5dSNick Mathewson 		    "Content-Type", type);
3264e794d5dSNick Mathewson 		evbuffer_add_file(evb, fd, 0, st.st_size);
3274e794d5dSNick Mathewson 	}
3284e794d5dSNick Mathewson 
3294e794d5dSNick Mathewson 	evhttp_send_reply(req, 200, "OK", evb);
3302e9f6655SNick Mathewson 	goto done;
3314e794d5dSNick Mathewson err:
3324e794d5dSNick Mathewson 	evhttp_send_error(req, 404, "Document was not found");
3334e794d5dSNick Mathewson 	if (fd>=0)
3344e794d5dSNick Mathewson 		close(fd);
3352e9f6655SNick Mathewson done:
3364e794d5dSNick Mathewson 	if (decoded)
3374e794d5dSNick Mathewson 		evhttp_uri_free(decoded);
3384e794d5dSNick Mathewson 	if (decoded_path)
3394e794d5dSNick Mathewson 		free(decoded_path);
3404e794d5dSNick Mathewson 	if (whole_path)
3414e794d5dSNick Mathewson 		free(whole_path);
3422e9f6655SNick Mathewson 	if (evb)
3432e9f6655SNick Mathewson 		evbuffer_free(evb);
3444e794d5dSNick Mathewson }
3454e794d5dSNick Mathewson 
3467c4da937SAzat Khuzhin static void
print_usage(FILE * out,const char * prog,int exit_code)3477c4da937SAzat Khuzhin print_usage(FILE *out, const char *prog, int exit_code)
3487c4da937SAzat Khuzhin {
349*4a6f1ccfSDavid Disseldorp 	fprintf(out,
350*4a6f1ccfSDavid Disseldorp 		"Syntax: %s [ OPTS ] <docroot>\n"
351*4a6f1ccfSDavid Disseldorp 		" -p      - port\n"
352*4a6f1ccfSDavid Disseldorp 		" -U      - bind to unix socket\n"
353*4a6f1ccfSDavid Disseldorp 		" -u      - unlink unix socket before bind\n"
354*4a6f1ccfSDavid Disseldorp 		" -I      - IOCP\n"
355*4a6f1ccfSDavid Disseldorp 		" -v      - verbosity, enables libevent debug logging too\n", prog);
3567c4da937SAzat Khuzhin 	exit(exit_code);
3577c4da937SAzat Khuzhin }
358b6309bccSAzat Khuzhin static struct options
parse_opts(int argc,char ** argv)359b6309bccSAzat Khuzhin parse_opts(int argc, char **argv)
3604e794d5dSNick Mathewson {
361b6309bccSAzat Khuzhin 	struct options o;
362b6309bccSAzat Khuzhin 	int opt;
363b6309bccSAzat Khuzhin 
364b6309bccSAzat Khuzhin 	memset(&o, 0, sizeof(o));
365b6309bccSAzat Khuzhin 
3667c4da937SAzat Khuzhin 	while ((opt = getopt(argc, argv, "hp:U:uIv")) != -1) {
367b6309bccSAzat Khuzhin 		switch (opt) {
368b6309bccSAzat Khuzhin 			case 'p': o.port = atoi(optarg); break;
3697c4da937SAzat Khuzhin 			case 'U': o.unixsock = optarg; break;
3707c4da937SAzat Khuzhin 			case 'u': o.unlink = 1; break;
371b6309bccSAzat Khuzhin 			case 'I': o.iocp = 1; break;
372b6309bccSAzat Khuzhin 			case 'v': ++o.verbose; break;
3737c4da937SAzat Khuzhin 			case 'h': print_usage(stdout, argv[0], 0); break;
374b6309bccSAzat Khuzhin 			default : fprintf(stderr, "Unknown option %c\n", opt); break;
375b6309bccSAzat Khuzhin 		}
376b6309bccSAzat Khuzhin 	}
377b6309bccSAzat Khuzhin 
378b6309bccSAzat Khuzhin 	if (optind >= argc || (argc - optind) > 1) {
3797c4da937SAzat Khuzhin 		print_usage(stdout, argv[0], 1);
380b6309bccSAzat Khuzhin 	}
381*4a6f1ccfSDavid Disseldorp 	o.docroot = argv[optind];
382b6309bccSAzat Khuzhin 
383b6309bccSAzat Khuzhin 	return o;
384b6309bccSAzat Khuzhin }
385b6309bccSAzat Khuzhin 
386b6309bccSAzat Khuzhin static void
do_term(int sig,short events,void * arg)387b6309bccSAzat Khuzhin do_term(int sig, short events, void *arg)
388b6309bccSAzat Khuzhin {
389b6309bccSAzat Khuzhin 	struct event_base *base = arg;
390b6309bccSAzat Khuzhin 	event_base_loopbreak(base);
391b6309bccSAzat Khuzhin 	fprintf(stderr, "Got %i, Terminating\n", sig);
3924e794d5dSNick Mathewson }
3934e794d5dSNick Mathewson 
3947c4da937SAzat Khuzhin static int
display_listen_sock(struct evhttp_bound_socket * handle)3957c4da937SAzat Khuzhin display_listen_sock(struct evhttp_bound_socket *handle)
3967c4da937SAzat Khuzhin {
3977c4da937SAzat Khuzhin 	struct sockaddr_storage ss;
3987c4da937SAzat Khuzhin 	evutil_socket_t fd;
3997c4da937SAzat Khuzhin 	ev_socklen_t socklen = sizeof(ss);
4007c4da937SAzat Khuzhin 	char addrbuf[128];
4017c4da937SAzat Khuzhin 	void *inaddr;
4027c4da937SAzat Khuzhin 	const char *addr;
4037c4da937SAzat Khuzhin 	int got_port = -1;
4047c4da937SAzat Khuzhin 
4057c4da937SAzat Khuzhin 	fd = evhttp_bound_socket_get_fd(handle);
4067c4da937SAzat Khuzhin 	memset(&ss, 0, sizeof(ss));
4077c4da937SAzat Khuzhin 	if (getsockname(fd, (struct sockaddr *)&ss, &socklen)) {
4087c4da937SAzat Khuzhin 		perror("getsockname() failed");
4097c4da937SAzat Khuzhin 		return 1;
4107c4da937SAzat Khuzhin 	}
4117c4da937SAzat Khuzhin 
4127c4da937SAzat Khuzhin 	if (ss.ss_family == AF_INET) {
4137c4da937SAzat Khuzhin 		got_port = ntohs(((struct sockaddr_in*)&ss)->sin_port);
4147c4da937SAzat Khuzhin 		inaddr = &((struct sockaddr_in*)&ss)->sin_addr;
4157c4da937SAzat Khuzhin 	} else if (ss.ss_family == AF_INET6) {
4167c4da937SAzat Khuzhin 		got_port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port);
4177c4da937SAzat Khuzhin 		inaddr = &((struct sockaddr_in6*)&ss)->sin6_addr;
4187c4da937SAzat Khuzhin 	}
4197c4da937SAzat Khuzhin #ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN
4207c4da937SAzat Khuzhin 	else if (ss.ss_family == AF_UNIX) {
4217c4da937SAzat Khuzhin 		printf("Listening on <%s>\n", ((struct sockaddr_un*)&ss)->sun_path);
4227c4da937SAzat Khuzhin 		return 0;
4237c4da937SAzat Khuzhin 	}
4247c4da937SAzat Khuzhin #endif
4257c4da937SAzat Khuzhin 	else {
4267c4da937SAzat Khuzhin 		fprintf(stderr, "Weird address family %d\n",
4277c4da937SAzat Khuzhin 		    ss.ss_family);
4287c4da937SAzat Khuzhin 		return 1;
4297c4da937SAzat Khuzhin 	}
4307c4da937SAzat Khuzhin 
4317c4da937SAzat Khuzhin 	addr = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf,
4327c4da937SAzat Khuzhin 	    sizeof(addrbuf));
4337c4da937SAzat Khuzhin 	if (addr) {
4347c4da937SAzat Khuzhin 		printf("Listening on %s:%d\n", addr, got_port);
4357c4da937SAzat Khuzhin 		evutil_snprintf(uri_root, sizeof(uri_root),
4367c4da937SAzat Khuzhin 		    "http://%s:%d",addr,got_port);
4377c4da937SAzat Khuzhin 	} else {
4387c4da937SAzat Khuzhin 		fprintf(stderr, "evutil_inet_ntop failed\n");
4397c4da937SAzat Khuzhin 		return 1;
4407c4da937SAzat Khuzhin 	}
4417c4da937SAzat Khuzhin 
4427c4da937SAzat Khuzhin 	return 0;
4437c4da937SAzat Khuzhin }
4447c4da937SAzat Khuzhin 
4454e794d5dSNick Mathewson int
main(int argc,char ** argv)4464e794d5dSNick Mathewson main(int argc, char **argv)
4474e794d5dSNick Mathewson {
448b6309bccSAzat Khuzhin 	struct event_config *cfg = NULL;
449b6309bccSAzat Khuzhin 	struct event_base *base = NULL;
450b6309bccSAzat Khuzhin 	struct evhttp *http = NULL;
451b6309bccSAzat Khuzhin 	struct evhttp_bound_socket *handle = NULL;
4527c4da937SAzat Khuzhin 	struct evconnlistener *lev = NULL;
453b6309bccSAzat Khuzhin 	struct event *term = NULL;
454b6309bccSAzat Khuzhin 	struct options o = parse_opts(argc, argv);
455b6309bccSAzat Khuzhin 	int ret = 0;
4564e794d5dSNick Mathewson 
4579f560bfaSNick Mathewson #ifdef _WIN32
458b6309bccSAzat Khuzhin 	{
459b6309bccSAzat Khuzhin 		WORD wVersionRequested;
460b6309bccSAzat Khuzhin 		WSADATA wsaData;
461b6309bccSAzat Khuzhin 		wVersionRequested = MAKEWORD(2, 2);
462b6309bccSAzat Khuzhin 		WSAStartup(wVersionRequested, &wsaData);
4634e794d5dSNick Mathewson 	}
464b6309bccSAzat Khuzhin #else
465b6309bccSAzat Khuzhin 	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
466b6309bccSAzat Khuzhin 		ret = 1;
467b6309bccSAzat Khuzhin 		goto err;
468b6309bccSAzat Khuzhin 	}
469b6309bccSAzat Khuzhin #endif
4704e794d5dSNick Mathewson 
471b6309bccSAzat Khuzhin 	setbuf(stdout, NULL);
472b6309bccSAzat Khuzhin 	setbuf(stderr, NULL);
473b6309bccSAzat Khuzhin 
4747c4da937SAzat Khuzhin 	/** Read env like in regress */
475b6309bccSAzat Khuzhin 	if (o.verbose || getenv("EVENT_DEBUG_LOGGING_ALL"))
476b6309bccSAzat Khuzhin 		event_enable_debug_logging(EVENT_DBG_ALL);
477b6309bccSAzat Khuzhin 
478b6309bccSAzat Khuzhin 	cfg = event_config_new();
479b6309bccSAzat Khuzhin #ifdef _WIN32
480b6309bccSAzat Khuzhin 	if (o.iocp) {
48184f6fb41SAzat Khuzhin #ifdef EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED
482b6309bccSAzat Khuzhin 		evthread_use_windows_threads();
483b6309bccSAzat Khuzhin 		event_config_set_num_cpus_hint(cfg, 8);
48484f6fb41SAzat Khuzhin #endif
48584f6fb41SAzat Khuzhin 		event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
486b6309bccSAzat Khuzhin 	}
487b6309bccSAzat Khuzhin #endif
488b6309bccSAzat Khuzhin 
489b6309bccSAzat Khuzhin 	base = event_base_new_with_config(cfg);
4904e794d5dSNick Mathewson 	if (!base) {
4914e794d5dSNick Mathewson 		fprintf(stderr, "Couldn't create an event_base: exiting\n");
492b6309bccSAzat Khuzhin 		ret = 1;
4934e794d5dSNick Mathewson 	}
494b6309bccSAzat Khuzhin 	event_config_free(cfg);
495b6309bccSAzat Khuzhin 	cfg = NULL;
4964e794d5dSNick Mathewson 
4974e794d5dSNick Mathewson 	/* Create a new evhttp object to handle requests. */
4984e794d5dSNick Mathewson 	http = evhttp_new(base);
4994e794d5dSNick Mathewson 	if (!http) {
5004e794d5dSNick Mathewson 		fprintf(stderr, "couldn't create evhttp. Exiting.\n");
501b6309bccSAzat Khuzhin 		ret = 1;
5024e794d5dSNick Mathewson 	}
5034e794d5dSNick Mathewson 
5044e794d5dSNick Mathewson 	/* The /dump URI will dump all requests to stdout and say 200 ok. */
5054e794d5dSNick Mathewson 	evhttp_set_cb(http, "/dump", dump_request_cb, NULL);
5064e794d5dSNick Mathewson 
5074e794d5dSNick Mathewson 	/* We want to accept arbitrary requests, so we need to set a "generic"
5084e794d5dSNick Mathewson 	 * cb.  We can also add callbacks for specific paths. */
509*4a6f1ccfSDavid Disseldorp 	evhttp_set_gencb(http, send_document_cb, &o);
5104e794d5dSNick Mathewson 
5117c4da937SAzat Khuzhin 	if (o.unixsock) {
5127c4da937SAzat Khuzhin #ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN
5137c4da937SAzat Khuzhin 		struct sockaddr_un addr;
5147c4da937SAzat Khuzhin 
5157c4da937SAzat Khuzhin 		if (o.unlink && (unlink(o.unixsock) && errno != ENOENT)) {
5167c4da937SAzat Khuzhin 			perror(o.unixsock);
5177c4da937SAzat Khuzhin 			ret = 1;
5187c4da937SAzat Khuzhin 			goto err;
5197c4da937SAzat Khuzhin 		}
5207c4da937SAzat Khuzhin 
5217c4da937SAzat Khuzhin 		addr.sun_family = AF_UNIX;
5227c4da937SAzat Khuzhin 		strcpy(addr.sun_path, o.unixsock);
5237c4da937SAzat Khuzhin 
5247c4da937SAzat Khuzhin 		lev = evconnlistener_new_bind(base, NULL, NULL,
5257c4da937SAzat Khuzhin 			LEV_OPT_CLOSE_ON_FREE, -1,
5267c4da937SAzat Khuzhin 			(struct sockaddr *)&addr, sizeof(addr));
5277c4da937SAzat Khuzhin 		if (!lev) {
5287c4da937SAzat Khuzhin 			perror("Cannot create listener");
5297c4da937SAzat Khuzhin 			ret = 1;
5307c4da937SAzat Khuzhin 			goto err;
5317c4da937SAzat Khuzhin 		}
5327c4da937SAzat Khuzhin 
5337c4da937SAzat Khuzhin 		handle = evhttp_bind_listener(http, lev);
5347c4da937SAzat Khuzhin 		if (!handle) {
5357c4da937SAzat Khuzhin 			fprintf(stderr, "couldn't bind to %s. Exiting.\n", o.unixsock);
5367c4da937SAzat Khuzhin 			ret = 1;
5377c4da937SAzat Khuzhin 			goto err;
5387c4da937SAzat Khuzhin 		}
5397c4da937SAzat Khuzhin #else /* !EVENT__HAVE_STRUCT_SOCKADDR_UN */
5407c4da937SAzat Khuzhin 		fprintf(stderr, "-U is not supported on this platform. Exiting.\n");
5417c4da937SAzat Khuzhin 		ret = 1;
5427c4da937SAzat Khuzhin 		goto err;
5437c4da937SAzat Khuzhin #endif /* EVENT__HAVE_STRUCT_SOCKADDR_UN */
5447c4da937SAzat Khuzhin 	}
5457c4da937SAzat Khuzhin 	else {
546b6309bccSAzat Khuzhin 		handle = evhttp_bind_socket_with_handle(http, "0.0.0.0", o.port);
5474e794d5dSNick Mathewson 		if (!handle) {
548b6309bccSAzat Khuzhin 			fprintf(stderr, "couldn't bind to port %d. Exiting.\n", o.port);
549b6309bccSAzat Khuzhin 			ret = 1;
5508eb00842SAzat Khuzhin 			goto err;
5514e794d5dSNick Mathewson 		}
5527c4da937SAzat Khuzhin 	}
5534e794d5dSNick Mathewson 
5547c4da937SAzat Khuzhin 	if (display_listen_sock(handle)) {
555b6309bccSAzat Khuzhin 		ret = 1;
5568eb00842SAzat Khuzhin 		goto err;
5574e794d5dSNick Mathewson 	}
5584e794d5dSNick Mathewson 
559b6309bccSAzat Khuzhin 	term = evsignal_new(base, SIGINT, do_term, base);
560b6309bccSAzat Khuzhin 	if (!term)
561b6309bccSAzat Khuzhin 		goto err;
562b6309bccSAzat Khuzhin 	if (event_add(term, NULL))
563b6309bccSAzat Khuzhin 		goto err;
564b6309bccSAzat Khuzhin 
5654e794d5dSNick Mathewson 	event_base_dispatch(base);
5664e794d5dSNick Mathewson 
567b6309bccSAzat Khuzhin #ifdef _WIN32
568b6309bccSAzat Khuzhin 	WSACleanup();
569b6309bccSAzat Khuzhin #endif
570b6309bccSAzat Khuzhin 
571b6309bccSAzat Khuzhin err:
572b6309bccSAzat Khuzhin 	if (cfg)
573b6309bccSAzat Khuzhin 		event_config_free(cfg);
574b6309bccSAzat Khuzhin 	if (http)
575b6309bccSAzat Khuzhin 		evhttp_free(http);
576b6309bccSAzat Khuzhin 	if (term)
577b6309bccSAzat Khuzhin 		event_free(term);
578b6309bccSAzat Khuzhin 	if (base)
579b6309bccSAzat Khuzhin 		event_base_free(base);
580b6309bccSAzat Khuzhin 
581b6309bccSAzat Khuzhin 	return ret;
5824e794d5dSNick Mathewson }
583