1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/third_party/libevent/sample/http-server.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,405 @@ 1.4 +/* 1.5 + A trivial static http webserver using Libevent's evhttp. 1.6 + 1.7 + This is not the best code in the world, and it does some fairly stupid stuff 1.8 + that you would never want to do in a production webserver. Caveat hackor! 1.9 + 1.10 + */ 1.11 + 1.12 +#include <stdio.h> 1.13 +#include <stdlib.h> 1.14 +#include <string.h> 1.15 + 1.16 +#include <sys/types.h> 1.17 +#include <sys/stat.h> 1.18 + 1.19 +#ifdef WIN32 1.20 +#include <winsock2.h> 1.21 +#include <ws2tcpip.h> 1.22 +#include <windows.h> 1.23 +#include <io.h> 1.24 +#include <fcntl.h> 1.25 +#ifndef S_ISDIR 1.26 +#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) 1.27 +#endif 1.28 +#else 1.29 +#include <sys/stat.h> 1.30 +#include <sys/socket.h> 1.31 +#include <signal.h> 1.32 +#include <fcntl.h> 1.33 +#include <unistd.h> 1.34 +#include <dirent.h> 1.35 +#endif 1.36 + 1.37 +#include <event2/event.h> 1.38 +#include <event2/http.h> 1.39 +#include <event2/buffer.h> 1.40 +#include <event2/util.h> 1.41 +#include <event2/keyvalq_struct.h> 1.42 + 1.43 +#ifdef _EVENT_HAVE_NETINET_IN_H 1.44 +#include <netinet/in.h> 1.45 +# ifdef _XOPEN_SOURCE_EXTENDED 1.46 +# include <arpa/inet.h> 1.47 +# endif 1.48 +#endif 1.49 + 1.50 +/* Compatibility for possible missing IPv6 declarations */ 1.51 +#include "../util-internal.h" 1.52 + 1.53 +#ifdef WIN32 1.54 +#define stat _stat 1.55 +#define fstat _fstat 1.56 +#define open _open 1.57 +#define close _close 1.58 +#define O_RDONLY _O_RDONLY 1.59 +#endif 1.60 + 1.61 +char uri_root[512]; 1.62 + 1.63 +static const struct table_entry { 1.64 + const char *extension; 1.65 + const char *content_type; 1.66 +} content_type_table[] = { 1.67 + { "txt", "text/plain" }, 1.68 + { "c", "text/plain" }, 1.69 + { "h", "text/plain" }, 1.70 + { "html", "text/html" }, 1.71 + { "htm", "text/htm" }, 1.72 + { "css", "text/css" }, 1.73 + { "gif", "image/gif" }, 1.74 + { "jpg", "image/jpeg" }, 1.75 + { "jpeg", "image/jpeg" }, 1.76 + { "png", "image/png" }, 1.77 + { "pdf", "application/pdf" }, 1.78 + { "ps", "application/postsript" }, 1.79 + { NULL, NULL }, 1.80 +}; 1.81 + 1.82 +/* Try to guess a good content-type for 'path' */ 1.83 +static const char * 1.84 +guess_content_type(const char *path) 1.85 +{ 1.86 + const char *last_period, *extension; 1.87 + const struct table_entry *ent; 1.88 + last_period = strrchr(path, '.'); 1.89 + if (!last_period || strchr(last_period, '/')) 1.90 + goto not_found; /* no exension */ 1.91 + extension = last_period + 1; 1.92 + for (ent = &content_type_table[0]; ent->extension; ++ent) { 1.93 + if (!evutil_ascii_strcasecmp(ent->extension, extension)) 1.94 + return ent->content_type; 1.95 + } 1.96 + 1.97 +not_found: 1.98 + return "application/misc"; 1.99 +} 1.100 + 1.101 +/* Callback used for the /dump URI, and for every non-GET request: 1.102 + * dumps all information to stdout and gives back a trivial 200 ok */ 1.103 +static void 1.104 +dump_request_cb(struct evhttp_request *req, void *arg) 1.105 +{ 1.106 + const char *cmdtype; 1.107 + struct evkeyvalq *headers; 1.108 + struct evkeyval *header; 1.109 + struct evbuffer *buf; 1.110 + 1.111 + switch (evhttp_request_get_command(req)) { 1.112 + case EVHTTP_REQ_GET: cmdtype = "GET"; break; 1.113 + case EVHTTP_REQ_POST: cmdtype = "POST"; break; 1.114 + case EVHTTP_REQ_HEAD: cmdtype = "HEAD"; break; 1.115 + case EVHTTP_REQ_PUT: cmdtype = "PUT"; break; 1.116 + case EVHTTP_REQ_DELETE: cmdtype = "DELETE"; break; 1.117 + case EVHTTP_REQ_OPTIONS: cmdtype = "OPTIONS"; break; 1.118 + case EVHTTP_REQ_TRACE: cmdtype = "TRACE"; break; 1.119 + case EVHTTP_REQ_CONNECT: cmdtype = "CONNECT"; break; 1.120 + case EVHTTP_REQ_PATCH: cmdtype = "PATCH"; break; 1.121 + default: cmdtype = "unknown"; break; 1.122 + } 1.123 + 1.124 + printf("Received a %s request for %s\nHeaders:\n", 1.125 + cmdtype, evhttp_request_get_uri(req)); 1.126 + 1.127 + headers = evhttp_request_get_input_headers(req); 1.128 + for (header = headers->tqh_first; header; 1.129 + header = header->next.tqe_next) { 1.130 + printf(" %s: %s\n", header->key, header->value); 1.131 + } 1.132 + 1.133 + buf = evhttp_request_get_input_buffer(req); 1.134 + puts("Input data: <<<"); 1.135 + while (evbuffer_get_length(buf)) { 1.136 + int n; 1.137 + char cbuf[128]; 1.138 + n = evbuffer_remove(buf, cbuf, sizeof(buf)-1); 1.139 + if (n > 0) 1.140 + (void) fwrite(cbuf, 1, n, stdout); 1.141 + } 1.142 + puts(">>>"); 1.143 + 1.144 + evhttp_send_reply(req, 200, "OK", NULL); 1.145 +} 1.146 + 1.147 +/* This callback gets invoked when we get any http request that doesn't match 1.148 + * any other callback. Like any evhttp server callback, it has a simple job: 1.149 + * it must eventually call evhttp_send_error() or evhttp_send_reply(). 1.150 + */ 1.151 +static void 1.152 +send_document_cb(struct evhttp_request *req, void *arg) 1.153 +{ 1.154 + struct evbuffer *evb = NULL; 1.155 + const char *docroot = arg; 1.156 + const char *uri = evhttp_request_get_uri(req); 1.157 + struct evhttp_uri *decoded = NULL; 1.158 + const char *path; 1.159 + char *decoded_path; 1.160 + char *whole_path = NULL; 1.161 + size_t len; 1.162 + int fd = -1; 1.163 + struct stat st; 1.164 + 1.165 + if (evhttp_request_get_command(req) != EVHTTP_REQ_GET) { 1.166 + dump_request_cb(req, arg); 1.167 + return; 1.168 + } 1.169 + 1.170 + printf("Got a GET request for <%s>\n", uri); 1.171 + 1.172 + /* Decode the URI */ 1.173 + decoded = evhttp_uri_parse(uri); 1.174 + if (!decoded) { 1.175 + printf("It's not a good URI. Sending BADREQUEST\n"); 1.176 + evhttp_send_error(req, HTTP_BADREQUEST, 0); 1.177 + return; 1.178 + } 1.179 + 1.180 + /* Let's see what path the user asked for. */ 1.181 + path = evhttp_uri_get_path(decoded); 1.182 + if (!path) path = "/"; 1.183 + 1.184 + /* We need to decode it, to see what path the user really wanted. */ 1.185 + decoded_path = evhttp_uridecode(path, 0, NULL); 1.186 + if (decoded_path == NULL) 1.187 + goto err; 1.188 + /* Don't allow any ".."s in the path, to avoid exposing stuff outside 1.189 + * of the docroot. This test is both overzealous and underzealous: 1.190 + * it forbids aceptable paths like "/this/one..here", but it doesn't 1.191 + * do anything to prevent symlink following." */ 1.192 + if (strstr(decoded_path, "..")) 1.193 + goto err; 1.194 + 1.195 + len = strlen(decoded_path)+strlen(docroot)+2; 1.196 + if (!(whole_path = malloc(len))) { 1.197 + perror("malloc"); 1.198 + goto err; 1.199 + } 1.200 + evutil_snprintf(whole_path, len, "%s/%s", docroot, decoded_path); 1.201 + 1.202 + if (stat(whole_path, &st)<0) { 1.203 + goto err; 1.204 + } 1.205 + 1.206 + /* This holds the content we're sending. */ 1.207 + evb = evbuffer_new(); 1.208 + 1.209 + if (S_ISDIR(st.st_mode)) { 1.210 + /* If it's a directory, read the comments and make a little 1.211 + * index page */ 1.212 +#ifdef WIN32 1.213 + HANDLE d; 1.214 + WIN32_FIND_DATAA ent; 1.215 + char *pattern; 1.216 + size_t dirlen; 1.217 +#else 1.218 + DIR *d; 1.219 + struct dirent *ent; 1.220 +#endif 1.221 + const char *trailing_slash = ""; 1.222 + 1.223 + if (!strlen(path) || path[strlen(path)-1] != '/') 1.224 + trailing_slash = "/"; 1.225 + 1.226 +#ifdef WIN32 1.227 + dirlen = strlen(whole_path); 1.228 + pattern = malloc(dirlen+3); 1.229 + memcpy(pattern, whole_path, dirlen); 1.230 + pattern[dirlen] = '\\'; 1.231 + pattern[dirlen+1] = '*'; 1.232 + pattern[dirlen+2] = '\0'; 1.233 + d = FindFirstFileA(pattern, &ent); 1.234 + free(pattern); 1.235 + if (d == INVALID_HANDLE_VALUE) 1.236 + goto err; 1.237 +#else 1.238 + if (!(d = opendir(whole_path))) 1.239 + goto err; 1.240 +#endif 1.241 + 1.242 + evbuffer_add_printf(evb, "<html>\n <head>\n" 1.243 + " <title>%s</title>\n" 1.244 + " <base href='%s%s%s'>\n" 1.245 + " </head>\n" 1.246 + " <body>\n" 1.247 + " <h1>%s</h1>\n" 1.248 + " <ul>\n", 1.249 + decoded_path, /* XXX html-escape this. */ 1.250 + uri_root, path, /* XXX html-escape this? */ 1.251 + trailing_slash, 1.252 + decoded_path /* XXX html-escape this */); 1.253 +#ifdef WIN32 1.254 + do { 1.255 + const char *name = ent.cFileName; 1.256 +#else 1.257 + while ((ent = readdir(d))) { 1.258 + const char *name = ent->d_name; 1.259 +#endif 1.260 + evbuffer_add_printf(evb, 1.261 + " <li><a href=\"%s\">%s</a>\n", 1.262 + name, name);/* XXX escape this */ 1.263 +#ifdef WIN32 1.264 + } while (FindNextFileA(d, &ent)); 1.265 +#else 1.266 + } 1.267 +#endif 1.268 + evbuffer_add_printf(evb, "</ul></body></html>\n"); 1.269 +#ifdef WIN32 1.270 + CloseHandle(d); 1.271 +#else 1.272 + closedir(d); 1.273 +#endif 1.274 + evhttp_add_header(evhttp_request_get_output_headers(req), 1.275 + "Content-Type", "text/html"); 1.276 + } else { 1.277 + /* Otherwise it's a file; add it to the buffer to get 1.278 + * sent via sendfile */ 1.279 + const char *type = guess_content_type(decoded_path); 1.280 + if ((fd = open(whole_path, O_RDONLY)) < 0) { 1.281 + perror("open"); 1.282 + goto err; 1.283 + } 1.284 + 1.285 + if (fstat(fd, &st)<0) { 1.286 + /* Make sure the length still matches, now that we 1.287 + * opened the file :/ */ 1.288 + perror("fstat"); 1.289 + goto err; 1.290 + } 1.291 + evhttp_add_header(evhttp_request_get_output_headers(req), 1.292 + "Content-Type", type); 1.293 + evbuffer_add_file(evb, fd, 0, st.st_size); 1.294 + } 1.295 + 1.296 + evhttp_send_reply(req, 200, "OK", evb); 1.297 + goto done; 1.298 +err: 1.299 + evhttp_send_error(req, 404, "Document was not found"); 1.300 + if (fd>=0) 1.301 + close(fd); 1.302 +done: 1.303 + if (decoded) 1.304 + evhttp_uri_free(decoded); 1.305 + if (decoded_path) 1.306 + free(decoded_path); 1.307 + if (whole_path) 1.308 + free(whole_path); 1.309 + if (evb) 1.310 + evbuffer_free(evb); 1.311 +} 1.312 + 1.313 +static void 1.314 +syntax(void) 1.315 +{ 1.316 + fprintf(stdout, "Syntax: http-server <docroot>\n"); 1.317 +} 1.318 + 1.319 +int 1.320 +main(int argc, char **argv) 1.321 +{ 1.322 + struct event_base *base; 1.323 + struct evhttp *http; 1.324 + struct evhttp_bound_socket *handle; 1.325 + 1.326 + unsigned short port = 0; 1.327 +#ifdef WIN32 1.328 + WSADATA WSAData; 1.329 + WSAStartup(0x101, &WSAData); 1.330 +#else 1.331 + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) 1.332 + return (1); 1.333 +#endif 1.334 + if (argc < 2) { 1.335 + syntax(); 1.336 + return 1; 1.337 + } 1.338 + 1.339 + base = event_base_new(); 1.340 + if (!base) { 1.341 + fprintf(stderr, "Couldn't create an event_base: exiting\n"); 1.342 + return 1; 1.343 + } 1.344 + 1.345 + /* Create a new evhttp object to handle requests. */ 1.346 + http = evhttp_new(base); 1.347 + if (!http) { 1.348 + fprintf(stderr, "couldn't create evhttp. Exiting.\n"); 1.349 + return 1; 1.350 + } 1.351 + 1.352 + /* The /dump URI will dump all requests to stdout and say 200 ok. */ 1.353 + evhttp_set_cb(http, "/dump", dump_request_cb, NULL); 1.354 + 1.355 + /* We want to accept arbitrary requests, so we need to set a "generic" 1.356 + * cb. We can also add callbacks for specific paths. */ 1.357 + evhttp_set_gencb(http, send_document_cb, argv[1]); 1.358 + 1.359 + /* Now we tell the evhttp what port to listen on */ 1.360 + handle = evhttp_bind_socket_with_handle(http, "0.0.0.0", port); 1.361 + if (!handle) { 1.362 + fprintf(stderr, "couldn't bind to port %d. Exiting.\n", 1.363 + (int)port); 1.364 + return 1; 1.365 + } 1.366 + 1.367 + { 1.368 + /* Extract and display the address we're listening on. */ 1.369 + struct sockaddr_storage ss; 1.370 + evutil_socket_t fd; 1.371 + ev_socklen_t socklen = sizeof(ss); 1.372 + char addrbuf[128]; 1.373 + void *inaddr; 1.374 + const char *addr; 1.375 + int got_port = -1; 1.376 + fd = evhttp_bound_socket_get_fd(handle); 1.377 + memset(&ss, 0, sizeof(ss)); 1.378 + if (getsockname(fd, (struct sockaddr *)&ss, &socklen)) { 1.379 + perror("getsockname() failed"); 1.380 + return 1; 1.381 + } 1.382 + if (ss.ss_family == AF_INET) { 1.383 + got_port = ntohs(((struct sockaddr_in*)&ss)->sin_port); 1.384 + inaddr = &((struct sockaddr_in*)&ss)->sin_addr; 1.385 + } else if (ss.ss_family == AF_INET6) { 1.386 + got_port = ntohs(((struct sockaddr_in6*)&ss)->sin6_port); 1.387 + inaddr = &((struct sockaddr_in6*)&ss)->sin6_addr; 1.388 + } else { 1.389 + fprintf(stderr, "Weird address family %d\n", 1.390 + ss.ss_family); 1.391 + return 1; 1.392 + } 1.393 + addr = evutil_inet_ntop(ss.ss_family, inaddr, addrbuf, 1.394 + sizeof(addrbuf)); 1.395 + if (addr) { 1.396 + printf("Listening on %s:%d\n", addr, got_port); 1.397 + evutil_snprintf(uri_root, sizeof(uri_root), 1.398 + "http://%s:%d",addr,got_port); 1.399 + } else { 1.400 + fprintf(stderr, "evutil_inet_ntop failed\n"); 1.401 + return 1; 1.402 + } 1.403 + } 1.404 + 1.405 + event_base_dispatch(base); 1.406 + 1.407 + return 0; 1.408 +}