Implement file descriptor accounting. The concept was taken from
authorreyk <reyk@openbsd.org>
Wed, 16 Jul 2014 10:25:28 +0000 (10:25 +0000)
committerreyk <reyk@openbsd.org>
Wed, 16 Jul 2014 10:25:28 +0000 (10:25 +0000)
relayd but had to be adjusted for httpd.  It now handles single-pass
HTTP connections, persistent connections with multiple requests, and
body-less HEAD requests.  With input from benno@

usr.sbin/httpd/httpd.h
usr.sbin/httpd/server.c
usr.sbin/httpd/server_file.c
usr.sbin/httpd/server_http.c

index 3834e80..0716e04 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: httpd.h,v 1.3 2014/07/14 00:19:48 reyk Exp $  */
+/*     $OpenBSD: httpd.h,v 1.4 2014/07/16 10:25:28 reyk Exp $  */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -265,10 +265,11 @@ struct client {
        struct bufferevent      *clt_file;
 
        off_t                    clt_toread;
+       size_t                   clt_headerlen;
        int                      clt_persist;
        int                      clt_line;
-       size_t                   clt_headerlen;
        int                      clt_done;
+       int                      clt_inflight;
 
        struct evbuffer         *clt_log;
        struct timeval           clt_timeout;
@@ -377,6 +378,7 @@ int  server_bufferevent_write_chunk(struct client *,
            struct evbuffer *, size_t);
 int     server_bufferevent_add(struct event *, int);
 int     server_bufferevent_write(struct client *, void *, size_t);
+void    server_inflight_dec(struct client *, const char *);
 
 SPLAY_PROTOTYPE(client_tree, client, clt_nodes, server_client_cmp);
 
index b72d556..10f26e3 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server.c,v 1.5 2014/07/14 00:19:48 reyk Exp $ */
+/*     $OpenBSD: server.c,v 1.6 2014/07/16 10:25:28 reyk Exp $ */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -389,7 +389,6 @@ void
 server_error(struct bufferevent *bev, short error, void *arg)
 {
        struct client           *clt = arg;
-       struct evbuffer         *dst;
 
        if (error & EVBUFFER_TIMEOUT) {
                server_close(clt, "buffer event timeout");
@@ -403,20 +402,7 @@ server_error(struct bufferevent *bev, short error, void *arg)
                bufferevent_disable(bev, EV_READ|EV_WRITE);
 
                clt->clt_done = 1;
-
-               if (bev != clt->clt_bev) {
-                       dst = EVBUFFER_OUTPUT(clt->clt_bev);
-                       if (EVBUFFER_LENGTH(dst))
-                               return;
-               } else
-                       return;
-
-               if (clt->clt_persist) {
-                       server_reset_http(clt);
-                       bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
-                       return;
-               } else
-                       server_close(clt, "done");
+               server_close(clt, "done");
                return;
        }
        server_close(clt, "buffer event error");
@@ -468,6 +454,7 @@ server_accept(int fd, short event, void *arg)
        clt->clt_id = ++server_cltid;
        clt->clt_serverid = srv->srv_conf.id;
        clt->clt_pid = getpid();
+       clt->clt_inflight = 1;
        switch (ss.ss_family) {
        case AF_INET:
                clt->clt_port = ((struct sockaddr_in *)&ss)->sin_port;
@@ -513,12 +500,26 @@ server_accept(int fd, short event, void *arg)
                 * the client struct was not completly set up, but still
                 * counted as an inflight client. account for this.
                 */
-               server_inflight--;
-               log_debug("%s: inflight decremented, now %d",
-                   __func__, server_inflight);
+               server_inflight_dec(clt, __func__);
        }
 }
 
+void
+server_inflight_dec(struct client *clt, const char *why)
+{
+       if (clt != NULL) {
+               /* the flight already left inflight mode. */
+               if (clt->clt_inflight == 0)
+                       return;
+               clt->clt_inflight = 0;
+       }
+
+       /* the file was never opened, thus this was an inflight client. */
+       server_inflight--;
+       log_debug("%s: inflight decremented, now %d, %s",
+           __func__, server_inflight, why);
+}
+
 void
 server_close(struct client *clt, const char *msg)
 {
@@ -559,19 +560,10 @@ server_close(struct client *clt, const char *msg)
                bufferevent_free(clt->clt_file);
        if (clt->clt_fd != -1)
                close(clt->clt_fd);
-
-       if (clt->clt_s != -1) {
+       if (clt->clt_s != -1)
                close(clt->clt_s);
-               if (/* XXX */ -1) {
-                       /*
-                        * the output was never connected,
-                        * thus this was an inflight client.
-                        */
-                       server_inflight--;
-                       log_debug("%s: clients inflight decremented, now %d",
-                           __func__, server_inflight);
-               }
-       }
+
+       server_inflight_dec(clt, __func__);
 
        if (clt->clt_log != NULL)
                evbuffer_free(clt->clt_log);
index 1942be1..2b51115 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server_file.c,v 1.5 2014/07/15 09:51:06 reyk Exp $    */
+/*     $OpenBSD: server_file.c,v 1.6 2014/07/16 10:25:28 reyk Exp $    */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -46,6 +46,8 @@
 #include "httpd.h"
 #include "http.h"
 
+void    server_file_error(struct bufferevent *, short, void *);
+
 int
 server_file(struct httpd *env, struct client *clt)
 {
@@ -88,6 +90,9 @@ server_file(struct httpd *env, struct client *clt)
        if ((fd = open(path, O_RDONLY)) == -1 || fstat(fd, &st) == -1)
                goto fail;
 
+       /* File descriptor is opened, decrement inflight counter */
+       server_inflight_dec(clt, __func__);
+
        media = media_find(env->sc_mediatypes, path);
        ret = server_response_http(clt, 200, media, st.st_size);
        switch (ret) {
@@ -104,8 +109,9 @@ server_file(struct httpd *env, struct client *clt)
        clt->clt_fd = fd;
        if (clt->clt_file != NULL)
                bufferevent_free(clt->clt_file);
+
        clt->clt_file = bufferevent_new(clt->clt_fd, server_read,
-           server_write, server_error, clt);
+           server_write, server_file_error, clt);
        if (clt->clt_file == NULL) {
                errstr = "failed to allocate file buffer event";
                goto fail;
@@ -114,6 +120,7 @@ server_file(struct httpd *env, struct client *clt)
        bufferevent_settimeout(clt->clt_file,
            srv->srv_conf.timeout.tv_sec, srv->srv_conf.timeout.tv_sec);
        bufferevent_enable(clt->clt_file, EV_READ);
+       bufferevent_disable(clt->clt_bev, EV_READ);
 
        return (0);
  fail:
@@ -122,3 +129,46 @@ server_file(struct httpd *env, struct client *clt)
        server_abort_http(clt, 500, errstr);
        return (-1);
 }
+
+void
+server_file_error(struct bufferevent *bev, short error, void *arg)
+{
+       struct client           *clt = arg;
+       struct evbuffer         *dst;
+
+       if (error & EVBUFFER_TIMEOUT) {
+               server_close(clt, "buffer event timeout");
+               return;
+       }
+       if (error & (EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF)) {
+               bufferevent_disable(bev, EV_READ);
+
+               clt->clt_done = 1;
+
+               dst = EVBUFFER_OUTPUT(clt->clt_bev);
+               if (EVBUFFER_LENGTH(dst)) {
+                       /* Finish writing all data first */
+                       bufferevent_enable(clt->clt_bev, EV_WRITE);
+                       return;
+               }
+
+               if (clt->clt_persist) {
+                       /* Close input file and wait for next HTTP request */
+                       if (clt->clt_fd != -1)
+                               close(clt->clt_fd);
+                       clt->clt_fd = -1;
+                       clt->clt_toread = TOREAD_HTTP_HEADER;
+                       server_reset_http(clt);
+                       bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
+                       return;
+               }
+               server_close(clt, "done");
+               return;
+       }
+       if (error & EVBUFFER_ERROR && errno == EFBIG) {
+               bufferevent_enable(bev, EV_READ);
+               return;
+       }
+       server_close(clt, "buffer event error");
+       return;
+}
index 42b2a2f..8bb8dae 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server_http.c,v 1.7 2014/07/14 09:03:08 reyk Exp $    */
+/*     $OpenBSD: server_http.c,v 1.8 2014/07/16 10:25:28 reyk Exp $    */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -319,8 +319,8 @@ server_read_http(struct bufferevent *bev, void *arg)
 
  done:
                if (clt->clt_toread <= 0) {
-                       if (server_response(env, clt) == -1)
-                               return;
+                       server_response(env, clt);
+                       return;
                }
        }
        if (clt->clt_done) {
@@ -511,7 +511,6 @@ server_reset_http(struct client *clt)
        clt->clt_headerlen = 0;
        clt->clt_line = 0;
        clt->clt_done = 0;
-       clt->clt_toread = TOREAD_HTTP_HEADER;
        clt->clt_bev->readcb = server_read_http;
 }
 
@@ -644,11 +643,9 @@ server_response(struct httpd *httpd, struct client *clt)
        if ((ret = server_file(httpd, clt)) == -1)
                return (-1);
 
-       /* XXX */
-       if (!(desc->http_method == HTTP_METHOD_HEAD && clt->clt_persist == 0))
-               server_reset_http(clt);
+       server_reset_http(clt);
 
-       return (ret);
+       return (0);
  fail:
        server_abort_http(clt, 400, "bad request");
        return (-1);