about summary refs log tree commit homepage
path: root/http.c
diff options
context:
space:
mode:
Diffstat (limited to 'http.c')
-rw-r--r--http.c142
1 files changed, 104 insertions, 38 deletions
diff --git a/http.c b/http.c
index 1485eb2..a4c2cb8 100644
--- a/http.c
+++ b/http.c
@@ -3,6 +3,7 @@
  * License: GPLv3 or later (see COPYING for details)
  */
 #include "cmogstored.h"
+#include "trace.h"
 #include "http.h"
 
 /*
@@ -33,15 +34,15 @@ static void
 http_defer_rbuf(struct mog_http *http, struct mog_rbuf *rbuf, size_t buf_len)
 {
         struct mog_rbuf *old = http->rbuf;
-        size_t defer_bytes = buf_len - http->offset;
-        char *src = rbuf->rptr + http->offset;
+        size_t defer_bytes = buf_len - http->_p.buf_off;
+        char *src = rbuf->rptr + http->_p.buf_off;
 
-        if (http->skip_rbuf_defer) {
-                http->skip_rbuf_defer = 0;
+        if (http->_p.skip_rbuf_defer) {
+                http->_p.skip_rbuf_defer = 0;
                 return;
         }
 
-        assert(http->offset >= 0 && "http->offset negative");
+        assert(http->_p.buf_off >= 0 && "http->_p.buf_off negative");
         assert(defer_bytes <= MOG_RBUF_MAX_SIZE && "defer bytes overflow");
 
         if (defer_bytes == 0) {
@@ -55,19 +56,21 @@ http_defer_rbuf(struct mog_http *http, struct mog_rbuf *rbuf, size_t buf_len)
                 memcpy(http->rbuf->rptr, src, defer_bytes);
                 http->rbuf->rsize = defer_bytes;
         }
-        http->offset = 0;
+        http->_p.buf_off = 0;
 }
 
 static void
-http_process_client(struct mog_http *http, char *buf, size_t buf_len)
+http_process_client(struct mog_fd *mfd, char *buf, size_t buf_len)
 {
-        switch (http->http_method) {
+        struct mog_http *http = &mfd->as.http;
+
+        switch (http->_p.http_method) {
         case MOG_HTTP_METHOD_NONE: assert(0 && "BUG: unset HTTP method");
-        case MOG_HTTP_METHOD_GET: mog_http_get_open(http, buf); break;
-        case MOG_HTTP_METHOD_HEAD: mog_http_get_open(http, buf); break;
-        case MOG_HTTP_METHOD_DELETE: mog_http_delete(http, buf); break;
-        case MOG_HTTP_METHOD_MKCOL: mog_http_mkcol(http, buf); break;
-        case MOG_HTTP_METHOD_PUT: mog_http_put(http, buf, buf_len); break;
+        case MOG_HTTP_METHOD_GET: mog_http_get_open(mfd, buf); break;
+        case MOG_HTTP_METHOD_HEAD: mog_http_get_open(mfd, buf); break;
+        case MOG_HTTP_METHOD_DELETE: mog_http_delete(mfd, buf); break;
+        case MOG_HTTP_METHOD_MKCOL: mog_http_mkcol(mfd, buf); break;
+        case MOG_HTTP_METHOD_PUT: mog_http_put(mfd, buf, buf_len); break;
         }
 }
 
@@ -87,10 +90,36 @@ MOG_NOINLINE static void http_close(struct mog_fd *mfd)
          * their connection to save ourselves bandwidth/cycles
          */
         tcp_push(mfd, false);
+        mog_packaddr_free(&http->mpa);
 
         mog_fd_put(mfd);
 }
 
