xref: /freebsd/contrib/libevent/sample/http-server.c (revision b50261e21f39a6c7249a49e7b60aa878c98512a8)
1c43e99fdSEd Maste /*
2c43e99fdSEd Maste   A trivial static http webserver using Libevent's evhttp.
3c43e99fdSEd Maste 
4c43e99fdSEd Maste   This is not the best code in the world, and it does some fairly stupid stuff
5c43e99fdSEd Maste   that you would never want to do in a production webserver. Caveat hackor!
6c43e99fdSEd Maste 
7c43e99fdSEd Maste  */
8c43e99fdSEd Maste 
9c43e99fdSEd Maste /* Compatibility for possible missing IPv6 declarations */
10c43e99fdSEd Maste #include "../util-internal.h"
11c43e99fdSEd Maste 
12c43e99fdSEd Maste #include <stdio.h>
13c43e99fdSEd Maste #include <stdlib.h>
14c43e99fdSEd Maste #include <string.h>
15c43e99fdSEd Maste 
16c43e99fdSEd Maste #include <sys/types.h>
17c43e99fdSEd Maste #include <sys/stat.h>
18c43e99fdSEd Maste 
19c43e99fdSEd Maste #ifdef _WIN32
20c43e99fdSEd Maste #include <winsock2.h>
21c43e99fdSEd Maste #include <ws2tcpip.h>
22c43e99fdSEd Maste #include <windows.h>
23*b50261e2SCy Schubert #include <getopt.h>
24c43e99fdSEd Maste #include <io.h>
25c43e99fdSEd Maste #include <fcntl.h>
26c43e99fdSEd Maste #ifndef S_ISDIR
27c43e99fdSEd Maste #define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
28c43e99fdSEd Maste #endif
29*b50261e2SCy Schubert #else /* !_WIN32 */
30c43e99fdSEd Maste #include <sys/stat.h>
31c43e99fdSEd Maste #include <sys/socket.h>
32c43e99fdSEd Maste #include <fcntl.h>
33c43e99fdSEd Maste #include <unistd.h>
34c43e99fdSEd Maste #include <dirent.h>
35*b50261e2SCy Schubert #endif /* _WIN32 */
36*b50261e2SCy Schubert #include <signal.h>
37*b50261e2SCy Schubert 
38*b50261e2SCy Schubert #ifdef EVENT__HAVE_SYS_UN_H
39*b50261e2SCy Schubert #include <sys/un.h>
40*b50261e2SCy Schubert #endif
41*b50261e2SCy Schubert #ifdef EVENT__HAVE_AFUNIX_H
42*b50261e2SCy Schubert #include <afunix.h>
43c43e99fdSEd Maste #endif
44c43e99fdSEd Maste 
45c43e99fdSEd Maste #include <event2/event.h>
46c43e99fdSEd Maste #include <event2/http.h>
47*b50261e2SCy Schubert #include <event2/listener.h>
48c43e99fdSEd Maste #include <event2/buffer.h>
49c43e99fdSEd Maste #include <event2/util.h>
50c43e99fdSEd Maste #include <event2/keyvalq_struct.h>
51c43e99fdSEd Maste 
52*b50261e2SCy Schubert #ifdef _WIN32
53*b50261e2SCy Schubert #include <event2/thread.h>
54*b50261e2SCy Schubert #endif /* _WIN32 */
55*b50261e2SCy Schubert 
56c43e99fdSEd Maste #ifdef EVENT__HAVE_NETINET_IN_H
57c43e99fdSEd Maste #include <netinet/in.h>
58c43e99fdSEd Maste # ifdef _XOPEN_SOURCE_EXTENDED
59c43e99fdSEd Maste #  include <arpa/inet.h>
60c43e99fdSEd Maste # endif
61c43e99fdSEd Maste #endif
62c43e99fdSEd Maste 
63c43e99fdSEd Maste #ifdef _WIN32
64c43e99fdSEd Maste #ifndef stat
65c43e99fdSEd Maste #define stat _stat
66c43e99fdSEd Maste #endif
67c43e99fdSEd Maste #ifndef fstat
68c43e99fdSEd Maste #define fstat _fstat
69c43e99fdSEd Maste #endif
70c43e99fdSEd Maste #ifndef open
71c43e99fdSEd Maste #define open _open
72c43e99fdSEd Maste #endif
73c43e99fdSEd Maste #ifndef close
74c43e99fdSEd Maste #define close _close
75c43e99fdSEd Maste #endif
76c43e99fdSEd Maste #ifndef O_RDONLY
77c43e99fdSEd Maste #define O_RDONLY _O_RDONLY
78c43e99fdSEd Maste #endif
79*b50261e2SCy Schubert #endif /* _WIN32 */
80c43e99fdSEd Maste 
81c43e99fdSEd Maste char uri_root[512];
82c43e99fdSEd Maste 
83c43e99fdSEd Maste static const struct table_entry {
84c43e99fdSEd Maste 	const char *extension;
85c43e99fdSEd Maste 	const char *content_type;
86c43e99fdSEd Maste } content_type_table[] = {
87c43e99fdSEd Maste 	{ "txt", "text/plain" },
88c43e99fdSEd Maste 	{ "c", "text/plain" },
89c43e99fdSEd Maste 	{ "h", "text/plain" },
90c43e99fdSEd Maste 	{ "html", "text/html" },
91c43e99fdSEd Maste 	{ "htm", "text/htm" },
92c43e99fdSEd Maste 	{ "css", "text/css" },
93c43e99fdSEd Maste 	{ "gif", "image/gif" },
94c43e99fdSEd Maste 	{ "jpg", "image/jpeg" },
95c43e99fdSEd Maste 	{ "jpeg", "image/jpeg" },
96c43e99fdSEd Maste 	{ "png", "image/png" },
97c43e99fdSEd Maste 	{ "pdf", "application/pdf" },
98c43e99fdSEd Maste 	{ "ps", "application/postscript" },
99c43e99fdSEd Maste 	{ NULL, NULL },
100c43e99fdSEd Maste };
101c43e99fdSEd Maste 
102*b50261e2SCy Schubert struct options {
103*b50261e2SCy Schubert 	int port;
104*b50261e2SCy Schubert 	int iocp;
105*b50261e2SCy Schubert 	int verbose;
106*b50261e2SCy Schubert 
107*b50261e2SCy Schubert 	int unlink;
108*b50261e2SCy Schubert 	const char *unixsock;
109*b50261e2SCy Schubert 	const char *docroot;
110*b50261e2SCy Schubert };
111*b50261e2SCy Schubert 
112c43e99fdSEd Maste /* Try to guess a good content-type for 'path' */
113c43e99fdSEd Maste static const char *
guess_content_type(const char * path)114c43e99fdSEd Maste guess_content_type(const char *path)
115c43e99fdSEd Maste {
116c43e99fdSEd Maste 	const char *last_period, *extension;
117c43e99fdSEd Maste 	const struct table_entry *ent;
118c43e99fdSEd Maste 	last_period = strrchr(path, '.');
119c43e99fdSEd Maste 	if (!last_period || strchr(last_period, '/'))
120c43e99fdSEd Maste 		goto not_found; /* no exension */
121c43e99fdSEd Maste 	extension = last_period + 1;
122c43e99fdSEd Maste 	for (ent = &content_type_table[0]; ent->extension; ++ent) {
123c43e99fdSEd Maste 		if (!evutil_ascii_strcasecmp(ent->extension, extension))
124c43e99fdSEd Maste 			return ent->content_type;
125c43e99fdSEd Maste 	}
126c43e99fdSEd Maste 
127c43e99fdSEd Maste not_found:
128c43e99fdSEd Maste 	return "application/misc";
129c43e99fdSEd Maste }
130c43e99fdSEd Maste 
131c43e99fdSEd Maste /* Callback used for the /dump URI, and for every non-GET request:
132c43e99fdSEd Maste  * dumps all information to stdout and gives back a trivial 200 ok */
133c43e99fdSEd Maste static void
dump_request_cb(struct evhttp_request * req,void * arg)134c43e99fdSEd Maste dump_request_cb(struct evhttp_request *req, void *arg)
135c43e99fdSEd Maste {
136c43e99fdSEd Maste 	const char *cmdtype;
137c43e99fdSEd Maste 	struct evkeyvalq *headers;
138c43e99fdSEd Maste 	struct evkeyval *header;
139c43e99fdSEd Maste 	struct evbuffer *buf;
140c43e99fdSEd Maste 
141c43e99fdSEd Maste 	switch (evhttp_request_get_command(req)) {
142c43e99fdSEd Maste 	case EVHTTP_REQ_GET: cmdtype = "GET"; break;
143c43e99fdSEd Maste 	case EVHTTP_REQ_POST: cmdtype = "POST"; break;
144c43e99fdSEd Maste 	case EVHTTP_REQ_HEAD: cmdtype = "HEAD"; break;
145c43e99fdSEd Maste 	case EVHTTP_REQ_PUT: cmdtype = "PUT"; break;
146c43e99fdSEd Maste 	case EVHTTP_REQ_DELETE: cmdtype = "DELETE"; break;
147c43e99fdSEd Maste 	case EVHTTP_REQ_OPTIONS: cmdtype = "OPTIONS"; break;
148c43e99fdSEd Maste 	case EVHTTP_REQ_TRACE: cmdtype = "TRACE"; break;
149c43e99fdSEd Maste 	case EVHTTP_REQ_CONNECT: cmdtype = "CONNECT"; break;
150c43e99fdSEd Maste 	case EVHTTP_REQ_PATCH: cmdtype = "PATCH"; break;
151c43e99fdSEd Maste 	default: cmdtype = "unknown"; break;
152c43e99fdSEd Maste 	}
153c43e99fdSEd Maste 
154c43e99fdSEd Maste 	printf("Received a %s request for %s\nHeaders:\n",
155c43e99fdSEd Maste 	    cmdtype, evhttp_request_get_uri(req));
156c43e99fdSEd Maste 
157c43e99fdSEd Maste 	headers = evhttp_request_get_input_headers(req);
158c43e99fdSEd Maste 	for (header = headers->tqh_first; header;
159c43e99fdSEd Maste 	    header = header->next.tqe_next) {
160c43e99fdSEd Maste 		printf("  %s: %s\n", header->key, header->value);
161c43e99fdSEd Maste 	}
162c43e99fdSEd Maste 
163c43e99fdSEd Maste 	buf = evhttp_request_get_input_buffer(req);
164c43e99fdSEd Maste 	puts("Input data: <<<");
165c43e99fdSEd Maste 	while (evbuffer_get_length(buf)) {
166c43e99fdSEd Maste 		int n;
167c43e99fdSEd Maste 		char cbuf[128];
168c43e99fdSEd Maste 		n = evbuffer_remove(buf, cbuf, sizeof(cbuf));
169c43e99fdSEd Maste 		if (n > 0)
170c43e99fdSEd Maste 			(void) fwrite(cbuf, 1, n, stdout);
171c43e99fdSEd Maste 	}
172c43e99fdSEd Maste 	puts(">>>");
173c43e99fdSEd Maste 
174c43e99fdSEd Maste 	evhttp_send_reply(req, 200, "OK", NULL);
175c43e99fdSEd Maste }
176c43e99fdSEd Maste 
177c43e99fdSEd Maste /* This callback gets invoked when we get any http request that doesn't match
178c43e99fdSEd Maste  * any other callback.  Like any evhttp server callback, it has a simple job:
179c43e99fdSEd Maste  * it must eventually call evhttp_send_error() or evhttp_send_reply().
180c43e99fdSEd Maste  */
181c43e99fdSEd Maste static void
send_document_cb(struct evhttp_request * req,void * arg)182c43e99fdSEd Maste send_document_cb(struct evhttp_request *req, void *arg)
183c43e99fdSEd Maste {
184c43e99fdSEd Maste 	struct evbuffer *evb = NULL;
185*b50261e2SCy Schubert 	struct options *o = arg;
186c43e99fdSEd Maste 	const char *uri = evhttp_request_get_uri(req);
187c43e99fdSEd Maste 	struct evhttp_uri *decoded = NULL;
188c43e99fdSEd Maste 	const char *path;
189c43e99fdSEd Maste 	char *decoded_path;
190c43e99fdSEd Maste 	char *whole_path = NULL;
191c43e99fdSEd Maste 	size_t len;
192c43e99fdSEd Maste 	int fd = -1;
193c43e99fdSEd Maste 	struct stat st;
194c43e99fdSEd Maste 
195c43e99fdSEd Maste 	if (evhttp_request_get_command(req) != EVHTTP_REQ_GET) {
196c43e99fdSEd Maste 		dump_request_cb(req, arg);
197c43e99fdSEd Maste 		return;
198c43e99fdSEd Maste 	}
199c43e99fdSEd Maste 
200c43e99fdSEd Maste 	printf("Got a GET request for <%s>\n",  uri);
201c43e99fdSEd Maste 
202c43e99fdSEd Maste 	/* Decode the URI */
203c43e99fdSEd Maste 	decoded = evhttp_uri_parse(uri);
204c43e99fdSEd Maste 	if (!decoded) {
205c43e99fdSEd Maste 		printf("It's not a good URI. Sending BADREQUEST\n");
206c43e99fdSEd Maste 		evhttp_send_error(req, HTTP_BADREQUEST, 0);
207c43e99fdSEd Maste 		return;
208c43e99fdSEd Maste 	}
209c43e99fdSEd Maste 
210c43e99fdSEd Maste 	/* Let's see what path the user asked for. */
211c43e99fdSEd Maste 	path = evhttp_uri_get_path(decoded);
212c43e99fdSEd Maste 	if (!path) path = "/";
213c43e99fdSEd Maste 
214c43e99fdSEd Maste 	/* We need to decode it, to see what path the user really wanted. */
215c43e99fdSEd Maste 	decoded_path = evhttp_uridecode(path, 0, NULL);
216c43e99fdSEd Maste 	if (decoded_path == NULL)
217c43e99fdSEd Maste 		goto err;
218c43e99fdSEd Maste 	/* Don't allow any ".."s in the path, to avoid exposing stuff outside
219c43e99fdSEd Maste 	 * of the docroot.  This test is both overzealous and underzealous:
220c43e99fdSEd Maste 	 * it forbids aceptable paths like "/this/one..here", but it doesn't
221c43e99fdSEd Maste 	 * do anything to prevent symlink following." */
222c43e99fdSEd Maste 	if (strstr(decoded_path, ".."))
223c43e99fdSEd Maste 		goto err;
224c43e99fdSEd Maste 
225*b50261e2SCy Schubert 	len = strlen(decoded_path)+strlen(o->docroot)+2;
226c43e99fdSEd Maste 	if (!(whole_path = malloc(len))) {
227c43e99fdSEd Maste 		perror("malloc");
228c43e99fdSEd Maste 		goto err;
229c43e99fdSEd Maste 	}
230*b50261e2SCy Schubert 	evutil_snprintf(whole_path, len, "%s/%s", o->docroot, decoded_path);
231c43e99fdSEd Maste 
232c43e99fdSEd Maste 	if (stat(whole_path, &st)<0) {
233c43e99fdSEd Maste 		goto err;
234c43e99fdSEd Maste 	}
235c43e99fdSEd Maste 
236c43e99fdSEd Maste 	/* This holds the content we're sending. */
237c43e99fdSEd Maste 	evb = evbuffer_new();
238c43e99fdSEd Maste 
239c43e99fdSEd Maste 	if (S_ISDIR(st.st_mode)) {
240c43e99fdSEd Maste 		/* If it's a directory, read the comments and make a little
241c43e99fdSEd Maste 		 * index page */
242c43e99fdSEd Maste #ifdef _WIN32
243c43e99fdSEd Maste 		HANDLE d;
244c43e99fdSEd Maste 		WIN32_FIND_DATAA ent;
245c43e99fdSEd Maste 		char *pattern;
246c43e99fdSEd Maste 		size_t dirlen;
247c43e99fdSEd Maste #else
248c43e99fdSEd Maste 		DIR *d;
249c43e99fdSEd Maste 		struct dirent *ent;
250c43e99fdSEd Maste #endif
251c43e99fdSEd Maste 		const char *trailing_slash = "";
252c43e99fdSEd Maste 
253c43e99fdSEd Maste 		if (!strlen(path) || path[strlen(path)-1] != '/')
254c43e99fdSEd Maste 			trailing_slash = "/";
255c43e99fdSEd Maste 
256c43e99fdSEd Maste #ifdef _WIN32
257c43e99fdSEd Maste 		dirlen = strlen(whole_path);
258c43e99fdSEd Maste 		pattern = malloc(dirlen+3);
259c43e99fdSEd Maste 		memcpy(pattern, whole_path, dirlen);
260c43e99fdSEd Maste 		pattern[dirlen] = '\\';
261c43e99fdSEd Maste 		pattern[dirlen+1] = '*';
262c43e99fdSEd Maste 		pattern[dirlen+2] = '\0';
263c43e99fdSEd Maste 		d = FindFirstFileA(pattern, &ent);
264c43e99fdSEd Maste 		free(pattern);
265c43e99fdSEd Maste 		if (d == INVALID_HANDLE_VALUE)
266c43e99fdSEd Maste 			goto err;
267c43e99fdSEd Maste #else
268c43e99fdSEd Maste 		if (!(d = opendir(whole_path)))
269c43e99fdSEd Maste 			goto err;
270c43e99fdSEd Maste #endif
271c43e99fdSEd Maste 
272c43e99fdSEd Maste 		evbuffer_add_printf(evb,
273c43e99fdSEd Maste                     "<!DOCTYPE html>\n"
274c43e99fdSEd Maste                     "<html>\n <head>\n"
275c43e99fdSEd Maste                     "  <meta charset='utf-8'>\n"
276c43e99fdSEd Maste 		    "  <title>%s</title>\n"
277c43e99fdSEd Maste 		    "  <base href='%s%s'>\n"
278c43e99fdSEd Maste 		    " </head>\n"
279c43e99fdSEd Maste 		    " <body>\n"
280c43e99fdSEd Maste 		    "  <h1>%s</h1>\n"
281c43e99fdSEd Maste 		    "  <ul>\n",
282c43e99fdSEd Maste 		    decoded_path, /* XXX html-escape this. */
283c43e99fdSEd Maste 		    path, /* XXX html-escape this? */
284c43e99fdSEd Maste 		    trailing_slash,
285c43e99fdSEd Maste 		    decoded_path /* XXX html-escape this */);
286c43e99fdSEd Maste #ifdef _WIN32
287c43e99fdSEd Maste 		do {
288c43e99fdSEd Maste 			const char *name = ent.cFileName;
289c43e99fdSEd Maste #else
290c43e99fdSEd Maste 		while ((ent = readdir(d))) {
291c43e99fdSEd Maste 			const char *name = ent->d_name;
292c43e99fdSEd Maste #endif
293c43e99fdSEd Maste 			evbuffer_add_printf(evb,
294c43e99fdSEd Maste 			    "    <li><a href=\"%s\">%s</a>\n",
295c43e99fdSEd Maste 			    name, name);/* XXX escape this */
296c43e99fdSEd Maste #ifdef _WIN32
297c43e99fdSEd Maste 		} while (FindNextFileA(d, &ent));
298c43e99fdSEd Maste #else
299c43e99fdSEd Maste 		}
300c43e99fdSEd Maste #endif
301c43e99fdSEd Maste 		evbuffer_add_printf(evb, "</ul></body></html>\n");
302c43e99fdSEd Maste #ifdef _WIN32
303c43e99fdSEd Maste 		FindClose(d);
304c43e99fdSEd Maste #else
305c43e99fdSEd Maste 		closedir(d);
306c43e99fdSEd Maste #endif
307c43e99fdSEd Maste 		evhttp_add_header(evhttp_request_get_output_headers(req),
308c43e99fdSEd Maste 		    "Content-Type", "text/html");
309c43e99fdSEd Maste 	} else {
310c43e99fdSEd Maste 		/* Otherwise it's a file; add it to the buffer to get
311c43e99fdSEd Maste 		 * sent via sendfile */
312c43e99fdSEd Maste 		const char *type = guess_content_type(decoded_path);
313c43e99fdSEd Maste 		if ((fd = open(whole_path, O_RDONLY)) < 0) {
314c43e99fdSEd Maste 			perror("open");
315c43e99fdSEd Maste 			goto err;
316c43e99fdSEd Maste 		}
317c43e99fdSEd Maste 
318c43e99fdSEd Maste 		if (fstat(fd, &st)<0) {
319c43e99fdSEd Maste 			/* Make sure the length still matches, now that we
320c43e99fdSEd Maste 			 * opened the file :/ */
321c43e99fdSEd Maste 			perror("fstat");
322c43e99fdSEd Maste 			goto err;
323c43e99fdSEd Maste 		}
324c43e99fdSEd Maste 		evhttp_add_header(evhttp_request_get_output_headers(req),
325c43e99fdSEd Maste 		    "Content-Type", type);
326c43e99fdSEd Maste 		evbuffer_add_file(evb, fd, 0, st.st_size);
327c43e99fdSEd Maste 	}
328c43e99fdSEd Maste 
329c43e99fdSEd Maste 	evhttp_send_reply(req, 200, "OK", evb);
330c43e99fdSEd Maste 	goto done;
331c43e99fdSEd Maste err:
332c43e99fdSEd Maste 	evhttp_send_error(req, 404, "Document was not found");
333c43e99fdSEd Maste 	if (fd>=0)
334c43e99fdSEd Maste 		close(fd);
335c43e99fdSEd Maste done:
336c43e99fdSEd Maste 	if (decoded)
337c43e99fdSEd Maste 		evhttp_uri_free(decoded);
338c43e99fdSEd Maste 	if (decoded_path)
339c43e99fdSEd Maste 		free(decoded_path);
340c43e99fdSEd Maste 	if (whole_path)
341c43e99fdSEd Maste 		free(whole_path);
342c43e99fdSEd Maste 	if (evb)
343c43e99fdSEd Maste 		evbuffer_free(evb);
344c43e99fdSEd Maste }
345c43e99fdSEd Maste 
346c43e99fdSEd Maste static void
print_usage(FILE * out,const char * prog,int exit_code)347*b50261e2SCy Schubert print_usage(FILE *out, const char *prog, int exit_code)
348c43e99fdSEd Maste {
349*b50261e2SCy Schubert 	fprintf(out,
350*b50261e2SCy Schubert 		"Syntax: %s [ OPTS ] <docroot>\n"
351*b50261e2SCy Schubert 		" -p      - port\n"
352*b50261e2SCy Schubert 		" -U      - bind to unix socket\n"
353*b50261e2SCy Schubert 		" -u      - unlink unix socket before bind\n"
354*b50261e2SCy Schubert 		" -I      - IOCP\n"
355*b50261e2SCy Schubert 		" -v      - verbosity, enables libevent debug logging too\n", prog);
356*b50261e2SCy Schubert 	exit(exit_code);
357c43e99fdSEd Maste }
358*b50261e2SCy Schubert static struct options
parse_opts(int argc,char ** argv)359*b50261e2SCy Schubert parse_opts(int argc, char **argv)
360c43e99fdSEd Maste {
361*b50261e2SCy Schubert 	struct options o;
362*b50261e2SCy Schubert 	int opt;
363c43e99fdSEd Maste 
364*b50261e2SCy Schubert 	memset(&o, 0, sizeof(o));
365*b50261e2SCy Schubert 
366*b50261e2SCy Schubert 	while ((opt = getopt(argc, argv, "hp:U:uIv")) != -1) {
367*b50261e2SCy Schubert 		switch (opt) {
368*b50261e2SCy Schubert 			case 'p': o.port = atoi(optarg); break;
369*b50261e2SCy Schubert 			case 'U': o.unixsock = optarg; break;
370*b50261e2SCy Schubert 			case 'u': o.unlink = 1; break;
371*b50261e2SCy Schubert 			case 'I': o.iocp = 1; break;
372*b50261e2SCy Schubert 			case 'v': ++o.verbose; break;
373*b50261e2SCy Schubert 			case 'h': print_usage(stdout, argv[0], 0); break;
374*b50261e2SCy Schubert 			default : fprintf(stderr, "Unknown option %c\n", opt); break;
375*b50261e2SCy Schubert 		}
376c43e99fdSEd Maste 	}
377c43e99fdSEd Maste 
378*b50261e2SCy Schubert 	if (optind >= argc || (argc - optind) > 1) {
379*b50261e2SCy Schubert 		print_usage(stdout, argv[0], 1);
380*b50261e2SCy Schubert 	}
381*b50261e2SCy Schubert 	o.docroot = argv[optind];
382*b50261e2SCy Schubert 
383*b50261e2SCy Schubert 	return o;
384c43e99fdSEd Maste }
385c43e99fdSEd Maste 
386*b50261e2SCy Schubert static void
do_term(int sig,short events,void * arg)387*b50261e2SCy Schubert do_term(int sig, short events, void *arg)
388c43e99fdSEd Maste {
389*b50261e2SCy Schubert 	struct event_base *base = arg;
390*b50261e2SCy Schubert 	event_base_loopbreak(base);
391*b50261e2SCy Schubert 	fprintf(stderr, "Got %i, Terminating\n", sig);
392*b50261e2SCy Schubert }
393*b50261e2SCy Schubert 
394*b50261e2SCy Schubert static int
display_listen_sock(struct evhttp_bound_socket * handle)395*b50261e2SCy Schubert display_listen_sock(struct evhttp_bound_socket *handle)
396*b50261e2SCy Schubert {
397c43e99fdSEd Maste 	struct sockaddr_storage ss;
398c43e99fdSEd Maste 	evutil_socket_t fd;
399c43e99fdSEd Maste 	ev_socklen_t socklen = sizeof(ss);
400c43e99fdSEd Maste 	char addrbuf[128];
401c43e99fdSEd Maste 	void *inaddr;
402c43e99fdSEd Maste 	const char *addr;
403c43e99fdSEd Maste 	int got_port = -1;
404*b50261e2SCy Schubert 
405c43e99fdSEd Maste 	fd = evhttp_bound_socket_get_fd(handle);
406c43e99fdSEd Maste 	memset(&ss, 0, sizeof(ss));
407c43e99fdSEd Maste 	if (getsockname(fd, (struct sockaddr *)&ss, &socklen)) {
408c43e99fdSEd Maste 		perror("getsockname() failed");
409c43e99fdSEd Maste 		return 1;
410c43e99fdSEd Maste 	}
411*b50261e2SCy Schubert 
412c43e99fdSEd Maste 	if (ss.ss_family == AF_INET) {
413c43e99fdSEd Maste 		got_port = ntohs(((struct sockaddr_in*)&ss)->sin_port);
414c43e99fdSEd Maste 		inaddr = &((struct sockaddr_in*)&ss)->sin_addr;
415c43e99fdSEd Maste 	} else if (ss.ss_family == AF_INET6) {
416c43e99fdSEd Maste 		got_port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port);
417c43e99fdSEd Maste 		inaddr = &((struct sockaddr_in6*)&ss)->sin6_addr;
418*b50261e2SCy Schubert 	}
419*b50261e2SCy Schubert #ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN
420*b50261e2SCy Schubert 	else if (ss.ss_family == AF_UNIX) {
421*b50261e2SCy Schubert 		printf("Listening on <%s>\n", ((struct sockaddr_un*)&ss)->sun_path);
422*b50261e2SCy Schubert 		return 0;
423*b50261e2SCy Schubert 	}
424*b50261e2SCy Schubert #endif
425*b50261e2SCy Schubert 	else {
426c43e99fdSEd Maste 		fprintf(stderr, "Weird address family %d\n",
427c43e99fdSEd Maste 		    ss.ss_family);
428c43e99fdSEd Maste 		return 1;
429c43e99fdSEd Maste 	}
430*b50261e2SCy Schubert 
431c43e99fdSEd Maste 	addr = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf,
432c43e99fdSEd Maste 	    sizeof(addrbuf));
433c43e99fdSEd Maste 	if (addr) {
434c43e99fdSEd Maste 		printf("Listening on %s:%d\n", addr, got_port);
435c43e99fdSEd Maste 		evutil_snprintf(uri_root, sizeof(uri_root),
436c43e99fdSEd Maste 		    "http://%s:%d",addr,got_port);
437c43e99fdSEd Maste 	} else {
438c43e99fdSEd Maste 		fprintf(stderr, "evutil_inet_ntop failed\n");
439c43e99fdSEd Maste 		return 1;
440c43e99fdSEd Maste 	}
441*b50261e2SCy Schubert 
442*b50261e2SCy Schubert 	return 0;
443c43e99fdSEd Maste }
444c43e99fdSEd Maste 
445*b50261e2SCy Schubert int
main(int argc,char ** argv)446*b50261e2SCy Schubert main(int argc, char **argv)
447*b50261e2SCy Schubert {
448*b50261e2SCy Schubert 	struct event_config *cfg = NULL;
449*b50261e2SCy Schubert 	struct event_base *base = NULL;
450*b50261e2SCy Schubert 	struct evhttp *http = NULL;
451*b50261e2SCy Schubert 	struct evhttp_bound_socket *handle = NULL;
452*b50261e2SCy Schubert 	struct evconnlistener *lev = NULL;
453*b50261e2SCy Schubert 	struct event *term = NULL;
454*b50261e2SCy Schubert 	struct options o = parse_opts(argc, argv);
455*b50261e2SCy Schubert 	int ret = 0;
456*b50261e2SCy Schubert 
457*b50261e2SCy Schubert #ifdef _WIN32
458*b50261e2SCy Schubert 	{
459*b50261e2SCy Schubert 		WORD wVersionRequested;
460*b50261e2SCy Schubert 		WSADATA wsaData;
461*b50261e2SCy Schubert 		wVersionRequested = MAKEWORD(2, 2);
462*b50261e2SCy Schubert 		WSAStartup(wVersionRequested, &wsaData);
463*b50261e2SCy Schubert 	}
464*b50261e2SCy Schubert #else
465*b50261e2SCy Schubert 	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
466*b50261e2SCy Schubert 		ret = 1;
467*b50261e2SCy Schubert 		goto err;
468*b50261e2SCy Schubert 	}
469*b50261e2SCy Schubert #endif
470*b50261e2SCy Schubert 
471*b50261e2SCy Schubert 	setbuf(stdout, NULL);
472*b50261e2SCy Schubert 	setbuf(stderr, NULL);
473*b50261e2SCy Schubert 
474*b50261e2SCy Schubert 	/** Read env like in regress */
475*b50261e2SCy Schubert 	if (o.verbose || getenv("EVENT_DEBUG_LOGGING_ALL"))
476*b50261e2SCy Schubert 		event_enable_debug_logging(EVENT_DBG_ALL);
477*b50261e2SCy Schubert 
478*b50261e2SCy Schubert 	cfg = event_config_new();
479*b50261e2SCy Schubert #ifdef _WIN32
480*b50261e2SCy Schubert 	if (o.iocp) {
481*b50261e2SCy Schubert #ifdef EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED
482*b50261e2SCy Schubert 		evthread_use_windows_threads();
483*b50261e2SCy Schubert 		event_config_set_num_cpus_hint(cfg, 8);
484*b50261e2SCy Schubert #endif
485*b50261e2SCy Schubert 		event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
486*b50261e2SCy Schubert 	}
487*b50261e2SCy Schubert #endif
488*b50261e2SCy Schubert 
489*b50261e2SCy Schubert 	base = event_base_new_with_config(cfg);
490*b50261e2SCy Schubert 	if (!base) {
491*b50261e2SCy Schubert 		fprintf(stderr, "Couldn't create an event_base: exiting\n");
492*b50261e2SCy Schubert 		ret = 1;
493*b50261e2SCy Schubert 	}
494*b50261e2SCy Schubert 	event_config_free(cfg);
495*b50261e2SCy Schubert 	cfg = NULL;
496*b50261e2SCy Schubert 
497*b50261e2SCy Schubert 	/* Create a new evhttp object to handle requests. */
498*b50261e2SCy Schubert 	http = evhttp_new(base);
499*b50261e2SCy Schubert 	if (!http) {
500*b50261e2SCy Schubert 		fprintf(stderr, "couldn't create evhttp. Exiting.\n");
501*b50261e2SCy Schubert 		ret = 1;
502*b50261e2SCy Schubert 	}
503*b50261e2SCy Schubert 
504*b50261e2SCy Schubert 	/* The /dump URI will dump all requests to stdout and say 200 ok. */
505*b50261e2SCy Schubert 	evhttp_set_cb(http, "/dump", dump_request_cb, NULL);
506*b50261e2SCy Schubert 
507*b50261e2SCy Schubert 	/* We want to accept arbitrary requests, so we need to set a "generic"
508*b50261e2SCy Schubert 	 * cb.  We can also add callbacks for specific paths. */
509*b50261e2SCy Schubert 	evhttp_set_gencb(http, send_document_cb, &o);
510*b50261e2SCy Schubert 
511*b50261e2SCy Schubert 	if (o.unixsock) {
512*b50261e2SCy Schubert #ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN
513*b50261e2SCy Schubert 		struct sockaddr_un addr;
514*b50261e2SCy Schubert 
515*b50261e2SCy Schubert 		if (o.unlink && (unlink(o.unixsock) && errno != ENOENT)) {
516*b50261e2SCy Schubert 			perror(o.unixsock);
517*b50261e2SCy Schubert 			ret = 1;
518*b50261e2SCy Schubert 			goto err;
519*b50261e2SCy Schubert 		}
520*b50261e2SCy Schubert 
521*b50261e2SCy Schubert 		addr.sun_family = AF_UNIX;
522*b50261e2SCy Schubert 		strcpy(addr.sun_path, o.unixsock);
523*b50261e2SCy Schubert 
524*b50261e2SCy Schubert 		lev = evconnlistener_new_bind(base, NULL, NULL,
525*b50261e2SCy Schubert 			LEV_OPT_CLOSE_ON_FREE, -1,
526*b50261e2SCy Schubert 			(struct sockaddr *)&addr, sizeof(addr));
527*b50261e2SCy Schubert 		if (!lev) {
528*b50261e2SCy Schubert 			perror("Cannot create listener");
529*b50261e2SCy Schubert 			ret = 1;
530*b50261e2SCy Schubert 			goto err;
531*b50261e2SCy Schubert 		}
532*b50261e2SCy Schubert 
533*b50261e2SCy Schubert 		handle = evhttp_bind_listener(http, lev);
534*b50261e2SCy Schubert 		if (!handle) {
535*b50261e2SCy Schubert 			fprintf(stderr, "couldn't bind to %s. Exiting.\n", o.unixsock);
536*b50261e2SCy Schubert 			ret = 1;
537*b50261e2SCy Schubert 			goto err;
538*b50261e2SCy Schubert 		}
539*b50261e2SCy Schubert #else /* !EVENT__HAVE_STRUCT_SOCKADDR_UN */
540*b50261e2SCy Schubert 		fprintf(stderr, "-U is not supported on this platform. Exiting.\n");
541*b50261e2SCy Schubert 		ret = 1;
542*b50261e2SCy Schubert 		goto err;
543*b50261e2SCy Schubert #endif /* EVENT__HAVE_STRUCT_SOCKADDR_UN */
544*b50261e2SCy Schubert 	}
545*b50261e2SCy Schubert 	else {
546*b50261e2SCy Schubert 		handle = evhttp_bind_socket_with_handle(http, "0.0.0.0", o.port);
547*b50261e2SCy Schubert 		if (!handle) {
548*b50261e2SCy Schubert 			fprintf(stderr, "couldn't bind to port %d. Exiting.\n", o.port);
549*b50261e2SCy Schubert 			ret = 1;
550*b50261e2SCy Schubert 			goto err;
551*b50261e2SCy Schubert 		}
552*b50261e2SCy Schubert 	}
553*b50261e2SCy Schubert 
554*b50261e2SCy Schubert 	if (display_listen_sock(handle)) {
555*b50261e2SCy Schubert 		ret = 1;
556*b50261e2SCy Schubert 		goto err;
557*b50261e2SCy Schubert 	}
558*b50261e2SCy Schubert 
559*b50261e2SCy Schubert 	term = evsignal_new(base, SIGINT, do_term, base);
560*b50261e2SCy Schubert 	if (!term)
561*b50261e2SCy Schubert 		goto err;
562*b50261e2SCy Schubert 	if (event_add(term, NULL))
563*b50261e2SCy Schubert 		goto err;
564*b50261e2SCy Schubert 
565c43e99fdSEd Maste 	event_base_dispatch(base);
566c43e99fdSEd Maste 
567*b50261e2SCy Schubert #ifdef _WIN32
568*b50261e2SCy Schubert 	WSACleanup();
569*b50261e2SCy Schubert #endif
570*b50261e2SCy Schubert 
571*b50261e2SCy Schubert err:
572*b50261e2SCy Schubert 	if (cfg)
573*b50261e2SCy Schubert 		event_config_free(cfg);
574*b50261e2SCy Schubert 	if (http)
575*b50261e2SCy Schubert 		evhttp_free(http);
576*b50261e2SCy Schubert 	if (term)
577*b50261e2SCy Schubert 		event_free(term);
578*b50261e2SCy Schubert 	if (base)
579*b50261e2SCy Schubert 		event_base_free(base);
580*b50261e2SCy Schubert 
581*b50261e2SCy Schubert 	return ret;
582c43e99fdSEd Maste }
583