about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2013-03-19 09:33:47 +0000
committerEric Wong <normalperson@yhbt.net>2013-03-19 09:48:15 +0000
commit9427f2989eae96106090d77ddff1656f8510957d (patch)
tree0f9ca86f9db1a8bb67c0e41f04d87f1e1f3121a4
parentcce7f3c33207c534f9e5a6c0cb389a97df21235b (diff)
downloadcmogstored-9427f2989eae96106090d77ddff1656f8510957d.tar.gz
This will allow easy use of memset to reset attributes in
between requests without clobbering more important data.
-rw-r--r--chunk_parser.rl48
-rw-r--r--cmogstored.h36
-rw-r--r--http.c46
-rw-r--r--http_common.rl32
-rw-r--r--http_get.c50
-rw-r--r--http_parser.rl76
-rw-r--r--http_put.c126
-rw-r--r--test/chunk-parser-1.c58
-rw-r--r--test/http-parser-1.c112
9 files changed, 295 insertions, 289 deletions
diff --git a/chunk_parser.rl b/chunk_parser.rl
index 9234f71..926db26 100644
--- a/chunk_parser.rl
+++ b/chunk_parser.rl
@@ -26,69 +26,69 @@ static inline off_t hexchar2off(int xdigit)
                 off_t buf_remain;
                 size_t wr_len;
 
-                if (http->content_len == 0) { /* final chunk */
-                        http->chunk_state = MOG_CHUNK_STATE_TRAILER;
+                if (http->_p.content_len == 0) { /* final chunk */
+                        http->_p.chunk_state = MOG_CHUNK_STATE_TRAILER;
                         fhold;
 
                         /* XXX this feels wrong ... */
                         if (fpc >= buf) {
                                 assert(fc == '\n' && "bad chunk end");
-                                http->line_end = to_u16(fpc - buf);
+                                http->_p.line_end = to_u16(fpc - buf);
                         }
                         fgoto more_trailers;
                 }
 
-                assert(http->content_len > 0 && "impossible content_len");
+                assert(http->_p.content_len > 0 && "impossible content_len");
 
                 buf_remain = len - (fpc - buf);
                 if (buf_remain == 0)
                         fbreak;
 
                 assert(buf_remain > 0 && "impossible buf_remain");
-                wr_len = MIN((size_t)http->content_len, (size_t)buf_remain);
+                wr_len = MIN((size_t)http->_p.content_len, (size_t)buf_remain);
                 assert(wr_len != 0 && "invalid wr_len");
                 if (! mog_http_write_full(http->forward, fpc, wr_len))
                         fbreak;
 
-                http->content_len -= wr_len;
+                http->_p.content_len -= wr_len;
                 p += wr_len - 1;
                 assert(p < pe && "buffer overrun");
 
-                if (http->content_len > 0) {
+                if (http->_p.content_len > 0) {
                         really_done = 1;
                         /* let caller handle reading the rest of the body */
                         fbreak;
                 }
 
                 /* next chunk header */
-                http->chunk_state = MOG_CHUNK_STATE_SIZE;
+                http->_p.chunk_state = MOG_CHUNK_STATE_SIZE;
                 if (wr_len == buf_remain) {
-                        if (http->content_len == 0)
+                        if (http->_p.content_len == 0)
                                 fgoto main;
                         really_done = 1;
                         fbreak;
                 }
 
                 /* more chunks in this buffer */
-                assert(http->content_len == 0 &&
+                assert(http->_p.content_len == 0 &&
                        "bad content_len at chunk end");
 
                 fgoto main;
         };
         chunk = "\r\n"? # account for trailing CRLF in previous chunk
                 (xdigit+) $ {
-                        off_t prev = http->content_len;
+                        off_t prev = http->_p.content_len;
 
-                        http->content_len *= 16;
-                        http->content_len += hexchar2off(fc);
-                        if (http->content_len < prev) {
+                        http->_p.content_len *= 16;
+                        http->_p.content_len += hexchar2off(fc);
+                        if (http->_p.content_len < prev) {
                                 errno = ERANGE;
-                                http->content_len = -1;
+                                http->_p.content_len = -1;
                                 fbreak;
                         }
                 }
                 (any -- [\r\n])*
-                '\r' '\n' @ { http->chunk_state = MOG_CHUNK_STATE_DATA; }
+                '\r' '\n' @ { http->_p.chunk_state = MOG_CHUNK_STATE_DATA; }
                 chunk_data;
         main := chunk+;
 }%%
@@ -100,12 +100,12 @@ void mog_chunk_init(struct mog_http *http)
         int cs;
 
         %% write init;
-        assert(http->chunked && "not chunked");
+        assert(http->_p.chunked && "not chunked");
         http->cs = cs;
-        http->line_end = 0;
-        http->content_len = 0;
-        http->offset = 0;
-        http->chunk_state = MOG_CHUNK_STATE_SIZE;
+        http->_p.line_end = 0;
+        http->_p.content_len = 0;
+        http->_p.offset = 0;
+        http->_p.chunk_state = MOG_CHUNK_STATE_SIZE;
 }
 
 enum mog_parser_state
@@ -114,7 +114,7 @@ mog_chunk_parse(struct mog_http *http, char *buf, size_t len)
         char *p, *pe, *eof = NULL;
         int cs = http->cs;
         int really_done = 0;
-        size_t off = http->offset;
+        size_t off = http->_p.offset;
 
         assert(http->wbuf == NULL && "unwritten data in buffer");
         assert(off <= len && "http offset past end of buffer");
@@ -132,13 +132,13 @@ mog_chunk_parse(struct mog_http *http, char *buf, size_t len)
                 cs = chunk_parser_first_final;
 
         http->cs = cs;
-        http->offset = p - buf;
+        http->_p.offset = p - buf;
 
         if (cs == chunk_parser_error || errno)
                 return MOG_PARSER_ERROR;
 
         assert(p <= pe && "buffer overflow after chunk parse");
-        assert(http->offset <= len && "offset longer than len");
+        assert(http->_p.offset <= len && "offset longer than len");
 
         if (http->cs == chunk_parser_first_final) return MOG_PARSER_DONE;
         return MOG_PARSER_CONTINUE;
diff --git a/cmogstored.h b/cmogstored.h
index e72c071..1681e1e 100644
--- a/cmogstored.h
+++ b/cmogstored.h
@@ -169,24 +169,26 @@ enum mog_chunk_state {
 
 struct mog_http {
         int cs;
-        enum mog_http_method http_method:8;
-        unsigned persistent:1;
-        unsigned chunked:1;
-        unsigned has_trailer_md5:1;
-        unsigned has_expect_md5:1;
-        unsigned has_content_range:1; /* for PUT */
-        unsigned has_range:1;         /* for GET */
-        unsigned skip_rbuf_defer:1;
-        enum mog_chunk_state chunk_state:2;
-        uint8_t path_tip;
-        uint8_t path_end;
-        uint16_t line_end;
-        uint16_t tmp_tip;
+        struct {
+                enum mog_http_method http_method:8;
+                unsigned persistent:1;
+                unsigned chunked:1;
+                unsigned has_trailer_md5:1;
+                unsigned has_expect_md5:1;
+                unsigned has_content_range:1; /* for PUT */
+                unsigned has_range:1;         /* for GET */
+                unsigned skip_rbuf_defer:1;
+                enum mog_chunk_state chunk_state:2;
+                uint8_t path_tip;
+                uint8_t path_end;
+                uint16_t line_end;
+                uint16_t tmp_tip;
+                size_t offset;
+                off_t range_beg;
+                off_t range_end;
+                off_t content_len;
+        } _p;
         struct mog_fd *forward;
-        size_t offset;
-        off_t range_beg;
-        off_t range_end;
-        off_t content_len;
         struct mog_rbuf *rbuf;
         struct mog_wbuf *wbuf; /* uncommonly needed */
         struct mog_svc *svc;
diff --git a/http.c b/http.c
index 1485eb2..4519498 100644
--- a/http.c
+++ b/http.c
@@ -33,15 +33,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.offset;
+        char *src = rbuf->rptr + http->_p.offset;
 
-        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.offset >= 0 && "http->_p.offset negative");
         assert(defer_bytes <= MOG_RBUF_MAX_SIZE && "defer bytes overflow");
 
         if (defer_bytes == 0) {
@@ -55,13 +55,13 @@ 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.offset = 0;
 }
 
 static void
 http_process_client(struct mog_http *http, char *buf, size_t buf_len)
 {
-        switch (http->http_method) {
+        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;
@@ -99,10 +99,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.offset == 0 && "bad offset");
                 return MOG_NEXT_ACTIVE;
         case MOG_WRSTATE_BUSY:
                 /* unlikely, we never put anything big in wbuf */
@@ -114,7 +114,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);
@@ -142,15 +142,16 @@ 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;
+        off = http->_p.offset;
         assert(off >= 0 && "offset is negative");
         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.offset <= buf_len
+                        && "bad offset from pipelining");
                 assert(buf_len <= http->rbuf->rcapa && "bad rsize stashed");
-                if (http->offset < buf_len)
+                if (http->_p.offset < buf_len)
                         goto parse;
         }
 reread:
