2 * copyright (c) 2001-2008 Emil Mikulic.
4 * http.c: embedded webserver.
5 * This borrows a lot of code from darkhttpd.
7 * You may use, modify and redistribute this file under the terms of the
8 * GNU General Public License version 2. (see COPYING.GPL)
22 #include <sys/socket.h>
23 #include <arpa/inet.h>
24 #include <netinet/in.h>
38 char *base_url
= NULL
;
40 static const char mime_type_xml
[] = "text/xml";
41 static const char mime_type_html
[] = "text/html; charset=us-ascii";
42 static const char mime_type_css
[] = "text/css";
43 static const char mime_type_js
[] = "text/javascript";
44 static const char encoding_identity
[] = "identity";
45 static const char encoding_gzip
[] = "gzip";
47 static const char server
[] = PACKAGE_NAME
"/" PACKAGE_VERSION
;
48 static int idletime
= 60;
49 static int sockin
= -1; /* socket to accept connections from */
50 #define MAX_REQUEST_LENGTH 4000
53 #define min(a,b) (((a) < (b)) ? (a) : (b))
57 LIST_ENTRY(connection
) entries
;
60 struct sockaddr_storage client
;
63 RECV_REQUEST
, /* receiving request */
64 SEND_HEADER_AND_REPLY
, /* try to send header+reply together */
65 SEND_HEADER
, /* sending generated header */
66 SEND_REPLY
, /* sending reply */
67 DONE
/* conn closed, need to remove from queue */
70 /* char request[request_length+1] is null-terminated */
72 size_t request_length
;
76 char *method
, *uri
, *query
; /* query can be NULL */
79 const char *mime_type
, *encoding
, *header_extra
;
80 size_t header_length
, header_sent
;
81 int header_dont_free
, header_only
, http_code
;
85 size_t reply_length
, reply_sent
;
87 unsigned int total_sent
; /* header + body = total, for logging */
90 static LIST_HEAD(conn_list_head
, connection
) connlist
=
91 LIST_HEAD_INITIALIZER(conn_list_head
);
93 /* ---------------------------------------------------------------------------
94 * Decode URL by converting %XX (where XX are hexadecimal digits) to the
95 * character it represents. Don't forget to free the return value.
97 static char *urldecode(const char *url
)
99 size_t i
, len
= strlen(url
);
100 char *out
= xmalloc(len
+1);
103 for (i
=0, pos
=0; i
<len
; i
++)
105 if (url
[i
] == '%' && i
+2 < len
&&
106 isxdigit(url
[i
+1]) && isxdigit(url
[i
+2]))
109 #define HEX_TO_DIGIT(hex) ( \
110 ((hex) >= 'A' && (hex) <= 'F') ? ((hex)-'A'+10): \
111 ((hex) >= 'a' && (hex) <= 'f') ? ((hex)-'a'+10): \
114 out
[pos
++] = HEX_TO_DIGIT(url
[i
+1]) * 16 +
115 HEX_TO_DIGIT(url
[i
+2]);
128 /* don't really need to realloc here - it's probably a performance hit */
129 out
= xrealloc(out
, strlen(out
)+1); /* dealloc what we don't need */
136 /* ---------------------------------------------------------------------------
137 * Consolidate slashes in-place by shifting parts of the string over repeated
140 static void consolidate_slashes(char *s
)
142 size_t left
= 0, right
= 0;
147 while (s
[right
] != '\0')
151 if (s
[right
] == '/') right
++;
155 s
[left
++] = s
[right
++];
160 if (s
[right
] == '/') saw_slash
++;
161 s
[left
++] = s
[right
++];
169 /* ---------------------------------------------------------------------------
170 * Resolve /./ and /../ in a URI, returing a new, safe URI, or NULL if the URI
171 * is invalid/unsafe. Returned buffer needs to be deallocated.
173 static char *make_safe_uri(char *uri
)
176 unsigned int slashes
= 0, elements
= 0;
177 size_t urilen
, i
, j
, pos
;
182 consolidate_slashes(uri
);
183 urilen
= strlen(uri
);
185 /* count the slashes */
186 for (i
=0, slashes
=0; i
<urilen
; i
++)
187 if (uri
[i
] == '/') slashes
++;
189 /* make an array for the URI elements */
190 elem
= xmalloc(sizeof(*elem
) * slashes
);
191 for (i
=0; i
<slashes
; i
++)
194 /* split by slashes and build elem[] array */
197 /* look for the next slash */
198 for (j
=i
; j
<urilen
&& uri
[j
] != '/'; j
++)
201 /* process uri[i,j) */
202 if ((j
== i
+1) && (uri
[i
] == '.'))
204 else if ((j
== i
+2) && (uri
[i
] == '.') && (uri
[i
+1] == '.'))
210 * Unsafe string so free elem[]. All its elements are free
219 free(elem
[elements
]);
222 else elem
[elements
++] = split_string(uri
, i
, j
);
224 i
= j
+ 1; /* uri[j] is a slash - move along one */
228 out
= xmalloc(urilen
+1); /* it won't expand */
230 for (i
=0; i
<elements
; i
++)
232 size_t delta
= strlen(elem
[i
]);
234 assert(pos
<= urilen
);
237 assert(pos
+delta
<= urilen
);
238 memcpy(out
+pos
, elem
[i
], delta
);
244 if ((elements
== 0) || (uri
[urilen
-1] == '/')) out
[pos
++] = '/';
245 assert(pos
<= urilen
);
249 /* don't really need to do this and it's probably a performance hit: */
250 /* shorten buffer if necessary */
251 if (pos
!= urilen
) out
= xrealloc(out
, strlen(out
)+1);
256 /* ---------------------------------------------------------------------------
257 * Allocate and initialize an empty connection.
259 static struct connection
*new_connection(void)
261 struct connection
*conn
= xmalloc(sizeof(*conn
));
264 memset(&conn
->client
, 0, sizeof(conn
->client
));
265 conn
->last_active
= now
;
266 conn
->request
= NULL
;
267 conn
->request_length
= 0;
268 conn
->accept_gzip
= 0;
273 conn
->mime_type
= NULL
;
274 conn
->encoding
= NULL
;
275 conn
->header_extra
= "";
276 conn
->header_length
= 0;
277 conn
->header_sent
= 0;
278 conn
->header_dont_free
= 0;
279 conn
->header_only
= 0;
282 conn
->reply_dont_free
= 0;
283 conn
->reply_length
= 0;
284 conn
->reply_sent
= 0;
285 conn
->total_sent
= 0;
287 /* Make it harmless so it gets garbage-collected if it should, for some
288 * reason, fail to be correctly filled out.
297 /* ---------------------------------------------------------------------------
298 * Accept a connection from sockin and add it to the connection queue.
300 static void accept_connection(void)
302 struct sockaddr_storage addrin
;
304 struct connection
*conn
;
305 char ipaddr
[INET6_ADDRSTRLEN
], portstr
[12];
308 sin_size
= (socklen_t
)sizeof(addrin
);
309 sock
= accept(sockin
, (struct sockaddr
*)&addrin
, &sin_size
);
312 if (errno
== ECONNABORTED
|| errno
== EINTR
)
314 verbosef("accept() failed: %s", strerror(errno
));
317 /* else */ err(1, "accept()");
320 fd_set_nonblock(sock
);
322 /* allocate and initialise struct connection */
323 conn
= new_connection();
325 conn
->state
= RECV_REQUEST
;
326 memcpy(&conn
->client
, &addrin
, sizeof(conn
->client
));
327 LIST_INSERT_HEAD(&connlist
, conn
, entries
);
329 getnameinfo((struct sockaddr
*) &addrin
, sin_size
,
330 ipaddr
, sizeof(ipaddr
), portstr
, sizeof(portstr
),
331 NI_NUMERICHOST
| NI_NUMERICSERV
);
332 verbosef("accepted connection from %s:%s", ipaddr
, portstr
);
337 /* ---------------------------------------------------------------------------
338 * Log a connection, then cleanly deallocate its internals.
340 static void free_connection(struct connection
*conn
)
342 dverbosef("free_connection(%d)", conn
->socket
);
343 if (conn
->socket
!= -1) close(conn
->socket
);
344 if (conn
->request
!= NULL
) free(conn
->request
);
345 if (conn
->method
!= NULL
) free(conn
->method
);
346 if (conn
->uri
!= NULL
) free(conn
->uri
);
347 if (conn
->query
!= NULL
) free(conn
->query
);
348 if (conn
->header
!= NULL
&& !conn
->header_dont_free
) free(conn
->header
);
349 if (conn
->reply
!= NULL
&& !conn
->reply_dont_free
) free(conn
->reply
);
354 /* ---------------------------------------------------------------------------
355 * Format [when] as an RFC1123 date, stored in the specified buffer. The same
356 * buffer is returned for convenience.
358 #define DATE_LEN 30 /* strlen("Fri, 28 Feb 2003 00:02:08 GMT")+1 */
359 static char *rfc1123_date(char *dest
, const time_t when
)
362 if (strftime(dest
, DATE_LEN
,
363 "%a, %d %b %Y %H:%M:%S %Z", gmtime(&tmp
) ) == 0)
364 errx(1, "strftime() failed [%s]", dest
);
368 static void generate_header(struct connection
*conn
,
369 const int code
, const char *text
)
373 assert(conn
->header
== NULL
);
374 assert(conn
->mime_type
!= NULL
);
375 if (conn
->encoding
== NULL
)
376 conn
->encoding
= encoding_identity
;
378 verbosef("http: %d %s (%s: %d bytes)", code
, text
,
379 conn
->encoding
, conn
->reply_length
);
380 conn
->header_length
= xasprintf(&(conn
->header
),
384 "Vary: Accept-Encoding\r\n"
385 "Content-Type: %s\r\n"
386 "Content-Length: %d\r\n"
387 "Content-Encoding: %s\r\n"
388 "X-Robots-Tag: noindex, noarchive\r\n"
393 rfc1123_date(date
, now
), server
,
394 conn
->mime_type
, conn
->reply_length
, conn
->encoding
,
396 conn
->http_code
= code
;
401 /* ---------------------------------------------------------------------------
402 * A default reply for any (erroneous) occasion.
404 static void default_reply(struct connection
*conn
,
405 const int errcode
, const char *errname
, const char *format
, ...)
410 va_start(va
, format
);
411 xvasprintf(&reason
, format
, va
);
414 conn
->reply_length
= xasprintf(&(conn
->reply
),
415 "<html><head><title>%d %s</title></head><body>\n"
416 "<h1>%s</h1>\n" /* errname */
421 errcode
, errname
, errname
, reason
, server
);
424 /* forget any dangling metadata */
425 conn
->mime_type
= mime_type_html
;
426 conn
->encoding
= encoding_identity
;
428 generate_header(conn
, errcode
, errname
);
433 /* ---------------------------------------------------------------------------
434 * Parses a single HTTP request field. Returns string from end of [field] to
435 * first \r, \n or end of request string. Returns NULL if [field] can't be
438 * You need to remember to deallocate the result.
439 * example: parse_field(conn, "Referer: ");
441 static char *parse_field(const struct connection
*conn
, const char *field
)
443 size_t bound1
, bound2
;
447 pos
= strstr(conn
->request
, field
);
450 bound1
= pos
- conn
->request
+ strlen(field
);
453 for (bound2
= bound1
;
454 conn
->request
[bound2
] != '\r' &&
455 bound2
< conn
->request_length
; bound2
++)
459 return (split_string(conn
->request
, bound1
, bound2
));
464 /* ---------------------------------------------------------------------------
465 * Parse an HTTP request like "GET /hosts/?sort=in HTTP/1.1" to get the method
466 * (GET), the uri (/hosts/), the query (sort=in) and whether the UA will
467 * accept gzip encoding. Remember to deallocate all these buffers. Query
468 * can be NULL. The method will be returned in uppercase.
470 static int parse_request(struct connection
*conn
)
472 size_t bound1
, bound2
, mid
;
476 for (bound1
= 0; bound1
< conn
->request_length
&&
477 conn
->request
[bound1
] != ' '; bound1
++)
480 conn
->method
= split_string(conn
->request
, 0, bound1
);
481 strntoupper(conn
->method
, bound1
);
484 for (; bound1
< conn
->request_length
&&
485 conn
->request
[bound1
] == ' '; bound1
++)
488 if (bound1
== conn
->request_length
)
489 return (0); /* fail */
491 for (bound2
=bound1
+1; bound2
< conn
->request_length
&&
492 conn
->request
[bound2
] != ' ' &&
493 conn
->request
[bound2
] != '\r'; bound2
++)
496 /* find query string */
497 for (mid
=bound1
; mid
<bound2
&& conn
->request
[mid
] != '?'; mid
++)
500 if (conn
->request
[mid
] == '?') {
501 conn
->query
= split_string(conn
->request
, mid
+1, bound2
);
505 conn
->uri
= split_string(conn
->request
, bound1
, bound2
);
507 /* parse important fields */
508 accept_enc
= parse_field(conn
, "Accept-Encoding: ");
509 if (accept_enc
!= NULL
) {
510 if (strstr(accept_enc
, "gzip") != NULL
)
511 conn
->accept_gzip
= 1;
517 /* FIXME: maybe we need a smarter way of doing static pages: */
519 /* ---------------------------------------------------------------------------
520 * Web interface: static stylesheet.
523 static_style_css(struct connection
*conn
)
525 #include "stylecss.h"
527 conn
->reply
= style_css
;
528 conn
->reply_length
= style_css_len
;
529 conn
->reply_dont_free
= 1;
530 conn
->mime_type
= mime_type_css
;
533 /* ---------------------------------------------------------------------------
534 * Web interface: static JavaScript.
537 static_graph_js(struct connection
*conn
)
541 conn
->reply
= graph_js
;
542 conn
->reply_length
= graph_js_len
;
543 conn
->reply_dont_free
= 1;
544 conn
->mime_type
= mime_type_js
;
547 /* ---------------------------------------------------------------------------
548 * gzip a reply, if requested and possible. Don't bother with a minimum
549 * length requirement, I've never seen a page fail to compress.
552 process_gzip(struct connection
*conn
)
558 if (!conn
->accept_gzip
)
561 buf
= xmalloc(conn
->reply_length
);
562 len
= conn
->reply_length
;
568 if (deflateInit2(&zs
, Z_BEST_COMPRESSION
, Z_DEFLATED
,
569 15+16, /* 15 = biggest window, 16 = add gzip header+trailer */
571 Z_DEFAULT_STRATEGY
) != Z_OK
)
574 zs
.avail_in
= conn
->reply_length
;
575 zs
.next_in
= (unsigned char *)conn
->reply
;
577 zs
.avail_out
= conn
->reply_length
;
578 zs
.next_out
= (unsigned char *)buf
;
580 if (deflate(&zs
, Z_FINISH
) != Z_STREAM_END
) {
583 verbosef("failed to compress %u bytes", (unsigned int)len
);
587 if (conn
->reply_dont_free
)
588 conn
->reply_dont_free
= 0;
593 conn
->reply_length
-= zs
.avail_out
;
594 conn
->encoding
= encoding_gzip
;
598 /* ---------------------------------------------------------------------------
599 * Process a GET/HEAD request
601 static void process_get(struct connection
*conn
)
603 char *decoded_url
, *safe_url
;
605 verbosef("http: %s \"%s\" %s", conn
->method
, conn
->uri
,
606 (conn
->query
== NULL
)?"":conn
->query
);
608 /* work out path of file being requested */
609 decoded_url
= urldecode(conn
->uri
);
611 /* make sure it's safe */
612 safe_url
= make_safe_uri(decoded_url
);
614 if (safe_url
== NULL
)
616 default_reply(conn
, 400, "Bad Request",
617 "You requested an invalid URI: %s", conn
->uri
);
621 /* make relative (or fail) */
622 decoded_url
= safe_url
;
623 if (!str_starts_with(decoded_url
, base_url
))
625 default_reply(conn
, 404, "Not Found",
626 "The page you requested could not be found.");
630 safe_url
= decoded_url
+ strlen(base_url
) - 1;
632 if (strcmp(safe_url
, "/") == 0) {
633 struct str
*buf
= html_front_page();
634 str_extract(buf
, &(conn
->reply_length
), &(conn
->reply
));
635 conn
->mime_type
= mime_type_html
;
637 else if (str_starts_with(safe_url
, "/hosts/")) {
638 /* FIXME here - make this saner */
639 struct str
*buf
= html_hosts(safe_url
, conn
->query
);
641 default_reply(conn
, 404, "Not Found",
642 "The page you requested could not be found.");
646 str_extract(buf
, &(conn
->reply_length
), &(conn
->reply
));
647 conn
->mime_type
= mime_type_html
;
649 else if (str_starts_with(safe_url
, "/graphs.xml")) {
650 struct str
*buf
= xml_graphs();
651 str_extract(buf
, &(conn
->reply_length
), &(conn
->reply
));
652 conn
->mime_type
= mime_type_xml
;
653 /* hack around Opera caching the XML */
654 conn
->header_extra
= "Pragma: no-cache\r\n";
656 else if (strcmp(safe_url
, "/style.css") == 0)
657 static_style_css(conn
);
658 else if (strcmp(safe_url
, "/graph.js") == 0)
659 static_graph_js(conn
);
661 default_reply(conn
, 404, "Not Found",
662 "The page you requested could not be found.");
669 assert(conn
->mime_type
!= NULL
);
670 generate_header(conn
, 200, "OK");
675 /* ---------------------------------------------------------------------------
676 * Process a request: build the header and reply, advance state.
678 static void process_request(struct connection
*conn
)
680 if (!parse_request(conn
))
682 default_reply(conn
, 400, "Bad Request",
683 "You sent a request that the server couldn't understand.");
685 else if (strcmp(conn
->method
, "GET") == 0)
689 else if (strcmp(conn
->method
, "HEAD") == 0)
692 conn
->header_only
= 1;
696 default_reply(conn
, 501, "Not Implemented",
697 "The method you specified (%s) is not implemented.",
702 if (conn
->header_only
)
703 conn
->state
= SEND_HEADER
;
705 conn
->state
= SEND_HEADER_AND_REPLY
;
707 /* request not needed anymore */
709 conn
->request
= NULL
; /* important: don't free it again later */
714 /* ---------------------------------------------------------------------------
717 static void poll_recv_request(struct connection
*conn
)
719 #define BUFSIZE 65536
723 recvd
= recv(conn
->socket
, buf
, BUFSIZE
, 0);
724 dverbosef("poll_recv_request(%d) got %d bytes", conn
->socket
, (int)recvd
);
728 verbosef("recv(%d) error: %s", conn
->socket
, strerror(errno
));
732 conn
->last_active
= now
;
735 /* append to conn->request */
736 conn
->request
= xrealloc(conn
->request
, conn
->request_length
+recvd
+1);
737 memcpy(conn
->request
+conn
->request_length
, buf
, (size_t)recvd
);
738 conn
->request_length
+= recvd
;
739 conn
->request
[conn
->request_length
] = 0;
741 /* process request if we have all of it */
742 if (conn
->request_length
> 4 &&
743 memcmp(conn
->request
+conn
->request_length
-4, "\r\n\r\n", 4) == 0)
744 process_request(conn
);
746 /* die if it's too long */
747 if (conn
->request_length
> MAX_REQUEST_LENGTH
)
749 default_reply(conn
, 413, "Request Entity Too Large",
750 "Your request was dropped because it was too long.");
751 conn
->state
= SEND_HEADER
;
757 /* ---------------------------------------------------------------------------
758 * Try to send header and [a part of the] reply in one packet.
760 static void poll_send_header_and_reply(struct connection
*conn
)
765 assert(!conn
->header_only
);
766 assert(conn
->reply_length
> 0);
767 assert(conn
->header_sent
== 0);
769 assert(conn
->reply_sent
== 0);
772 iov
[0].iov_base
= conn
->header
;
773 iov
[0].iov_len
= conn
->header_length
;
775 iov
[1].iov_base
= conn
->reply
+ conn
->reply_sent
;
776 iov
[1].iov_len
= conn
->reply_length
- conn
->reply_sent
;
778 sent
= writev(conn
->socket
, iov
, 2);
779 conn
->last_active
= now
;
781 /* handle any errors (-1) or closure (0) in send() */
784 verbosef("writev(%d) error: %s", conn
->socket
, strerror(errno
));
789 /* Figure out what we've sent. */
790 conn
->total_sent
+= (unsigned int)sent
;
791 if (sent
< (ssize_t
)conn
->header_length
) {
792 verbosef("partially sent header");
793 conn
->header_sent
= sent
;
794 conn
->state
= SEND_HEADER
;
798 conn
->header_sent
= conn
->header_length
;
799 sent
-= conn
->header_length
;
801 if (conn
->reply_sent
+ sent
< conn
->reply_length
) {
802 verbosef("partially sent reply");
803 conn
->reply_sent
+= sent
;
804 conn
->state
= SEND_REPLY
;
808 conn
->reply_sent
= conn
->reply_length
;
812 /* ---------------------------------------------------------------------------
813 * Sending header. Assumes conn->header is not NULL.
815 static void poll_send_header(struct connection
*conn
)
819 sent
= send(conn
->socket
, conn
->header
+ conn
->header_sent
,
820 conn
->header_length
- conn
->header_sent
, 0);
821 conn
->last_active
= now
;
822 dverbosef("poll_send_header(%d) sent %d bytes", conn
->socket
, (int)sent
);
824 /* handle any errors (-1) or closure (0) in send() */
828 verbosef("send(%d) error: %s", conn
->socket
, strerror(errno
));
832 conn
->header_sent
+= (unsigned int)sent
;
833 conn
->total_sent
+= (unsigned int)sent
;
835 /* check if we're done sending */
836 if (conn
->header_sent
== conn
->header_length
)
838 if (conn
->header_only
)
841 conn
->state
= SEND_REPLY
;
847 /* ---------------------------------------------------------------------------
850 static void poll_send_reply(struct connection
*conn
)
854 sent
= send(conn
->socket
,
855 conn
->reply
+ conn
->reply_sent
,
856 conn
->reply_length
- conn
->reply_sent
, 0);
857 conn
->last_active
= now
;
858 dverbosef("poll_send_reply(%d) sent %d: [%d-%d] of %d",
859 conn
->socket
, (int)sent
,
860 (int)conn
->reply_sent
,
861 (int)(conn
->reply_sent
+ sent
- 1),
862 (int)conn
->reply_length
);
864 /* handle any errors (-1) or closure (0) in send() */
868 verbosef("send(%d) error: %s", conn
->socket
, strerror(errno
));
870 verbosef("send(%d) closure", conn
->socket
);
874 conn
->reply_sent
+= (unsigned int)sent
;
875 conn
->total_sent
+= (unsigned int)sent
;
877 /* check if we're done sending */
878 if (conn
->reply_sent
== conn
->reply_length
) conn
->state
= DONE
;
883 /* --------------------------------------------------------------------------
884 * Initialize the base path.
886 static void http_init_base(const char *url
)
888 char *slashed_url
, *safe_url
;
892 base_url
= strdup("/");
896 /* make sure that the url has leading and trailing slashes */
897 urllen
= strlen(url
);
898 slashed_url
= xmalloc(urllen
+3);
899 memset(slashed_url
, '/', urllen
+2);
900 memcpy(slashed_url
+1, url
, urllen
); /* don't copy NULL */
901 slashed_url
[urllen
+2] = '\0';
904 safe_url
= make_safe_uri(slashed_url
);
906 if (safe_url
== NULL
) {
907 verbosef("invalid base \"%s\", ignored", url
);
908 base_url
= strdup("/"); /* set to default */
915 /* --------------------------------------------------------------------------
916 * Initialize the sockin global. This is the socket that we accept
917 * connections from. Pass -1 as max_conn for system limit.
919 void http_init(const char *base
, const char *bindaddr
,
920 const unsigned short bindport
, const int max_conn
)
922 struct sockaddr_storage addrin
;
923 struct addrinfo hints
, *ai
, *aiptr
;
924 char ipaddr
[INET6_ADDRSTRLEN
], portstr
[12];
927 http_init_base(base
);
929 memset(&hints
, 0, sizeof(hints
));
930 hints
.ai_family
= AF_UNSPEC
;
931 hints
.ai_socktype
= SOCK_STREAM
;
932 hints
.ai_flags
= AI_PASSIVE
;
934 hints
.ai_flags
|= AI_ADDRCONFIG
;
936 snprintf(portstr
, sizeof(portstr
), "%u", bindport
);
938 if ((ret
= getaddrinfo(bindaddr
, portstr
, &hints
, &aiptr
)))
939 err(1, "getaddrinfo(): %s", gai_strerror(ret
));
941 for (ai
= aiptr
; ai
; ai
= ai
->ai_next
) {
942 /* create incoming socket */
943 sockin
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
949 if (setsockopt(sockin
, SOL_SOCKET
, SO_REUSEADDR
,
950 &sockopt
, sizeof(sockopt
)) == -1) {
955 /* Recover address and port strings. */
956 getnameinfo(ai
->ai_addr
, ai
->ai_addrlen
, ipaddr
, sizeof(ipaddr
),
957 NULL
, 0, NI_NUMERICHOST
);
960 memcpy(&addrin
, ai
->ai_addr
, ai
->ai_addrlen
);
961 if (bind(sockin
, (struct sockaddr
*)&addrin
, ai
->ai_addrlen
) == -1) {
966 verbosef("listening on %s:%u", ipaddr
, bindport
);
968 /* listen on socket */
969 if (listen(sockin
, max_conn
) >= 0)
970 /* Successfully bound and now listening. */
973 /* Next candidate. */
980 err(1, "getaddrinfo() unable to locate address");
983 if (signal(SIGPIPE
, SIG_IGN
) == SIG_ERR
)
984 err(1, "can't ignore SIGPIPE");
989 /* ---------------------------------------------------------------------------
990 * Set recv/send fd_sets and calculate timeout length.
993 http_fd_set(fd_set
*recv_set
, fd_set
*send_set
, int *max_fd
,
994 struct timeval
*timeout
, int *need_timeout
)
996 struct connection
*conn
, *next
;
997 int minidle
= idletime
+ 1;
999 #define MAX_FD_SET(sock, fdset) do { \
1000 FD_SET(sock, fdset); *max_fd = max(*max_fd, sock); } while(0)
1002 MAX_FD_SET(sockin
, recv_set
);
1004 LIST_FOREACH_SAFE(conn
, &connlist
, entries
, next
)
1006 int idlefor
= now
- conn
->last_active
;
1008 /* Time out dead connections. */
1009 if (idlefor
>= idletime
) {
1010 char ipaddr
[INET6_ADDRSTRLEN
];
1011 /* FIXME: this is too late on FreeBSD, socket is invalid */
1012 int ret
= getnameinfo((struct sockaddr
*)&conn
->client
,
1013 sizeof(conn
->client
), ipaddr
, sizeof(ipaddr
),
1014 NULL
, 0, NI_NUMERICHOST
);
1016 verbosef("http socket timeout from %s (fd %d)",
1017 ipaddr
, conn
->socket
);
1019 warn("http socket timeout: getnameinfo error: %s",
1024 /* Connections that need a timeout. */
1025 if (conn
->state
!= DONE
)
1026 minidle
= min(minidle
, (idletime
- idlefor
));
1028 switch (conn
->state
)
1031 /* clean out stale connection */
1032 LIST_REMOVE(conn
, entries
);
1033 free_connection(conn
);
1038 MAX_FD_SET(conn
->socket
, recv_set
);
1041 case SEND_HEADER_AND_REPLY
:
1044 MAX_FD_SET(conn
->socket
, send_set
);
1047 default: errx(1, "invalid state");
1052 /* Only set timeout if cap hasn't already. */
1053 if ((*need_timeout
== 0) && (minidle
<= idletime
)) {
1055 timeout
->tv_sec
= minidle
;
1056 timeout
->tv_usec
= 0;
1062 /* ---------------------------------------------------------------------------
1063 * poll connections that select() says need attention
1065 void http_poll(fd_set
*recv_set
, fd_set
*send_set
)
1067 struct connection
*conn
;
1069 if (FD_ISSET(sockin
, recv_set
)) accept_connection();
1071 LIST_FOREACH(conn
, &connlist
, entries
)
1072 switch (conn
->state
)
1075 if (FD_ISSET(conn
->socket
, recv_set
)) poll_recv_request(conn
);
1078 case SEND_HEADER_AND_REPLY
:
1079 if (FD_ISSET(conn
->socket
, send_set
)) poll_send_header_and_reply(conn
);
1083 if (FD_ISSET(conn
->socket
, send_set
)) poll_send_header(conn
);
1087 if (FD_ISSET(conn
->socket
, send_set
)) poll_send_reply(conn
);
1090 case DONE
: /* fallthrough */
1091 default: errx(1, "invalid state");
1095 void http_stop(void) {
1100 /* vim:set ts=4 sw=4 et tw=78: */