diff --git a/include/common/htx.h b/include/common/htx.h index 65f0ec300..df75e3397 100644 --- a/include/common/htx.h +++ b/include/common/htx.h @@ -149,7 +149,11 @@ struct htx { uint32_t used; /* number of blocks in use */ uint32_t tail; /* last inserted block */ uint32_t head; /* older inserted block */ - uint32_t front; /* block's position of the first content before the blocks table */ + + uint32_t tail_addr; /* start address of the free space in front of the the blocks table */ + uint32_t head_addr; /* start address of the free space at the beginning */ + uint32_t end_addr; /* end address of the free space at the beginning */ + uint64_t extra; /* known bytes amount remaining to receive */ uint32_t flags; /* HTX_FL_* */ @@ -187,7 +191,7 @@ struct htx_blk *htx_add_blk_type_size(struct htx *htx, enum htx_blk_type type, u struct htx_blk *htx_add_all_headers(struct htx *htx, const struct http_hdr *hdrs); struct htx_blk *htx_add_all_trailers(struct htx *htx, const struct http_hdr *hdrs); struct htx_blk *htx_add_endof(struct htx *htx, enum htx_blk_type type); -struct htx_blk *htx_add_data_atonce(struct htx *htx, const struct ist data); +struct htx_blk *htx_add_data_atonce(struct htx *htx, struct ist data); size_t htx_add_data(struct htx *htx, const struct ist data); struct htx_blk *htx_add_last_data(struct htx *htx, struct ist data); @@ -308,21 +312,6 @@ static inline uint32_t htx_get_blksz(const struct htx_blk *blk) } } -/* Returns the wrap position, ie the position where the blocks table wraps. - * - * An signed 32-bits integer is returned to handle -1 case. Blocks position are - * store on unsigned 32-bits integer, but it is impossible to have so much - * blocks to overflow a 32-bits signed integer ! - */ -static inline int32_t htx_get_wrap(const struct htx *htx) -{ - if (!htx->used) - return -1; - return ((htx->tail >= htx->head) - ? (htx->used + htx->head) - : (htx->used - 1) + (htx->head - htx->tail)); -} - /* Returns the position of the oldest entry (head). * * An signed 32-bits integer is returned to handle -1 case. Blocks position are @@ -434,10 +423,6 @@ static inline int32_t htx_get_prev(const struct htx *htx, uint32_t pos) head = htx_get_head(htx); if (head == -1 || pos == head) return -1; - if (!pos) { - /* htx_get_wrap() is always greater than 1 here */ - return (htx_get_wrap(htx) - 1); - } return (pos - 1); } @@ -464,10 +449,7 @@ static inline int32_t htx_get_next(const struct htx *htx, uint32_t pos) { if (!htx->used || pos == htx->tail) return -1; - pos++; - if (pos == htx_get_wrap(htx)) - return 0; - return pos; + return (pos + 1); } @@ -483,28 +465,6 @@ static inline struct htx_blk *htx_get_next_blk(const struct htx *htx, return ((pos == -1) ? NULL : htx_get_blk(htx, pos)); } -static inline int32_t htx_find_front(const struct htx *htx) -{ - int32_t front, pos; - uint32_t addr = 0; - - if (!htx->used) - return -1; - - front = -1; - for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) { - struct htx_blk *blk = htx_get_blk(htx, pos); - enum htx_blk_type type = htx_get_blk_type(blk); - - if (type != HTX_BLK_UNUSED && blk->addr >= addr) { - front = pos; - addr = blk->addr; - } - } - - return front; -} - /* Changes the size of the value. It is the caller responsibility to change the * value itself, make sure there is enough space and update allocated value. */ @@ -593,6 +553,8 @@ static inline struct ist htx_get_blk_value(const struct htx *htx, const struct h */ static inline void htx_cut_data_blk(struct htx *htx, struct htx_blk *blk, uint32_t n) { + if (blk->addr == htx->end_addr) + htx->end_addr += n; blk->addr += n; blk->info -= n; htx->data -= n; @@ -661,7 +623,8 @@ static inline int htx_almost_full(const struct htx *htx) static inline void htx_reset(struct htx *htx) { - htx->data = htx->used = htx->tail = htx->head = htx->front = 0; + htx->data = htx->used = htx->tail = htx->head = 0; + htx->tail_addr = htx->head_addr = htx->end_addr = 0; htx->extra = 0; htx->flags = HTX_FL_NONE; htx->first = -1; @@ -768,10 +731,12 @@ static inline void htx_dump(struct htx *htx) int32_t pos; fprintf(stderr, "htx:%p [ size=%u - data=%u - used=%u - wrap=%s - extra=%llu]\n", - htx, htx->size, htx->data, htx->used, (htx->tail >= htx->head) ? "NO" : "YES", + htx, htx->size, htx->data, htx->used, (!htx->head_addr) ? "NO" : "YES", (unsigned long long)htx->extra); - fprintf(stderr, "\tfirst=%d - head=%u, tail=%u - front=%u\n", - htx->first, htx->head, htx->tail, htx->front); + fprintf(stderr, "\tfirst=%d - head=%u, tail=%u\n", + htx->first, htx->head, htx->tail); + fprintf(stderr, "\ttail_addr=%d - head_addr=%u, end_addr=%u\n", + htx->tail_addr, htx->head_addr, htx->end_addr); for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) { struct htx_sl *sl; diff --git a/src/http_htx.c b/src/http_htx.c index 9892fd32a..9db87c12d 100644 --- a/src/http_htx.c +++ b/src/http_htx.c @@ -153,7 +153,6 @@ int http_add_header(struct htx *htx, const struct ist n, const struct ist v) if (blk->addr == pblk->addr) blk->addr += htx_get_blksz(pblk); - htx->front = prev; /* Stop when end-of-header is reached */ if (type == HTX_BLK_EOH) @@ -162,9 +161,6 @@ int http_add_header(struct htx *htx, const struct ist n, const struct ist v) blk = pblk; } - if (htx_get_blk_pos(htx, blk) != htx->front) - htx_defrag(htx, NULL); - return 1; } diff --git a/src/htx.c b/src/htx.c index 8dce7c892..24d3ed4c9 100644 --- a/src/htx.c +++ b/src/htx.c @@ -67,12 +67,42 @@ struct htx_blk *htx_defrag(struct htx *htx, struct htx_blk *blk) htx->used = new; htx->first = first; htx->head = 0; - htx->front = htx->tail = new - 1; + htx->tail = new - 1; + htx->head_addr = htx->end_addr = 0; + htx->tail_addr = addr; memcpy((void *)htx->blocks, (void *)tmp->blocks, htx->size); return ((blkpos == -1) ? NULL : htx_get_blk(htx, blkpos)); } +static void htx_defrag_blks(struct htx *htx) +{ + int32_t pos, new; + + new = 0; + for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) { + struct htx_blk *posblk, *newblk; + + if (pos == new) { + new++; + continue; + } + + posblk = htx_get_blk(htx, pos); + if (htx_get_blk_type(posblk) == HTX_BLK_UNUSED) + continue; + + if (htx->first == pos) + htx->first = new; + newblk = htx_get_blk(htx, new++); + newblk->info = posblk->info; + newblk->addr = posblk->addr; + } + BUG_ON(!new); + htx->head = 0; + htx->tail = new - 1; +} + /* Reserves a new block in the HTTP message with a content of * bytes. If there is not enough space, NULL is returned. Otherwise the reserved * block is returned and the HTTP message is updated. Space for this new block @@ -81,108 +111,77 @@ struct htx_blk *htx_defrag(struct htx *htx, struct htx_blk *blk) */ static struct htx_blk *htx_reserve_nxblk(struct htx *htx, uint32_t blksz) { - struct htx_blk *blk, *prevblk, *headblk, *frtblk; - uint32_t used; - uint32_t tail; - uint32_t prev; - uint32_t wrap; - uint32_t head; - int32_t headroom, tailroom; + struct htx_blk *blk; + uint32_t used, tail; + uint32_t headroom, tailroom; if (blksz > htx_free_data_space(htx)) return NULL; /* full */ if (!htx->used) { /* Empty message */ - htx->front = htx->head = htx->tail = htx->first = 0; + htx->head = htx->tail = htx->first = 0; htx->used = 1; blk = htx_get_blk(htx, htx->tail); blk->addr = 0; htx->data = blksz; + htx->tail_addr = blksz; return blk; } - used = htx->used + 1; - prev = htx->tail; - head = htx->head; - tail = htx->tail + 1; - wrap = htx_get_wrap(htx); - - if (tail == wrap) { - frtblk = htx_get_blk(htx, htx->front); - - /* Blocks don't wrap for now. We either need to push the new one - * on top of others or to defragement the table. */ - if (sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, wrap+1) >= frtblk->addr + htx_get_blksz(frtblk)) - wrap++; - else if (tail >= used) /* There is hole at the beginning */ - tail = 0; - else { - /* No more room, tail hits data. We have to realign the - * whole message. */ - goto defrag; - } - } - else if (used >= wrap) { - /* We have hit the tail, we need to reorganize the blocks. */ - goto defrag; - } - - /* Now we have updated tail, used and wrap, we know that there is some - * available room at least from the protocol's perspective. This space - * is split in two areas : - * - * 1: the space between the beginning of the blocks table and the - * front data's address. This space will only be used if data don't - * wrap yet. - - * 2: If the previous tail was the front block, the space between the - * beginning of the message and the head data's address. - * Otherwise, the space between the tail data's address and the - * tail's one. + /* Find the block's position. First, we try to get the next position in + * the message, increasing the tail by one. If this position is not + * available with some holes, we try to defrag the blocks without + * touching their paylood. If it is impossible, we fully defrag the + * message. */ - prevblk = htx_get_blk(htx, prev); - headblk = htx_get_blk(htx, head); - if (prevblk->addr >= headblk->addr) { - /* the area was contiguous */ - frtblk = htx_get_blk(htx, htx->front); - tailroom = sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, wrap) - (frtblk->addr + htx_get_blksz(frtblk)); - headroom = headblk->addr; + tail = htx->tail + 1; + if (sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, tail) >= htx->tail_addr) + used = htx->used + 1; + else if (tail > htx->used) { + htx_defrag_blks(htx); + tail = htx->tail + 1; + used = htx->used + 1; + BUG_ON(sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, tail) < htx->tail_addr); + } + else + goto defrag; - if (tailroom >= (int32_t)blksz) { - /* install upfront and update ->front */ - blk = htx_get_blk(htx, tail); - blk->addr = frtblk->addr + htx_get_blksz(frtblk); - htx->front = tail; - } - else if (headroom >= (int32_t)blksz) { - blk = htx_get_blk(htx, tail); - blk->addr = 0; - } - else { - /* need to defragment the table before inserting upfront */ - goto defrag; - } + /* Now, we have found the block's position. Try do find where to put its + * payload. The free space is split in two areas: + * + * * The free space in front of the blocks table. This one is used iff + * the other one was not used yet. + * + * * The free space at the beginning of the message. Once this one is + * used, the other one is never used again, until the next defrag. + */ + headroom = (htx->end_addr - htx->head_addr); + tailroom = (!htx->head_addr + ? sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, tail) - htx->tail_addr + : 0); + BUG_ON((int32_t)headroom < 0); + BUG_ON((int32_t)tailroom < 0); + + if (blksz <= tailroom) { + blk = htx_get_blk(htx, tail); + blk->addr = htx->tail_addr; + htx->tail_addr += blksz; + } + else if (blksz <= headroom) { + blk = htx_get_blk(htx, tail); + blk->addr = htx->head_addr; + htx->head_addr += blksz; } else { - /* it's already wrapped so we can't store anything in the tailroom */ - headroom = headblk->addr - (prevblk->addr + htx_get_blksz(prevblk)); - - if (headroom >= (int32_t)blksz) { - blk = htx_get_blk(htx, tail); - blk->addr = prevblk->addr + htx_get_blksz(prevblk); - } - else { - defrag: - /* need to defragment the table before inserting upfront */ - htx_defrag(htx, NULL); - frtblk = htx_get_blk(htx, htx->front); - tail = htx->tail + 1; - used = htx->used + 1; - blk = htx_get_blk(htx, tail); - blk->addr = frtblk->addr + htx_get_blksz(frtblk); - htx->front = tail; - } + defrag: + /* need to defragment the table before inserting upfront */ + htx_defrag(htx, NULL); + tail = htx->tail + 1; + used = htx->used + 1; + blk = htx_get_blk(htx, tail); + blk->addr = htx->tail_addr; + htx->tail_addr += blksz; } htx->tail = tail; @@ -191,9 +190,109 @@ static struct htx_blk *htx_reserve_nxblk(struct htx *htx, uint32_t blksz) /* Set first position if not already set */ if (htx->first == -1) htx->first = tail; + + BUG_ON((int32_t)htx->tail_addr < 0); + BUG_ON((int32_t)htx->head_addr < 0); + BUG_ON(htx->end_addr > htx->tail_addr); + BUG_ON(htx->head_addr > htx->end_addr); + return blk; } +/* Prepares the block to an expansion of its payload. The payload will be + * expanded by bytes and we need find where this expansion will be + * performed. It can be a compression if is negative. This function only + * updates all addresses. The caller have the responsibility to performe the + * expansion and update the block and the HTX message accordingly. No error msut + * occurre. It returns following values: + * + * 0: The expansion cannot be performed, there is not enough space. + * + * 1: the expansion must be performed in place, there is enought space after + * the block's payload to handle it. This is especially true if it is a + * compression and not an expension. + * + * 2: the block's payload must be moved at the new block address before doing + * the expansion. + * + * 3: the HTX message message must be defragmented + */ +static int htx_prepare_blk_expansion(struct htx *htx, struct htx_blk *blk, int32_t delta) +{ + uint32_t sz, tailroom, headroom; + int ret = 3; + + headroom = (htx->end_addr - htx->head_addr); + tailroom = sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, htx->tail) - htx->tail_addr; + BUG_ON((int32_t)headroom < 0); + BUG_ON((int32_t)tailroom < 0); + + sz = htx_get_blksz(blk); + if (delta <= 0) { + /* It is a compression, it can be performed in place */ + if (blk->addr+sz == htx->tail_addr) + htx->tail_addr += delta; + else if (blk->addr+sz == htx->head_addr) + htx->head_addr += delta; + ret = 1; + } + else if (delta > htx_free_space(htx)) { + /* There is not enought space to handle the expansion */ + ret = 0; + } + else if (blk->addr+sz == htx->tail_addr) { + /* The block's payload is just before the tail room */ + if (delta < tailroom) { + /* Expand the block's payload */ + htx->tail_addr += delta; + ret = 1; + } + else if ((sz + delta) < headroom) { + /* Move the block's payload into the headroom */ + if (blk->addr == htx->end_addr) + htx->end_addr += sz; + blk->addr = htx->head_addr; + htx->tail_addr -= sz; + htx->head_addr += sz + delta; + ret = 2; + } + } + else if (blk->addr+sz == htx->head_addr) { + /* The block's payload is just before the head room */ + if (delta < headroom) { + /* Expand the block's payload */ + htx->head_addr += delta; + ret = 1; + } + } + else { + /* The block's payload is not at the rooms edge */ + if (!htx->head_addr && sz+delta < tailroom) { + /* Move the block's payload into the tailroom */ + if (blk->addr == htx->end_addr) + htx->end_addr += sz; + blk->addr = htx->tail_addr; + htx->tail_addr += sz + delta; + ret = 2; + } + else if (sz+delta < headroom) { + /* Move the block's payload into the headroom */ + if (blk->addr == htx->end_addr) + htx->end_addr += sz; + blk->addr = htx->head_addr; + htx->head_addr += sz + delta; + ret = 2; + } + } + /* Otherwise defrag the HTX message */ + + BUG_ON((int32_t)htx->tail_addr < 0); + BUG_ON((int32_t)htx->head_addr < 0); + BUG_ON(htx->end_addr > htx->tail_addr); + BUG_ON(htx->head_addr > htx->end_addr); + return ret; +} + /* Adds a new block of type in the HTTP message . Its content size * is passed but it is the caller responsibility to do the copy. */ @@ -204,6 +303,7 @@ struct htx_blk *htx_add_blk(struct htx *htx, enum htx_blk_type type, uint32_t bl blk = htx_reserve_nxblk(htx, blksz); if (!blk) return NULL; + BUG_ON(blk->addr > htx->size); blk->info = (type << 28); return blk; @@ -215,15 +315,8 @@ struct htx_blk *htx_add_blk(struct htx *htx, enum htx_blk_type type, uint32_t bl */ struct htx_blk *htx_remove_blk(struct htx *htx, struct htx_blk *blk) { - enum htx_blk_type type = htx_get_blk_type(blk); - uint32_t next, pos, wrap; - - pos = htx_get_blk_pos(htx, blk); - if (type != HTX_BLK_UNUSED) { - /* Mark the block as unused, decrement allocated size */ - htx->data -= htx_get_blksz(blk); - blk->info = ((uint32_t)HTX_BLK_UNUSED << 28); - } + enum htx_blk_type type; + uint32_t pos, addr, sz; /* This is the last block in use */ if (htx->used == 1) { @@ -231,50 +324,62 @@ struct htx_blk *htx_remove_blk(struct htx *htx, struct htx_blk *blk) return NULL; } - /* There is at least 2 blocks, so tail is always >= 0 */ - blk = NULL; - next = pos + 1; /* By default retrun the next block */ - wrap = htx_get_wrap(htx); - if (htx->tail + 1 == wrap) { - /* The HTTP message doesn't wrap */ - if (pos == htx->head) { - /* move the head forward */ - htx->used--; - htx->head++; - } - else if (pos == htx->tail) { - /* remove the tail. this was the last inserted block so - * return NULL. */ - htx->tail--; - htx->used--; - goto end; - } - } - else { - /* The HTTP message wraps */ - if (pos == htx->tail) { - /* remove the tail. try to unwrap the message (pos == 0) - * and return NULL. */ - htx->tail = ((pos == 0) ? wrap-1 : htx->tail-1); - htx->used--; - goto end; - } - else if (pos == htx->head) { - /* move the head forward and try to unwrap the message - * (head+1 == wrap) */ - htx->used--; - htx->head++; - if (htx->head == wrap) - htx->head = next = 0; - } + type = htx_get_blk_type(blk); + pos = htx_get_blk_pos(htx, blk); + sz = htx_get_blksz(blk); + addr = blk->addr; + if (type != HTX_BLK_UNUSED) { + /* Mark the block as unused, decrement allocated size */ + htx->data -= htx_get_blksz(blk); + blk->info = ((uint32_t)HTX_BLK_UNUSED << 28); } - blk = htx_get_blk(htx, next); + /* There is at least 2 blocks, so tail is always >= 0 */ + if (pos == htx->head) { + /* move the head forward */ + htx->used--; + htx->head++; + } + else if (pos == htx->tail) { + /* remove the tail. this was the last inserted block so + * return NULL. */ + htx->tail--; + htx->used--; + blk = NULL; + goto end; + } + blk = htx_get_blk(htx, pos+1); + end: if (pos == htx->first) htx->first = (blk ? htx_get_blk_pos(htx, blk) : -1); - if (pos == htx->front) - htx->front = htx_find_front(htx); + + if (htx->used == 1) { + /* If there is just one block in the HTX message, free space can + * be ajusted. This operation could save some defrags. */ + struct htx_blk *lastblk = htx_get_blk(htx, htx->tail); + + htx->head_addr = 0; + htx->end_addr = lastblk->addr; + htx->tail_addr = lastblk->addr+htx->data; + } + else { + if (addr+sz == htx->tail_addr) + htx->tail_addr = addr; + else if (addr+sz == htx->head_addr) + htx->head_addr = addr; + if (addr == htx->end_addr) + htx->end_addr += sz; + if (htx->tail_addr == htx->end_addr) { + htx->tail_addr = htx->head_addr; + htx->head_addr = htx->end_addr = 0; + } + } + + BUG_ON((int32_t)htx->tail_addr < 0); + BUG_ON((int32_t)htx->head_addr < 0); + BUG_ON(htx->end_addr > htx->tail_addr); + BUG_ON(htx->head_addr > htx->end_addr); return blk; } @@ -296,6 +401,11 @@ void htx_truncate(struct htx *htx, uint32_t offset) if (type == HTX_BLK_DATA) { htx_set_blk_value_len(blk, offset); htx->data -= (sz - offset); + + if (blk->addr+sz == htx->tail_addr) + htx->tail_addr -= offset; + else if (blk->addr+sz == htx->head_addr) + htx->head_addr -= offset; } offset = 0; } @@ -353,11 +463,11 @@ struct htx_ret htx_drain(struct htx *htx, uint32_t count) * returned. Due to its nature this function can be expensive and should be * avoided whenever possible. */ -struct htx_blk *htx_add_data_atonce(struct htx *htx, const struct ist data) +struct htx_blk *htx_add_data_atonce(struct htx *htx, struct ist data) { - struct htx_blk *blk, *tailblk, *headblk, *frtblk; - struct ist v; - int32_t room; + struct htx_blk *blk, *tailblk; + void *ptr; + uint32_t len, sz, tailroom, headroom; if (!htx->used) goto add_new_block; @@ -366,11 +476,11 @@ struct htx_blk *htx_add_data_atonce(struct htx *htx, const struct ist data) if (data.len > htx_free_data_space(htx)) return NULL; - /* get the tail and head block */ + /* get the tail block and its size */ tailblk = htx_get_tail_blk(htx); - headblk = htx_get_head_blk(htx); - if (tailblk == NULL || headblk == NULL) + if (tailblk == NULL) goto add_new_block; + sz = htx_get_blksz(tailblk); /* Don't try to append data if the last inserted block is not of the * same type */ @@ -380,31 +490,45 @@ struct htx_blk *htx_add_data_atonce(struct htx *htx, const struct ist data) /* * Same type and enough space: append data */ - frtblk = htx_get_blk(htx, htx->front); - if (tailblk->addr >= headblk->addr) { - if (htx->tail != htx->front) - goto add_new_block; - room = sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, htx->tail) - (frtblk->addr + htx_get_blksz(frtblk)); - } - else - room = headblk->addr - (tailblk->addr + htx_get_blksz(tailblk)); + headroom = (htx->end_addr - htx->head_addr); + tailroom = sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, htx->tail) - htx->tail_addr; + BUG_ON((int32_t)headroom < 0); + BUG_ON((int32_t)tailroom < 0); - if (room < (int32_t)data.len) - tailblk = htx_defrag(htx, tailblk); + len = data.len; + if (tailblk->addr+sz == htx->tail_addr) { + if (data.len <= tailroom) + goto append_data; + else if (!htx->head_addr) { + len = tailroom; + goto append_data; + } + } + else if (tailblk->addr+sz == htx->head_addr && data.len <= headroom) + goto append_data; + + goto add_new_block; append_data: - /* get the value of the tail block */ /* FIXME: check v.len + data.len < 256MB */ - v = htx_get_blk_value(htx, tailblk); - /* Append data and update the block itself */ - memcpy(v.ptr + v.len, data.ptr, data.len); - htx_set_blk_value_len(tailblk, v.len + data.len); + ptr = htx_get_blk_ptr(htx, tailblk); + memcpy(ptr+sz, data.ptr, len); + htx_set_blk_value_len(tailblk, sz+len); /* Update HTTP message */ - htx->data += data.len; + htx->data += len; + if (tailblk->addr+sz == htx->tail_addr) + htx->tail_addr += len; + else if (tailblk->addr+sz == htx->head_addr) + htx->head_addr += len; - return tailblk; + if (data.len == len) { + blk = tailblk; + goto end; + } + data.ptr += len; + data.len -= len; add_new_block: /* FIXME: check data.len (< 256MB) */ @@ -414,6 +538,12 @@ struct htx_blk *htx_add_data_atonce(struct htx *htx, const struct ist data) blk->info += data.len; memcpy(htx_get_blk_ptr(htx, blk), data.ptr, data.len); + + end: + BUG_ON((int32_t)htx->tail_addr < 0); + BUG_ON((int32_t)htx->head_addr < 0); + BUG_ON(htx->end_addr > htx->tail_addr); + BUG_ON(htx->head_addr > htx->end_addr); return blk; } @@ -425,59 +555,72 @@ struct htx_blk *htx_add_data_atonce(struct htx *htx, const struct ist data) struct htx_blk *htx_replace_blk_value(struct htx *htx, struct htx_blk *blk, const struct ist old, const struct ist new) { - struct buffer *tmp; struct ist n, v; int32_t delta; + int ret; - n = htx_get_blk_name(htx, blk); - v = htx_get_blk_value(htx, blk); - + n = htx_get_blk_name(htx, blk); + v = htx_get_blk_value(htx, blk); delta = new.len - old.len; - - /* easy case, new data are smaller, so replace it in-place */ - if (delta <= 0) { - memcpy(old.ptr, new.ptr, new.len); - if (old.len != v.len) - memmove(old.ptr + new.len, old.ptr + old.len, (v.ptr + v.len) - (old.ptr + old.len)); - htx_set_blk_value_len(blk, v.len + delta); - htx->data += delta; - return blk; - } - - /* we need to allocate more space to store the new header value */ - if (delta > htx_free_space(htx)) + ret = htx_prepare_blk_expansion(htx, blk, delta); + if (!ret) return NULL; /* not enough space */ - /* - * Copy the new header in a temp buffer - */ - tmp = get_trash_chunk(); - - /* 1. copy the header name */ - chunk_memcat(tmp, n.ptr, n.len); - - /* 2. copy value before old part, if any */ - if (old.ptr != v.ptr) - chunk_memcat(tmp, v.ptr, old.ptr - v.ptr); - - /* 3. copy new value */ - chunk_memcat(tmp, new.ptr, new.len); - - /* 4. copy value after old part if any */ - if (old.len != v.len) - chunk_memcat(tmp, old.ptr + old.len, (v.ptr + v.len) - (old.ptr + old.len)); - - - /* Expand the block size. But data are not copied yet. Then defragment - * the HTX message. - */ + /* Before updating the payload, set the new block size and update HTX + * message */ htx_set_blk_value_len(blk, v.len + delta); htx->data += delta; - blk = htx_defrag(htx, blk); - /* Finaly, copy data. */ - memcpy(htx_get_blk_ptr(htx, blk), tmp->area, tmp->data); + if (ret == 1) { + if (delta <= 0) { + /* compression: copy new data first */ + memcpy(old.ptr, new.ptr, new.len); + memmove(old.ptr + new.len, old.ptr + old.len, (v.ptr + v.len) - (old.ptr + old.len)); + } + else { + /* expansion: move the end first */ + memmove(old.ptr + new.len, old.ptr + old.len, (v.ptr + v.len) - (old.ptr + old.len)); + memcpy(old.ptr, new.ptr, new.len); + } + } + else if (ret == 2) { + void *ptr = htx_get_blk_ptr(htx, blk); + /* Copy the name, if any */ + memcpy(ptr, n.ptr, n.len); + ptr += n.len; + + /* Copy value before old part, if any */ + memcpy(ptr, v.ptr, old.ptr - v.ptr); + ptr += old.ptr - v.ptr; + + /* Copy new value */ + memcpy(ptr, new.ptr, new.len); + ptr += new.len; + + /* Copy value after old part, if any */ + memcpy(ptr, old.ptr + old.len, (v.ptr + v.len) - (old.ptr + old.len)); + } + else { + struct buffer *tmp = get_trash_chunk(); + + /* Copy the header name, if any */ + chunk_memcat(tmp, n.ptr, n.len); + + /* Copy value before old part, if any */ + chunk_memcat(tmp, v.ptr, old.ptr - v.ptr); + + /* Copy new value */ + chunk_memcat(tmp, new.ptr, new.len); + + /* Copy value after old part if any */ + chunk_memcat(tmp, old.ptr + old.len, (v.ptr + v.len) - (old.ptr + old.len)); + + blk = htx_defrag(htx, blk); + + /* Finaly, copy data. */ + memcpy(htx_get_blk_ptr(htx, blk), tmp->area, tmp->data); + } return blk; } @@ -574,36 +717,30 @@ struct htx_blk *htx_replace_header(struct htx *htx, struct htx_blk *blk, const struct ist name, const struct ist value) { enum htx_blk_type type; + void *ptr; int32_t delta; + int ret; type = htx_get_blk_type(blk); if (type != HTX_BLK_HDR) return NULL; delta = name.len + value.len - htx_get_blksz(blk); - - /* easy case, new value is smaller, so replace it in-place */ - if (delta <= 0) { - blk->info = (type << 28) + (value.len << 8) + name.len; - htx->data += delta; - goto copy; - } - - /* we need to allocate more space to store the new value */ - if (delta > htx_free_space(htx)) + ret = htx_prepare_blk_expansion(htx, blk, delta); + if (!ret) return NULL; /* not enough space */ - /* Expand the block size. But data are not copied yet. Then defragment - * the HTX message. - */ + /* Set the new block size and update HTX message */ blk->info = (type << 28) + (value.len << 8) + name.len; htx->data += delta; - blk = htx_defrag(htx, blk); - copy: + if (ret == 3) + blk = htx_defrag(htx, blk); + /* Finaly, copy data. */ - ist2bin_lc(htx_get_blk_ptr(htx, blk), name); - memcpy(htx_get_blk_ptr(htx, blk) + name.len, value.ptr, value.len); + ptr = htx_get_blk_ptr(htx, blk); + ist2bin_lc(ptr, name); + memcpy(ptr + name.len, value.ptr, value.len); return blk; } @@ -614,11 +751,12 @@ struct htx_blk *htx_replace_header(struct htx *htx, struct htx_blk *blk, struct htx_sl *htx_replace_stline(struct htx *htx, struct htx_blk *blk, const struct ist p1, const struct ist p2, const struct ist p3) { + enum htx_blk_type type; struct htx_sl *sl; struct htx_sl tmp; /* used to save sl->info and sl->flags */ - enum htx_blk_type type; - uint32_t size; + uint32_t sz; int32_t delta; + int ret; type = htx_get_blk_type(blk); if (type != HTX_BLK_REQ_SL && type != HTX_BLK_RES_SL) @@ -629,28 +767,19 @@ struct htx_sl *htx_replace_stline(struct htx *htx, struct htx_blk *blk, const st tmp.info = sl->info; tmp.flags = sl->flags; - size = sizeof(*sl) + p1.len + p2.len + p3.len; - delta = size - htx_get_blksz(blk); - - /* easy case, new data are smaller, so replace it in-place */ - if (delta <= 0) { - htx_set_blk_value_len(blk, size); - htx->data += delta; - goto copy; - } - - /* we need to allocate more space to store the new value */ - if (delta > htx_free_space(htx)) + sz = htx_get_blksz(blk); + delta = sizeof(*sl) + p1.len + p2.len + p3.len - sz; + ret = htx_prepare_blk_expansion(htx, blk, delta); + if (!ret) return NULL; /* not enough space */ - /* Expand the block size. But data are not copied yet. Then defragment - * the HTX message. - */ - htx_set_blk_value_len(blk, size); + /* Set the new block size and update HTX message */ + htx_set_blk_value_len(blk, sz+delta); htx->data += delta; - blk = htx_defrag(htx, blk); - copy: + if (ret == 3) + blk = htx_defrag(htx, blk); + /* Restore start-line info and flags and copy parts of the start-line */ sl = htx_get_blk_ptr(htx, blk); sl->info = tmp.info; @@ -803,9 +932,9 @@ struct htx_blk *htx_add_endof(struct htx *htx, enum htx_blk_type type) */ size_t htx_add_data(struct htx *htx, const struct ist data) { - struct htx_blk *blk, *tailblk, *headblk, *frtblk; - struct ist v; - int32_t room; + struct htx_blk *blk, *tailblk; + void *ptr; + uint32_t sz, room; int32_t len = data.len; if (!htx->used) @@ -820,9 +949,9 @@ size_t htx_add_data(struct htx *htx, const struct ist data) /* get the tail and head block */ tailblk = htx_get_tail_blk(htx); - headblk = htx_get_head_blk(htx); - if (tailblk == NULL || headblk == NULL) + if (tailblk == NULL) goto add_new_block; + sz = htx_get_blksz(tailblk); /* Don't try to append data if the last inserted block is not of the * same type */ @@ -832,29 +961,38 @@ size_t htx_add_data(struct htx *htx, const struct ist data) /* * Same type and enough space: append data */ - frtblk = htx_get_blk(htx, htx->front); - if (tailblk->addr >= headblk->addr) { - if (htx->tail != htx->front) + if (!htx->head_addr) { + if (tailblk->addr+sz != htx->tail_addr) goto add_new_block; - room = sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, htx->tail) - (frtblk->addr + htx_get_blksz(frtblk)); + room = sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, htx->tail) - htx->tail_addr; } - else - room = headblk->addr - (tailblk->addr + htx_get_blksz(tailblk)); - + else { + if (tailblk->addr+sz != htx->head_addr) + goto add_new_block; + room = (htx->end_addr - htx->head_addr); + } + BUG_ON((int32_t)room < 0); if (room < len) len = room; append_data: - /* get the value of the tail block */ - /* FIXME: check v.len + len < 256MB */ - v = htx_get_blk_value(htx, tailblk); - + /* FIXME: check v.len + data.len < 256MB */ /* Append data and update the block itself */ - memcpy(v.ptr + v.len, data.ptr, len); - htx_set_blk_value_len(tailblk, v.len + len); + ptr = htx_get_blk_ptr(htx, tailblk); + memcpy(ptr + sz, data.ptr, len); + htx_set_blk_value_len(tailblk, sz + len); /* Update HTTP message */ htx->data += len; + if (tailblk->addr+sz == htx->tail_addr) + htx->tail_addr += len; + else if (tailblk->addr+sz == htx->head_addr) + htx->head_addr += len; + + BUG_ON((int32_t)htx->tail_addr < 0); + BUG_ON((int32_t)htx->head_addr < 0); + BUG_ON(htx->end_addr > htx->tail_addr); + BUG_ON(htx->head_addr > htx->end_addr); return len; add_new_block: @@ -884,14 +1022,9 @@ struct htx_blk *htx_add_last_data(struct htx *htx, struct ist data) return NULL; for (pblk = htx_get_prev_blk(htx, blk); pblk; pblk = htx_get_prev_blk(htx, pblk)) { - int32_t cur, prev; - if (htx_get_blk_type(pblk) <= HTX_BLK_DATA) break; - cur = htx_get_blk_pos(htx, blk); - prev = htx_get_blk_pos(htx, pblk); - /* Swap .addr and .info fields */ blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr; blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info; @@ -899,15 +1032,8 @@ struct htx_blk *htx_add_last_data(struct htx *htx, struct ist data) if (blk->addr == pblk->addr) blk->addr += htx_get_blksz(pblk); blk = pblk; - if (htx->front == cur) - htx->front = prev; - else if (htx->front == prev) - htx->front = cur; } - if (htx_get_blk_pos(htx, blk) != htx->front) - htx_defrag(htx, NULL); - return blk; }