@@ -166,14 +167,14 @@ 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.offset >= 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.offset;
                         goto reread;
                 case MOG_PARSER_DONE:
                         http_process_client(http, buf, buf_len);
@@ -185,7 +186,7 @@ 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 {
                                 http_defer_rbuf(http, rbuf, buf_len);
@@ -288,15 +289,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 +332,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");
diff --git a/http_common.rl b/http_common.rl
index 8df5fed..de54bc1 100644
--- a/http_common.rl
+++ b/http_common.rl
@@ -6,18 +6,20 @@
         machine http_common;
 
         LWS = (' ' | '\t');
-        LF = '\n' > { http->line_end = to_u16(fpc - buf); };
+        LF = '\n' > { http->_p.line_end = to_u16(fpc - buf); };
         eor = LWS*'\r'LF;
         CTL = (cntrl | 127);
         header_name = [a-zA-Z0-9\-]+;
         header_value = (any -- (LWS|CTL))(any -- CTL)*;
         sep = (LWS*)|(eor LWS+);
-        b64_val = ([a-zA-Z0-9/+]{22}) > { http->tmp_tip = to_u16(fpc - buf); }
-                  "=="
-                  eor > {
+        b64_val = ([a-zA-Z0-9/+]{22}) > {
+                        http->_p.tmp_tip = to_u16(fpc - buf);
+                }
+                "=="
+                eor > {
                         uint16_t tmp_end = to_u16(fpc - buf);
-                        char *in = buf + http->tmp_tip;
-                        size_t inlen = tmp_end - http->tmp_tip;
+                        char *in = buf + http->_p.tmp_tip;
+                        size_t inlen = tmp_end - http->_p.tmp_tip;
                         char *out = (char *)http->expect_md5;
                         size_t outlen = sizeof(http->expect_md5);
                         bool rc;
@@ -25,10 +27,10 @@
                         rc = base64_decode_ctx(NULL, in, inlen, out, &outlen);
                         assert(rc == true && outlen == 16
                                && "base64_decoder broke for HTTP");
-                        http->has_expect_md5 = 1;
-                  };
+                        http->_p.has_expect_md5 = 1;
+                };
         content_md5 = "Content-MD5:"i sep ( b64_val ) $!  {
-                                if (!http->has_expect_md5) {
+                                if (!http->_p.has_expect_md5) {
                                         errno = EINVAL;
                                         fbreak;
                                 }
@@ -38,10 +40,10 @@
         };
         trailer_line = ( content_md5 ) $!
                 {
-                        if (http->line_end > 0) {
-                                assert(buf[http->line_end] == '\n'
-                                       && "bad http->line_end");
-                                p = buf + http->line_end + 1;
+                        if (http->_p.line_end > 0) {
+                                assert(buf[http->_p.line_end] == '\n'
+                                       && "bad http->_p.line_end");
+                                p = buf + http->_p.line_end + 1;
                         } else {
                                 p = buf;
                         }
@@ -49,8 +51,8 @@
                         fgoto ignored_trailer;
                 };
         trailers = trailer_line* '\r''\n' > {
-                http->chunk_state = MOG_CHUNK_STATE_DONE;
-                http->line_end = to_u16(fpc - buf);
+                http->_p.chunk_state = MOG_CHUNK_STATE_DONE;
+                http->_p.line_end = to_u16(fpc - buf);
                 really_done = 1;
                 fbreak;
         };
diff --git a/http_get.c b/http_get.c
index a104b47..cb3a6eb 100644
--- a/http_get.c
+++ b/http_get.c
@@ -60,42 +60,42 @@ static off_t http_get_resp_hdr(struct mog_http *http, struct stat *sb)
         mog_http_date(modified, MOG_HTTPDATE_CAPA, &sb->st_mtime);
 
         /* validate ranges */
-        if (http->has_range) {
+        if (http->_p.has_range) {
                 long long offset;
 
-                if (http->range_end < 0 && http->range_beg < 0)
+                if (http->_p.range_end < 0 && http->_p.range_beg < 0)
                         goto bad_range;
-                if (http->range_beg >= sb->st_size)
+                if (http->_p.range_beg >= sb->st_size)
                         goto bad_range;
 
                 /* bytes=M-N where M > N */
-                if (http->range_beg >= 0 && http->range_end >= 0
-                    && http->range_beg > http->range_end)
+                if (http->_p.range_beg >= 0 && http->_p.range_end >= 0
+                    && http->_p.range_beg > http->_p.range_end)
                         goto bad_range;
 
-                if (http->range_end < 0) { /* bytes=M- */
+                if (http->_p.range_end < 0) { /* bytes=M- */
                         /* bytes starting at M until EOF */
-                        assert(http->range_beg >= 0 && "should've sent 416");
-                        offset = (long long)http->range_beg;
+                        assert(http->_p.range_beg >= 0 && "should've sent 416");
+                        offset = (long long)http->_p.range_beg;
                         count = (long long)(sb->st_size - offset);
-                } else if (http->range_beg < 0) { /* bytes=-N */
+                } else if (http->_p.range_beg < 0) { /* bytes=-N */
                         /* last N bytes */
-                        assert(http->range_end >= 0 && "should've sent 416");
-                        offset = (long long)(sb->st_size - http->range_end);
+                        assert(http->_p.range_end >= 0 && "should've sent 416");
+                        offset = (long long)(sb->st_size - http->_p.range_end);
 
                         /* serve the entire file if client requested too much */
                         if (offset < 0)
                                 goto resp_200;
                         count = (long long)(sb->st_size - offset);
                 } else { /* bytes=M-N*/
-                        assert(http->range_beg >= 0 && http->range_end >= 0
+                        assert(http->_p.range_beg >= 0 && http->_p.range_end >= 0
                                && "should've sent 416");
-                        offset = (long long)http->range_beg;
+                        offset = (long long)http->_p.range_beg;
 
                         /* truncate responses to current file size */
-                        if (http->range_end >= sb->st_size)
-                                http->range_end = sb->st_size - 1;
-                        count = (long long)http->range_end + 1 - offset;
+                        if (http->_p.range_end >= sb->st_size)
+                                http->_p.range_end = sb->st_size - 1;
+                        count = (long long)http->_p.range_end + 1 - offset;
                 }
 
                 assert(count > 0 && "bad count for 206 response");
@@ -123,7 +123,7 @@ static off_t http_get_resp_hdr(struct mog_http *http, struct stat *sb)
                         count, /* Content-Length */
                         offset, offset + count - 1, /* bytes M-N */
                         (long long)sb->st_size,
-                        http->persistent ? "keep-alive" : "close");
+                        http->_p.persistent ? "keep-alive" : "close");
         } else {
 resp_200:
                 count = (long long)sb->st_size;
@@ -140,7 +140,7 @@ resp_200:
                         now->httpdate,
                         modified,
                         count,
-                        http->persistent ? "keep-alive" : "close");
+                        http->_p.persistent ? "keep-alive" : "close");
         }
 
         /* TODO: put down the crack pipe and refactor this */
@@ -151,7 +151,7 @@ bad_range:
                         mog_file_close(http->forward);
                         http->forward = NULL;
                 } else {
-                        assert(http->http_method == MOG_HTTP_METHOD_HEAD
+                        assert(http->_p.http_method == MOG_HTTP_METHOD_HEAD
                                && "not HTTP HEAD");
                 }
                 rc = snprintf(buf, len,
@@ -165,14 +165,14 @@ bad_range:
                         "\r\n",
                         now->httpdate,
                         (long long)sb->st_size,
-                        http->persistent ? "keep-alive" : "close");
+                        http->_p.persistent ? "keep-alive" : "close");
         }
 
         assert(rc > 0 && rc < len && "we suck at snprintf");
         len = (size_t)rc;
         assert(http->wbuf == NULL && "tried to write to a busy client");
 
