diff options
Diffstat (limited to 'http.c')
-rw-r--r-- | http.c | 142 |
1 files changed, 104 insertions, 38 deletions
@@ -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"); |