3ec78488ba554e52c2330feb47320be110d3a5dd
[darkstat] / http.c
1 /* darkstat 3
2 * copyright (c) 2001-2008 Emil Mikulic.
3 *
4 * http.c: embedded webserver.
5 * This borrows a lot of code from darkhttpd.
6 *
7 * You may use, modify and redistribute this file under the terms of the
8 * GNU General Public License version 2. (see COPYING.GPL)
9 */
10
11 #include "darkstat.h"
12 #include "http.h"
13 #include "conv.h"
14 #include "hosts_db.h"
15 #include "graph_db.h"
16 #include "err.h"
17 #include "queue.h"
18 #include "now.h"
19
20 #include <sys/uio.h>
21 #include <sys/socket.h>
22 #include <arpa/inet.h>
23 #include <assert.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <signal.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <zlib.h>
34
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";
42
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
47
48 #ifndef min
49 #define min(a,b) (((a) < (b)) ? (a) : (b))
50 #endif
51
52 struct connection {
53 LIST_ENTRY(connection) entries;
54
55 int socket;
56 in_addr_t client;
57 time_t last_active;
58 enum {
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 */
64 } state;
65
66 /* char request[request_length+1] is null-terminated */
67 char *request;
68 size_t request_length;
69 int accept_gzip;
70
71 /* request fields */
72 char *method, *uri, *query; /* query can be NULL */
73
74 char *header;
75 const char *mime_type, *encoding, *header_extra;
76 size_t header_length, header_sent;
77 int header_dont_free, header_only, http_code;
78
79 char *reply;
80 int reply_dont_free;
81 size_t reply_length, reply_sent;
82
83 unsigned int total_sent; /* header + body = total, for logging */
84 };
85
86 static LIST_HEAD(conn_list_head, connection) connlist =
87 LIST_HEAD_INITIALIZER(conn_list_head);
88
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.
92 */
93 static char *urldecode(const char *url)
94 {
95 size_t i, len = strlen(url);
96 char *out = xmalloc(len+1);
97 int pos;
98
99 for (i=0, pos=0; i<len; i++)
100 {
101 if (url[i] == '%' && i+2 < len &&
102 isxdigit(url[i+1]) && isxdigit(url[i+2]))
103 {
104 /* decode %XX */
105 #define HEX_TO_DIGIT(hex) ( \
106 ((hex) >= 'A' && (hex) <= 'F') ? ((hex)-'A'+10): \
107 ((hex) >= 'a' && (hex) <= 'f') ? ((hex)-'a'+10): \
108 ((hex)-'0') )
109
110 out[pos++] = HEX_TO_DIGIT(url[i+1]) * 16 +
111 HEX_TO_DIGIT(url[i+2]);
112 i += 2;
113
114 #undef HEX_TO_DIGIT
115 }
116 else
117 {
118 /* straight copy */
119 out[pos++] = url[i];
120 }
121 }
122 out[pos] = 0;
123 #if 0
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 */
126 #endif
127 return (out);
128 }
129
130
131
132 /* ---------------------------------------------------------------------------
133 * Consolidate slashes in-place by shifting parts of the string over repeated
134 * slashes.
135 */
136 static void consolidate_slashes(char *s)
137 {
138 size_t left = 0, right = 0;
139 int saw_slash = 0;
140
141 assert(s != NULL);
142
143 while (s[right] != '\0')
144 {
145 if (saw_slash)
146 {
147 if (s[right] == '/') right++;
148 else
149 {
150 saw_slash = 0;
151 s[left++] = s[right++];
152 }
153 }
154 else
155 {
156 if (s[right] == '/') saw_slash++;
157 s[left++] = s[right++];
158 }
159 }
160 s[left] = '\0';
161 }
162
163
164
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.
168 */
169 static char *make_safe_uri(char *uri)
170 {
171 char **elem, *out;
172 unsigned int slashes = 0, elements = 0;
173 size_t urilen, i, j, pos;
174
175 assert(uri != NULL);
176 if (uri[0] != '/')
177 return (NULL);
178 consolidate_slashes(uri);
179 urilen = strlen(uri);
180
181 /* count the slashes */
182 for (i=0, slashes=0; i<urilen; i++)
183 if (uri[i] == '/') slashes++;
184
185 /* make an array for the URI elements */
186 elem = xmalloc(sizeof(*elem) * slashes);
187 for (i=0; i<slashes; i++)
188 elem[i] = (NULL);
189
190 /* split by slashes and build elem[] array */
191 for (i=1; i<urilen;)
192 {
193 /* look for the next slash */
194 for (j=i; j<urilen && uri[j] != '/'; j++)
195 ;
196
197 /* process uri[i,j) */
198 if ((j == i+1) && (uri[i] == '.'))
199 /* "." */;
200 else if ((j == i+2) && (uri[i] == '.') && (uri[i+1] == '.'))
201 {
202 /* ".." */
203 if (elements == 0)
204 {
205 /*
206 * Unsafe string so free elem[]. All its elements are free
207 * at this point.
208 */
209 free(elem);
210 return (NULL);
211 }
212 else
213 {
214 elements--;
215 free(elem[elements]);
216 }
217 }
218 else elem[elements++] = split_string(uri, i, j);
219
220 i = j + 1; /* uri[j] is a slash - move along one */
221 }
222
223 /* reassemble */
224 out = xmalloc(urilen+1); /* it won't expand */
225 pos = 0;
226 for (i=0; i<elements; i++)
227 {
228 size_t delta = strlen(elem[i]);
229
230 assert(pos <= urilen);
231 out[pos++] = '/';
232
233 assert(pos+delta <= urilen);
234 memcpy(out+pos, elem[i], delta);
235 free(elem[i]);
236 pos += delta;
237 }
238 free(elem);
239
240 if ((elements == 0) || (uri[urilen-1] == '/')) out[pos++] = '/';
241 assert(pos <= urilen);
242 out[pos] = '\0';
243
244 #if 0
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);
248 #endif
249 return (out);
250 }
251
252 /* ---------------------------------------------------------------------------
253 * Allocate and initialize an empty connection.
254 */
255 static struct connection *new_connection(void)
256 {
257 struct connection *conn = xmalloc(sizeof(*conn));
258
259 conn->socket = -1;
260 conn->client = INADDR_ANY;
261 conn->last_active = now;
262 conn->request = NULL;
263 conn->request_length = 0;
264 conn->accept_gzip = 0;
265 conn->method = NULL;
266 conn->uri = NULL;
267 conn->query = NULL;
268 conn->header = NULL;
269 conn->mime_type = NULL;
270 conn->encoding = "";
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;
276 conn->http_code = 0;
277 conn->reply = NULL;
278 conn->reply_dont_free = 0;
279 conn->reply_length = 0;
280 conn->reply_sent = 0;
281 conn->total_sent = 0;
282
283 /* Make it harmless so it gets garbage-collected if it should, for some
284 * reason, fail to be correctly filled out.
285 */
286 conn->state = DONE;
287
288 return (conn);
289 }
290
291
292
293 /* ---------------------------------------------------------------------------
294 * Accept a connection from sockin and add it to the connection queue.
295 */
296 static void accept_connection(void)
297 {
298 struct sockaddr_in addrin;
299 socklen_t sin_size;
300 struct connection *conn;
301 int sock;
302
303 sin_size = (socklen_t)sizeof(struct sockaddr);
304 sock = accept(sockin, (struct sockaddr *)&addrin, &sin_size);
305 if (sock == -1)
306 {
307 if (errno == ECONNABORTED || errno == EINTR)
308 {
309 verbosef("accept() failed: %s", strerror(errno));
310 return;
311 }
312 /* else */ err(1, "accept()");
313 }
314
315 fd_set_nonblock(sock);
316
317 /* allocate and initialise struct connection */
318 conn = new_connection();
319 conn->socket = sock;
320 conn->state = RECV_REQUEST;
321 conn->client = addrin.sin_addr.s_addr;
322 LIST_INSERT_HEAD(&connlist, conn, entries);
323
324 verbosef("accepted connection from %s:%u",
325 inet_ntoa(addrin.sin_addr),
326 ntohs(addrin.sin_port) );
327 }
328
329
330
331 /* ---------------------------------------------------------------------------
332 * Log a connection, then cleanly deallocate its internals.
333 */
334 static void free_connection(struct connection *conn)
335 {
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);
344 }
345
346
347
348 /* ---------------------------------------------------------------------------
349 * Format [when] as an RFC1123 date, stored in the specified buffer. The same
350 * buffer is returned for convenience.
351 */
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)
354 {
355 time_t tmp = 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);
359 return (dest);
360 }
361
362
363
364 /* ---------------------------------------------------------------------------
365 * A default reply for any (erroneous) occasion.
366 */
367 static void default_reply(struct connection *conn,
368 const int errcode, const char *errname, const char *format, ...)
369 {
370 char *reason, date[DATE_LEN];
371 va_list va;
372
373 va_start(va, format);
374 xvasprintf(&reason, format, va);
375 va_end(va);
376
377 /* Only really need to calculate the date once. */
378 (void)rfc1123_date(date, now);
379
380 conn->reply_length = xasprintf(&(conn->reply),
381 "<html><head><title>%d %s</title></head><body>\n"
382 "<h1>%s</h1>\n" /* errname */
383 "%s\n" /* reason */
384 "<hr>\n"
385 "Generated by %s on %s\n"
386 "</body></html>\n",
387 errcode, errname, errname, reason, server, date);
388 free(reason);
389
390 conn->header_length = xasprintf(&(conn->header),
391 "HTTP/1.1 %d %s\r\n"
392 "Date: %s\r\n"
393 "Server: %s\r\n"
394 "Content-Length: %d\r\n"
395 "Content-Type: text/html\r\n"
396 "\r\n",
397 errcode, errname, date, server, conn->reply_length);
398
399 conn->http_code = errcode;
400 }
401
402
403
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
407 * matched.
408 *
409 * You need to remember to deallocate the result.
410 * example: parse_field(conn, "Referer: ");
411 */
412 static char *parse_field(const struct connection *conn, const char *field)
413 {
414 size_t bound1, bound2;
415 char *pos;
416
417 /* find start */
418 pos = strstr(conn->request, field);
419 if (pos == NULL)
420 return (NULL);
421 bound1 = pos - conn->request + strlen(field);
422
423 /* find end */
424 for (bound2 = bound1;
425 conn->request[bound2] != '\r' &&
426 bound2 < conn->request_length; bound2++)
427 ;
428
429 /* copy to buffer */
430 return (split_string(conn->request, bound1, bound2));
431 }
432
433
434
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.
440 */
441 static int parse_request(struct connection *conn)
442 {
443 size_t bound1, bound2, mid;
444 char *accept_enc;
445
446 /* parse method */
447 for (bound1 = 0; bound1 < conn->request_length &&
448 conn->request[bound1] != ' '; bound1++)
449 ;
450
451 conn->method = split_string(conn->request, 0, bound1);
452 strntoupper(conn->method, bound1);
453
454 /* parse uri */
455 for (; bound1 < conn->request_length &&
456 conn->request[bound1] == ' '; bound1++)
457 ;
458
459 if (bound1 == conn->request_length)
460 return (0); /* fail */
461
462 for (bound2=bound1+1; bound2 < conn->request_length &&
463 conn->request[bound2] != ' ' &&
464 conn->request[bound2] != '\r'; bound2++)
465 ;
466
467 /* find query string */
468 for (mid=bound1; mid<bound2 && conn->request[mid] != '?'; mid++)
469 ;
470
471 if (conn->request[mid] == '?') {
472 conn->query = split_string(conn->request, mid+1, bound2);
473 bound2 = mid;
474 }
475
476 conn->uri = split_string(conn->request, bound1, bound2);
477
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;
483 free(accept_enc);
484 }
485 return (1);
486 }
487
488 /* FIXME: maybe we need a smarter way of doing static pages: */
489
490 /* ---------------------------------------------------------------------------
491 * Web interface: static stylesheet.
492 */
493 static void
494 static_style_css(struct connection *conn)
495 {
496 #include "stylecss.h"
497
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;
502 }
503
504 /* ---------------------------------------------------------------------------
505 * Web interface: static JavaScript.
506 */
507 static void
508 static_graph_js(struct connection *conn)
509 {
510 #include "graphjs.h"
511
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;
516 }
517
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.
521 */
522 static void
523 process_gzip(struct connection *conn)
524 {
525 char *buf;
526 size_t len;
527 z_stream zs;
528
529 if (!conn->accept_gzip)
530 return;
531
532 buf = xmalloc(conn->reply_length);
533 len = conn->reply_length;
534
535 zs.zalloc = Z_NULL;
536 zs.zfree = Z_NULL;
537 zs.opaque = Z_NULL;
538
539 if (deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED,
540 15+16, /* 15 = biggest window, 16 = add gzip header+trailer */
541 8 /* default */,
542 Z_DEFAULT_STRATEGY) != Z_OK)
543 return;
544
545 zs.avail_in = conn->reply_length;
546 zs.next_in = (unsigned char *)conn->reply;
547
548 zs.avail_out = conn->reply_length;
549 zs.next_out = (unsigned char *)buf;
550
551 if (deflate(&zs, Z_FINISH) != Z_STREAM_END) {
552 deflateEnd(&zs);
553 free(buf);
554 verbosef("failed to compress %u bytes", (unsigned int)len);
555 return;
556 }
557
558 if (conn->reply_dont_free)
559 conn->reply_dont_free = 0;
560 else
561 free(conn->reply);
562
563 conn->reply = buf;
564 conn->reply_length -= zs.avail_out;
565 conn->encoding = encoding_gzip;
566 deflateEnd(&zs);
567 }
568
569 /* ---------------------------------------------------------------------------
570 * Process a GET/HEAD request
571 */
572 static void process_get(struct connection *conn)
573 {
574 char *decoded_url, *safe_url;
575 char date[DATE_LEN];
576
577 verbosef("http: %s \"%s\" %s", conn->method, conn->uri,
578 (conn->query == NULL)?"":conn->query);
579
580 /* work out path of file being requested */
581 decoded_url = urldecode(conn->uri);
582
583 /* make sure it's safe */
584 safe_url = make_safe_uri(decoded_url);
585 free(decoded_url);
586 if (safe_url == NULL)
587 {
588 default_reply(conn, 400, "Bad Request",
589 "You requested an invalid URI: %s", conn->uri);
590 return;
591 }
592
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;
597 }
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);
601 if (buf == NULL) {
602 default_reply(conn, 404, "Not Found",
603 "The page you requested could not be found.");
604 return;
605 }
606 str_extract(buf, &(conn->reply_length), &(conn->reply));
607 conn->mime_type = mime_type_html;
608 }
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";
615 }
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);
620 else {
621 default_reply(conn, 404, "Not Found",
622 "The page you requested could not be found.");
623 return;
624 }
625 free(safe_url);
626
627 process_gzip(conn);
628 assert(conn->mime_type != NULL);
629 conn->header_length = xasprintf(&(conn->header),
630 "HTTP/1.1 200 OK\r\n"
631 "Date: %s\r\n"
632 "Server: %s\r\n"
633 "Content-Length: %d\r\n"
634 "Content-Type: %s\r\n"
635 "%s"
636 "%s"
637 "\r\n"
638 ,
639 rfc1123_date(date, now), server,
640 conn->reply_length, conn->mime_type, conn->encoding,
641 conn->header_extra);
642 conn->http_code = 200;
643 }
644
645
646
647 /* ---------------------------------------------------------------------------
648 * Process a request: build the header and reply, advance state.
649 */
650 static void process_request(struct connection *conn)
651 {
652 if (!parse_request(conn))
653 {
654 default_reply(conn, 400, "Bad Request",
655 "You sent a request that the server couldn't understand.");
656 }
657 else if (strcmp(conn->method, "GET") == 0)
658 {
659 process_get(conn);
660 }
661 else if (strcmp(conn->method, "HEAD") == 0)
662 {
663 process_get(conn);
664 conn->header_only = 1;
665 }
666 else
667 {
668 default_reply(conn, 501, "Not Implemented",
669 "The method you specified (%s) is not implemented.",
670 conn->method);
671 }
672
673 /* advance state */
674 if (conn->header_only)
675 conn->state = SEND_HEADER;
676 else
677 conn->state = SEND_HEADER_AND_REPLY;
678
679 /* request not needed anymore */
680 free(conn->request);
681 conn->request = NULL; /* important: don't free it again later */
682 }
683
684
685
686 /* ---------------------------------------------------------------------------
687 * Receiving request.
688 */
689 static void poll_recv_request(struct connection *conn)
690 {
691 #define BUFSIZE 65536
692 char buf[BUFSIZE];
693 ssize_t recvd;
694
695 recvd = recv(conn->socket, buf, BUFSIZE, 0);
696 dverbosef("poll_recv_request(%d) got %d bytes", conn->socket, (int)recvd);
697 if (recvd <= 0)
698 {
699 if (recvd == -1)
700 verbosef("recv(%d) error: %s", conn->socket, strerror(errno));
701 conn->state = DONE;
702 return;
703 }
704 conn->last_active = now;
705 #undef BUFSIZE
706
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;
712
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);
717
718 /* die if it's too long */
719 if (conn->request_length > MAX_REQUEST_LENGTH)
720 {
721 default_reply(conn, 413, "Request Entity Too Large",
722 "Your request was dropped because it was too long.");
723 conn->state = SEND_HEADER;
724 }
725 }
726
727
728
729 /* ---------------------------------------------------------------------------
730 * Try to send header and [a part of the] reply in one packet.
731 */
732 static void poll_send_header_and_reply(struct connection *conn)
733 {
734 ssize_t sent;
735 struct iovec iov[2];
736
737 assert(!conn->header_only);
738 assert(conn->reply_length > 0);
739 assert(conn->header_sent == 0);
740
741 assert(conn->reply_sent == 0);
742
743 /* Fill out iovec */
744 iov[0].iov_base = conn->header;
745 iov[0].iov_len = conn->header_length;
746
747 iov[1].iov_base = conn->reply + conn->reply_sent;
748 iov[1].iov_len = conn->reply_length - conn->reply_sent;
749
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);
754
755 /* handle any errors (-1) or closure (0) in send() */
756 if (sent < 1) {
757 if (sent == -1)
758 verbosef("writev(%d) error: %s", conn->socket, strerror(errno));
759 conn->state = DONE;
760 return;
761 }
762
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;
769 return;
770 }
771 /* else */
772 conn->header_sent = conn->header_length;
773 sent -= conn->header_length;
774
775 if (conn->reply_sent + sent < conn->reply_length) {
776 verbosef("partially sent reply");
777 conn->reply_sent += sent;
778 conn->state = SEND_REPLY;
779 return;
780 }
781 /* else */
782 conn->reply_sent = conn->reply_length;
783 conn->state = DONE;
784 }
785
786 /* ---------------------------------------------------------------------------
787 * Sending header. Assumes conn->header is not NULL.
788 */
789 static void poll_send_header(struct connection *conn)
790 {
791 ssize_t sent;
792
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);
797
798 /* handle any errors (-1) or closure (0) in send() */
799 if (sent < 1)
800 {
801 if (sent == -1)
802 verbosef("send(%d) error: %s", conn->socket, strerror(errno));
803 conn->state = DONE;
804 return;
805 }
806 conn->header_sent += (unsigned int)sent;
807 conn->total_sent += (unsigned int)sent;
808
809 /* check if we're done sending */
810 if (conn->header_sent == conn->header_length)
811 {
812 if (conn->header_only)
813 conn->state = DONE;
814 else
815 conn->state = SEND_REPLY;
816 }
817 }
818
819
820
821 /* ---------------------------------------------------------------------------
822 * Sending reply.
823 */
824 static void poll_send_reply(struct connection *conn)
825 {
826 ssize_t sent;
827
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);
837
838 /* handle any errors (-1) or closure (0) in send() */
839 if (sent < 1)
840 {
841 if (sent == -1)
842 verbosef("send(%d) error: %s", conn->socket, strerror(errno));
843 else if (sent == 0)
844 verbosef("send(%d) closure", conn->socket);
845 conn->state = DONE;
846 return;
847 }
848 conn->reply_sent += (unsigned int)sent;
849 conn->total_sent += (unsigned int)sent;
850
851 /* check if we're done sending */
852 if (conn->reply_sent == conn->reply_length) conn->state = DONE;
853 }
854
855
856
857 /* ---------------------------------------------------------------------------
858 * Initialize the sockin global. This is the socket that we accept
859 * connections from. Pass -1 as max_conn for system limit.
860 */
861 void http_init(const in_addr_t bindaddr, const unsigned short bindport,
862 const int max_conn)
863 {
864 struct sockaddr_in addrin;
865 int sockopt;
866
867 /* create incoming socket */
868 sockin = socket(PF_INET, SOCK_STREAM, 0);
869 if (sockin == -1) err(1, "socket()");
870
871 /* reuse address */
872 sockopt = 1;
873 if (setsockopt(sockin, SOL_SOCKET, SO_REUSEADDR,
874 &sockopt, sizeof(sockopt)) == -1)
875 err(1, "setsockopt(SO_REUSEADDR)");
876
877 /* bind socket */
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);
885
886 verbosef("listening on %s:%u", inet_ntoa(addrin.sin_addr), bindport);
887
888 /* listen on socket */
889 if (listen(sockin, max_conn) == -1)
890 err(1, "listen()");
891
892 /* ignore SIGPIPE */
893 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
894 err(1, "can't ignore SIGPIPE");
895 }
896
897
898
899 /* ---------------------------------------------------------------------------
900 * Set recv/send fd_sets and calculate timeout length.
901 */
902 void
903 http_fd_set(fd_set *recv_set, fd_set *send_set, int *max_fd,
904 struct timeval *timeout, int *need_timeout)
905 {
906 struct connection *conn, *next;
907 int minidle = idletime + 1;
908
909 #define MAX_FD_SET(sock, fdset) do { \
910 FD_SET(sock, fdset); *max_fd = max(*max_fd, sock); } while(0)
911
912 MAX_FD_SET(sockin, recv_set);
913
914 LIST_FOREACH_SAFE(conn, &connlist, entries, next)
915 {
916 int idlefor = now - conn->last_active;
917
918 /* Time out dead connections. */
919 if (idlefor >= idletime)
920 {
921 verbosef("timeout on %d", conn->socket);
922 conn->state = DONE;
923 }
924
925 /* Connections that need a timeout. */
926 if (conn->state != DONE)
927 minidle = min(minidle, (idletime - idlefor));
928
929 switch (conn->state)
930 {
931 case DONE:
932 /* clean out stale connection */
933 LIST_REMOVE(conn, entries);
934 free_connection(conn);
935 free(conn);
936 break;
937
938 case RECV_REQUEST:
939 MAX_FD_SET(conn->socket, recv_set);
940 break;
941
942 case SEND_HEADER_AND_REPLY:
943 case SEND_HEADER:
944 case SEND_REPLY:
945 MAX_FD_SET(conn->socket, send_set);
946 break;
947
948 default: errx(1, "invalid state");
949 }
950 }
951 #undef MAX_FD_SET
952
953 /* Only set timeout if cap hasn't already. */
954 if ((*need_timeout == 0) && (minidle <= idletime)) {
955 *need_timeout = 1;
956 timeout->tv_sec = minidle;
957 timeout->tv_usec = 0;
958 }
959 }
960
961
962
963 /* ---------------------------------------------------------------------------
964 * poll connections that select() says need attention
965 */
966 void http_poll(fd_set *recv_set, fd_set *send_set)
967 {
968 struct connection *conn;
969
970 if (FD_ISSET(sockin, recv_set)) accept_connection();
971
972 LIST_FOREACH(conn, &connlist, entries)
973 switch (conn->state)
974 {
975 case RECV_REQUEST:
976 if (FD_ISSET(conn->socket, recv_set)) poll_recv_request(conn);
977 break;
978
979 case SEND_HEADER_AND_REPLY:
980 if (FD_ISSET(conn->socket, send_set)) poll_send_header_and_reply(conn);
981 break;
982
983 case SEND_HEADER:
984 if (FD_ISSET(conn->socket, send_set)) poll_send_header(conn);
985 break;
986
987 case SEND_REPLY:
988 if (FD_ISSET(conn->socket, send_set)) poll_send_reply(conn);
989 break;
990
991 default: errx(1, "invalid state");
992 }
993 }
994
995 /* vim:set ts=4 sw=4 et tw=78: */