-        if (http->http_method == MOG_HTTP_METHOD_HEAD)
+        if (http->_p.http_method == MOG_HTTP_METHOD_HEAD)
                 count = 0;
 
         http->wbuf = mog_trysend(mog_fd_of(http)->fd, buf, len, (off_t)count);
@@ -194,7 +194,7 @@ void mog_http_get_open(struct mog_http *http, char *buf)
         if (path[1] == '\0') { /* keep "mogadm check" happy */
                 sb.st_mtime = 0;
                 sb.st_size = 0;
-        } else if (http->http_method == MOG_HTTP_METHOD_HEAD) {
+        } else if (http->_p.http_method == MOG_HTTP_METHOD_HEAD) {
                 if (mog_stat(http->svc, path, &sb) < 0) goto err;
                 if (!S_ISREG(sb.st_mode)) goto forbidden;
         } else {
@@ -272,20 +272,20 @@ retry:
                 case_EAGAIN: return MOG_NEXT_WAIT_WR;
                 case EINTR: goto retry;
                 }
-                http->persistent = 0;
+                http->_p.persistent = 0;
         } else { /* w == 0 */
                 /*
                  * if we can't fulfill the value set by our Content-Length:
                  * header, we must kill the TCP connection
                  */
-                http->persistent = 0;
+                http->_p.persistent = 0;
                 syslog(LOG_ERR,
                        "sendfile()-d 0 bytes at offset=%lld; file truncated?",
                        (long long)file->foff);
         }
 done:
         mog_file_close(http->forward);
-        if (http->persistent) {
+        if (http->_p.persistent) {
                 mog_http_reset(http);
                 return MOG_NEXT_ACTIVE;
         }
diff --git a/http_parser.rl b/http_parser.rl
index d84d0ce..a643862 100644
--- a/http_parser.rl
+++ b/http_parser.rl
@@ -30,69 +30,69 @@ static bool length_incr(off_t *len, unsigned c)
         };
 
         mog_path = '/'[a-zA-Z0-9/\.\-]{0,36}; # only stuff MogileFS will use
-        GET = "GET "> { http->http_method = MOG_HTTP_METHOD_GET; };
-        HEAD = "HEAD "> { http->http_method = MOG_HTTP_METHOD_HEAD; };
-        PUT = "PUT "> { http->http_method = MOG_HTTP_METHOD_PUT; };
-        DELETE = "DELETE "> { http->http_method = MOG_HTTP_METHOD_DELETE; };
-        MKCOL = "MKCOL "> { http->http_method = MOG_HTTP_METHOD_MKCOL; };
+        GET = "GET "> { http->_p.http_method = MOG_HTTP_METHOD_GET; };
+        HEAD = "HEAD "> { http->_p.http_method = MOG_HTTP_METHOD_HEAD; };
+        PUT = "PUT "> { http->_p.http_method = MOG_HTTP_METHOD_PUT; };
+        DELETE = "DELETE "> { http->_p.http_method = MOG_HTTP_METHOD_DELETE; };
+        MKCOL = "MKCOL "> { http->_p.http_method = MOG_HTTP_METHOD_MKCOL; };
 
         # no HTTP/0.9 for now, sorry (not :P)
         req_line = (HEAD|GET|PUT|DELETE|MKCOL)
                 ("http://" [^/]+)?
-                '/'*(mog_path) > { http->path_tip = to_u8(fpc - buf); }
+                '/'*(mog_path) > { http->_p.path_tip = to_u8(fpc - buf); }
                 # TODO: maybe folks use query string/fragments for logging...
-                (" HTTP/1.") > { http->path_end = to_u8(fpc - buf); }
-                ('0'|'1'> { http->persistent = 1; }) '\r'LF;
+                (" HTTP/1.") > { http->_p.path_end = to_u8(fpc - buf); }
+                ('0'|'1'> { http->_p.persistent = 1; }) '\r'LF;
 
         content_length = "Content-Length:"i sep
                 (digit+) $ {
-                        if (!length_incr(&http->content_len, fc))
+                        if (!length_incr(&http->_p.content_len, fc))
                                 fbreak;
                 }
                 $! { errno = EINVAL; fbreak; }
                 eor;
         content_range = "Content-Range:"i sep "bytes"LWS+
                 (digit+) $ {
-                        if (!length_incr(&http->range_beg, fc))
+                        if (!length_incr(&http->_p.range_beg, fc))
                                 fbreak;
                 }
                 $! { errno = EINVAL; fbreak; }
                 "-"
                 (digit+) $ {
-                        if (!length_incr(&http->range_end, fc))
+                        if (!length_incr(&http->_p.range_end, fc))
                                 fbreak;
                 }
                 $! { errno = EINVAL; fbreak; }
                 "/*"