+void mog_http_unlink_ftmp(struct mog_http *http)
+{
+        struct mog_file *file = &http->forward->as.file;
+
+        if (!file->tmppath)
+                return;
+
+        if (mog_unlink(http->svc, file->tmppath) != 0)
+                syslog(LOG_ERR, "Failed to unlink %s (in %s): %m",
+                       file->tmppath, http->svc->docroot);
+}
+
+/* called if epoll/kevent is out-of-space */
+void mog_http_drop(struct mog_fd *mfd)
+{
+        struct mog_http *http = &mfd->as.http;
+
+        assert(http->forward != MOG_IOSTAT);
+        if (http->forward) {
+                mog_http_unlink_ftmp(http);
+                mog_file_close(http->forward);
+        }
+        http_close(mfd);
+}
+
 /* returns true if we can continue queue step, false if not */
 static enum mog_next http_wbuf_in_progress(struct mog_http *http)
 {
@@ -99,10 +128,10 @@ static enum mog_next http_wbuf_in_progress(struct mog_http *http)
         case MOG_WRSTATE_ERR:
                 return MOG_NEXT_CLOSE;
         case MOG_WRSTATE_DONE:
-                if (!http->persistent) return MOG_NEXT_CLOSE;
+                if (!http->_p.persistent) return MOG_NEXT_CLOSE;
                 if (http->forward == NULL)
                         mog_http_reset(http);
-                assert(http->offset == 0 && "bad offset");
+                assert(http->_p.buf_off == 0 && "bad offset");
                 return MOG_NEXT_ACTIVE;
         case MOG_WRSTATE_BUSY:
                 /* unlikely, we never put anything big in wbuf */
@@ -114,7 +143,7 @@ static enum mog_next http_wbuf_in_progress(struct mog_http *http)
 
 static enum mog_next http_forward_in_progress(struct mog_fd *mfd)
 {
-        enum mog_http_method method = mfd->as.http.http_method;
+        enum mog_http_method method = mfd->as.http._p.http_method;
 
         if (method == MOG_HTTP_METHOD_GET)
                 return mog_http_get_in_progress(mfd);
@@ -130,7 +159,7 @@ static enum mog_next http_queue_step(struct mog_fd *mfd)
         struct mog_rbuf *rbuf;
         char *buf;
         ssize_t r;
-        off_t off;
+        uint32_t off;
         size_t buf_len = 0;
         enum mog_parser_state state;
 
@@ -142,20 +171,23 @@ static enum mog_next http_queue_step(struct mog_fd *mfd)
         /* we may have pipelined data in http->rbuf */
         rbuf = http->rbuf ? http->rbuf : mog_rbuf_get(MOG_RBUF_BASE_SIZE);
         buf = rbuf->rptr;
-        off = http->offset;
-        assert(off >= 0 && "offset is negative");
+        off = http->_p.buf_off;
         assert(off < rbuf->rcapa && "offset is too big");
         if (http->rbuf) {
                 /* request got pipelined, resuming now */
                 buf_len = http->rbuf->rsize;
-                assert(http->offset <= buf_len && "bad offset from pipelining");
+                assert(http->_p.buf_off <= buf_len
+                        && "bad offset from pipelining");
                 assert(buf_len <= http->rbuf->rcapa && "bad rsize stashed");
-                if (http->offset < buf_len)
+                if (http->_p.buf_off < buf_len)
                         goto parse;
         }
 reread:
         r = read(mfd->fd, buf + off, rbuf->rcapa - off);
         if (r > 0) {
+                if (off == 0)
+                        TRACE(CMOGSTORED_HTTP_REQ_BEGIN(false));
+
                 buf_len = r + off;
 parse:
                 state = mog_http_parse(http, buf, buf_len);
@@ -166,17 +198,17 @@ parse:
                 case MOG_PARSER_CONTINUE:
                         assert(http->wbuf == NULL &&
                                "tried to write (and failed) with partial req");
-                        if (http->offset >= rbuf->rcapa) {
+                        if (http->_p.buf_off >= rbuf->rcapa) {
                                 rbuf->rsize = buf_len;
                                 http->rbuf = rbuf = mog_rbuf_grow(rbuf);
                                 if (!rbuf)
                                         goto err400;
                                 buf = rbuf->rptr;
                         }
-                        off = http->offset;
+                        off = http->_p.buf_off;
                         goto reread;
                 case MOG_PARSER_DONE:
-                        http_process_client(http, buf, buf_len);
+                        http_process_client(mfd, buf, buf_len);
                         if (http->wbuf == MOG_WR_ERROR)
                                 return MOG_NEXT_CLOSE;
                         if (http->wbuf) {
@@ -185,15 +217,20 @@ parse:
                         } else if (http->forward) {
                                 http_defer_rbuf(http, rbuf, buf_len);
                                 return http_forward_in_progress(mfd);
-                        } else if (!http->persistent) {
+                        } else if (!http->_p.persistent) {
                                 return MOG_NEXT_CLOSE;
                         } else {
+                                /* pipelined request */
+                                if (buf_len)
+                                        TRACE(CMOGSTORED_HTTP_REQ_BEGIN(true));
+
                                 http_defer_rbuf(http, rbuf, buf_len);
                                 mog_http_reset(http);
                         }
                         return MOG_NEXT_ACTIVE;
                 }
         } else if (r == 0) { /* client shut down */
+                TRACE(CMOGSTORED_HTTP_RDCLOSE(buf_len));
                 return MOG_NEXT_CLOSE;
         } else {
                 switch (errno) {
@@ -207,8 +244,11 @@ parse:
                 case EINTR: goto reread;
                 case ECONNRESET:
                 case ENOTCONN:
+                        /* these errors are too common to log, normally */
+                        TRACE(CMOGSTORED_HTTP_RDERR(buf_len, errno));
                         return MOG_NEXT_CLOSE;
                 default:
+                        TRACE(CMOGSTORED_HTTP_RDERR(buf_len, errno));
                         syslog(LOG_NOTICE, "http client died: %m");
                         return MOG_NEXT_CLOSE;
                 }
@@ -262,24 +302,49 @@ void mog_http_quit_step(struct mog_fd *mfd)
         }
 }
 
-/* called immediately after accept(), this initializes the mfd (once) */
-void mog_http_post_accept(int fd, struct mog_svc *svc)
+/* stringify the address for tracers */
+static MOG_NOINLINE void
+trace_http_accepted(struct mog_fd *mfd)
+{
+#ifdef HAVE_SYSTEMTAP
+        struct mog_packaddr *mpa = &mfd->as.http.mpa;
+        struct mog_ni ni;
+
+        mog_nameinfo(mpa, &ni);
+        TRACE(CMOGSTORED_HTTP_ACCEPTED(mfd->fd, ni.ni_host, ni.ni_serv));
+#endif /* !HAVE_SYSTEMTAP */
+}
+
+static void http_post_accept_common(struct mog_fd *mfd, struct mog_svc *svc,
+                        union mog_sockaddr *msa, socklen_t salen)
 {
-        struct mog_fd *mfd = mog_fd_init(fd, MOG_FD_TYPE_HTTP);
         struct mog_http *http = &mfd->as.http;
 
         mog_http_init(http, svc);
+        mog_packaddr_init(&http->mpa, msa, salen);
+
+        if (TRACE_ENABLED(CMOGSTORED_HTTP_ACCEPTED))
+                trace_http_accepted(mfd);
+
         mog_idleq_add(svc->queue, mfd, MOG_QEV_RD);
 }
 
 /* called immediately after accept(), this initializes the mfd (once) */
-void mog_httpget_post_accept(int fd, struct mog_svc *svc)
+void mog_http_post_accept(int fd, struct mog_svc *svc,
+                        union mog_sockaddr *msa, socklen_t salen)
+{
+        struct mog_fd *mfd = mog_fd_init(fd, MOG_FD_TYPE_HTTP);
+
+        http_post_accept_common(mfd, svc, msa, salen);
+}
+
+/* called immediately after accept(), this initializes the mfd (once) */
+void mog_httpget_post_accept(int fd, struct mog_svc *svc,
+                        union mog_sockaddr *msa, socklen_t salen)
 {
         struct mog_fd *mfd = mog_fd_init(fd, MOG_FD_TYPE_HTTPGET);
-        struct mog_http *http = &mfd->as.http;
 
-        mog_http_init(http, svc);
-        mog_idleq_add(svc->queue, mfd, MOG_QEV_RD);
+        http_post_accept_common(mfd, svc, msa, salen);
 }
 
 /*
@@ -288,15 +353,16 @@ void mog_httpget_post_accept(int fd, struct mog_svc *svc)
  */
 char *mog_http_path(struct mog_http *http, char *buf)
 {
-        char *path = buf + http->path_tip;
-        size_t len = http->path_end - http->path_tip;
+        char *path = buf + http->_p.path_tip;
+        size_t len = http->_p.path_end - http->_p.path_tip;
 
-        assert(http->path_end > http->path_tip && "bad HTTP path from parser");
+        assert(http->_p.path_end > http->_p.path_tip
+                && "bad HTTP path from parser");
 
         if (! mog_valid_path(path, len))
                 return NULL;
 
-        if (http->http_method == MOG_HTTP_METHOD_PUT) {
+        if (http->_p.http_method == MOG_HTTP_METHOD_PUT) {
                 if (!mog_valid_put_path(path, len)) {
                         errno = EINVAL;
                         return NULL;
@@ -330,12 +396,12 @@ mog_http_resp0(
         dst = CPY("\r\nDate: ");
         now = mog_now();
         dst = mempcpy(dst, now->httpdate, sizeof(now->httpdate)-1);
-        if (alive && http->persistent) {
+        if (alive && http->_p.persistent) {
                 dst = CPY("\r\nContent-Length: 0"
                         "\r\nContent-Type: text/plain"
                         "\r\nConnection: keep-alive\r\n\r\n");
         } else {
-                http->persistent = 0;
+                http->_p.persistent = 0;
                 dst = CPY("\r\nContent-Length: 0"
                         "\r\nContent-Type: text/plain"
                         "\r\nConnection: close\r\n\r\n");