diff options
Diffstat (limited to 'http_get.c')
-rw-r--r-- | http_get.c | 93 |
1 files changed, 85 insertions, 8 deletions
@@ -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"); |