-                eor > { http->has_content_range = 1; };
+                eor > { http->_p.has_content_range = 1; };
         range = "Range:"i sep (
                         "bytes=" > {
-                                http->range_beg = http->range_end = -1;
+                                http->_p.range_beg = http->_p.range_end = -1;
                         }
                         (digit*) $ {
-                                if (http->range_beg < 0)
-                                        http->range_beg = 0;
-                                if (!length_incr(&http->range_beg, fc))
+                                if (http->_p.range_beg < 0)
+                                        http->_p.range_beg = 0;
+                                if (!length_incr(&http->_p.range_beg, fc))
                                         fbreak;
                         }
                         '-'
                         (digit*) $ {
-                                if (http->range_end < 0)
-                                        http->range_end = 0;
-                                if (!length_incr(&http->range_end, fc))
+                                if (http->_p.range_end < 0)
+                                        http->_p.range_end = 0;
+                                if (!length_incr(&http->_p.range_end, fc))
                                         fbreak;
                         }
                 ) $! { errno = EINVAL; fbreak; }
-                eor @ { http->has_range = 1; };
+                eor @ { http->_p.has_range = 1; };
         transfer_encoding_chunked = "Transfer-Encoding:"i sep
-                "chunked"i eor > { http->chunked = 1; };
+                "chunked"i eor > { http->_p.chunked = 1; };
         trailer = "Trailer:"i sep
