2 * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
5 * "THE BEER-WARE LICENSE" (Revision 42):
6 * Sergey Lyubka wrote this file. As long as you retain this notice you
7 * can do whatever you want with this stuff. If we meet some day, and you think
8 * this stuff is worth it, you can buy me a beer in return.
12 * Small and portable HTTP server, http://shttpd.sourceforge.net
13 * $Id: shttpd.c,v 1.28 2008/02/17 21:45:09 drozd Exp $
18 time_t current_time; /* Current UTC time */
19 int tz_offset; /* Time zone offset from UTC */
21 const struct vec known_http_methods[] = {
32 struct shttpd_ctx *ctx; /* Context that socket belongs */
33 int sock; /* Listening socket */
34 int is_ssl; /* Should be SSL-ed */
38 * This structure tells how HTTP headers must be parsed.
39 * Used by parse_headers() function.
41 #define OFFSET(x) offsetof(struct headers, x)
42 static const struct http_header http_headers[] = {
43 {16, HDR_INT, OFFSET(cl), "Content-Length: " },
44 {14, HDR_STRING, OFFSET(ct), "Content-Type: " },
45 {12, HDR_STRING, OFFSET(useragent), "User-Agent: " },
46 {19, HDR_DATE, OFFSET(ims), "If-Modified-Since: " },
47 {15, HDR_STRING, OFFSET(auth), "Authorization: " },
48 {9, HDR_STRING, OFFSET(referer), "Referer: " },
49 {8, HDR_STRING, OFFSET(cookie), "Cookie: " },
50 {10, HDR_STRING, OFFSET(location), "Location: " },
51 {8, HDR_INT, OFFSET(status), "Status: " },
52 {7, HDR_STRING, OFFSET(range), "Range: " },
53 {12, HDR_STRING, OFFSET(connection), "Connection: " },
54 {19, HDR_STRING, OFFSET(transenc), "Transfer-Encoding: " },
55 {0, HDR_INT, 0, NULL }
58 struct shttpd_ctx *init_ctx(const char *config_file, int argc, char *argv[]);
59 static void process_connection(struct conn *, int, int);
62 url_decode(const char *src, int src_len, char *dst, int dst_len)
65 #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
67 for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++)
70 if (isxdigit(((unsigned char *) src)[i + 1]) &&
71 isxdigit(((unsigned char *) src)[i + 2])) {
72 a = tolower(((unsigned char *)src)[i + 1]);
73 b = tolower(((unsigned char *)src)[i + 2]);
74 dst[j] = (HEXTOI(a) << 4) | HEXTOI(b);
85 dst[j] = '\0'; /* Null-terminate the destination */
91 is_alias(struct shttpd_ctx *ctx, const char *uri,
92 struct vec *a_uri, struct vec *a_path)
94 const char *p, *s = ctx->options[OPT_ALIASES];
97 DBG(("is_alias: aliases [%s]", s == NULL ? "" : s));
99 FOR_EACH_WORD_IN_LIST(s, len) {
101 if ((p = memchr(s, '=', len)) == NULL || p >= s + len || p == s)
104 if (memcmp(uri, s, p - s) == 0) {
108 a_path->len = (s + len) - p;
117 stop_stream(struct stream *stream)
119 if (stream->io_class != NULL && stream->io_class->close != NULL)
120 stream->io_class->close(stream);
122 stream->io_class= NULL;
123 stream->flags |= FLAG_CLOSED;
124 stream->flags &= ~(FLAG_R | FLAG_W | FLAG_ALWAYS_READY);
126 DBG(("%d %s stopped. %lu of content data, %d now in a buffer",
127 stream->conn->rem.chan.sock,
128 stream->io_class ? stream->io_class->name : "(null)",
129 (unsigned long) stream->io.total, io_data_len(&stream->io)));
133 * Setup listening socket on given port, return socket
136 open_listening_port(int port)
142 {WSADATA data; WSAStartup(MAKEWORD(2,2), &data);}
145 sa.len = sizeof(sa.u.sin);
146 sa.u.sin.sin_family = AF_INET;
147 sa.u.sin.sin_port = htons((uint16_t) port);
148 sa.u.sin.sin_addr.s_addr = htonl(INADDR_ANY);
150 if ((sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
152 if (set_non_blocking_mode(sock) != 0)
154 if (setsockopt(sock, SOL_SOCKET,
155 SO_REUSEADDR,(char *) &on, sizeof(on)) != 0)
157 if (bind(sock, &sa.u.sa, sa.len) < 0)
159 if (listen(sock, 128) != 0)
163 (void) fcntl(sock, F_SETFD, FD_CLOEXEC);
169 (void) closesocket(sock);
170 elog(E_LOG, NULL, "open_listening_port(%d): %s", port, strerror(errno));
175 * Check whether full request is buffered Return headers length, or 0
178 get_headers_len(const char *buf, size_t buflen)
183 for (s = buf, e = s + buflen - 1; len == 0 && s < e; s++)
184 /* Control characters are not allowed but >=128 is. */
185 if (!isprint(* (unsigned char *) s) && *s != '\r' &&
186 *s != '\n' && * (unsigned char *) s < 128)
188 else if (s[0] == '\n' && s[1] == '\n')
190 else if (s[0] == '\n' && &s[1] < e &&
191 s[1] == '\r' && s[2] == '\n')
198 * Send error message back to a client.
201 send_server_error(struct conn *c, int status, const char *reason)
204 struct error_handler *e;
206 LL_FOREACH(&c->ctx->error_handlers, lp) {
207 e = LL_ENTRY(lp, struct error_handler, link);
209 if (e->code == status) {
210 if (c->loc.io_class != NULL &&
211 c->loc.io_class->close != NULL)
212 c->loc.io_class->close(&c->loc);
213 io_clear(&c->loc.io);
214 setup_embedded_stream(c, e->callback, e->callback_data);
219 io_clear(&c->loc.io);
220 c->loc.io.head = my_snprintf(c->loc.io.buf, c->loc.io.size,
222 "Content-Type: text/plain\r\n"
223 "Content-Length: 12\r\n"
226 status, reason, status);
227 c->loc.content_len = 10;
229 stop_stream(&c->loc);
233 * Convert month to the month number. Return -1 on error, or month number
236 montoi(const char *s)
238 static const char *ar[] = {
239 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
240 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
244 for (i = 0; i < sizeof(ar) / sizeof(ar[0]); i++)
245 if (!strcmp(s, ar[i]))
252 * Parse date-time string, and return the corresponding time_t value
255 date_to_epoch(const char *s)
259 int sec, min, hour, mday, month, year;
261 (void) memset(&tm, 0, sizeof(tm));
262 sec = min = hour = mday = month = year = 0;
264 if (((sscanf(s, "%d/%3s/%d %d:%d:%d",
265 &mday, mon, &year, &hour, &min, &sec) == 6) ||
266 (sscanf(s, "%d %3s %d %d:%d:%d",
267 &mday, mon, &year, &hour, &min, &sec) == 6) ||
268 (sscanf(s, "%*3s, %d %3s %d %d:%d:%d",
269 &mday, mon, &year, &hour, &min, &sec) == 6) ||
270 (sscanf(s, "%d-%3s-%d %d:%d:%d",
271 &mday, mon, &year, &hour, &min, &sec) == 6)) &&
272 (month = montoi(mon)) != -1) {
281 if (tm.tm_year > 1900)
283 else if (tm.tm_year < 70)
286 /* Set Daylight Saving Time field */
287 tmp = localtime(¤t_time);
288 tm.tm_isdst = tmp->tm_isdst;
290 return (mktime(&tm));
294 remove_double_dots(char *s)
300 if (s[-1] == '/' || s[-1] == '\\')
301 while (*s == '.' || *s == '/' || *s == '\\')
308 parse_headers(const char *s, int len, struct headers *parsed)
310 const struct http_header *h;
312 const char *p, *e = s + len;
314 DBG(("parsing headers (len %d): [%.*s]", len, len, s));
316 /* Loop through all headers in the request */
319 /* Find where this header ends */
320 for (p = s; p < e && *p != '\n'; ) p++;
322 /* Is this header known to us ? */
323 for (h = http_headers; h->len != 0; h++)
324 if (e - s > h->len &&
325 !my_strncasecmp(s, h->name, h->len))
328 /* If the header is known to us, store its value */
331 /* Shift to where value starts */
334 /* Find place to store the value */
335 v = (union variant *) ((char *) parsed + h->offset);
337 /* Fetch header value into the connection structure */
338 if (h->type == HDR_STRING) {
340 v->v_vec.len = p - s;
341 if (p[-1] == '\r' && v->v_vec.len > 0)
343 } else if (h->type == HDR_INT) {
344 v->v_big_int = strtoul(s, NULL, 10);
345 } else if (h->type == HDR_DATE) {
346 v->v_time = date_to_epoch(s);
350 s = p + 1; /* Shift to the next header */
354 static const struct {
355 const char *extension;
357 const char *mime_type;
358 } builtin_mime_types[] = {
359 {"html", 4, "text/html" },
360 {"htm", 3, "text/html" },
361 {"txt", 3, "text/plain" },
362 {"css", 3, "text/css" },
363 {"ico", 3, "image/x-icon" },
364 {"gif", 3, "image/gif" },
365 {"jpg", 3, "image/jpeg" },
366 {"jpeg", 4, "image/jpeg" },
367 {"png", 3, "image/png" },
368 {"svg", 3, "image/svg+xml" },
369 {"torrent", 7, "application/x-bittorrent" },
370 {"wav", 3, "audio/x-wav" },
371 {"mp3", 3, "audio/x-mp3" },
372 {"mid", 3, "audio/mid" },
373 {"m3u", 3, "audio/x-mpegurl" },
374 {"ram", 3, "audio/x-pn-realaudio" },
375 {"ra", 2, "audio/x-pn-realaudio" },
376 {"doc", 3, "application/msword", },
377 {"exe", 3, "application/octet-stream" },
378 {"zip", 3, "application/x-zip-compressed" },
379 {"xls", 3, "application/excel" },
380 {"tgz", 3, "application/x-tar-gz" },
381 {"tar.gz", 6, "application/x-tar-gz" },
382 {"tar", 3, "application/x-tar" },
383 {"gz", 2, "application/x-gunzip" },
384 {"arj", 3, "application/x-arj-compressed" },
385 {"rar", 3, "application/x-arj-compressed" },
386 {"rtf", 3, "application/rtf" },
387 {"pdf", 3, "application/pdf" },
388 {"swf", 3, "application/x-shockwave-flash" },
389 {"mpg", 3, "video/mpeg" },
390 {"mpeg", 4, "video/mpeg" },
391 {"asf", 3, "video/x-ms-asf" },
392 {"avi", 3, "video/x-msvideo" },
393 {"bmp", 3, "image/bmp" },
398 get_mime_type(struct shttpd_ctx *ctx, const char *uri, int len, struct vec *vec)
400 const char *eq, *p = ctx->options[OPT_MIME_TYPES];
403 /* Firt, loop through the custom mime types if any */
404 FOR_EACH_WORD_IN_LIST(p, n) {
405 if ((eq = memchr(p, '=', n)) == NULL || eq >= p + n || eq == p)
408 if (len > ext_len && uri[len - ext_len - 1] == '.' &&
409 !my_strncasecmp(p, &uri[len - ext_len], ext_len)) {
411 vec->len = p + n - vec->ptr;
416 /* If no luck, try built-in mime types */
417 for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
418 ext_len = builtin_mime_types[i].ext_len;
419 if (len > ext_len && uri[len - ext_len - 1] == '.' &&
420 !my_strncasecmp(builtin_mime_types[i].extension,
421 &uri[len - ext_len], ext_len)) {
422 vec->ptr = builtin_mime_types[i].mime_type;
423 vec->len = strlen(vec->ptr);
428 /* Oops. This extension is unknown to us. Fallback to text/plain */
429 vec->ptr = "text/plain";
430 vec->len = strlen(vec->ptr);
434 * For given directory path, substitute it to valid index file.
435 * Return 0 if index file has been found, -1 if not found
438 find_index_file(struct conn *c, char *path, size_t maxpath, struct stat *stp)
440 char buf[FILENAME_MAX];
441 const char *s = c->ctx->options[OPT_INDEX_FILES];
444 FOR_EACH_WORD_IN_LIST(s, len) {
445 my_snprintf(buf, sizeof(buf), "%s%c%.*s",path, DIRSEP, len, s);
446 if (my_stat(buf, stp) == 0) {
447 my_strlcpy(path, buf, maxpath);
448 get_mime_type(c->ctx, s, len, &c->mime_type);
457 * Try to open requested file, return 0 if OK, -1 if error.
458 * If the file is given arguments using PATH_INFO mechanism,
459 * initialize pathinfo pointer.
462 get_path_info(struct conn *c, char *path, struct stat *stp)
466 if (my_stat(path, stp) == 0)
469 p = path + strlen(path);
470 e = path + strlen(c->ctx->options[OPT_ROOT]) + 2;
472 /* Strip directory parts of the path one by one */
476 if (!my_stat(path, stp) && !S_ISDIR(stp->st_mode)) {
477 c->path_info = p + 1;
489 decide_what_to_do(struct conn *c)
491 char path[URI_MAX], buf[1024], *root;
492 struct vec alias_uri, alias_path;
495 struct registered_uri *ruri;
497 DBG(("decide_what_to_do: [%s]", c->uri));
499 if ((c->query = strchr(c->uri, '?')) != NULL)
502 url_decode(c->uri, strlen(c->uri), c->uri, strlen(c->uri) + 1);
503 remove_double_dots(c->uri);
505 root = c->ctx->options[OPT_ROOT];
506 if (strlen(c->uri) + strlen(root) >= sizeof(path)) {
507 send_server_error(c, 400, "URI is too long");
511 (void) my_snprintf(path, sizeof(path), "%s%s", root, c->uri);
513 /* User may use the aliases - check URI for mount point */
514 if (is_alias(c->ctx, c->uri, &alias_uri, &alias_path) != NULL) {
515 (void) my_snprintf(path, sizeof(path), "%.*s%s",
516 alias_path.len, alias_path.ptr, c->uri + alias_uri.len);
517 DBG(("using alias %.*s -> %.*s", alias_uri.len, alias_uri.ptr,
518 alias_path.len, alias_path.ptr));
521 #if !defined(NO_AUTH)
522 if (check_authorization(c, path) != 1) {
523 send_authorization_request(c);
526 if ((ruri = is_registered_uri(c->ctx, c->uri)) != NULL) {
527 setup_embedded_stream(c, ruri->callback, ruri->callback_data);
529 if (strstr(path, HTPASSWD)) {
530 /* Do not allow to view passwords files */
531 send_server_error(c, 403, "Forbidden");
533 #if !defined(NO_AUTH)
534 if ((c->method == METHOD_PUT || c->method == METHOD_DELETE) &&
535 (c->ctx->options[OPT_AUTH_PUT] == NULL ||
536 !is_authorized_for_put(c))) {
537 send_authorization_request(c);
540 if (c->method == METHOD_PUT) {
541 c->status = my_stat(path, &st) == 0 ? 200 : 201;
543 if (c->ch.range.v_vec.len > 0) {
544 send_server_error(c, 501, "PUT Range Not Implemented");
545 } else if ((rc = put_dir(path)) == 0) {
546 send_server_error(c, 200, "OK");
547 } else if (rc == -1) {
548 send_server_error(c, 500, "PUT Directory Error");
549 } else if (c->rem.content_len == 0) {
550 send_server_error(c, 411, "Length Required");
551 } else if ((c->loc.chan.fd = my_open(path, O_WRONLY | O_BINARY |
552 O_CREAT | O_NONBLOCK | O_TRUNC, 0644)) == -1) {
553 send_server_error(c, 500, "PUT Error");
555 DBG(("PUT file [%s]", c->uri));
556 c->loc.io_class = &io_file;
557 c->loc.flags |= FLAG_W | FLAG_ALWAYS_READY ;
559 } else if (c->method == METHOD_DELETE) {
560 DBG(("DELETE [%s]", c->uri));
561 if (my_remove(path) == 0)
562 send_server_error(c, 200, "OK");
564 send_server_error(c, 500, "DELETE Error");
565 } else if (get_path_info(c, path, &st) != 0) {
566 send_server_error(c, 404, "Not Found");
567 } else if (S_ISDIR(st.st_mode) && path[strlen(path) - 1] != '/') {
568 (void) my_snprintf(buf, sizeof(buf),
569 "Moved Permanently\r\nLocation: %s/", c->uri);
570 send_server_error(c, 301, buf);
571 } else if (S_ISDIR(st.st_mode) &&
572 find_index_file(c, path, sizeof(path) - 1, &st) == -1 &&
573 !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
574 send_server_error(c, 403, "Directory Listing Denied");
575 } else if (S_ISDIR(st.st_mode) && IS_TRUE(c->ctx, OPT_DIR_LIST)) {
576 if ((c->loc.chan.dir.path = my_strdup(path)) != NULL)
579 send_server_error(c, 500, "GET Directory Error");
580 } else if (S_ISDIR(st.st_mode) && !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
581 send_server_error(c, 403, "Directory listing denied");
583 } else if (match_extension(path, c->ctx->options[OPT_CGI_EXTENSIONS])) {
584 if (c->method != METHOD_POST && c->method != METHOD_GET) {
585 send_server_error(c, 501, "Bad method ");
586 } else if ((run_cgi(c, path)) == -1) {
587 send_server_error(c, 500, "Cannot exec CGI");
593 } else if (match_extension(path, c->ctx->options[OPT_SSI_EXTENSIONS])) {
594 if ((c->loc.chan.fd = my_open(path,
595 O_RDONLY | O_BINARY, 0644)) == -1) {
596 send_server_error(c, 500, "SSI open error");
601 } else if (c->ch.ims.v_time && st.st_mtime <= c->ch.ims.v_time) {
602 send_server_error(c, 304, "Not Modified");
603 } else if ((c->loc.chan.fd = my_open(path,
604 O_RDONLY | O_BINARY, 0644)) != -1) {
607 send_server_error(c, 500, "Internal Error");
612 set_request_method(struct conn *c)
616 /* Set the request method */
617 for (v = known_http_methods; v->ptr != NULL; v++)
618 if (!memcmp(c->rem.io.buf, v->ptr, v->len)) {
619 c->method = v - known_http_methods;
623 return (v->ptr == NULL);
627 parse_http_request(struct conn *c)
629 char *s, *e, *p, *start;
630 int uri_len, req_len, n;
632 s = io_data(&c->rem.io);;
633 req_len = c->rem.headers_len =
634 get_headers_len(s, io_data_len(&c->rem.io));
636 if (req_len == 0 && io_space_len(&c->rem.io) == 0) {
637 io_clear(&c->rem.io);
638 send_server_error(c, 400, "Request is too big");
641 io_inc_tail(&c->rem.io, req_len);
645 } else if (req_len < 16) { /* Minimal: "GET / HTTP/1.0\n\n" */
646 send_server_error(c, 400, "Bad request");
647 } else if (set_request_method(c)) {
648 send_server_error(c, 501, "Method Not Implemented");
649 } else if ((c->request = my_strndup(s, req_len)) == NULL) {
650 send_server_error(c, 500, "Cannot allocate request");
653 if (c->loc.flags & FLAG_CLOSED)
656 DBG(("Conn %d: parsing request: [%.*s]", c->rem.chan.sock, req_len, s));
657 c->rem.flags |= FLAG_HEADERS_PARSED;
659 /* Set headers pointer. Headers follow the request line */
660 c->headers = memchr(c->request, '\n', req_len);
661 assert(c->headers != NULL);
662 assert(c->headers < c->request + req_len);
663 if (c->headers > c->request && c->headers[-1] == '\r')
664 c->headers[-1] = '\0';
665 *c->headers++ = '\0';
668 * Now make a copy of the URI, because it will be URL-decoded,
669 * and we need a copy of unmodified URI for the access log.
670 * First, we skip the REQUEST_METHOD and shift to the URI.
672 for (p = c->request, e = p + req_len; *p != ' ' && p < e; p++);
673 while (p < e && *p == ' ')
676 /* Now remember where URI starts, and shift to the end of URI */
677 for (start = p; p < e && !isspace((unsigned char)*p); ) p++;
680 /* Skip space following the URI */
681 while (p < e && *p == ' ')
684 /* Now comes the HTTP-Version in the form HTTP/<major>.<minor> */
685 if (sscanf(p, "HTTP/%lu.%lu%n",
686 &c->major_version, &c->minor_version, &n) != 2 || p[n] != '\0') {
687 send_server_error(c, 400, "Bad HTTP version");
688 } else if (c->major_version > 1 ||
689 (c->major_version == 1 && c->minor_version > 1)) {
690 send_server_error(c, 505, "HTTP version not supported");
691 } else if (uri_len <= 0) {
692 send_server_error(c, 400, "Bad URI");
693 } else if ((c->uri = malloc(uri_len + 1)) == NULL) {
694 send_server_error(c, 500, "Cannot allocate URI");
696 my_strlcpy(c->uri, (char *) start, uri_len + 1);
697 parse_headers(c->headers,
698 (c->request + req_len) - c->headers, &c->ch);
700 /* Remove the length of request from total, count only data */
701 assert(c->rem.io.total >= (big_int_t) req_len);
702 c->rem.io.total -= req_len;
703 c->rem.content_len = c->ch.cl.v_big_int;
704 decide_what_to_do(c);
709 shttpd_add_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
713 int l = IS_TRUE(ctx, OPT_INETD) ? E_FATAL : E_LOG;
718 sa.len = sizeof(sa.u.sin);
719 (void) set_non_blocking_mode(sock);
721 if (getpeername(sock, &sa.u.sa, &sa.len)) {
722 elog(l, NULL, "add_socket: %s", strerror(errno));
724 } else if (is_ssl && (ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
725 elog(l, NULL, "add_socket: SSL_new: %s", strerror(ERRNO));
726 (void) closesocket(sock);
727 } else if (is_ssl && SSL_set_fd(ssl, sock) == 0) {
728 elog(l, NULL, "add_socket: SSL_set_fd: %s", strerror(ERRNO));
729 (void) closesocket(sock);
732 } else if ((c = calloc(1, sizeof(*c) + 2 * URI_MAX)) == NULL) {
737 (void) closesocket(sock);
738 elog(l, NULL, "add_socket: calloc: %s", strerror(ERRNO));
741 c->rem.conn = c->loc.conn = c;
744 c->birth_time = current_time;
745 c->expire_time = current_time + EXPIRE_TIME;
747 (void) getsockname(sock, &sa.u.sa, &sa.len);
748 c->loc_port = sa.u.sin.sin_port;
750 set_close_on_exec(sock);
752 c->loc.io_class = NULL;
754 c->rem.io_class = &io_socket;
755 c->rem.chan.sock = sock;
758 c->loc.io.buf = (char *) (c + 1);
759 c->rem.io.buf = c->loc.io.buf + URI_MAX;
760 c->loc.io.size = c->rem.io.size = URI_MAX;
764 c->rem.io_class = &io_ssl;
765 c->rem.chan.ssl.sock = sock;
766 c->rem.chan.ssl.ssl = ssl;
767 ssl_handshake(&c->rem);
771 EnterCriticalSection(&ctx->mutex);
772 LL_TAIL(&ctx->connections, &c->link);
774 LeaveCriticalSection(&ctx->mutex);
776 DBG(("%s:%hu connected (socket %d)",
777 inet_ntoa(* (struct in_addr *) &sa.u.sin.sin_addr.s_addr),
778 ntohs(sa.u.sin.sin_port), sock));
783 shttpd_active(struct shttpd_ctx *ctx)
785 return (ctx->nactive);
789 * Setup a listening socket on given port. Return opened socket or -1
792 shttpd_listen(struct shttpd_ctx *ctx, int port, int is_ssl)
797 if ((sock = open_listening_port(port)) == -1) {
798 elog(E_FATAL, NULL, "cannot open port %d", port);
799 } else if ((l = calloc(1, sizeof(*l))) == NULL) {
800 (void) closesocket(sock);
801 elog(E_FATAL, NULL, "cannot allocate listener");
802 } else if (is_ssl && ctx->ssl_ctx == NULL) {
803 (void) closesocket(sock);
804 elog(E_FATAL, NULL, "cannot add SSL socket, "
805 "please specify certificate file");
810 LL_TAIL(&ctx->listeners, &l->link);
811 DBG(("shttpd_listen: added socket %d", sock));
818 shttpd_accept(int lsn_sock, int milliseconds)
825 tv.tv_sec = milliseconds / 1000;
826 tv.tv_usec = milliseconds % 1000;
827 sa.len = sizeof(sa.u.sin);
829 FD_SET(lsn_sock, &read_set);
831 if (select(lsn_sock + 1, &read_set, NULL, NULL, &tv) == 1)
832 sock = accept(lsn_sock, &sa.u.sa, &sa.len);
838 read_stream(struct stream *stream)
842 len = io_space_len(&stream->io);
845 /* Do not read more that needed */
846 if (stream->content_len > 0 &&
847 stream->io.total + len > stream->content_len)
848 len = stream->content_len - stream->io.total;
850 /* Read from underlying channel */
851 n = stream->nread_last = stream->io_class->read(stream,
852 io_space(&stream->io), len);
855 io_inc_head(&stream->io, n);
856 else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
857 n = n; /* Ignore EINTR and EAGAIN */
858 else if (!(stream->flags & FLAG_DONT_CLOSE))
861 DBG(("read_stream (%d %s): read %d/%d/%lu bytes (errno %d)",
862 stream->conn->rem.chan.sock,
863 stream->io_class ? stream->io_class->name : "(null)",
864 n, len, (unsigned long) stream->io.total, ERRNO));
867 * Close the local stream if everything was read
868 * XXX We do not close the remote stream though! It may be
869 * a POST data completed transfer, we do not want the socket
872 if (stream->content_len > 0 && stream == &stream->conn->loc) {
873 assert(stream->io.total <= stream->content_len);
874 if (stream->io.total == stream->content_len)
878 stream->conn->expire_time = current_time + EXPIRE_TIME;
882 write_stream(struct stream *from, struct stream *to)
886 len = io_data_len(&from->io);
889 /* TODO: should be assert on CAN_WRITE flag */
890 n = to->io_class->write(to, io_data(&from->io), len);
891 to->conn->expire_time = current_time + EXPIRE_TIME;
892 DBG(("write_stream (%d %s): written %d/%d bytes (errno %d)",
893 to->conn->rem.chan.sock,
894 to->io_class ? to->io_class->name : "(null)", n, len, ERRNO));
897 io_inc_tail(&from->io, n);
898 else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
899 n = n; /* Ignore EINTR and EAGAIN */
900 else if (!(to->flags & FLAG_DONT_CLOSE))
906 disconnect(struct llhead *lp)
908 struct conn *c = LL_ENTRY(lp, struct conn, link);
909 static const struct vec vec = {"close", 5};
912 DBG(("Disconnecting %d (%.*s)", c->rem.chan.sock,
913 c->ch.connection.v_vec.len, c->ch.connection.v_vec.ptr));
915 if (c->request != NULL && c->ctx->access_log != NULL)
916 log_access(c->ctx->access_log, c);
918 /* In inetd mode, exit if request is finished. */
919 if (IS_TRUE(c->ctx, OPT_INETD))
922 if (c->loc.io_class != NULL && c->loc.io_class->close != NULL)
923 c->loc.io_class->close(&c->loc);
926 * Check the "Connection: " header before we free c->request
927 * If it its 'keep-alive', then do not close the connection
929 do_close = (c->ch.connection.v_vec.len >= vec.len &&
930 !my_strncasecmp(vec.ptr, c->ch.connection.v_vec.ptr, vec.len)) ||
931 (c->major_version < 1 ||
932 (c->major_version >= 1 && c->minor_version < 1));
939 /* Keep the connection open only if we have Content-Length set */
940 if (!do_close && c->loc.content_len > 0) {
941 c->loc.io_class = NULL;
943 c->loc.content_len = 0;
944 c->rem.flags = FLAG_W | FLAG_R;
945 c->query = c->request = c->uri = c->path_info = NULL;
946 c->mime_type.len = 0;
947 (void) memset(&c->ch, 0, sizeof(c->ch));
948 io_clear(&c->loc.io);
949 c->birth_time = current_time;
950 if (io_data_len(&c->rem.io) > 0)
951 process_connection(c, 0, 0);
953 if (c->rem.io_class != NULL)
954 c->rem.io_class->close(&c->rem);
956 EnterCriticalSection(&c->ctx->mutex);
959 assert(c->ctx->nactive >= 0);
960 LeaveCriticalSection(&c->ctx->mutex);
967 is_allowed(const struct shttpd_ctx *ctx, const struct usa *usa)
969 const struct acl *acl;
970 const struct llhead *lp;
974 LL_FOREACH(&ctx->acl, lp) {
975 acl = LL_ENTRY(lp, struct acl, link);
976 (void) memcpy(&ip, &usa->u.sin.sin_addr, sizeof(ip));
977 if (acl->ip == (ntohl(ip) & acl->mask))
981 return (allowed == '+');
985 add_to_set(int fd, fd_set *set, int *max_fd)
993 process_connection(struct conn *c, int remote_ready, int local_ready)
995 /* Read from remote end if it is ready */
996 if (remote_ready && io_space_len(&c->rem.io))
997 read_stream(&c->rem);
999 /* If the request is not parsed yet, do so */
1000 if (!(c->rem.flags & FLAG_HEADERS_PARSED))
1001 parse_http_request(c);
1003 DBG(("loc: %u [%.*s]", io_data_len(&c->loc.io),
1004 io_data_len(&c->loc.io), io_data(&c->loc.io)));
1005 DBG(("rem: %u [%.*s]", io_data_len(&c->rem.io),
1006 io_data_len(&c->rem.io), io_data(&c->rem.io)));
1008 /* Read from the local end if it is ready */
1009 if (local_ready && io_space_len(&c->loc.io))
1010 read_stream(&c->loc);
1012 if (io_data_len(&c->rem.io) > 0 && (c->loc.flags & FLAG_W) &&
1013 c->loc.io_class != NULL && c->loc.io_class->write != NULL)
1014 write_stream(&c->rem, &c->loc);
1016 if (io_data_len(&c->loc.io) > 0 && c->rem.io_class != NULL)
1017 write_stream(&c->loc, &c->rem);
1019 if (c->rem.nread_last > 0)
1020 c->ctx->in += c->rem.nread_last;
1021 if (c->loc.nread_last > 0)
1022 c->ctx->out += c->loc.nread_last;
1024 /* Check whether we should close this connection */
1025 if ((current_time > c->expire_time) ||
1026 (c->rem.flags & FLAG_CLOSED) ||
1027 ((c->loc.flags & FLAG_CLOSED) && !io_data_len(&c->loc.io)))
1028 disconnect(&c->link);
1032 * One iteration of server loop. This is the core of the data exchange.
1035 shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
1037 struct llhead *lp, *tmp;
1040 struct timeval tv; /* Timeout for select() */
1041 fd_set read_set, write_set;
1042 int sock, max_fd = -1, msec = milliseconds;
1045 current_time = time(0);
1047 FD_ZERO(&write_set);
1049 /* Add listening sockets to the read set */
1050 LL_FOREACH(&ctx->listeners, lp) {
1051 l = LL_ENTRY(lp, struct listener, link);
1052 FD_SET(l->sock, &read_set);
1053 if (l->sock > max_fd)
1055 DBG(("FD_SET(%d) (listening)", l->sock));
1058 /* Multiplex streams */
1059 LL_FOREACH(&ctx->connections, lp) {
1060 c = LL_ENTRY(lp, struct conn, link);
1062 /* If there is a space in remote IO, check remote socket */
1063 if (io_space_len(&c->rem.io))
1064 add_to_set(c->rem.chan.fd, &read_set, &max_fd);
1066 #if !defined(NO_CGI)
1068 * If there is a space in local IO, and local endpoint is
1069 * CGI, check local socket for read availability
1071 if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
1072 c->loc.io_class == &io_cgi)
1073 add_to_set(c->loc.chan.fd, &read_set, &max_fd);
1076 * If there is some data read from remote socket, and
1077 * local endpoint is CGI, check local for write availability
1079 if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
1080 c->loc.io_class == &io_cgi)
1081 add_to_set(c->loc.chan.fd, &write_set, &max_fd);
1085 * If there is some data read from local endpoint, check the
1086 * remote socket for write availability
1088 if (io_data_len(&c->loc.io))
1089 add_to_set(c->rem.chan.fd, &write_set, &max_fd);
1091 if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
1092 (c->loc.flags & FLAG_ALWAYS_READY))
1095 if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
1096 (c->loc.flags & FLAG_ALWAYS_READY))
1100 tv.tv_sec = msec / 1000;
1101 tv.tv_usec = (msec % 1000) * 1000;
1103 /* Check IO readiness */
1104 if (select(max_fd + 1, &read_set, &write_set, NULL, &tv) < 0) {
1107 * On windows, if read_set and write_set are empty,
1108 * select() returns "Invalid parameter" error
1109 * (at least on my Windows XP Pro). So in this case,
1112 Sleep(milliseconds);
1114 DBG(("select: %d", ERRNO));
1118 /* Check for incoming connections on listener sockets */
1119 LL_FOREACH(&ctx->listeners, lp) {
1120 l = LL_ENTRY(lp, struct listener, link);
1121 if (!FD_ISSET(l->sock, &read_set))
1124 sa.len = sizeof(sa.u.sin);
1125 if ((sock = accept(l->sock, &sa.u.sa, &sa.len)) != -1) {
1127 if (!is_allowed(ctx, &sa)) {
1128 elog(E_LOG, NULL, "shttpd_poll: %s "
1129 "is not allowed to connect",
1130 inet_ntoa(sa.u.sin.sin_addr));
1131 (void) closesocket(sock);
1133 shttpd_add_socket(ctx, sock, l->is_ssl);
1136 if (sock >= (int) FD_SETSIZE) {
1138 "shttpd_poll: ctx %p: disarding "
1139 "socket %d, too busy", ctx, sock);
1140 (void) closesocket(sock);
1141 } else if (!is_allowed(ctx, &sa)) {
1142 elog(E_LOG, NULL, "shttpd_poll: %s "
1143 "is not allowed to connect",
1144 inet_ntoa(sa.u.sin.sin_addr));
1145 (void) closesocket(sock);
1147 shttpd_add_socket(ctx, sock, l->is_ssl);
1151 } while (sock != -1);
1154 /* Process all connections */
1155 LL_FOREACH_SAFE(&ctx->connections, lp, tmp) {
1156 c = LL_ENTRY(lp, struct conn, link);
1157 process_connection(c, FD_ISSET(c->rem.chan.fd, &read_set),
1158 ((c->loc.flags & FLAG_ALWAYS_READY)
1159 #if !defined(NO_CGI)
1160 || (c->loc.io_class == &io_cgi &&
1161 FD_ISSET(c->loc.chan.fd, &read_set))
1168 free_list(struct llhead *head, void (*dtor)(struct llhead *))
1170 struct llhead *lp, *tmp;
1172 LL_FOREACH_SAFE(head, lp, tmp) {
1179 listener_destructor(struct llhead *lp)
1181 struct listener *listener = LL_ENTRY(lp, struct listener, link);
1183 (void) closesocket(listener->sock);
1188 registered_uri_destructor(struct llhead *lp)
1190 struct registered_uri *ruri = LL_ENTRY(lp, struct registered_uri, link);
1192 free((void *) ruri->uri);
1197 acl_destructor(struct llhead *lp)
1199 struct acl *acl = LL_ENTRY(lp, struct acl, link);
1204 * Deallocate shttpd object, free up the resources
1207 shttpd_fini(struct shttpd_ctx *ctx)
1211 free_list(&ctx->connections, disconnect);
1212 free_list(&ctx->registered_uris, registered_uri_destructor);
1213 free_list(&ctx->acl, acl_destructor);
1214 free_list(&ctx->listeners, listener_destructor);
1215 free_list(&ctx->ssi_funcs, ssi_func_destructor);
1217 for (i = 0; i < NELEMS(ctx->options); i++)
1218 if (ctx->options[i] != NULL)
1219 free(ctx->options[i]);
1221 if (ctx->access_log) (void) fclose(ctx->access_log);
1222 if (ctx->error_log) (void) fclose(ctx->error_log);
1224 /* TODO: free SSL context */