From: reyk Date: Wed, 16 Jul 2014 10:25:28 +0000 (+0000) Subject: Implement file descriptor accounting. The concept was taken from X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=12312c27a52c7492151ec1a7c8da06ced905d71d;p=openbsd Implement file descriptor accounting. The concept was taken from 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@ --- diff --git a/usr.sbin/httpd/httpd.h b/usr.sbin/httpd/httpd.h index 3834e800c44..0716e04468e 100644 --- a/usr.sbin/httpd/httpd.h +++ b/usr.sbin/httpd/httpd.h @@ -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 @@ -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); diff --git a/usr.sbin/httpd/server.c b/usr.sbin/httpd/server.c index b72d55621a5..10f26e3f2df 100644 --- a/usr.sbin/httpd/server.c +++ b/usr.sbin/httpd/server.c @@ -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 @@ -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); diff --git a/usr.sbin/httpd/server_file.c b/usr.sbin/httpd/server_file.c index 1942be13beb..2b511158cfa 100644 --- a/usr.sbin/httpd/server_file.c +++ b/usr.sbin/httpd/server_file.c @@ -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 @@ -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; +} diff --git a/usr.sbin/httpd/server_http.c b/usr.sbin/httpd/server_http.c index 42b2a2fb4ab..8bb8daefddc 100644 --- a/usr.sbin/httpd/server_http.c +++ b/usr.sbin/httpd/server_http.c @@ -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 @@ -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);