MEDIUM: htx: Store the first block position instead of the start-line one

We don't store the start-line position anymore in the HTX message. Instead we
store the first block position to analyze. For now, it is almost the same. But
once all changes will be made on this part, this position will have to be used
by HTX analyzers, and only in the analysis context, to know where the analyse
should start.

When new blocks are added in an HTX message, if the first block position is not
defined, it is set. When the block pointed by it is removed, it is set to the
block following it. -1 remains the value to unset the position. the first block
position is unset when the HTX message is empty. It may also be unset on a
non-empty message, meaning every blocks were already analyzed.

From HTX analyzers point of view, this position is always set during headers
analysis. When they are waiting for a request or a response, if it is unset, it
means the analysis should wait. But once the analysis is started, and as long as
headers are not forwarded, it points to the message start-line.

As mentionned, outside the HTX analysis, no code must rely on the first block
position. So multiplexers and applets must always use the head position to start
a loop on an HTX message.
This commit is contained in:
Christopher Faulet 2019-05-23 11:03:26 +02:00 committed by Willy Tarreau
parent ee1bd4b4f7
commit 29f1758285
8 changed files with 45 additions and 56 deletions

View File

@ -161,7 +161,7 @@ struct htx {
uint64_t extra; /* known bytes amount remaining to receive */ uint64_t extra; /* known bytes amount remaining to receive */
uint32_t flags; /* HTX_FL_* */ uint32_t flags; /* HTX_FL_* */
int32_t sl_pos; /* position of the start-line of the HTTP message. -1 if unset */ int32_t first; /* position of the first block to (re)start the analyse. -1 if unset. */
struct htx_blk blocks[0]; /* Blocks representing the HTTP message itself */ struct htx_blk blocks[0]; /* Blocks representing the HTTP message itself */
}; };
@ -413,8 +413,8 @@ static inline enum htx_blk_type htx_get_tail_type(const struct htx *htx)
return (blk ? htx_get_blk_type(blk) : HTX_BLK_UNUSED); return (blk ? htx_get_blk_type(blk) : HTX_BLK_UNUSED);
} }
/* Returns the position of the first block in the HTX message <htx>. It is the /* Returns the position of the first block in the HTX message <htx>. If unset,
* sl_pos if set, otherwise it is the head. * or if <htx> is empty, -1 is returned.
* *
* An signed 32-bits integer is returned to handle -1 case. Blocks position are * 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 * store on unsigned 32-bits integer, but it is impossible to have so much
@ -422,13 +422,13 @@ static inline enum htx_blk_type htx_get_tail_type(const struct htx *htx)
*/ */
static inline int32_t htx_get_first(const struct htx *htx) static inline int32_t htx_get_first(const struct htx *htx)
{ {
if (htx->sl_pos != -1) if (!htx->used)
return htx->sl_pos; return -1;
return htx->head; return htx->first;
} }
/* Returns the first HTX block in the HTX message <htx>. If <blk> is the head, /* Returns the first HTX block in the HTX message <htx>. If unset or if <htx> is
* NULL returned. * empty, NULL returned.
*/ */
static inline struct htx_blk *htx_get_first_blk(const struct htx *htx) static inline struct htx_blk *htx_get_first_blk(const struct htx *htx)
{ {
@ -717,7 +717,7 @@ 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 = htx->front = 0;
htx->extra = 0; htx->extra = 0;
htx->flags = HTX_FL_NONE; htx->flags = HTX_FL_NONE;
htx->sl_pos = -1; htx->first = -1;
} }
/* returns the available room for raw data in buffer <buf> once HTX overhead is /* returns the available room for raw data in buffer <buf> once HTX overhead is
@ -832,8 +832,8 @@ static inline void htx_dump(struct htx *htx)
fprintf(stderr, "htx:%p [ size=%u - data=%u - used=%u - wrap=%s - extra=%llu]\n", 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->tail >= htx->head) ? "NO" : "YES",
(unsigned long long)htx->extra); (unsigned long long)htx->extra);
fprintf(stderr, "\tsl_pos=%d - head=%u, tail=%u - front=%u\n", fprintf(stderr, "\tfirst=%d - head=%u, tail=%u - front=%u\n",
htx->sl_pos, htx->head, htx->tail, htx->front); htx->first, htx->head, htx->tail, htx->front);
for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) { for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
struct htx_sl *sl; struct htx_sl *sl;

View File

@ -923,7 +923,7 @@ static inline void channel_htx_fwd_headers(struct channel *chn, struct htx *htx)
struct htx_blk *blk = htx_get_blk(htx, pos); struct htx_blk *blk = htx_get_blk(htx, pos);
data += htx_get_blksz(blk); data += htx_get_blksz(blk);
if (htx_get_blk_type(blk) == HTX_BLK_EOH) { if (htx_get_blk_type(blk) == HTX_BLK_EOH) {
htx->sl_pos = htx_get_next(htx, pos); htx->first = htx_get_next(htx, pos);
break; break;
} }
} }

View File

@ -913,10 +913,6 @@ static size_t htx_cache_dump_headers(struct appctx *appctx, struct htx *htx)
if (!blk) if (!blk)
return 0; return 0;
/* Set the start-line offset */
if (type == HTX_BLK_RES_SL)
htx->sl_pos = htx_get_blk_pos(htx, blk);
/* Copy info and data */ /* Copy info and data */
blk->info = info; blk->info = info;
memcpy(htx_get_blk_ptr(htx, blk), b_peek(tmp, offset+4), sz); memcpy(htx_get_blk_ptr(htx, blk), b_peek(tmp, offset+4), sz);

