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.10 2007/06/01 17:59:32 drozd Exp $
18 time_t current_time; /* Current UTC time */
19 int tz_offset; /* Time zone offset from UTC */
21 static LL_HEAD(listeners); /* List of listening sockets */
23 const struct vec known_http_methods[] = {
34 struct shttpd_ctx *ctx; /* Context that socket belongs */
35 int sock; /* Listening socket */
36 int is_ssl; /* Should be SSL-ed */
40 * This structure tells how HTTP headers must be parsed.
41 * Used by parse_headers() function.
43 #define OFFSET(x) offsetof(struct headers, x)
44 static const struct http_header http_headers[] = {
45 {16, HDR_INT, OFFSET(cl), "Content-Length: " },
46 {14, HDR_STRING, OFFSET(ct), "Content-Type: " },
47 {12, HDR_STRING, OFFSET(useragent), "User-Agent: " },
48 {19, HDR_DATE, OFFSET(ims), "If-Modified-Since: " },
49 {15, HDR_STRING, OFFSET(auth), "Authorization: " },
50 {9, HDR_STRING, OFFSET(referer), "Referer: " },
51 {8, HDR_STRING, OFFSET(cookie), "Cookie: " },
52 {10, HDR_STRING, OFFSET(location), "Location: " },
53 {8, HDR_INT, OFFSET(status), "Status: " },
54 {7, HDR_STRING, OFFSET(range), "Range: " },
55 {12, HDR_STRING, OFFSET(connection), "Connection: " },
56 {19, HDR_STRING, OFFSET(transenc), "Transfer-Encoding: " },
57 {0, HDR_INT, 0, NULL }
60 struct shttpd_ctx *init_ctx(const char *config_file, int argc, char *argv[]);
63 url_decode(const char *src, int src_len, char *dst, int dst_len)
66 #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
68 for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++)
71 if (isxdigit(((unsigned char *) src)[i + 1]) &&
72 isxdigit(((unsigned char *) src)[i + 2])) {
73 a = tolower(((unsigned char *)src)[i + 1]);
74 b = tolower(((unsigned char *)src)[i + 2]);
75 dst[j] = (HEXTOI(a) << 4) | HEXTOI(b);
89 dst[j] = '\0'; /* Null-terminate the destination */
95 shttpd_add_mime_type(struct shttpd_ctx *ctx, const char *ext, const char *mime)
97 struct mime_type_link *e;
98 const char *error_msg = "shttpd_add_mime_type: no memory";
100 if ((e = malloc(sizeof(*e))) == NULL) {
101 elog(E_FATAL, 0, error_msg);
102 } else if ((e->ext= my_strdup(ext)) == NULL) {
103 elog(E_FATAL, 0, error_msg);
104 } else if ((e->mime = my_strdup(mime)) == NULL) {
105 elog(E_FATAL, 0, error_msg);
107 e->ext_len = strlen(ext);
108 LL_TAIL(&ctx->mime_types, &e->link);
114 is_alias(struct shttpd_ctx *ctx, const char *uri,
115 struct vec *a_uri, struct vec *a_path)
117 const char *p, *s = ctx->aliases;
120 DBG(("is_alias: aliases [%s]", s == NULL ? "" : s));
122 FOR_EACH_WORD_IN_LIST(s, len) {
123 if ((p = memchr(s, '=', len)) != NULL &&
124 memcmp(uri, s, p - s) == 0) {
128 a_path->len = (s + len) - p;
137 stop_stream(struct stream *stream)
139 if (stream->io_class != NULL && stream->io_class->close != NULL)
140 stream->io_class->close(stream);
142 stream->io_class= NULL;
143 stream->flags |= FLAG_CLOSED;
144 stream->flags &= ~(FLAG_R | FLAG_W | FLAG_ALWAYS_READY);
146 DBG(("%d %s stopped. %lu of content data, %d now in a buffer",
147 stream->conn->rem.chan.sock,
148 stream->io_class ? stream->io_class->name : "(null)",
149 (unsigned long) stream->io.total, io_data_len(&stream->io)));
153 * Setup listening socket on given port, return socket
156 open_listening_port(int port)
162 {WSADATA data; WSAStartup(MAKEWORD(2,2), &data);}
165 sa.len = sizeof(sa.u.sin);
166 sa.u.sin.sin_family = AF_INET;
167 sa.u.sin.sin_port = htons((uint16_t) port);
168 sa.u.sin.sin_addr.s_addr = htonl(INADDR_ANY);
170 if ((sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
172 if (set_non_blocking_mode(sock) != 0)
174 if (setsockopt(sock, SOL_SOCKET,
175 SO_REUSEADDR,(char *) &on, sizeof(on)) != 0)
177 if (bind(sock, &sa.u.sa, sa.len) < 0)
179 if (listen(sock, 128) != 0)
183 (void) fcntl(sock, F_SETFD, FD_CLOEXEC);
189 (void) closesocket(sock);
190 elog(E_LOG, NULL, "open_listening_port(%d): %s", port, strerror(errno));
195 * Check whether full request is buffered Return headers length, or 0
198 get_headers_len(const char *buf, size_t buflen)
203 for (s = buf, e = s + buflen - 1; len == 0 && s < e; s++)
204 /* Control characters are not allowed but >=128 is. */
205 if (!isprint(*(unsigned char *)s) && *s != '\r' && *s != '\n' && *(unsigned char *)s < 128)
207 else if (s[0] == '\n' && s[1] == '\n')
209 else if (s[0] == '\n' && &s[1] < e &&
210 s[1] == '\r' && s[2] == '\n')
217 * Send error message back to a client.
220 send_server_error(struct conn *c, int status, const char *reason)
224 struct error_handler *e;
226 LL_FOREACH(&c->ctx->error_handlers, lp) {
227 e = LL_ENTRY(lp, struct error_handler, link);
229 if (e->code == status) {
230 if (c->loc.io_class != NULL &&
231 c->loc.io_class->close != NULL)
232 c->loc.io_class->close(&c->loc);
233 io_clear(&c->loc.io);
234 setup_embedded_stream(c, e->callback, NULL);
238 #endif /* EMBEDDED */
240 io_clear(&c->loc.io);
241 c->loc.headers_len = c->loc.io.head = my_snprintf(c->loc.io.buf,
242 c->loc.io.size, "HTTP/1.1 %d %s\r\n\r\n%d %s",
243 status, reason, status, reason);
245 stop_stream(&c->loc);
249 * Convert month to the month number. Return -1 on error, or month number
252 montoi(const char *s)
254 static const char *ar[] = {
255 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
256 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
260 for (i = 0; i < sizeof(ar) / sizeof(ar[0]); i++)
261 if (!strcmp(s, ar[i]))
268 * Parse date-time string, and return the corresponding time_t value
271 date_to_epoch(const char *s)
275 int sec, min, hour, mday, month, year;
277 (void) memset(&tm, 0, sizeof(tm));
278 sec = min = hour = mday = month = year = 0;
280 if (((sscanf(s, "%d/%3s/%d %d:%d:%d",
281 &mday, mon, &year, &hour, &min, &sec) == 6) ||
282 (sscanf(s, "%d %3s %d %d:%d:%d",
283 &mday, mon, &year, &hour, &min, &sec) == 6) ||
284 (sscanf(s, "%*3s, %d %3s %d %d:%d:%d",
285 &mday, mon, &year, &hour, &min, &sec) == 6) ||
286 (sscanf(s, "%d-%3s-%d %d:%d:%d",
287 &mday, mon, &year, &hour, &min, &sec) == 6)) &&
288 (month = montoi(mon)) != -1) {
297 if (tm.tm_year > 1900)
299 else if (tm.tm_year < 70)
302 /* Set Daylight Saving Time field */
303 tmp = localtime(¤t_time);
304 tm.tm_isdst = tmp->tm_isdst;
306 return (mktime(&tm));
310 remove_double_dots(char *s)
317 while (*s == '.' || *s == '/')
324 parse_headers(const char *s, int len, struct headers *parsed)
326 const struct http_header *h;
328 const char *p, *e = s + len;
330 DBG(("parsing headers (len %d): [%.*s]", len, len, s));
332 /* Loop through all headers in the request */
335 /* Find where this header ends */
336 for (p = s; p < e && *p != '\n'; ) p++;
338 /* Is this header known to us ? */
339 for (h = http_headers; h->len != 0; h++)
340 if (e - s > h->len &&
341 !my_strncasecmp(s, h->name, h->len))
344 /* If the header is known to us, store its value */
347 /* Shift to where value starts */
350 /* Find place to store the value */
351 v = (union variant *) ((char *) parsed + h->offset);
353 /* Fetch header value into the connection structure */
354 if (h->type == HDR_STRING) {
356 v->v_vec.len = p - s;
357 if (p[-1] == '\r' && v->v_vec.len > 0)
359 } else if (h->type == HDR_INT) {
360 v->v_big_int = strtoul(s, NULL, 10);
361 } else if (h->type == HDR_DATE) {
362 v->v_time = date_to_epoch(s);
366 s = p + 1; /* Shift to the next header */
371 * For given directory path, substitute it to valid index file.
372 * Return 0 if index file has been found, -1 if not found
375 find_index_file(struct conn *c, char *path, size_t maxpath, struct stat *stp)
377 char buf[FILENAME_MAX];
378 const char *s = c->ctx->index_files;
381 FOR_EACH_WORD_IN_LIST(s, len) {
382 my_snprintf(buf, sizeof(buf), "%s%c%.*s",path, DIRSEP, len, s);
383 if (my_stat(buf, stp) == 0) {
384 my_strlcpy(path, buf, maxpath);
385 c->mime_type = get_mime_type(c->ctx, s, len);
394 * Try to open requested file, return 0 if OK, -1 if error.
395 * If the file is given arguments using PATH_INFO mechanism,
396 * initialize pathinfo pointer.
399 get_path_info(struct conn *c, char *path, struct stat *stp)
403 if (my_stat(path, stp) == 0)
406 p = path + strlen(path);
407 e = path + strlen(c->ctx->document_root) + 2;
409 /* Strip directory parts of the path one by one */
413 if (!my_stat(path, stp) && !S_ISDIR(stp->st_mode)) {
414 c->path_info = p + 1;
426 decide_what_to_do(struct conn *c)
428 char path[URI_MAX], buf[1024];
429 struct vec alias_uri, alias_path;
433 struct registered_uri *ruri;
434 #endif /* EMBEDDED */
436 DBG(("decide_what_to_do: [%s]", c->uri));
438 if ((c->query = strchr(c->uri, '?')) != NULL)
441 url_decode(c->uri, strlen(c->uri), c->uri, strlen(c->uri) + 1);
442 remove_double_dots(c->uri);
444 if (strlen(c->uri) + strlen(c->ctx->document_root) >= sizeof(path)) {
445 send_server_error(c, 400, "URI is too long");
449 (void) my_snprintf(path, sizeof(path), "%s%s",
450 c->ctx->document_root, c->uri);
452 /* User may use the aliases - check URI for mount point */
453 if (is_alias(c->ctx, c->uri, &alias_uri, &alias_path) != NULL) {
454 (void) my_snprintf(path, sizeof(path), "%.*s%s",
455 alias_path.len, alias_path.ptr, c->uri + alias_uri.len);
456 DBG(("using alias %.*s -> %.*s", alias_uri.len, alias_uri.ptr,
457 alias_path.len, alias_path.ptr));
460 #if !defined(NO_AUTH)
461 if (check_authorization(c, path) != 1) {
462 send_authorization_request(c);
466 if ((ruri = is_registered_uri(c->ctx, c->uri)) != NULL) {
467 setup_embedded_stream(c, ruri->callback, ruri->callback_data);
469 #endif /* EMBEDDED */
470 if (strstr(path, HTPASSWD)) {
471 /* Do not allow to view passwords files */
472 send_server_error(c, 403, "Forbidden");
474 #if !defined(NO_AUTH)
475 if ((c->method == METHOD_PUT || c->method == METHOD_DELETE) &&
476 (c->ctx->put_auth_file == NULL || !is_authorized_for_put(c))) {
477 send_authorization_request(c);
480 if (c->method == METHOD_PUT) {
481 c->status = my_stat(path, &st) == 0 ? 200 : 201;
483 if (c->ch.range.v_vec.len > 0) {
484 send_server_error(c, 501, "PUT Range Not Implemented");
485 } else if ((rc = put_dir(path)) == 0) {
486 send_server_error(c, 200, "OK");
487 } else if (rc == -1) {
488 send_server_error(c, 500, "PUT Directory Error");
489 } else if (c->rem.content_len == 0) {
490 send_server_error(c, 411, "Length Required");
491 } else if ((c->loc.chan.fd = my_open(path, O_WRONLY | O_BINARY |
492 O_CREAT | O_NONBLOCK | O_TRUNC, 0644)) == -1) {
493 send_server_error(c, 500, "PUT Error");
495 DBG(("PUT file [%s]", c->uri));
496 c->loc.io_class = &io_file;
497 c->loc.flags |= FLAG_W | FLAG_ALWAYS_READY ;
499 } else if (c->method == METHOD_DELETE) {
500 DBG(("DELETE [%s]", c->uri));
501 if (my_remove(path) == 0)
502 send_server_error(c, 200, "OK");
504 send_server_error(c, 500, "DELETE Error");
505 } else if (get_path_info(c, path, &st) != 0) {
506 send_server_error(c, 404, "Not Found");
507 } else if (S_ISDIR(st.st_mode) && path[strlen(path) - 1] != '/') {
508 (void) my_snprintf(buf, sizeof(buf),
509 "Moved Permanently\r\nLocation: %s/", c->uri);
510 send_server_error(c, 301, buf);
511 } else if (S_ISDIR(st.st_mode) &&
512 find_index_file(c, path, sizeof(path) - 1, &st) == -1 &&
513 c->ctx->dirlist == 0) {
514 send_server_error(c, 403, "Directory Listing Denied");
515 } else if (S_ISDIR(st.st_mode) && c->ctx->dirlist) {
516 if ((c->loc.chan.dir.path = my_strdup(path)) != NULL)
519 send_server_error(c, 500, "GET Directory Error");
520 } else if (S_ISDIR(st.st_mode) && c->ctx->dirlist == 0) {
521 send_server_error(c, 403, "Directory listing denied");
523 } else if (match_extension(path, c->ctx->cgi_extensions)) {
524 if (c->method != METHOD_POST && c->method != METHOD_GET) {
525 send_server_error(c, 501, "Bad method ");
526 } else if ((run_cgi(c, path)) == -1) {
527 send_server_error(c, 500, "Cannot exec CGI");
533 } else if (match_extension(path, c->ctx->ssi_extensions)) {
534 if ((c->loc.chan.fd = my_open(path,
535 O_RDONLY | O_BINARY, 0644)) == -1) {
536 send_server_error(c, 500, "SSI open error");
541 } else if (c->ch.ims.v_time && st.st_mtime <= c->ch.ims.v_time) {
542 send_server_error(c, 304, "Not Modified");
543 } else if ((c->loc.chan.fd = my_open(path,
544 O_RDONLY | O_BINARY, 0644)) != -1) {
547 send_server_error(c, 500, "Internal Error");
552 set_request_method(struct conn *c)
556 assert(c->rem.io.head >= MIN_REQ_LEN);
558 /* Set the request method */
559 for (v = known_http_methods; v->ptr != NULL; v++)
560 if (!memcmp(c->rem.io.buf, v->ptr, v->len)) {
561 c->method = v - known_http_methods;
565 return (v->ptr == NULL);
569 parse_http_request(struct conn *c)
571 char *s, *e, *p, *start;
573 int uri_len, req_len;
576 req_len = c->rem.headers_len = get_headers_len(s, c->rem.io.head);
578 if (req_len == 0 && io_space_len(&c->rem.io) == 0)
579 send_server_error(c, 400, "Request is too big");
583 else if (req_len < MIN_REQ_LEN)
584 send_server_error(c, 400, "Bad request");
585 else if (set_request_method(c))
586 send_server_error(c, 501, "Method Not Implemented");
587 else if ((c->request = my_strndup(s, req_len)) == NULL)
588 send_server_error(c, 500, "Cannot allocate request");
590 if (c->loc.flags & FLAG_CLOSED)
593 DBG(("Conn %d: parsing request: [%.*s]", c->rem.chan.sock, req_len, s));
594 c->rem.flags |= FLAG_HEADERS_PARSED;
596 /* Set headers pointer. Headers follow the request line */
597 c->headers = memchr(c->request, '\n', req_len);
598 assert(c->headers != NULL);
599 assert(c->headers < c->request + req_len);
600 if (c->headers > c->request && c->headers[-1] == '\r')
601 c->headers[-1] = '\0';
602 *c->headers++ = '\0';
605 * Now make a copy of the URI, because it will be URL-decoded,
606 * and we need a copy of unmodified URI for the access log.
607 * First, we skip the REQUEST_METHOD and shift to the URI.
609 for (p = c->request, e = p + req_len; *p != ' ' && p < e; p++);
610 while (p < e && *p == ' ') p++;
612 /* Now remember where URI starts, and shift to the end of URI */
613 for (start = p; p < e && !isspace((unsigned char)*p); ) p++;
615 /* Skip space following the URI */
616 while (p < e && *p == ' ') p++;
618 /* Now comes the HTTP-Version in the form HTTP/<major>.<minor> */
619 if (strncmp(p, "HTTP/", 5) != 0) {
620 send_server_error(c, 400, "Bad HTTP version");
624 /* Parse the HTTP major version number */
625 c->major_version = strtoul(p, &end_number, 10);
626 if (end_number == p || *end_number != '.') {
627 send_server_error(c, 400, "Bad HTTP major version");
631 /* Parse the minor version number */
632 c->minor_version = strtoul(p, &end_number, 10);
633 if (end_number == p || *end_number != '\0') {
634 send_server_error(c, 400, "Bad HTTP minor version");
637 /* Version must be <=1.1 */
638 if (c->major_version > 1 ||
639 (c->major_version == 1 && c->minor_version > 1)) {
640 send_server_error(c, 505, "HTTP version not supported");
645 send_server_error(c, 400, "Bad URI");
646 } else if ((c->uri = malloc(uri_len + 1)) == NULL) {
647 send_server_error(c, 500, "Cannot allocate URI");
649 my_strlcpy(c->uri, (char *) start, uri_len + 1);
650 parse_headers(c->headers,
651 (c->request + req_len) - c->headers, &c->ch);
653 /* Remove the length of request from total, count only data */
654 assert(c->rem.io.total >= (big_int_t) req_len);
655 c->rem.io.total -= req_len;
657 c->rem.content_len = c->ch.cl.v_big_int;
658 io_inc_tail(&c->rem.io, req_len);
660 decide_what_to_do(c);
665 shttpd_add_socket(struct shttpd_ctx *ctx, int sock)
669 int l = ctx->inetd_mode ? E_FATAL : E_LOG;
674 sa.len = sizeof(sa.u.sin);
675 (void) set_non_blocking_mode(sock);
677 if (getpeername(sock, &sa.u.sa, &sa.len)) {
678 elog(l, NULL, "add_socket: %s", strerror(errno));
680 } else if (ctx->ssl_ctx && (ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
681 elog(l, NULL, "add_socket: SSL_new: %s", strerror(ERRNO));
682 (void) closesocket(sock);
683 } else if (ctx->ssl_ctx && SSL_set_fd(ssl, sock) == 0) {
684 elog(l, NULL, "add_socket: SSL_set_fd: %s", strerror(ERRNO));
685 (void) closesocket(sock);
688 } else if ((c = calloc(1, sizeof(*c) + 2 * ctx->io_buf_size)) == NULL) {
694 (void) closesocket(sock);
695 elog(l, NULL, "add_socket: calloc: %s", strerror(ERRNO));
698 c->rem.conn = c->loc.conn = c;
701 c->birth_time = current_time;
702 c->expire_time = current_time + EXPIRE_TIME;
704 (void) getsockname(sock, &sa.u.sa, &sa.len);
705 c->loc_port = sa.u.sin.sin_port;
707 set_close_on_exec(sock);
709 c->loc.io_class = NULL;
711 c->rem.io_class = &io_socket;
712 c->rem.chan.sock = sock;
715 c->loc.io.buf = (char *) (c + 1);
716 c->rem.io.buf = c->loc.io.buf + ctx->io_buf_size;
717 c->loc.io.size = c->rem.io.size = ctx->io_buf_size;
721 c->rem.io_class = &io_ssl;
722 c->rem.chan.ssl.sock = sock;
723 c->rem.chan.ssl.ssl = ssl;
724 ssl_handshake(&c->rem);
728 EnterCriticalSection(&ctx->mutex);
729 LL_TAIL(&ctx->connections, &c->link);
731 LeaveCriticalSection(&ctx->mutex);
733 DBG(("%s:%hu connected (socket %d)",
734 inet_ntoa(* (struct in_addr *) &sa.u.sin.sin_addr.s_addr),
735 ntohs(sa.u.sin.sin_port), sock));
740 shttpd_active(struct shttpd_ctx *ctx)
742 return (ctx->nactive);
746 * Setup a listening socket on given port. Return opened socket or -1
749 shttpd_listen(struct shttpd_ctx *ctx, int port, int is_ssl)
754 if ((sock = open_listening_port(port)) == -1) {
755 elog(E_FATAL, NULL, "cannot open port %d", port);
756 } else if ((l = calloc(1, sizeof(*l))) == NULL) {
757 (void) closesocket(sock);
758 elog(E_FATAL, NULL, "cannot allocate listener");
759 } else if (is_ssl && ctx->ssl_ctx == NULL) {
760 (void) closesocket(sock);
761 elog(E_FATAL, NULL, "cannot add SSL socket, "
762 "please specify certificate file");
767 LL_TAIL(&listeners, &l->link);
768 DBG(("shttpd_listen: added socket %d", sock));
775 shttpd_accept(int lsn_sock, int milliseconds)
782 tv.tv_sec = milliseconds / 1000;
783 tv.tv_usec = milliseconds % 1000;
784 sa.len = sizeof(sa.u.sin);
786 FD_SET(lsn_sock, &read_set);
788 if (select(lsn_sock + 1, &read_set, NULL, NULL, &tv) == 1)
789 sock = accept(lsn_sock, &sa.u.sa, &sa.len);
795 read_stream(struct stream *stream)
799 len = io_space_len(&stream->io);
802 /* Do not read more that needed */
803 if (stream->content_len > 0 &&
804 stream->io.total + len > stream->content_len)
805 len = stream->content_len - stream->io.total;
807 /* Read from underlying channel */
808 n = stream->nread_last = stream->io_class->read(stream,
809 io_space(&stream->io), len);
812 io_inc_head(&stream->io, n);
813 else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
814 n = n; /* Ignore EINTR and EAGAIN */
815 else if (!(stream->flags & FLAG_DONT_CLOSE))
818 DBG(("read_stream (%d %s): read %d/%d/%lu bytes (errno %d)",
819 stream->conn->rem.chan.sock,
820 stream->io_class ? stream->io_class->name : "(null)",
821 n, len, (unsigned long) stream->io.total, ERRNO));
824 * Close the local stream if everything was read
825 * XXX We do not close the remote stream though! It may be
826 * a POST data completed transfer, we do not want the socket
829 if (stream->content_len > 0 && stream == &stream->conn->loc) {
830 assert(stream->io.total <= stream->content_len);
831 if (stream->io.total == stream->content_len)
835 stream->conn->expire_time = current_time + EXPIRE_TIME;
839 write_stream(struct stream *from, struct stream *to)
843 len = io_data_len(&from->io);
846 /* TODO: should be assert on CAN_WRITE flag */
847 n = to->io_class->write(to, io_data(&from->io), len);
848 to->conn->expire_time = current_time + EXPIRE_TIME;
849 DBG(("write_stream (%d %s): written %d/%d bytes (errno %d)",
850 to->conn->rem.chan.sock,
851 to->io_class ? to->io_class->name : "(null)", n, len, ERRNO));
854 io_inc_tail(&from->io, n);
855 else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
856 n = n; /* Ignore EINTR and EAGAIN */
857 else if (!(to->flags & FLAG_DONT_CLOSE))
863 disconnect(struct conn *c)
865 static const struct vec ka = {"keep-alive", 10};
868 DBG(("Disconnecting %d (%.*s)", c->rem.chan.sock,
869 c->ch.connection.v_vec.len, c->ch.connection.v_vec.ptr));
871 #if !defined(_WIN32) || defined(NO_GUI)
872 if (c->ctx->access_log != NULL)
874 log_access(c->ctx->access_log, c);
876 /* In inetd mode, exit if request is finished. */
877 if (c->ctx->inetd_mode)
880 if (c->loc.io_class != NULL && c->loc.io_class->close != NULL)
881 c->loc.io_class->close(&c->loc);
884 * Check the "Connection: " header before we free c->request
885 * If it its 'keep-alive', then do not close the connection
887 dont_close = c->ch.connection.v_vec.len >= ka.len &&
888 !my_strncasecmp(ka.ptr, c->ch.connection.v_vec.ptr, ka.len);
895 /* Handle Keep-Alive */
898 c->loc.io_class = NULL;
899 c->loc.flags = c->rem.flags = 0;
900 c->query = c->request = c->uri = c->path_info = NULL;
902 (void) memset(&c->ch, 0, sizeof(c->ch));
903 io_clear(&c->rem.io);
904 io_clear(&c->loc.io);
905 c->rem.io.total = c->loc.io.total = 0;
907 if (c->rem.io_class != NULL)
908 c->rem.io_class->close(&c->rem);
910 EnterCriticalSection(&c->ctx->mutex);
913 assert(c->ctx->nactive >= 0);
914 LeaveCriticalSection(&c->ctx->mutex);
921 add_to_set(int fd, fd_set *set, int *max_fd)
929 * One iteration of server loop. This is the core of the data exchange.
932 shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
934 struct llhead *lp, *tmp;
937 struct timeval tv; /* Timeout for select() */
938 fd_set read_set, write_set;
939 int sock, max_fd = -1, msec = milliseconds;
942 current_time = time(0);
946 /* Add listening sockets to the read set */
947 LL_FOREACH(&listeners, lp) {
948 l = LL_ENTRY(lp, struct listener, link);
949 FD_SET(l->sock, &read_set);
950 if (l->sock > max_fd)
952 DBG(("FD_SET(%d) (listening)", l->sock));
955 /* Multiplex streams */
956 LL_FOREACH(&ctx->connections, lp) {
957 c = LL_ENTRY(lp, struct conn, link);
959 /* If there is a space in remote IO, check remote socket */
960 if (io_space_len(&c->rem.io))
961 add_to_set(c->rem.chan.fd, &read_set, &max_fd);
965 * If there is a space in local IO, and local endpoint is
966 * CGI, check local socket for read availability
968 if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
969 c->loc.io_class == &io_cgi)
970 add_to_set(c->loc.chan.fd, &read_set, &max_fd);
973 * If there is some data read from remote socket, and
974 * local endpoint is CGI, check local for write availability
976 if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
977 c->loc.io_class == &io_cgi)
978 add_to_set(c->loc.chan.fd, &write_set, &max_fd);
982 * If there is some data read from local endpoint, check the
983 * remote socket for write availability
985 if (io_data_len(&c->loc.io))
986 add_to_set(c->rem.chan.fd, &write_set, &max_fd);
988 if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
989 (c->loc.flags & FLAG_ALWAYS_READY))
992 if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
993 (c->loc.flags & FLAG_ALWAYS_READY))
997 tv.tv_sec = msec / 1000;
998 tv.tv_usec = msec % 1000;
1000 /* Check IO readiness */
1001 if (select(max_fd + 1, &read_set, &write_set, NULL, &tv) < 0) {
1004 * On windows, if read_set and write_set are empty,
1005 * select() returns "Invalid parameter" error
1006 * (at least on my Windows XP Pro). So in this case,
1009 Sleep(milliseconds);
1011 DBG(("select: %d", ERRNO));
1015 /* Check for incoming connections on listener sockets */
1016 LL_FOREACH(&listeners, lp) {
1017 l = LL_ENTRY(lp, struct listener, link);
1018 if (!FD_ISSET(l->sock, &read_set))
1021 sa.len = sizeof(sa.u.sin);
1022 if ((sock = accept(l->sock, &sa.u.sa, &sa.len)) != -1) {
1024 shttpd_add_socket(ctx, sock);
1026 if (sock < (int) FD_SETSIZE) {
1027 shttpd_add_socket(ctx, sock);
1030 "shttpd_poll: ctx %p: disarding "
1031 "socket %d, too busy", ctx, sock);
1032 (void) closesocket(sock);
1036 } while (sock != -1);
1039 /* Process all connections */
1040 LL_FOREACH_SAFE(&ctx->connections, lp, tmp) {
1041 c = LL_ENTRY(lp, struct conn, link);
1043 /* Read from remote end if it is ready */
1044 if (FD_ISSET(c->rem.chan.fd, &read_set) &&
1045 io_space_len(&c->rem.io))
1046 read_stream(&c->rem);
1048 /* If the request is not parsed yet, do so */
1049 if (!(c->rem.flags & FLAG_HEADERS_PARSED))
1050 parse_http_request(c);
1052 DBG(("loc: %u [%.*s]", io_data_len(&c->loc.io),
1053 io_data_len(&c->loc.io), io_data(&c->loc.io)));
1054 DBG(("rem: %u [%.*s]", io_data_len(&c->rem.io),
1055 io_data_len(&c->rem.io), io_data(&c->rem.io)));
1057 /* Read from the local end if it is ready */
1058 if (io_space_len(&c->loc.io) &&
1059 ((c->loc.flags & FLAG_ALWAYS_READY)
1061 #if !defined(NO_CGI)
1062 ||(c->loc.io_class == &io_cgi &&
1063 FD_ISSET(c->loc.chan.fd, &read_set))
1066 read_stream(&c->loc);
1068 if (io_data_len(&c->rem.io) > 0 && (c->loc.flags & FLAG_W) &&
1069 c->loc.io_class != NULL && c->loc.io_class->write != NULL)
1070 write_stream(&c->rem, &c->loc);
1072 if (io_data_len(&c->loc.io) > 0 && c->rem.io_class != NULL)
1073 write_stream(&c->loc, &c->rem);
1075 if (c->rem.nread_last > 0)
1076 c->ctx->in += c->rem.nread_last;
1077 if (c->loc.nread_last > 0)
1078 c->ctx->out += c->loc.nread_last;
1080 /* Check whether we should close this connection */
1081 if ((current_time > c->expire_time) ||
1082 (c->rem.flags & FLAG_CLOSED) ||
1083 ((c->loc.flags & FLAG_CLOSED) && !io_data_len(&c->loc.io)))
1089 * Deallocate shttpd object, free up the resources
1092 shttpd_fini(struct shttpd_ctx *ctx)
1094 struct llhead *lp, *tmp;
1095 struct mime_type_link *mtl;
1098 struct registered_uri *ruri;
1100 /* Free configured mime types */
1101 LL_FOREACH_SAFE(&ctx->mime_types, lp, tmp) {
1102 mtl = LL_ENTRY(lp, struct mime_type_link, link);
1108 /* Free all connections */
1109 LL_FOREACH_SAFE(&ctx->connections, lp, tmp) {
1110 c = LL_ENTRY(lp, struct conn, link);
1114 /* Free registered URIs (must be done after disconnect()) */
1115 LL_FOREACH_SAFE(&ctx->registered_uris, lp, tmp) {
1116 ruri = LL_ENTRY(lp, struct registered_uri, link);
1117 free((void *)ruri->uri);
1121 /* Free listener sockets for this context */
1122 LL_FOREACH_SAFE(&listeners, lp, tmp) {
1123 l = LL_ENTRY(lp, struct listener, link);
1124 (void) closesocket(l->sock);
1129 #if !defined(NO_SSI)
1130 free_ssi_funcs(ctx);
1133 if (ctx->access_log) (void) fclose(ctx->access_log);
1134 if (ctx->error_log) (void) fclose(ctx->error_log);
1135 if (ctx->put_auth_file) free(ctx->put_auth_file);
1136 if (ctx->document_root) free(ctx->document_root);
1137 if (ctx->index_files) free(ctx->index_files);
1138 if (ctx->aliases) free(ctx->aliases);
1139 #if !defined(NO_CGI)
1140 if (ctx->cgi_vars) free(ctx->cgi_vars);
1141 if (ctx->cgi_extensions) free(ctx->cgi_extensions);
1142 if (ctx->cgi_interpreter) free(ctx->cgi_interpreter);
1144 if (ctx->auth_realm) free(ctx->auth_realm);
1145 if (ctx->global_passwd_file) free(ctx->global_passwd_file);
1146 if (ctx->uid) free(ctx->uid);
1148 /* TODO: free SSL context */
1154 open_listening_ports(struct shttpd_ctx *ctx)
1156 const char *p = ctx->ports;
1159 FOR_EACH_WORD_IN_LIST(p, len) {
1160 is_ssl = p[len - 1] == 's' ? 1 : 0;
1161 if (shttpd_listen(ctx, atoi(p), is_ssl) == -1)
1163 "Cannot open socket on port %d", atoi(p));