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)
21 #include <sys/socket.h>
22 #include <arpa/inet.h>
35 static const char mime_type_xml
[] = "text/xml";
36 static const char mime_type_html
[] = "text/html; charset=us-ascii";
37 static const char mime_type_css
[] = "text/css";
38 static const char mime_type_js
[] = "text/javascript";
39 static const char encoding_gzip
[] =
40 "Vary: Accept-Encoding\r\n"
41 "Content-Encoding: gzip\r\n";
43 static const char server
[] = PACKAGE_NAME
"/" PACKAGE_VERSION
;
44 static int idletime
= 60;
45 static int sockin
= -1; /* socket to accept connections from */
46 #define MAX_REQUEST_LENGTH 4000
49 #define min(a,b) (((a) < (b)) ? (a) : (b))
53 LIST_ENTRY(connection
) entries
;
59 RECV_REQUEST
, /* receiving request */
60 SEND_HEADER_AND_REPLY
, /* try to send header+reply together */
61 SEND_HEADER
, /* sending generated header */
62 SEND_REPLY
, /* sending reply */
63 DONE
/* conn closed, need to remove from queue */
66 /* char request[request_length+1] is null-terminated */
68 size_t request_length
;
72 char *method
, *uri
, *query
; /* query can be NULL */
75 const char *mime_type
, *encoding
, *header_extra
;
76 size_t header_length
, header_sent
;
77 int header_dont_free
, header_only
, http_code
;
81 size_t reply_length
, reply_sent
;
83 unsigned int total_sent
; /* header + body = total, for logging */
86 static LIST_HEAD(conn_list_head
, connection
) connlist
=
87 LIST_HEAD_INITIALIZER(conn_list_head
);
89 /* ---------------------------------------------------------------------------
90 * Decode URL by converting %XX (where XX are hexadecimal digits) to the
91 * character it represents. Don't forget to free the return value.
93 static char *urldecode(const char *url
)
95 size_t i
, len
= strlen(url
);
96 char *out
= xmalloc(len
+1);
99 for (i
=0, pos
=0; i
<len
; i
++)
101 if (url
[i
] == '%' && i
+2 < len
&&
102 isxdigit(url
[i
+1]) && isxdigit(url
[i
+2]))
105 #define HEX_TO_DIGIT(hex) ( \
106 ((hex) >= 'A' && (hex) <= 'F') ? ((hex)-'A'+10): \
107 ((hex) >= 'a' && (hex) <= 'f') ? ((hex)-'a'+10): \
110 out
[pos
++] = HEX_TO_DIGIT(url
[i
+1]) * 16 +
111 HEX_TO_DIGIT(url
[i
+2]);
124 /* don't really need to realloc here - it's probably a performance hit */
125 out
= xrealloc(out
, strlen(out
)+1); /* dealloc what we don't need */
132 /* ---------------------------------------------------------------------------
133 * Consolidate slashes in-place by shifting parts of the string over repeated
136 static void consolidate_slashes(char *s
)
138 size_t left
= 0, right
= 0;
143 while (s
[right
] != '\0')
147 if (s
[right
] == '/') right
++;
151 s
[left
++] = s
[right
++];
156 if (s
[right
] == '/') saw_slash
++;
157 s
[left
++] = s
[right
++];
165 /* ---------------------------------------------------------------------------
166 * Resolve /./ and /../ in a URI, returing a new, safe URI, or NULL if the URI
167 * is invalid/unsafe. Returned buffer needs to be deallocated.
169 static char *make_safe_uri(char *uri
)
172 unsigned int slashes
= 0, elements
= 0;
173 size_t urilen
, i
, j
, pos
;
178 consolidate_slashes(uri
);
179 urilen
= strlen(uri
);
181 /* count the slashes */
182 for (i
=0, slashes
=0; i
<urilen
; i
++)
183 if (uri
[i
] == '/') slashes
++;
185 /* make an array for the URI elements */
186 elem
= xmalloc(sizeof(*elem
) * slashes
);
187 for (i
=0; i
<slashes
; i
++)
190 /* split by slashes and build elem[] array */
193 /* look for the next slash */
194 for (j
=i
; j
<urilen
&& uri
[j
] != '/'; j
++)
197 /* process uri[i,j) */
198 if ((j
== i
+1) && (uri
[i
] == '.'))
200 else if ((j
== i
+2) && (uri
[i
] == '.') && (uri
[i
+1] == '.'))
206 * Unsafe string so free elem[]. All its elements are free
215 free(elem
[elements
]);
218 else elem
[elements
++] = split_string(uri
, i
, j
);
220 i
= j
+ 1; /* uri[j] is a slash - move along one */
224 out
= xmalloc(urilen
+1); /* it won't expand */
226 for (i
=0; i
<elements
; i
++)
228 size_t delta
= strlen(elem
[i
]);
230 assert(pos
<= urilen
);
233 assert(pos
+delta
<= urilen
);
234 memcpy(out
+pos
, elem
[i
], delta
);
240 if ((elements
== 0) || (uri
[urilen
-1] == '/')) out
[pos
++] = '/';
241 assert(pos
<= urilen
);
245 /* don't really need to do this and it's probably a performance hit: */
246 /* shorten buffer if necessary */
247 if (pos
!= urilen
) out
= xrealloc(out
, strlen(out
)+1);
252 /* ---------------------------------------------------------------------------
253 * Allocate and initialize an empty connection.
255 static struct connection
*new_connection(void)
257 struct connection
*conn
= xmalloc(sizeof(*conn
));
260 conn
->client
= INADDR_ANY
;
261 conn
->last_active
= now
;
262 conn
->request
= NULL
;
263 conn
->request_length
= 0;
264 conn
->accept_gzip
= 0;
269 conn
->mime_type
= NULL
;
271 conn
->header_extra
= "";
272 conn
->header_length
= 0;
273 conn
->header_sent
= 0;
274 conn
->header_dont_free
= 0;
275 conn
->header_only
= 0;
278 conn
->reply_dont_free
= 0;
279 conn
->reply_length
= 0;
280 conn
->reply_sent
= 0;
281 conn
->total_sent
= 0;
283 /* Make it harmless so it gets garbage-collected if it should, for some
284 * reason, fail to be correctly filled out.
293 /* ---------------------------------------------------------------------------
294 * Accept a connection from sockin and add it to the connection queue.
296 static void accept_connection(void)
298 struct sockaddr_in addrin
;
300 struct connection
*conn
;
303 sin_size
= (socklen_t
)sizeof(struct sockaddr
);
304 sock
= accept(sockin
, (struct sockaddr
*)&addrin
, &sin_size
);
307 if (errno
== ECONNABORTED
|| errno
== EINTR
)
309 verbosef("accept() failed: %s", strerror(errno
));
312 /* else */ err(1, "accept()");
315 fd_set_nonblock(sock
);
317 /* allocate and initialise struct connection */
318 conn
= new_connection();
320 conn
->state
= RECV_REQUEST
;
321 conn
->client
= addrin
.sin_addr
.s_addr
;
322 LIST_INSERT_HEAD(&connlist
, conn
, entries
);
324 verbosef("accepted connection from %s:%u",
325 inet_ntoa(addrin
.sin_addr
),
326 ntohs(addrin
.sin_port
) );
331 /* ---------------------------------------------------------------------------
332 * Log a connection, then cleanly deallocate its internals.
334 static void free_connection(struct connection
*conn
)
336 dverbosef("free_connection(%d)", conn
->socket
);
337 if (conn
->socket
!= -1) close(conn
->socket
);
338 if (conn
->request
!= NULL
) free(conn
->request
);
339 if (conn
->method
!= NULL
) free(conn
->method
);
340 if (conn
->uri
!= NULL
) free(conn
->uri
);
341 if (conn
->query
!= NULL
) free(conn
->query
);
342 if (conn
->header
!= NULL
&& !conn
->header_dont_free
) free(conn
->header
);
343 if (conn
->reply
!= NULL
&& !conn
->reply_dont_free
) free(conn
->reply
);
348 /* ---------------------------------------------------------------------------
349 * Format [when] as an RFC1123 date, stored in the specified buffer. The same
350 * buffer is returned for convenience.
352 #define DATE_LEN 30 /* strlen("Fri, 28 Feb 2003 00:02:08 GMT")+1 */
353 static char *rfc1123_date(char *dest
, const time_t when
)
356 if (strftime(dest
, DATE_LEN
,
357 "%a, %d %b %Y %H:%M:%S %Z", gmtime(&tmp
) ) == 0)
358 errx(1, "strftime() failed [%s]", dest
);
364 /* ---------------------------------------------------------------------------
365 * A default reply for any (erroneous) occasion.
367 static void default_reply(struct connection
*conn
,
368 const int errcode
, const char *errname
, const char *format
, ...)
370 char *reason
, date
[DATE_LEN
];
373 va_start(va
, format
);
374 xvasprintf(&reason
, format
, va
);
377 /* Only really need to calculate the date once. */
378 (void)rfc1123_date(date
, now
);
380 conn
->reply_length
= xasprintf(&(conn
->reply
),
381 "<html><head><title>%d %s</title></head><body>\n"
382 "<h1>%s</h1>\n" /* errname */
385 "Generated by %s on %s\n"
387 errcode
, errname
, errname
, reason
, server
, date
);
390 conn
->header_length
= xasprintf(&(conn
->header
),
394 "Content-Length: %d\r\n"
395 "Content-Type: text/html\r\n"
397 errcode
, errname
, date
, server
, conn
->reply_length
);
399 conn
->http_code
= errcode
;
404 /* ---------------------------------------------------------------------------
405 * Parses a single HTTP request field. Returns string from end of [field] to
406 * first \r, \n or end of request string. Returns NULL if [field] can't be
409 * You need to remember to deallocate the result.
410 * example: parse_field(conn, "Referer: ");
412 static char *parse_field(const struct connection
*conn
, const char *field
)
414 size_t bound1
, bound2
;
418 pos
= strstr(conn
->request
, field
);
421 bound1
= pos
- conn
->request
+ strlen(field
);
424 for (bound2
= bound1
;
425 conn
->request
[bound2
] != '\r' &&
426 bound2
< conn
->request_length
; bound2
++)
430 return (split_string(conn
->request
, bound1
, bound2
));
435 /* ---------------------------------------------------------------------------
436 * Parse an HTTP request like "GET /hosts/?sort=in HTTP/1.1" to get the method
437 * (GET), the uri (/hosts/), the query (sort=in) and whether the UA will
438 * accept gzip encoding. Remember to deallocate all these buffers. Query
439 * can be NULL. The method will be returned in uppercase.
441 static int parse_request(struct connection
*conn
)
443 size_t bound1
, bound2
, mid
;
447 for (bound1
= 0; bound1
< conn
->request_length
&&
448 conn
->request
[bound1
] != ' '; bound1
++)
451 conn
->method
= split_string(conn
->request
, 0, bound1
);
452 strntoupper(conn
->method
, bound1
);
455 for (; bound1
< conn
->request_length
&&
456 conn
->request
[bound1
] == ' '; bound1
++)
459 if (bound1
== conn
->request_length
)
460 return (0); /* fail */
462 for (bound2
=bound1
+1; bound2
< conn
->request_length
&&
463 conn
->request
[bound2
] != ' ' &&
464 conn
->request
[bound2
] != '\r'; bound2
++)
467 /* find query string */
468 for (mid
=bound1
; mid
<bound2
&& conn
->request
[mid
] != '?'; mid
++)
471 if (conn
->request
[mid
] == '?') {
472 conn
->query
= split_string(conn
->request
, mid
+1, bound2
);
476 conn
->uri
= split_string(conn
->request
, bound1
, bound2
);
478 /* parse important fields */
479 accept_enc
= parse_field(conn
, "Accept-Encoding: ");
480 if (accept_enc
!= NULL
) {
481 if (strstr(accept_enc
, "gzip") != NULL
)
482 conn
->accept_gzip
= 1;
488 /* FIXME: maybe we need a smarter way of doing static pages: */
490 /* ---------------------------------------------------------------------------
491 * Web interface: static stylesheet.
494 static_style_css(struct connection
*conn
)
496 #include "stylecss.h"
498 conn
->reply
= style_css
;
499 conn
->reply_length
= style_css_len
;
500 conn
->reply_dont_free
= 1;
501 conn
->mime_type
= mime_type_css
;
504 /* ---------------------------------------------------------------------------
505 * Web interface: static JavaScript.
508 static_graph_js(struct connection
*conn
)
512 conn
->reply
= graph_js
;
513 conn
->reply_length
= graph_js_len
;
514 conn
->reply_dont_free
= 1;
515 conn
->mime_type
= mime_type_js
;
518 /* ---------------------------------------------------------------------------
519 * gzip a reply, if requested and possible. Don't bother with a minimum
520 * length requirement, I've never seen a page fail to compress.
523 process_gzip(struct connection
*conn
)
529 if (!conn
->accept_gzip
)
532 buf
= xmalloc(conn
->reply_length
);
533 len
= conn
->reply_length
;
539 if (deflateInit2(&zs
, Z_BEST_COMPRESSION
, Z_DEFLATED
,
540 15+16, /* 15 = biggest window, 16 = add gzip header+trailer */
542 Z_DEFAULT_STRATEGY
) != Z_OK
)
545 zs
.avail_in
= conn
->reply_length
;
546 zs
.next_in
= (unsigned char *)conn
->reply
;
548 zs
.avail_out
= conn
->reply_length
;
549 zs
.next_out
= (unsigned char *)buf
;
551 if (deflate(&zs
, Z_FINISH
) != Z_STREAM_END
) {
554 verbosef("failed to compress %u bytes", (unsigned int)len
);
558 if (conn
->reply_dont_free
)
559 conn
->reply_dont_free
= 0;
564 conn
->reply_length
-= zs
.avail_out
;
565 conn
->encoding
= encoding_gzip
;
569 /* ---------------------------------------------------------------------------
570 * Process a GET/HEAD request
572 static void process_get(struct connection
*conn
)
574 char *decoded_url
, *safe_url
;
577 verbosef("http: %s \"%s\" %s", conn
->method
, conn
->uri
,
578 (conn
->query
== NULL
)?"":conn
->query
);
580 /* work out path of file being requested */
581 decoded_url
= urldecode(conn
->uri
);
583 /* make sure it's safe */
584 safe_url
= make_safe_uri(decoded_url
);
586 if (safe_url
== NULL
)
588 default_reply(conn
, 400, "Bad Request",
589 "You requested an invalid URI: %s", conn
->uri
);
593 if (strcmp(safe_url
, "/") == 0) {
594 struct str
*buf
= html_front_page();
595 str_extract(buf
, &(conn
->reply_length
), &(conn
->reply
));
596 conn
->mime_type
= mime_type_html
;
598 else if (str_starts_with(safe_url
, "/hosts/")) {
599 /* FIXME here - make this saner */
600 struct str
*buf
= html_hosts(safe_url
, conn
->query
);
602 default_reply(conn
, 404, "Not Found",
603 "The page you requested could not be found.");
606 str_extract(buf
, &(conn
->reply_length
), &(conn
->reply
));
607 conn
->mime_type
= mime_type_html
;
609 else if (str_starts_with(safe_url
, "/graphs.xml")) {
610 struct str
*buf
= xml_graphs();
611 str_extract(buf
, &(conn
->reply_length
), &(conn
->reply
));
612 conn
->mime_type
= mime_type_xml
;
613 /* hack around Opera caching the XML */
614 conn
->header_extra
= "Pragma: no-cache\r\n";
616 else if (strcmp(safe_url
, "/style.css") == 0)
617 static_style_css(conn
);
618 else if (strcmp(safe_url
, "/graph.js") == 0)
619 static_graph_js(conn
);
621 default_reply(conn
, 404, "Not Found",
622 "The page you requested could not be found.");
628 assert(conn
->mime_type
!= NULL
);
629 conn
->header_length
= xasprintf(&(conn
->header
),
630 "HTTP/1.1 200 OK\r\n"
633 "Content-Length: %d\r\n"
634 "Content-Type: %s\r\n"
639 rfc1123_date(date
, now
), server
,
640 conn
->reply_length
, conn
->mime_type
, conn
->encoding
,
642 conn
->http_code
= 200;
647 /* ---------------------------------------------------------------------------
648 * Process a request: build the header and reply, advance state.
650 static void process_request(struct connection
*conn
)
652 if (!parse_request(conn
))
654 default_reply(conn
, 400, "Bad Request",
655 "You sent a request that the server couldn't understand.");
657 else if (strcmp(conn
->method
, "GET") == 0)
661 else if (strcmp(conn
->method
, "HEAD") == 0)
664 conn
->header_only
= 1;
668 default_reply(conn
, 501, "Not Implemented",
669 "The method you specified (%s) is not implemented.",
674 if (conn
->header_only
)
675 conn
->state
= SEND_HEADER
;
677 conn
->state
= SEND_HEADER_AND_REPLY
;
679 /* request not needed anymore */
681 conn
->request
= NULL
; /* important: don't free it again later */
686 /* ---------------------------------------------------------------------------
689 static void poll_recv_request(struct connection
*conn
)
691 #define BUFSIZE 65536
695 recvd
= recv(conn
->socket
, buf
, BUFSIZE
, 0);
696 dverbosef("poll_recv_request(%d) got %d bytes", conn
->socket
, (int)recvd
);
700 verbosef("recv(%d) error: %s", conn
->socket
, strerror(errno
));
704 conn
->last_active
= now
;
707 /* append to conn->request */
708 conn
->request
= xrealloc(conn
->request
, conn
->request_length
+recvd
+1);
709 memcpy(conn
->request
+conn
->request_length
, buf
, (size_t)recvd
);
710 conn
->request_length
+= recvd
;
711 conn
->request
[conn
->request_length
] = 0;
713 /* process request if we have all of it */
714 if (conn
->request_length
> 4 &&
715 memcmp(conn
->request
+conn
->request_length
-4, "\r\n\r\n", 4) == 0)
716 process_request(conn
);
718 /* die if it's too long */
719 if (conn
->request_length
> MAX_REQUEST_LENGTH
)
721 default_reply(conn
, 413, "Request Entity Too Large",
722 "Your request was dropped because it was too long.");
723 conn
->state
= SEND_HEADER
;
729 /* ---------------------------------------------------------------------------
730 * Try to send header and [a part of the] reply in one packet.
732 static void poll_send_header_and_reply(struct connection
*conn
)
737 assert(!conn
->header_only
);
738 assert(conn
->reply_length
> 0);
739 assert(conn
->header_sent
== 0);
741 assert(conn
->reply_sent
== 0);
744 iov
[0].iov_base
= conn
->header
;
745 iov
[0].iov_len
= conn
->header_length
;
747 iov
[1].iov_base
= conn
->reply
+ conn
->reply_sent
;
748 iov
[1].iov_len
= conn
->reply_length
- conn
->reply_sent
;
750 sent
= writev(conn
->socket
, iov
, 2);
751 conn
->last_active
= now
;
752 verbosef("poll_send_header_and_reply(%d) sent %d bytes",
753 conn
->socket
, (int)sent
);
755 /* handle any errors (-1) or closure (0) in send() */
758 verbosef("writev(%d) error: %s", conn
->socket
, strerror(errno
));
763 /* Figure out what we've sent. */
764 conn
->total_sent
+= (unsigned int)sent
;
765 if (sent
< (ssize_t
)conn
->header_length
) {
766 verbosef("partially sent header");
767 conn
->header_sent
= sent
;
768 conn
->state
= SEND_HEADER
;
772 conn
->header_sent
= conn
->header_length
;
773 sent
-= conn
->header_length
;
775 if (conn
->reply_sent
+ sent
< conn
->reply_length
) {
776 verbosef("partially sent reply");
777 conn
->reply_sent
+= sent
;
778 conn
->state
= SEND_REPLY
;
782 conn
->reply_sent
= conn
->reply_length
;
786 /* ---------------------------------------------------------------------------
787 * Sending header. Assumes conn->header is not NULL.
789 static void poll_send_header(struct connection
*conn
)
793 sent
= send(conn
->socket
, conn
->header
+ conn
->header_sent
,
794 conn
->header_length
- conn
->header_sent
, 0);
795 conn
->last_active
= now
;
796 dverbosef("poll_send_header(%d) sent %d bytes", conn
->socket
, (int)sent
);
798 /* handle any errors (-1) or closure (0) in send() */
802 verbosef("send(%d) error: %s", conn
->socket
, strerror(errno
));
806 conn
->header_sent
+= (unsigned int)sent
;
807 conn
->total_sent
+= (unsigned int)sent
;
809 /* check if we're done sending */
810 if (conn
->header_sent
== conn
->header_length
)
812 if (conn
->header_only
)
815 conn
->state
= SEND_REPLY
;
821 /* ---------------------------------------------------------------------------
824 static void poll_send_reply(struct connection
*conn
)
828 sent
= send(conn
->socket
,
829 conn
->reply
+ conn
->reply_sent
,
830 conn
->reply_length
- conn
->reply_sent
, 0);
831 conn
->last_active
= now
;
832 dverbosef("poll_send_reply(%d) sent %d: [%d-%d] of %d",
833 conn
->socket
, (int)sent
,
834 (int)conn
->reply_sent
,
835 (int)(conn
->reply_sent
+ sent
- 1),
836 (int)conn
->reply_length
);
838 /* handle any errors (-1) or closure (0) in send() */
842 verbosef("send(%d) error: %s", conn
->socket
, strerror(errno
));
844 verbosef("send(%d) closure", conn
->socket
);
848 conn
->reply_sent
+= (unsigned int)sent
;
849 conn
->total_sent
+= (unsigned int)sent
;
851 /* check if we're done sending */
852 if (conn
->reply_sent
== conn
->reply_length
) conn
->state
= DONE
;
857 /* ---------------------------------------------------------------------------
858 * Initialize the sockin global. This is the socket that we accept
859 * connections from. Pass -1 as max_conn for system limit.
861 void http_init(const in_addr_t bindaddr
, const unsigned short bindport
,
864 struct sockaddr_in addrin
;
867 /* create incoming socket */
868 sockin
= socket(PF_INET
, SOCK_STREAM
, 0);
869 if (sockin
== -1) err(1, "socket()");
873 if (setsockopt(sockin
, SOL_SOCKET
, SO_REUSEADDR
,
874 &sockopt
, sizeof(sockopt
)) == -1)
875 err(1, "setsockopt(SO_REUSEADDR)");
878 addrin
.sin_family
= (u_char
)PF_INET
;
879 addrin
.sin_port
= htons(bindport
);
880 addrin
.sin_addr
.s_addr
= bindaddr
;
881 memset(&(addrin
.sin_zero
), 0, 8);
882 if (bind(sockin
, (struct sockaddr
*)&addrin
,
883 sizeof(struct sockaddr
)) == -1)
884 err(1, "bind(%s:%u)", inet_ntoa(addrin
.sin_addr
), bindport
);
886 verbosef("listening on %s:%u", inet_ntoa(addrin
.sin_addr
), bindport
);
888 /* listen on socket */
889 if (listen(sockin
, max_conn
) == -1)
893 if (signal(SIGPIPE
, SIG_IGN
) == SIG_ERR
)
894 err(1, "can't ignore SIGPIPE");
899 /* ---------------------------------------------------------------------------
900 * Set recv/send fd_sets and calculate timeout length.
903 http_fd_set(fd_set
*recv_set
, fd_set
*send_set
, int *max_fd
,
904 struct timeval
*timeout
, int *need_timeout
)
906 struct connection
*conn
, *next
;
907 int minidle
= idletime
+ 1;
909 #define MAX_FD_SET(sock, fdset) do { \
910 FD_SET(sock, fdset); *max_fd = max(*max_fd, sock); } while(0)
912 MAX_FD_SET(sockin
, recv_set
);
914 LIST_FOREACH_SAFE(conn
, &connlist
, entries
, next
)
916 int idlefor
= now
- conn
->last_active
;
918 /* Time out dead connections. */
919 if (idlefor
>= idletime
)
921 struct sockaddr_in addrin
;
922 addrin
.sin_addr
.s_addr
= conn
->client
;
923 verbosef("http socket timeout from %s (fd %d)",
924 inet_ntoa(addrin
.sin_addr
),
929 /* Connections that need a timeout. */
930 if (conn
->state
!= DONE
)
931 minidle
= min(minidle
, (idletime
- idlefor
));
936 /* clean out stale connection */
937 LIST_REMOVE(conn
, entries
);
938 free_connection(conn
);
943 MAX_FD_SET(conn
->socket
, recv_set
);
946 case SEND_HEADER_AND_REPLY
:
949 MAX_FD_SET(conn
->socket
, send_set
);
952 default: errx(1, "invalid state");
957 /* Only set timeout if cap hasn't already. */
958 if ((*need_timeout
== 0) && (minidle
<= idletime
)) {
960 timeout
->tv_sec
= minidle
;
961 timeout
->tv_usec
= 0;
967 /* ---------------------------------------------------------------------------
968 * poll connections that select() says need attention
970 void http_poll(fd_set
*recv_set
, fd_set
*send_set
)
972 struct connection
*conn
;
974 if (FD_ISSET(sockin
, recv_set
)) accept_connection();
976 LIST_FOREACH(conn
, &connlist
, entries
)
980 if (FD_ISSET(conn
->socket
, recv_set
)) poll_recv_request(conn
);
983 case SEND_HEADER_AND_REPLY
:
984 if (FD_ISSET(conn
->socket
, send_set
)) poll_send_header_and_reply(conn
);
988 if (FD_ISSET(conn
->socket
, send_set
)) poll_send_header(conn
);
992 if (FD_ISSET(conn
->socket
, send_set
)) poll_send_reply(conn
);
995 default: errx(1, "invalid state");
999 /* vim:set ts=4 sw=4 et tw=78: */