-                (("Content-MD5"i @ { http->has_trailer_md5 = 1; })
+                (("Content-MD5"i @ { http->_p.has_trailer_md5 = 1; })
                  | header_name | ',')
                 eor;
         connection = "Connection:"i sep
-                (("close"i @ { http->persistent = 0; }) |
-                 ("keep-alive"i @ { http->persistent = 1; })) eor;
+                (("close"i @ { http->_p.persistent = 0; }) |
+                 ("keep-alive"i @ { http->_p.persistent = 1; })) eor;
         header_line =
                 ( content_length |
                   transfer_encoding_chunked |
@@ -102,11 +102,11 @@ static bool length_incr(off_t *len, unsigned c)
                   content_md5 |
                   connection ) $!
                 {
-                        assert(http->line_end > 0 &&
+                        assert(http->_p.line_end > 0 &&
                                "no previous request/header line");
-                        assert(buf[http->line_end] == '\n' &&
-                               "bad http->line_end");
-                        p = buf + http->line_end + 1;
+                        assert(buf[http->_p.line_end] == '\n' &&
+                               "bad http->_p.line_end");
+                        p = buf + http->_p.line_end + 1;
                         assert(p <= pe && "overflow");
                         fgoto ignored_header;
                 };
@@ -120,21 +120,21 @@ static bool length_incr(off_t *len, unsigned c)
 void mog_http_reset_parser(struct mog_http *http)
 {
         int cs;
-        struct mog_rbuf *rbuf = http->rbuf;
-        struct mog_svc *svc = http->svc;
 
         %% write init;
-        memset(http, 0, sizeof(struct mog_http));
         http->cs = cs;
-        http->rbuf = rbuf;
-        http->svc = svc;
+        memset(&http->_p, 0, sizeof(http->_p));
+
+        /* these should probably be in mog_http_init */
+        http->forward = NULL;
+        http->wbuf = NULL;
 }
 
 void mog_http_init(struct mog_http *http, struct mog_svc *svc)
 {
-        http->svc = svc;
-        http->rbuf = NULL;
         mog_http_reset_parser(http);
+        http->rbuf = NULL;
+        http->svc = svc;
 }
 
 enum mog_parser_state
@@ -143,7 +143,7 @@ mog_http_parse(struct mog_http *http, char *buf, size_t len)
         char *p, *pe, *eof = NULL;
         int cs = http->cs;
         int really_done = 0;
-        size_t off = http->offset;
+        size_t off = http->_p.offset;
 
         assert(http->wbuf == NULL && "unwritten data in buffer");
         assert(off <= len && "http offset past end of buffer");
@@ -161,13 +161,13 @@ mog_http_parse(struct mog_http *http, char *buf, size_t len)
                 cs = http_parser_first_final;
 
         http->cs = cs;
-        http->offset = p - buf;
+        http->_p.offset = p - buf;
 
         if (cs == http_parser_error || errno)
                 return MOG_PARSER_ERROR;
 
         assert(p <= pe && "buffer overflow after http parse");
-        assert(http->offset <= len && "offset longer than len");
+        assert(http->_p.offset <= len && "offset longer than len");
 
         if (http->cs == http_parser_first_final) return MOG_PARSER_DONE;
         return MOG_PARSER_CONTINUE;
diff --git a/http_put.c b/http_put.c
index 15ff9f7..740ce08 100644
--- a/http_put.c
+++ b/http_put.c
@@ -173,7 +173,7 @@ static enum mog_next http_put_commit(struct mog_http *http)
 
         if (http->wbuf && http->wbuf != MOG_WR_ERROR)
                 return MOG_NEXT_WAIT_WR;
-        if (!http->persistent || http->wbuf == MOG_WR_ERROR)
+        if (!http->_p.persistent || http->wbuf == MOG_WR_ERROR)
                 return MOG_NEXT_CLOSE;
         mog_http_reset(http);
         return MOG_NEXT_ACTIVE;
@@ -182,65 +182,65 @@ static enum mog_next http_put_commit(struct mog_http *http)
 static void stash_advance_rbuf(struct mog_http *http, char *buf, size_t buf_len)
 {
         struct mog_rbuf *rbuf = http->rbuf;
-        size_t end = http->line_end + 1;
+        size_t end = http->_p.line_end + 1;
 
-        if (http->line_end == 0 || buf_len <= end) {
-                http->offset = 0;
+        if (http->_p.line_end == 0 || buf_len <= end) {
+                http->_p.offset = 0;
                 mog_rbuf_free_and_null(&http->rbuf);
                 return;
         }
 
-        assert(buf[http->line_end] == '\n' && "line_end is not LF");
+        assert(buf[http->_p.line_end] == '\n' && "line_end is not LF");
         assert(buf_len <= MOG_RBUF_MAX_SIZE && "bad rbuf size");
-        assert(end <= http->offset && "invalid line end");
+        assert(end <= http->_p.offset && "invalid line end");
         if (rbuf == NULL)
                 http->rbuf = rbuf = mog_rbuf_new(MOG_RBUF_BASE_SIZE);
 
         memmove(rbuf->rptr, buf + end, buf_len - end);
         rbuf->rsize = buf_len - end;
-        http->offset -= end;
-        if (http->tmp_tip >= end)
-                http->tmp_tip -= end;
-        http->line_end = 0;
+        http->_p.offset -= end;
+        if (http->_p.tmp_tip >= end)
+                http->_p.tmp_tip -= end;
+        http->_p.line_end = 0;
 }
 
 static void
 chunked_body_after_header(struct mog_http *http, char *buf, size_t buf_len)
 {
-        size_t tmpoff = http->offset;
+        size_t tmpoff = http->_p.offset;
 
         mog_chunk_init(http);
-        http->offset = tmpoff;
+        http->_p.offset = tmpoff;
 
         switch (mog_chunk_parse(http, buf, buf_len)) {
         case MOG_PARSER_ERROR:
                 (void)write_err(http, "400 Bad Request");
                 return;
         case MOG_PARSER_CONTINUE:
-                assert(http->chunk_state != MOG_CHUNK_STATE_DONE);
+                assert(http->_p.chunk_state != MOG_CHUNK_STATE_DONE);
                 /* fall through */
         case MOG_PARSER_DONE:
-                switch (http->chunk_state) {
+                switch (http->_p.chunk_state) {
                 case MOG_CHUNK_STATE_SIZE:
-                        assert(http->offset == buf_len
+                        assert(http->_p.offset == buf_len
                                && "HTTP chunk parser didn't finish size");
                         return;
                 case MOG_CHUNK_STATE_DATA:
-                        assert(http->offset == buf_len
+                        assert(http->_p.offset == buf_len
                                && "HTTP chunk parser didn't finish data");
                         return;
                 case MOG_CHUNK_STATE_TRAILER:
-                        assert(http->offset > 0 &&
-                               "http->offset unset while in trailer");
+                        assert(http->_p.offset > 0 &&
+                               "http->_p.offset unset while in trailer");
                         stash_advance_rbuf(http, buf, buf_len);
-                        http->skip_rbuf_defer = 1;
+                        http->_p.skip_rbuf_defer = 1;
                         return;
                 case MOG_CHUNK_STATE_DONE:
                         put_commit_resp(http);
-                        assert(http->offset > 0 &&
-                               "http->offset unset after chunk body done");
+                        assert(http->_p.offset > 0 &&
+                               "http->_p.offset unset after chunk body done");
                         stash_advance_rbuf(http, buf, buf_len);
-                        http->skip_rbuf_defer = 1;
+                        http->_p.skip_rbuf_defer = 1;
                 }
         }
 }
@@ -248,45 +248,45 @@ chunked_body_after_header(struct mog_http *http, char *buf, size_t buf_len)
 static void
 identity_body_after_header(struct mog_http *http, char *buf, size_t buf_len)
 {
-        size_t body_len = buf_len - http->offset;
-        char *body_ptr = buf + http->offset;
+        size_t body_len = buf_len - http->_p.offset;
+        char *body_ptr = buf + http->_p.offset;
 
-        if (http->content_len < body_len)
-                body_len = http->content_len;
+        if (http->_p.content_len < body_len)
+                body_len = http->_p.content_len;
         if (body_len == 0)
                 return;
-        http->offset += body_len;
+        http->_p.offset += body_len;
         if (!mog_http_write_full(http->forward, body_ptr, body_len))
                 (void)write_err(http, NULL);
 }
 
 static bool lengths_ok(struct mog_http *http)
 {
-        if (http->content_len < 0)
+        if (http->_p.content_len < 0)
                 return false;        /* ERANGE */
 
-        if (http->has_content_range) {
-                if (http->chunked)
+        if (http->_p.has_content_range) {
+                if (http->_p.chunked)
                         return false;
 
-                if (http->range_end < 0 || http->range_beg < 0)
+                if (http->_p.range_end < 0 || http->_p.range_beg < 0)
                         return false;        /* ERANGE */
 
-                assert(http->range_end >= 0 && http->range_beg >= 0 &&
+                assert(http->_p.range_end >= 0 && http->_p.range_beg >= 0 &&
                        "bad range, http_parser.rl broken");
 
                 /* can't end after we start */
-                if (http->range_end < http->range_beg)
+                if (http->_p.range_end < http->_p.range_beg)
                         return false;
 
                 /*
                  * Content-Length should match Content-Range boundaries
                  * WARNING: Eric Wong sucks at arithmetic, check this:
                  */
-                if (http->content_len >= 0) {
-                        off_t expect = http->range_end - http->range_beg + 1;
+                if (http->_p.content_len >= 0) {
+                        off_t expect = http->_p.range_end - http->_p.range_beg + 1;
 
-                        if (http->content_len != expect)
+                        if (http->_p.content_len != expect)
                                 return false;
                 }
         }
@@ -324,7 +324,7 @@ static struct mog_file * open_put(struct mog_http *http, char *path)
          * we can't do an atomic rename(2) on successful PUT
          * if we have a partial upload
          */
-        if (http->has_content_range) {
+        if (http->_p.has_content_range) {
                 http->forward = mog_file_open_put(http->svc, path, O_CREAT);
                 if (http->forward == NULL)
                         return NULL;
@@ -354,9 +354,9 @@ static struct mog_file * open_put(struct mog_http *http, char *path)
 
         file->path = xstrdup(path);
         assert(file->foff == 0 && "file->foff should be zero");
-        if (http->has_content_range)
-                file->foff = http->range_beg;
-        if (http->has_trailer_md5 || http->has_expect_md5)
+        if (http->_p.has_content_range)
+                file->foff = http->_p.range_beg;
+        if (http->_p.has_trailer_md5 || http->_p.has_expect_md5)
                 mog_digest_init(&file->digest, GC_MD5);
 
         return file;
@@ -387,12 +387,12 @@ void mog_http_put(struct mog_http *http, char *buf, size_t buf_len)
         if (file == NULL)
                 goto err;
 
-        if (buf_len == http->offset) {
+        if (buf_len == http->_p.offset) {
                 /* we got the HTTP header in one read() */
-                if (http->chunked) {
+                if (http->_p.chunked) {
                         mog_rbuf_free_and_null(&http->rbuf);
                         mog_chunk_init(http);
-                        http->offset = buf_len;
+                        http->_p.offset = buf_len;
                 }
                 return;
         }
@@ -400,10 +400,10 @@ void mog_http_put(struct mog_http *http, char *buf, size_t buf_len)
          * otherwise we got part of the request body with the header,
          * write partially read body
          */
-        assert(buf_len > http->offset && http->offset > 0
-               && "http->offset is wrong");
+        assert(buf_len > http->_p.offset && http->_p.offset > 0
+               && "http->_p.offset is wrong");
 
-        if (http->chunked)
+        if (http->_p.chunked)
                 chunked_body_after_header(http, buf, buf_len);
         else
                 identity_body_after_header(http, buf, buf_len);
@@ -502,9 +502,9 @@ static enum mog_next identity_put_in_progress(struct mog_fd *mfd)
         assert(http->wbuf == NULL && "can't receive file with http->wbuf");
         assert(http->forward && http->forward != MOG_IOSTAT && "bad forward");
 
-        need = http->content_len - http->forward->as.file.foff;
-        if (http->has_content_range)
-                need += http->range_beg;
+        need = http->_p.content_len - http->forward->as.file.foff;
+        if (http->_p.has_content_range)
+                need += http->_p.range_beg;
         if (need == 0)
                 return http_put_commit(http);
 
@@ -549,19 +549,19 @@ again:
         assert(http->wbuf == NULL && "can't receive file with http->wbuf");
         assert(http->forward && http->forward != MOG_IOSTAT && "bad forward");
 
-        switch (http->chunk_state) {
+        switch (http->_p.chunk_state) {
         case MOG_CHUNK_STATE_DATA:
                 assert(http->rbuf == NULL && "unexpected http->rbuf");
-                if (http->content_len == 0) { /* final chunk */
-                        http->chunk_state = MOG_CHUNK_STATE_TRAILER;
-                        http->offset = 0;
+                if (http->_p.content_len == 0) { /* final chunk */
+                        http->_p.chunk_state = MOG_CHUNK_STATE_TRAILER;
+                        http->_p.offset = 0;
                         goto chunk_state_trailer;
                 }
-                assert(http->content_len > 0 && "bad chunk length");
+                assert(http->_p.content_len > 0 && "bad chunk length");
                 /* read the chunk into memory */
                 buf = mog_fsbuf_get(&buf_len);
-                if (buf_len > http->content_len)
-                        buf_len = http->content_len;
+                if (buf_len > http->_p.content_len)
+                        buf_len = http->_p.content_len;
                 do {
                         r = read(mfd->fd, buf, buf_len);
                 } while (r < 0 && errno == EINTR);
@@ -571,10 +571,10 @@ again:
                 if (!mog_http_write_full(http->forward, buf, r))
                         return write_err(http, NULL);
 
-                http->content_len -= r;
+                http->_p.content_len -= r;
 
                 /* chunk is complete */
-                if (http->content_len == 0)
+                if (http->_p.content_len == 0)
                         mog_chunk_init(http);
                 goto again;
         case MOG_CHUNK_STATE_TRAILER:
@@ -610,15 +610,15 @@ chunk_state_trailer:
                 case MOG_PARSER_ERROR:
                         return write_err(http, "400 Bad Request");
                 case MOG_PARSER_CONTINUE:
-                        assert(http->chunk_state != MOG_CHUNK_STATE_DONE);
+                        assert(http->_p.chunk_state != MOG_CHUNK_STATE_DONE);
                 case MOG_PARSER_DONE:
-                        switch (http->chunk_state) {
+                        switch (http->_p.chunk_state) {
                         case MOG_CHUNK_STATE_SIZE:
                                 if (in_trailer)
                                         assert(0 && "bad chunk state: size");
                                 /* client is trickling chunk size :< */
                                 mog_rbuf_free_and_null(&http->rbuf);
-                                http->offset = 0;
+                                http->_p.offset = 0;
                                 goto again;
                         case MOG_CHUNK_STATE_DATA:
                                 if (in_trailer)
@@ -635,7 +635,7 @@ chunk_state_trailer:
                                 /* pipelined HTTP request after trailers! */
                                 if (http->rbuf)
                                         assert(http->rbuf->rsize > 0
-                                               && http->offset == 0
+                                               && http->_p.offset == 0
                                                && "bad rbuf");
                                 return http_put_commit(http);
                         }
@@ -657,7 +657,7 @@ read_err:
 
 enum mog_next mog_http_put_in_progress(struct mog_fd *mfd)
 {
-        if (mfd->as.http.chunked)
+        if (mfd->as.http._p.chunked)
                 return chunked_put_in_progress(mfd);
 
         return identity_put_in_progress(mfd);
diff --git a/test/chunk-parser-1.c b/test/chunk-parser-1.c
index 6804a17..335a39b 100644
--- a/test/chunk-parser-1.c
+++ b/test/chunk-parser-1.c
@@ -23,7 +23,7 @@ static void buf_set(const char *s)
 {
         struct mog_file *file;
 
-        http->chunked = 1;
+        http->_p.chunked = 1;
         reset();
         tmpfp = tmpfile();
         assert(tmpfp != NULL && "tmpfile(3) failed");
@@ -46,16 +46,16 @@ int main(void)
                 buf_set("666\r\n");
                 state = mog_chunk_parse(http, buf, len);
                 assert(state == MOG_PARSER_DONE);
-                assert(http->content_len == 0x666);
-                assert(http->chunk_state == MOG_CHUNK_STATE_DATA);
+                assert(http->_p.content_len == 0x666);
+                assert(http->_p.chunk_state == MOG_CHUNK_STATE_DATA);
         }
 
         if ("incomplete chunk") {
                 buf_set("666\r");
                 state = mog_chunk_parse(http, buf, len);
                 assert(state == MOG_PARSER_CONTINUE);
-                assert(http->content_len == 0x666);
-                assert(http->chunk_state == MOG_CHUNK_STATE_SIZE);
+                assert(http->_p.content_len == 0x666);
+                assert(http->_p.chunk_state == MOG_CHUNK_STATE_SIZE);
         }
 
         if ("bad chunk") {
@@ -68,8 +68,8 @@ int main(void)
                 buf_set("abcde; foo=bar\r\n");
                 state = mog_chunk_parse(http, buf, len);
                 assert(state == MOG_PARSER_DONE);
-                assert(http->content_len == 0xabcde);
-                assert(http->chunk_state == MOG_CHUNK_STATE_DATA);
+                assert(http->_p.content_len == 0xabcde);
+                assert(http->_p.chunk_state == MOG_CHUNK_STATE_DATA);
         }
 
         if ("chunk with complete header and data") {
@@ -77,8 +77,8 @@ int main(void)
                 buf_set("5\r\nabcde");
                 state = mog_chunk_parse(http, buf, len);
                 assert(state == MOG_PARSER_CONTINUE);
-                assert(http->content_len == 0);
-                assert(http->chunk_state == MOG_CHUNK_STATE_SIZE);
+                assert(http->_p.content_len == 0);
+                assert(http->_p.chunk_state == MOG_CHUNK_STATE_SIZE);
                 assert(sizeof(tmp) == pread(tmpfd, tmp, sizeof(tmp), 0));
                 assert(0 == memcmp(tmp, "abcde", sizeof(tmp)));
         }
@@ -88,33 +88,33 @@ int main(void)
                 buf_set("5\r\nabcde\r\n3");
                 state = mog_chunk_parse(http, buf, len);
                 assert(state == MOG_PARSER_CONTINUE);
-                assert(http->content_len == 3);
-                assert(http->chunk_state == MOG_CHUNK_STATE_SIZE);
+                assert(http->_p.content_len == 3);
+                assert(http->_p.chunk_state == MOG_CHUNK_STATE_SIZE);
                 assert(sizeof(tmp) == pread(tmpfd, tmp, sizeof(tmp), 0));
                 assert(0 == memcmp(tmp, "abcde", sizeof(tmp)));
-                assert(http->offset == len);
+                assert(http->_p.offset == len);
         }
         if ("multiple chunks with end") {
                 char tmp[8];
                 buf_set("5\r\nabcde\r\n3\r\n123\r\n0\r\n\r\n");
                 state = mog_chunk_parse(http, buf, len);
                 assert(state == MOG_PARSER_DONE);
-                assert(http->chunk_state == MOG_CHUNK_STATE_DONE);
+                assert(http->_p.chunk_state == MOG_CHUNK_STATE_DONE);
                 assert(sizeof(tmp) == pread(tmpfd, tmp, sizeof(tmp), 0));
                 assert(0 == memcmp(tmp, "abcde123", sizeof(tmp)));
-                assert(http->offset == len);
+                assert(http->_p.offset == len);
         }
 
         if ("multiple chunks with trailer") {
                 char tmp[8];
                 buf_set("5\r\nabcde\r\n3\r\n123\r\n0\r\nFoo: bar\r\n\r\n");
                 state = mog_chunk_parse(http, buf, len);
-                assert(http->chunk_state == MOG_CHUNK_STATE_DONE);
+                assert(http->_p.chunk_state == MOG_CHUNK_STATE_DONE);
                 assert(state == MOG_PARSER_DONE);
-                assert(http->content_len == 0);
+                assert(http->_p.content_len == 0);
                 assert(sizeof(tmp) == pread(tmpfd, tmp, sizeof(tmp), 0));
                 assert(0 == memcmp(tmp, "abcde123", sizeof(tmp)));
-                assert(http->offset == len);
+                assert(http->_p.offset == len);
         }
 
         if ("multiple chunks with almost end") {
@@ -122,11 +122,11 @@ int main(void)
                 buf_set("5\r\nabcde\r\n3\r\n123\r\n0\r\n");
                 state = mog_chunk_parse(http, buf, len);
                 assert(state == MOG_PARSER_DONE);
-                assert(http->chunk_state == MOG_CHUNK_STATE_DATA);
-                assert(http->content_len == 0);
+                assert(http->_p.chunk_state == MOG_CHUNK_STATE_DATA);
+                assert(http->_p.content_len == 0);
                 assert(sizeof(tmp) == pread(tmpfd, tmp, sizeof(tmp), 0));
                 assert(0 == memcmp(tmp, "abcde123", sizeof(tmp)));
-                assert(http->offset == len);
+                assert(http->_p.offset == len);
         }
 
         if ("multiple chunks with almost end (more)") {
@@ -134,11 +134,11 @@ int main(void)
                 buf_set("5\r\nabcde\r\n3\r\n123\r\n0\r\n\r");
                 state = mog_chunk_parse(http, buf, len);
                 assert(state == MOG_PARSER_CONTINUE);
-                assert(http->chunk_state == MOG_CHUNK_STATE_TRAILER);
-                assert(http->content_len == 0);
+                assert(http->_p.chunk_state == MOG_CHUNK_STATE_TRAILER);
+                assert(http->_p.content_len == 0);
                 assert(sizeof(tmp) == pread(tmpfd, tmp, sizeof(tmp), 0));
                 assert(0 == memcmp(tmp, "abcde123", sizeof(tmp)));
-                assert(http->offset == len);
+                assert(http->_p.offset == len);
         }
 
         if ("multiple chunks with incomplete") {
@@ -146,11 +146,11 @@ int main(void)
                 buf_set("5\r\nabcde\r\n3\r\n12");
                 state = mog_chunk_parse(http, buf, len);
                 assert(state == MOG_PARSER_DONE);
-                assert(http->chunk_state == MOG_CHUNK_STATE_DATA);
-                assert(http->content_len == 1);
+                assert(http->_p.chunk_state == MOG_CHUNK_STATE_DATA);
+                assert(http->_p.content_len == 1);
                 assert(sizeof(tmp) == pread(tmpfd, tmp, sizeof(tmp), 0));
                 assert(0 == memcmp(tmp, "abcde12", sizeof(tmp)));
-                assert(http->offset == len);
+                assert(http->_p.offset == len);
         }
 
         if ("incomplete data") {
@@ -158,11 +158,11 @@ int main(void)
                 buf_set("666\r\nabc");
                 state = mog_chunk_parse(http, buf, len);
                 assert(state == MOG_PARSER_DONE);
-                assert(http->chunk_state == MOG_CHUNK_STATE_DATA);
-                assert(http->content_len == (0x666 - sizeof(tmp)));
+                assert(http->_p.chunk_state == MOG_CHUNK_STATE_DATA);
+                assert(http->_p.content_len == (0x666 - sizeof(tmp)));
                 assert(sizeof(tmp) == pread(tmpfd, tmp, sizeof(tmp), 0));
                 assert(0 == memcmp(tmp, "abc", sizeof(tmp)));
-                assert(http->offset == len);
+                assert(http->_p.offset == len);
         }
 
         reset();
diff --git a/test/http-parser-1.c b/test/http-parser-1.c
index 688e0a5..861c19f 100644
--- a/test/http-parser-1.c
+++ b/test/http-parser-1.c
@@ -14,8 +14,8 @@ static void assert_path_equal(const char *str)
 {
         size_t slen = strlen(str);
 
-        assert(0 == memcmp(str, buf + http->path_tip, slen));
-        assert(http->path_end == http->path_tip + slen);
+        assert(0 == memcmp(str, buf + http->_p.path_tip, slen));
+        assert(http->_p.path_end == http->_p.path_tip + slen);
 }
 
 static void reset(void)
@@ -36,9 +36,9 @@ int main(void)
         if ("normal HTTP GET request") {
                 buf_set("GET /foo HTTP/1.1\r\nHost: 127.6.6.6\r\n\r\n");
                 state = mog_http_parse(http, buf, len);
-                assert(http->http_method == MOG_HTTP_METHOD_GET
+                assert(http->_p.http_method == MOG_HTTP_METHOD_GET
                        && "http_method should be GET");
-                assert(http->persistent && "not persistent");
+                assert(http->_p.persistent && "not persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
         }
@@ -46,9 +46,9 @@ int main(void)
         if ("normal HTTP GET request with redundant leading slash") {
                 buf_set("GET //foo HTTP/1.1\r\nHost: 127.6.6.6\r\n\r\n");
                 state = mog_http_parse(http, buf, len);
-                assert(http->http_method == MOG_HTTP_METHOD_GET
+                assert(http->_p.http_method == MOG_HTTP_METHOD_GET
                        && "http_method should be GET");
-                assert(http->persistent && "not persistent");
+                assert(http->_p.persistent && "not persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
         }
@@ -59,9 +59,9 @@ int main(void)
                         "Connection: close\r\n"
                         "\r\n");
                 state = mog_http_parse(http, buf, len);
-                assert(http->http_method == MOG_HTTP_METHOD_GET
+                assert(http->_p.http_method == MOG_HTTP_METHOD_GET
                        && "http_method should be GET");
-                assert(http->persistent == 0 && "should not be persistent");
+                assert(http->_p.persistent == 0 && "should not be persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
         }
@@ -71,9 +71,9 @@ int main(void)
                         "Connection:\r\n keep-alive\r\n"
                         "\r\n");
                 state = mog_http_parse(http, buf, len);
-                assert(http->http_method == MOG_HTTP_METHOD_GET
+                assert(http->_p.http_method == MOG_HTTP_METHOD_GET
                        && "http_method should be GET");
-                assert(http->persistent == 1 && "should be persistent");
+                assert(http->_p.persistent == 1 && "should be persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
         }
@@ -99,9 +99,9 @@ int main(void)
                         "Host: 127.6.6.6\r\n"
                         "\r\n");
                 state = mog_http_parse(http, buf, len);
-                assert(http->http_method == MOG_HTTP_METHOD_HEAD
+                assert(http->_p.http_method == MOG_HTTP_METHOD_HEAD
                        && "http_method should be HEAD ");
-                assert(http->persistent == 1 && "should be persistent");
+                assert(http->_p.persistent == 1 && "should be persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
         }
@@ -113,13 +113,13 @@ int main(void)
                         "\r\n"
                         "partial body request");
                 state = mog_http_parse(http, buf, len);
-                assert(http->content_len == 12345);
-                assert(http->http_method == MOG_HTTP_METHOD_PUT
+                assert(http->_p.content_len == 12345);
+                assert(http->_p.http_method == MOG_HTTP_METHOD_PUT
                        && "http_method should be PUT");
-                assert(http->persistent == 1 && "should be persistent");
+                assert(http->_p.persistent == 1 && "should be persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
-                assert(strcmp(buf + http->offset, "partial body request") == 0
+                assert(strcmp(buf + http->_p.offset, "partial body request") == 0
                        && "buffer repositioned to body start");
         }
 
@@ -130,14 +130,14 @@ int main(void)
                         "\r\n"
                         "16\r\npartial...");
                 state = mog_http_parse(http, buf, len);
-                assert(http->chunked);
-                assert(http->has_trailer_md5 == 0);
-                assert(http->http_method == MOG_HTTP_METHOD_PUT
+                assert(http->_p.chunked);
+                assert(http->_p.has_trailer_md5 == 0);
+                assert(http->_p.http_method == MOG_HTTP_METHOD_PUT
                        && "http_method should be PUT");
-                assert(http->persistent == 1 && "should be persistent");
+                assert(http->_p.persistent == 1 && "should be persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
-                assert(strcmp(buf + http->offset, "16\r\npartial...") == 0
+                assert(strcmp(buf + http->_p.offset, "16\r\npartial...") == 0
                        && "buffer repositioned to body start");
         }
 
@@ -149,16 +149,16 @@ int main(void)
                         "\r\n"
                         "16\r\npartial...");
                 state = mog_http_parse(http, buf, len);
-                assert(http->range_beg == 666);
-                assert(http->range_end == 666666);
-                assert(http->has_content_range == 1);
-                assert(http->has_trailer_md5 == 0);
-                assert(http->http_method == MOG_HTTP_METHOD_PUT
+                assert(http->_p.range_beg == 666);
+                assert(http->_p.range_end == 666666);
+                assert(http->_p.has_content_range == 1);
+                assert(http->_p.has_trailer_md5 == 0);
+                assert(http->_p.http_method == MOG_HTTP_METHOD_PUT
                        && "http_method should be PUT");
-                assert(http->persistent == 1 && "should be persistent");
+                assert(http->_p.persistent == 1 && "should be persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
-                assert(strcmp(buf + http->offset, "16\r\npartial...") == 0
+                assert(strcmp(buf + http->_p.offset, "16\r\npartial...") == 0
                        && "buffer repositioned to body start");
         }
 
@@ -170,14 +170,14 @@ int main(void)
                         "\r\n"
                         "16\r\npartial...");
                 state = mog_http_parse(http, buf, len);
-                assert(http->chunked);
-                assert(http->has_trailer_md5 == 1);
-                assert(http->http_method == MOG_HTTP_METHOD_PUT
+                assert(http->_p.chunked);
+                assert(http->_p.has_trailer_md5 == 1);
+                assert(http->_p.http_method == MOG_HTTP_METHOD_PUT
                        && "http_method should be PUT");
-                assert(http->persistent == 1 && "should be persistent");
+                assert(http->_p.persistent == 1 && "should be persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
-                assert(strcmp(buf + http->offset, "16\r\npartial...") == 0
+                assert(strcmp(buf + http->_p.offset, "16\r\npartial...") == 0
                        && "buffer repositioned to body start");
         }
 
@@ -186,11 +186,11 @@ int main(void)
                         "Host: 127.6.6.6\r\n"
                         "\r\n");
                 state = mog_http_parse(http, buf, len);
-                assert(http->content_len == 0);
-                assert(http->has_trailer_md5 == 0);
-                assert(http->http_method == MOG_HTTP_METHOD_DELETE
+                assert(http->_p.content_len == 0);
+                assert(http->_p.has_trailer_md5 == 0);
+                assert(http->_p.http_method == MOG_HTTP_METHOD_DELETE
                        && "http_method should be DELETE");
-                assert(http->persistent == 1 && "should be persistent");
+                assert(http->_p.persistent == 1 && "should be persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
         }
@@ -200,11 +200,11 @@ int main(void)
                         "Host: 127.6.6.6\r\n"
                         "\r\n");
                 state = mog_http_parse(http, buf, len);
-                assert(http->content_len == 0);
-                assert(http->has_trailer_md5 == 0);
-                assert(http->http_method == MOG_HTTP_METHOD_MKCOL
+                assert(http->_p.content_len == 0);
+                assert(http->_p.has_trailer_md5 == 0);
+                assert(http->_p.http_method == MOG_HTTP_METHOD_MKCOL
                        && "http_method should be MKCOL");
-                assert(http->persistent == 1 && "should be persistent");
+                assert(http->_p.persistent == 1 && "should be persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
         }
@@ -215,12 +215,12 @@ int main(void)
                         "Range: bytes=5-55\r\n"
                         "\r\n");
                 state = mog_http_parse(http, buf, len);
-                assert(http->has_range == 1);
-                assert(http->range_beg == 5 && "range_beg didn't match");
-                assert(http->range_end == 55 && "range_end didn't match");
-                assert(http->http_method == MOG_HTTP_METHOD_GET
+                assert(http->_p.has_range == 1);
+                assert(http->_p.range_beg == 5 && "range_beg didn't match");
+                assert(http->_p.range_end == 55 && "range_end didn't match");
+                assert(http->_p.http_method == MOG_HTTP_METHOD_GET
                        && "http_method should be GET");
-                assert(http->persistent == 1 && "should be persistent");
+                assert(http->_p.persistent == 1 && "should be persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
         }
@@ -231,12 +231,12 @@ int main(void)
                         "Range: bytes=-55\r\n"
                         "\r\n");
                 state = mog_http_parse(http, buf, len);
-                assert(http->has_range == 1);
-                assert(http->range_beg == -1 && "range_beg didn't match");
-                assert(http->range_end == 55 && "range_end didn't match");
-                assert(http->http_method == MOG_HTTP_METHOD_GET
+                assert(http->_p.has_range == 1);
+                assert(http->_p.range_beg == -1 && "range_beg didn't match");
+                assert(http->_p.range_end == 55 && "range_end didn't match");
+                assert(http->_p.http_method == MOG_HTTP_METHOD_GET
                        && "http_method should be GET");
-                assert(http->persistent == 1 && "should be persistent");
+                assert(http->_p.persistent == 1 && "should be persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
         }
@@ -247,12 +247,12 @@ int main(void)
                         "Range: bytes=55-\r\n"
                         "\r\n");
                 state = mog_http_parse(http, buf, len);
-                assert(http->has_range == 1);
-                assert(http->range_beg == 55 && "range_beg didn't match");
-                assert(http->range_end == -1 && "range_end didn't match");
-                assert(http->http_method == MOG_HTTP_METHOD_GET
+                assert(http->_p.has_range == 1);
+                assert(http->_p.range_beg == 55 && "range_beg didn't match");
+                assert(http->_p.range_end == -1 && "range_end didn't match");
+                assert(http->_p.http_method == MOG_HTTP_METHOD_GET
                        && "http_method should be GET");
-                assert(http->persistent == 1 && "should be persistent");
+                assert(http->_p.persistent == 1 && "should be persistent");
                 assert(state == MOG_PARSER_DONE && "parser not done");
                 assert_path_equal("/foo");
         }