about summary refs log tree commit homepage
path: root/http_get.c
diff options
context:
space:
mode:
Diffstat (limited to 'http_get.c')
-rw-r--r--http_get.c93
1 files changed, 85 insertions, 8 deletions
diff --git a/http_get.c b/http_get.c
index 154d8c5..1af4fd2 100644
--- a/http_get.c
+++ b/http_get.c
@@ -47,6 +47,16 @@ static ssize_t linux_sendfile(int sockfd, int filefd, off_t *off, size_t count)
 
 #define ERR416 "416 Requested Range Not Satisfiable"
 
+static void
+http_hdr_prepare(char **buf, char **modified, size_t *len, time_t *mtime)
+{
+        /* single buffer so we can use MSG_MORE */
+        *buf = mog_fsbuf_get(len);
+        *modified = *buf + *len / 2;
+        assert((*len / 2) > MOG_HTTPDATE_CAPA && "fsbuf too small");
+        mog_http_date(*modified, MOG_HTTPDATE_CAPA, mtime);
+}
+
 /*
  * TODO: refactor this
  *
@@ -58,18 +68,13 @@ static ssize_t linux_sendfile(int sockfd, int filefd, off_t *off, size_t count)
 static off_t http_get_resp_hdr(struct mog_fd *mfd, struct stat *sb)
 {
         struct mog_http *http = &mfd->as.http;
-        char *modified;
-        char *buf;
+        char *buf, *modified;
         size_t len;
         struct mog_now *now = mog_now();
         long long count;
         int rc;
 
-        /* single buffer so we can use MSG_MORE */
-        buf = mog_fsbuf_get(&len);
-        modified = buf + len / 2;
-        assert((len / 2) > MOG_HTTPDATE_CAPA && "fsbuf too small");
-        mog_http_date(modified, MOG_HTTPDATE_CAPA, &sb->st_mtime);
+        http_hdr_prepare(&buf, &modified, &len, &sb->st_mtime);
 
         /* validate ranges */
         if (http->_p.has_range) {
@@ -194,14 +199,86 @@ bad_range:
         return (off_t)count;
 }
 
+static void emit_dev_usage(struct mog_fd *mfd)
+{
+        struct mog_http *http = &mfd->as.http;
+        struct mog_dev *dev = mog_dev_for(http->svc, http->_p.mog_devid, false);
+        void *ok = NULL;
+
+        if (dev) {
+                char *buf, *modified;
+                size_t len, ilen;
+                struct mog_now *now;
+                int rc;
+                bool retried = false;
+                struct iovec iov;
+
+retry:
+                now = mog_now();
+                CHECK(int, 0, pthread_mutex_lock(&dev->usage_lock));
+                ok = dev->usage_txt;
+                if (!ok) {
+                        if (retried)
+                                goto out_unlock;
+                        retried = true;
+                        CHECK(int, 0, pthread_mutex_unlock(&dev->usage_lock));
+                        mog_dev_usage_update(dev, http->svc);
+                        goto retry;
+                }
+
+                http_hdr_prepare(&buf, &modified, &len,
+                                 &dev->usage_mtime);
+                ilen = len;
+                rc = snprintf(buf, len,
+                        "HTTP/1.1 200 OK\r\n"
+                        "Date: %s\r\n"
+                        "Last-Modified: %s\r\n"
+                        "Content-Length: %u\r\n"
+                        "Content-Type: text/plain\r\n"
+                        "Accept-Ranges: bytes\r\n"
+                        "Connection: %s\r\n"
+                        "\r\n",
+                        now->httpdate,
+                        modified,
+                        dev->usage_len,
+                        http->_p.persistent ? "keep-alive" : "close");
+
+                ok = NULL;
+                if (rc > 0) {
+                        len -= rc;
+                        if (http->_p.http_method == MOG_HTTP_METHOD_HEAD) {
+                                ok = iov.iov_base = buf;
+                                iov.iov_len = rc;
+                        } else if (len >= dev->usage_len && len < ilen) {
+                                memcpy(buf + rc, dev->usage_txt,
+                                        dev->usage_len);
+                                ok = iov.iov_base = buf;
+                                iov.iov_len = rc + dev->usage_len;
+                        }
+                }
+out_unlock:
+                CHECK(int, 0, pthread_mutex_unlock(&dev->usage_lock));
+                if (ok)
+                        http->wbuf = mog_trywritev(mfd->fd, &iov, 1);
+        }
+        if (!dev || !ok)
+                mog_http_resp(mfd, "404 Not Found", true);
+}
+
 void mog_http_get_open(struct mog_fd *mfd, char *buf)
 {
         struct mog_http *http = &mfd->as.http;
         struct stat sb;
         struct mog_file *file = NULL;
-        char *path = mog_http_path(http, buf);
+        char *path;
         off_t len;
 
+        if (http->_p.usage_txt) {
+                emit_dev_usage(mfd);
+                return;
+        }
+
+        path = mog_http_path(http, buf);
         if (!path) goto forbidden; /* path traversal attack */
         assert(http->forward == NULL && "already have http->forward");
         assert(path[0] == '/' && "bad path");