View File

@ -3944,11 +3944,16 @@ static int hlua_applet_http_new(lua_State *L, struct appctx *ctx)
if (IS_HTX_STRM(s)) { if (IS_HTX_STRM(s)) {
/* HTX version */ /* HTX version */
struct htx *htx = htxbuf(&s->req.buf); struct htx *htx = htxbuf(&s->req.buf);
struct htx_sl *sl = http_get_stline(htx); struct htx_blk *blk;
struct htx_sl *sl;
struct ist path; struct ist path;
unsigned long long len = 0; unsigned long long len = 0;
int32_t pos; int32_t pos;
blk = htx_get_first_blk(htx);
BUG_ON(htx_get_blk_type(blk) != HTX_BLK_REQ_SL);
sl = htx_get_blk_ptr(htx, blk);
/* Stores the request method. */ /* Stores the request method. */
lua_pushstring(L, "method"); lua_pushstring(L, "method");
lua_pushlstring(L, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); lua_pushlstring(L, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl));
@ -4366,7 +4371,7 @@ __LJMP static int hlua_applet_htx_recv_yield(lua_State *L, int status, lua_KCont
htx = htx_from_buf(&req->buf); htx = htx_from_buf(&req->buf);
len = MAY_LJMP(luaL_checkinteger(L, 2)); len = MAY_LJMP(luaL_checkinteger(L, 2));
count = co_data(req); count = co_data(req);
blk = htx_get_first_blk(htx); blk = htx_get_head_blk(htx);
while (count && len && blk) { while (count && len && blk) {
enum htx_blk_type type = htx_get_blk_type(blk); enum htx_blk_type type = htx_get_blk_type(blk);
uint32_t sz = htx_get_blksz(blk); uint32_t sz = htx_get_blksz(blk);

View File

@ -203,7 +203,7 @@ struct htx *smp_prefetch_htx(struct sample *smp, struct channel *chn, int vol)
if (msg->msg_state < HTTP_MSG_BODY) { if (msg->msg_state < HTTP_MSG_BODY) {
/* Analyse not yet started */ /* Analyse not yet started */
if (htx_is_empty(htx) || htx->sl_pos == -1) { if (htx_is_empty(htx) || htx->first == -1) {
/* Parsing is done by the mux, just wait */ /* Parsing is done by the mux, just wait */
smp->flags |= SMP_F_MAY_CHANGE; smp->flags |= SMP_F_MAY_CHANGE;
return NULL; return NULL;

View File

@ -11,6 +11,7 @@
*/ */
#include <common/config.h> #include <common/config.h>
#include <common/debug.h>
#include <common/cfgparse.h> #include <common/cfgparse.h>
#include <common/h1.h> #include <common/h1.h>
#include <common/http.h> #include <common/http.h>
@ -21,19 +22,18 @@
struct buffer htx_err_chunks[HTTP_ERR_SIZE]; struct buffer htx_err_chunks[HTTP_ERR_SIZE];
/* Returns the next unporocessed start line in the HTX message. It returns NULL /* Returns the next unporocessed start line in the HTX message. It returns NULL
* is the start-line is undefined (sl_pos == 1). Otherwise, it returns the * if the start-line is undefined (first == -1). Otherwise, it returns the
* pointer on the htx_sl structure. * pointer on the htx_sl structure.
*/ */
struct htx_sl *http_get_stline(struct htx *htx) struct htx_sl *http_get_stline(struct htx *htx)
{ {
struct htx_blk *blk; struct htx_blk *blk;
if (htx->sl_pos == -1) BUG_ON(htx->first == -1);
return NULL; blk = htx_get_first_blk(htx);
blk = htx_get_blk(htx, htx->sl_pos);
if (!blk) if (!blk)
return NULL; return NULL;
BUG_ON(htx_get_blk_type(blk) != HTX_BLK_REQ_SL && htx_get_blk_type(blk) != HTX_BLK_RES_SL);
return htx_get_blk_ptr(htx, blk); return htx_get_blk_ptr(htx, blk);
} }
@ -143,7 +143,7 @@ int http_add_header(struct htx *htx, const struct ist n, const struct ist v)
/* <blk> is the head, swap it iteratively with its predecessor to place /* <blk> is the head, swap it iteratively with its predecessor to place
* it just before the end-of-header block. So blocks remains ordered. */ * it just before the end-of-header block. So blocks remains ordered. */
for (prev = htx_get_prev(htx, htx->tail); prev != htx->sl_pos; prev = htx_get_prev(htx, prev)) { for (prev = htx_get_prev(htx, htx->tail); prev != htx->first; prev = htx_get_prev(htx, prev)) {
struct htx_blk *pblk = htx_get_blk(htx, prev); struct htx_blk *pblk = htx_get_blk(htx, prev);
enum htx_blk_type type = htx_get_blk_type(pblk); enum htx_blk_type type = htx_get_blk_type(pblk);
@ -169,18 +169,14 @@ int http_add_header(struct htx *htx, const struct ist n, const struct ist v)
} }
/* Replaces parts of the start-line of the HTX message <htx>. It returns 1 on /* Replaces parts of the start-line of the HTX message <htx>. It returns 1 on
* success, otherwise it returns 0. The right block is search in the HTX * success, otherwise it returns 0.
* message.
*/ */
int http_replace_stline(struct htx *htx, const struct ist p1, const struct ist p2, const struct ist p3) int http_replace_stline(struct htx *htx, const struct ist p1, const struct ist p2, const struct ist p3)
{ {
struct htx_blk *blk; struct htx_blk *blk;
if (htx->sl_pos == -1) blk = htx_get_first_blk(htx);
return 0; if (!blk || !htx_replace_stline(htx, blk, p1, p2, p3))
blk = htx_get_blk(htx, htx->sl_pos);
if (!htx_replace_stline(htx, blk, p1, p2, p3))
return 0; return 0;
return 1; return 1;
} }

View File

@ -28,7 +28,7 @@ struct htx_blk *htx_defrag(struct htx *htx, struct htx_blk *blk)
struct htx_blk *newblk, *oldblk; struct htx_blk *newblk, *oldblk;
uint32_t new, old, blkpos; uint32_t new, old, blkpos;
uint32_t addr, blksz; uint32_t addr, blksz;
int32_t sl_pos = -1; int32_t first = -1;
if (!htx->used) if (!htx->used)
return NULL; return NULL;
@ -51,8 +51,8 @@ struct htx_blk *htx_defrag(struct htx *htx, struct htx_blk *blk)
blksz = htx_get_blksz(oldblk); blksz = htx_get_blksz(oldblk);
/* update the start-line position */ /* update the start-line position */
if (htx->sl_pos == old) if (htx->first == old)
sl_pos = new; first = new;
/* if <blk> is defined, set its new position */ /* if <blk> is defined, set its new position */
if (blk != NULL && blk == oldblk) if (blk != NULL && blk == oldblk)
@ -65,7 +65,7 @@ struct htx_blk *htx_defrag(struct htx *htx, struct htx_blk *blk)
} }
htx->used = new; htx->used = new;
htx->sl_pos = sl_pos; htx->first = first;
htx->head = 0; htx->head = 0;
htx->front = htx->tail = new - 1; htx->front = htx->tail = new - 1;
memcpy((void *)htx->blocks, (void *)tmp->blocks, htx->size); memcpy((void *)htx->blocks, (void *)tmp->blocks, htx->size);
@ -94,7 +94,7 @@ static struct htx_blk *htx_reserve_nxblk(struct htx *htx, uint32_t blksz)
if (!htx->used) { if (!htx->used) {
/* Empty message */ /* Empty message */
htx->front = htx->head = htx->tail = 0; htx->front = htx->head = htx->tail = htx->first = 0;
htx->used = 1; htx->used = 1;
blk = htx_get_blk(htx, htx->tail); blk = htx_get_blk(htx, htx->tail);
blk->addr = 0; blk->addr = 0;
@ -188,6 +188,9 @@ static struct htx_blk *htx_reserve_nxblk(struct htx *htx, uint32_t blksz)
htx->tail = tail; htx->tail = tail;
htx->used = used; htx->used = used;
htx->data += blksz; htx->data += blksz;
/* Set first position if not already set */
if (htx->first == -1)
htx->first = tail;
return blk; return blk;
} }
@ -220,15 +223,11 @@ struct htx_blk *htx_remove_blk(struct htx *htx, struct htx_blk *blk)
/* Mark the block as unused, decrement allocated size */ /* Mark the block as unused, decrement allocated size */
htx->data -= htx_get_blksz(blk); htx->data -= htx_get_blksz(blk);
blk->info = ((uint32_t)HTX_BLK_UNUSED << 28); blk->info = ((uint32_t)HTX_BLK_UNUSED << 28);
if (htx->sl_pos == pos)
htx->sl_pos = -1;
} }
/* This is the last block in use */ /* This is the last block in use */
if (htx->used == 1/* || !htx->data */) { if (htx->used == 1) {
htx->front = htx->head = htx->tail = 0; htx_reset(htx);
htx->used = 0;
htx->data = 0;
return NULL; return NULL;
} }
@ -271,13 +270,9 @@ struct htx_blk *htx_remove_blk(struct htx *htx, struct htx_blk *blk)
} }
blk = htx_get_blk(htx, next); blk = htx_get_blk(htx, next);
if (htx->sl_pos == -1) {
/* Try to update the start-line payload addr, if possible */
type = htx_get_blk_type(blk);
if (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL)
htx->sl_pos = htx_get_blk_pos(htx, blk);
}
end: end:
if (pos == htx->first)
htx->first = (blk ? htx_get_blk_pos(htx, blk) : -1);
if (pos == htx->front) if (pos == htx->front)
htx->front = htx_find_front(htx); htx->front = htx_find_front(htx);
return blk; return blk;
@ -542,9 +537,6 @@ struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
htx_cut_data_blk(src, blk, sz); htx_cut_data_blk(src, blk, sz);
break; break;
} }
if (dst->sl_pos == -1 && src->sl_pos == htx_get_blk_pos(src, blk))
dst->sl_pos = htx_get_blk_pos(dst, dstblk);
next: next:
blk = htx_remove_blk(src, blk); blk = htx_remove_blk(src, blk);
if (type == mark) if (type == mark)
@ -682,8 +674,6 @@ struct htx_sl *htx_add_stline(struct htx *htx, enum htx_blk_type type, unsigned
blk->info += size; blk->info += size;
sl = htx_get_blk_ptr(htx, blk); sl = htx_get_blk_ptr(htx, blk);
if (htx->sl_pos == -1)
htx->sl_pos = htx_get_blk_pos(htx, blk);
sl->hdrs_bytes = -1; sl->hdrs_bytes = -1;
sl->flags = flags; sl->flags = flags;

View File

@ -132,7 +132,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit)
* a timeout or connection reset is not counted as an error. However * a timeout or connection reset is not counted as an error. However
* a bad request is. * a bad request is.
*/ */
if (unlikely(htx_is_empty(htx) || htx->sl_pos == -1)) { if (unlikely(htx_is_empty(htx) || htx->first == -1)) {
if (htx->flags & HTX_FL_UPGRADE) if (htx->flags & HTX_FL_UPGRADE)
goto failed_keep_alive; goto failed_keep_alive;
@ -279,6 +279,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit)
txn->flags &= ~TX_WAIT_NEXT_RQ; txn->flags &= ~TX_WAIT_NEXT_RQ;
req->analyse_exp = TICK_ETERNITY; req->analyse_exp = TICK_ETERNITY;
BUG_ON(htx_get_first_type(htx) != HTX_BLK_REQ_SL);
sl = http_get_stline(htx); sl = http_get_stline(htx);
/* 0: we might have to print this header in debug mode */ /* 0: we might have to print this header in debug mode */
@ -1470,7 +1471,7 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
* errors somewhere else. * errors somewhere else.
*/ */
next_one: next_one:
if (unlikely(htx_is_empty(htx) || htx->sl_pos == -1)) { if (unlikely(htx_is_empty(htx) || htx->first == -1)) {
/* 1: have we encountered a read error ? */ /* 1: have we encountered a read error ? */
if (rep->flags & CF_READ_ERROR) { if (rep->flags & CF_READ_ERROR) {
struct connection *conn = NULL; struct connection *conn = NULL;
@ -1625,6 +1626,7 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
*/ */
msg->msg_state = HTTP_MSG_BODY; msg->msg_state = HTTP_MSG_BODY;
BUG_ON(htx_get_first_type(htx) != HTX_BLK_RES_SL);
sl = http_get_stline(htx); sl = http_get_stline(htx);
/* 0: we might have to print this header in debug mode */ /* 0: we might have to print this header in debug mode */