Coverage Report

Created: 2026-09-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/haproxy/src/htx.c
Line
Count
Source
1
/*
2
 * internal HTTP message
3
 *
4
 * Copyright 2018 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version
9
 * 2 of the License, or (at your option) any later version.
10
 *
11
 */
12
13
#include <haproxy/chunk.h>
14
#include <haproxy/dynbuf.h>
15
#include <haproxy/global.h>
16
#include <haproxy/htx.h>
17
#include <haproxy/net_helper.h>
18
19
struct htx htx_empty = { .size = 0, .data = 0, .head  = -1, .tail = -1, .first = -1 };
20
21
/* tests show that 63% of these calls are for 64-bit chunks, so better avoid calling
22
 * memcpy() for that!
23
 */
24
static inline __attribute__((always_inline)) void htx_memcpy(void *dst, void *src, size_t len)
25
0
{
26
0
  if (likely(len == 8))
27
0
    write_u64(dst, read_u64(src));
28
0
  else
29
0
    memcpy(dst, src, len);
30
0
}
31
32
/* Retruns 1 if the headers size with addition of <len> exceeds the maximum size
33
 * allowed for headers.
34
 */
35
static inline __attribute__((always_inline)) int htx_hdrs_too_big(const struct htx *htx, int32_t len)
36
0
{
37
0
  return (sizeof(struct htx) + htx->hdrs_data + len > global.tune.bufsize);
38
0
}
39
40
/* Defragments an HTX message. It removes unused blocks and unwraps the payloads
41
 * part. A temporary buffer is used to do so. This function never fails. Most of
42
 * time, we need keep a ref on a specific HTX block. Thus is <blk> is set, the
43
 * pointer on its new position, after defrag, is returned. If <blk> is a DATA
44
 * block, no merge with any previous DATA block is performed. In addition, if
45
 * the size of the block must be altered, <blkinfo> info must be provided (!=
46
 * 0). But in this case, it remains the caller responsibility to update the
47
 * block content.
48
 */
49
struct htx_blk *htx_defrag(struct htx *htx, struct htx_blk *blk, uint32_t blkinfo)
50
0
{
51
0
  struct buffer *chunk = get_trash_chunk_sz(htx->size+sizeof(struct htx));
52
0
  struct htx *tmp = htxbuf(chunk);
53
0
  struct htx_blk *newblk, *oldblk;
54
0
  enum htx_blk_type type;
55
0
  uint32_t new, old, blkpos;
56
0
  uint32_t blksz;
57
58
0
  if (htx->head == -1)
59
0
    return NULL;
60
61
0
  blkpos = -1;
62
63
0
  new  = 0;
64
0
  tmp->size = htx->size;
65
0
  tmp->data = 0;
66
67
  /* start from the head */
68
0
  for (old = htx_get_head(htx); old != -1; old = htx_get_next(htx, old)) {
69
0
    oldblk = htx_get_blk(htx, old);
70
0
    if (htx_get_blk_type(oldblk) == HTX_BLK_UNUSED)
71
0
      continue;
72
73
0
    type = htx_get_blk_type(oldblk);
74
0
    blksz = htx_get_blksz(oldblk);
75
0
    switch (type) {
76
0
      case HTX_BLK_DATA:
77
0
        if (blk != oldblk) {
78
0
          newblk = htx_add_data_atonce(tmp, htx_get_blk_value(htx, oldblk));
79
0
          break;
80
0
        }
81
0
        __fallthrough;
82
0
      default:
83
0
        if (blk == oldblk && blkinfo) {
84
0
          newblk = htx_add_blk(tmp, type, __htx_blkinfo_size(blkinfo));
85
0
          newblk->info = blkinfo;
86
0
        }
87
0
        else {
88
0
          newblk = htx_add_blk(tmp, type, blksz);
89
0
          newblk->info = oldblk->info;
90
0
        }
91
0
        htx_memcpy(htx_get_blk_ptr(tmp, newblk), htx_get_blk_ptr(htx, oldblk), blksz);
92
0
        break;
93
0
    };
94
95
    /* update the start-line position */
96
0
    if (htx->first == old)
97
0
      tmp->first = new;
98
99
    /* if <blk> is defined, save its new position */
100
0
    if (blk == oldblk)
101
0
      blkpos = new;
102
103
0
    new++;
104
0
  }
105
106
0
  htx->data = tmp->data;
107
0
  htx->first = tmp->first;
108
0
  htx->head = tmp->head;
109
0
  htx->tail = tmp->tail;
110
0
  htx->head_addr = tmp->head_addr;
111
0
  htx->end_addr = tmp->end_addr;
112
0
  htx->tail_addr = tmp->tail_addr;
113
0
  htx->flags &= ~(HTX_FL_FRAGMENTED|HTX_FL_UNORDERED);
114
0
  htx_memcpy((void *)htx->blocks, (void *)tmp->blocks, htx->size);
115
116
0
  return ((blkpos == -1) ? NULL : htx_get_blk(htx, blkpos));
117
0
}
118
119
/* Degragments HTX blocks of an HTX message. Payloads part is keep untouched
120
 * here. This function will move back all blocks starting at the position 0,
121
 * removing unused blocks. It must never be called with an empty message.
122
 */
123
static void htx_defrag_blks(struct htx *htx)
124
0
{
125
0
  int32_t pos, new;
126
127
0
  new = 0;
128
0
  for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
129
0
    struct htx_blk *posblk, *newblk;
130
131
0
    if (pos == new) {
132
0
      new++;
133
0
      continue;
134
0
    }
135
136
0
    posblk = htx_get_blk(htx, pos);
137
0
    if (htx_get_blk_type(posblk) == HTX_BLK_UNUSED)
138
0
      continue;
139
140
0
    if (htx->first == pos)
141
0
      htx->first = new;
142
0
    newblk = htx_get_blk(htx, new++);
143
0
    newblk->info = posblk->info;
144
0
    newblk->addr = posblk->addr;
145
0
  }
146
0
  BUG_ON(!new);
147
0
  htx->head = 0;
148
0
  htx->tail = new - 1;
149
0
}
150
151
/* Reserves a new block in the HTX message <htx> with a content of <blksz>
152
 * bytes. If there is not enough space, NULL is returned. Otherwise the reserved
153
 * block is returned and the HTX message is updated. Space for this new block is
154
 * reserved in the HTX message. But it is the caller responsibility to set right
155
 * info in the block to reflect the stored data.
156
 */
157
static struct htx_blk *htx_reserve_nxblk(struct htx *htx, uint32_t blksz)
158
0
{
159
0
  struct htx_blk *blk;
160
0
  uint32_t tail, headroom, tailroom;
161
162
0
  if (blksz > htx_free_data_space(htx))
163
0
    return NULL; /* full */
164
165
0
  if (htx->head == -1) {
166
    /* Empty message */
167
0
    htx->head = htx->tail = htx->first = 0;
168
0
    blk = htx_get_blk(htx, htx->tail);
169
0
    blk->addr = 0;
170
0
    htx->data = blksz;
171
0
    htx->tail_addr = blksz;
172
0
    return blk;
173
0
  }
174
175
  /* Find the block's position. First, we try to get the next position in
176
   * the message, increasing the tail by one. If this position is not
177
   * available with some holes, we try to defrag the blocks without
178
   * touching their payload. If it is impossible, we fully defrag the
179
   * message.
180
   */
181
0
  tail = htx->tail + 1;
182
0
  if (htx_pos_to_addr(htx, tail) >= htx->tail_addr)
183
0
    ;
184
0
  else if (htx->head > 0) {
185
0
    htx_defrag_blks(htx);
186
0
    tail = htx->tail + 1;
187
0
    BUG_ON(htx_pos_to_addr(htx, tail) < htx->tail_addr);
188
0
  }
189
0
  else
190
0
    goto defrag;
191
192
  /* Now, we have found the block's position. Try to find where to put its
193
   * payload. The free space is split in two areas:
194
   *
195
   *   * The free space in front of the blocks table. This one is used if and
196
   *     only if the other one was not used yet.
197
   *
198
   *   * The free space at the beginning of the message. Once this one is
199
   *     used, the other one is never used again, until the next defrag.
200
   */
201
0
  headroom = (htx->end_addr - htx->head_addr);
202
0
  tailroom = (!htx->head_addr ? htx_pos_to_addr(htx, tail) - htx->tail_addr : 0);
203
0
  BUG_ON((int32_t)headroom < 0);
204
0
  BUG_ON((int32_t)tailroom < 0);
205
206
0
  if (blksz <= tailroom) {
207
0
    blk = htx_get_blk(htx, tail);
208
0
    blk->addr = htx->tail_addr;
209
0
    htx->tail_addr += blksz;
210
0
  }
211
0
  else if (blksz <= headroom) {
212
0
    blk = htx_get_blk(htx, tail);
213
0
    blk->addr = htx->head_addr;
214
0
    htx->head_addr += blksz;
215
0
  }
216
0
  else {
217
0
    defrag:
218
    /* need to defragment the message before inserting upfront */
219
0
    htx_defrag(htx, NULL, 0);
220
0
    tail = htx->tail + 1;
221
0
    blk = htx_get_blk(htx, tail);
222
0
    blk->addr = htx->tail_addr;
223
0
    htx->tail_addr += blksz;
224
0
  }
225
226
0
  htx->tail  = tail;
227
0
  htx->data += blksz;
228
  /* Set first position if not already set */
229
0
  if (htx->first == -1)
230
0
    htx->first = tail;
231
232
0
  BUG_ON((int32_t)htx->tail_addr < 0);
233
0
  BUG_ON((int32_t)htx->head_addr < 0);
234
0
  BUG_ON(htx->end_addr > htx->tail_addr);
235
0
  BUG_ON(htx->head_addr > htx->end_addr);
236
237
0
  return blk;
238
0
}
239
240
/* Prepares the block to an expansion of its payload. The payload will be
241
 * expanded by <delta> bytes and we need find where this expansion will be
242
 * performed. It can be a compression if <delta> is negative. This function only
243
 * updates all addresses. The caller have the responsibility to perform the
244
 * expansion and update the block and the HTX message accordingly. No error must
245
 * occur. It returns following values:
246
 *
247
 *  0: The expansion cannot be performed, there is not enough space.
248
 *
249
 *  1: the expansion must be performed in place, there is enough space after
250
 *      the block's payload to handle it. This is especially true if it is a
251
 *      compression and not an expansion.
252
 *
253
 *  2: the block's payload must be moved at the new block address before doing
254
 *     the expansion.
255
 *
256
 *  3: the HTX message message must be defragmented
257
 */
258
static int htx_prepare_blk_expansion(struct htx *htx, struct htx_blk *blk, int32_t delta)
259
0
{
260
0
  uint32_t sz, tailroom, headroom;
261
0
  int ret = 3;
262
263
0
  BUG_ON(htx->head == -1);
264
265
0
  headroom = (htx->end_addr - htx->head_addr);
266
0
  tailroom = (htx_pos_to_addr(htx, htx->tail) - htx->tail_addr);
267
0
  BUG_ON((int32_t)headroom < 0);
268
0
  BUG_ON((int32_t)tailroom < 0);
269
270
0
  sz = htx_get_blksz(blk);
271
0
  if (delta <= 0) {
272
    /* It is a compression, it can be performed in place */
273
0
    if (blk->addr+sz == htx->tail_addr)
274
0
      htx->tail_addr += delta;
275
0
    else if (blk->addr+sz == htx->head_addr)
276
0
      htx->head_addr += delta;
277
0
    ret = 1;
278
0
  }
279
0
  else if (delta > htx_free_space(htx)) {
280
    /* There is not enough space to handle the expansion */
281
0
    ret = 0;
282
0
  }
283
0
  else if (blk->addr+sz == htx->tail_addr) {
284
    /* The block's payload is just before the tail room */
285
0
    if (delta < tailroom) {
286
      /* Expand the block's payload */
287
0
      htx->tail_addr += delta;
288
0
      ret = 1;
289
0
    }
290
0
    else if ((sz + delta) < headroom) {
291
0
      uint32_t oldaddr = blk->addr;
292
293
      /* Move the block's payload into the headroom */
294
0
      blk->addr = htx->head_addr;
295
0
      htx->tail_addr -= sz;
296
0
      htx->head_addr += sz + delta;
297
0
      if (oldaddr == htx->end_addr) {
298
0
        if (htx->end_addr == htx->tail_addr) {
299
0
          htx->tail_addr = htx->head_addr;
300
0
          htx->head_addr = htx->end_addr = 0;
301
0
        }
302
0
        else
303
0
          htx->end_addr += sz;
304
0
      }
305
0
      ret = 2;
306
0
    }
307
0
  }
308
0
  else if (blk->addr+sz == htx->head_addr) {
309
    /* The block's payload is just before the head room */
310
0
    if (delta < headroom) {
311
      /* Expand the block's payload */
312
0
      htx->head_addr += delta;
313
0
      ret = 1;
314
0
    }
315
0
  }
316
0
  else {
317
    /* The block's payload is not at the rooms edge */
318
0
    if (!htx->head_addr && sz+delta < tailroom) {
319
      /* Move the block's payload into the tailroom */
320
0
      if (blk->addr == htx->end_addr)
321
0
        htx->end_addr += sz;
322
0
      blk->addr = htx->tail_addr;
323
0
      htx->tail_addr += sz + delta;
324
0
      ret = 2;
325
0
    }
326
0
    else if (sz+delta < headroom) {
327
      /* Move the block's payload into the headroom */
328
0
      if (blk->addr == htx->end_addr)
329
0
        htx->end_addr += sz;
330
0
      blk->addr = htx->head_addr;
331
0
      htx->head_addr += sz + delta;
332
0
      ret = 2;
333
0
    }
334
0
  }
335
  /* Otherwise defrag the HTX message */
336
337
0
  BUG_ON((int32_t)htx->tail_addr < 0);
338
0
  BUG_ON((int32_t)htx->head_addr < 0);
339
0
  BUG_ON(htx->end_addr > htx->tail_addr);
340
0
  BUG_ON(htx->head_addr > htx->end_addr);
341
0
  return ret;
342
0
}
343
344
/* Adds a new block of type <type> in the HTX message <htx>. Its content size is
345
 * passed but it is the caller responsibility to do the copy.
346
 */
347
struct htx_blk *htx_add_blk(struct htx *htx, enum htx_blk_type type, uint32_t blksz)
348
0
{
349
0
  struct htx_blk *blk;
350
351
0
  BUG_ON(blksz >= 256 << 20);
352
0
  if (unlikely(type < HTX_BLK_EOH && htx_hdrs_too_big(htx, blksz)))
353
0
    return NULL;
354
0
  blk = htx_reserve_nxblk(htx, blksz);
355
0
  if (!blk)
356
0
    return NULL;
357
0
  BUG_ON(blk->addr > htx->size);
358
359
0
  blk->info = (type << 28);
360
0
  if (type < HTX_BLK_EOH)
361
0
    htx->hdrs_data += blksz;
362
0
  return blk;
363
0
}
364
365
/* Removes the block <blk> from the HTX message <htx>. The function returns the
366
 * block following <blk> or NULL if <blk> is the last block or the last inserted
367
 * one.
368
 */
369
struct htx_blk *htx_remove_blk(struct htx *htx, struct htx_blk *blk)
370
0
{
371
0
  enum htx_blk_type type;
372
0
  uint32_t pos, addr, sz;
373
374
0
  BUG_ON(!blk || htx->head == -1);
375
376
  /* This is the last block in use */
377
0
  if (htx->head == htx->tail) {
378
0
    uint32_t flags = (htx->flags & ~HTX_FL_FRAGMENTED); /* Preserve flags except FRAGMENTED */
379
380
0
    htx_reset(htx);
381
0
    htx->flags = flags; /* restore flags */
382
0
    return NULL;
383
0
  }
384
385
0
  type = htx_get_blk_type(blk);
386
0
  pos  = htx_get_blk_pos(htx, blk);
387
0
  sz   = htx_get_blksz(blk);
388
0
  addr = blk->addr;
389
0
  if (type != HTX_BLK_UNUSED) {
390
    /* Mark the block as unused, decrement allocated size */
391
0
    if (type < HTX_BLK_EOH)
392
0
      htx->hdrs_data -= sz;
393
0
    htx->data -= htx_get_blksz(blk);
394
0
    blk->info = ((uint32_t)HTX_BLK_UNUSED << 28);
395
0
  }
396
397
  /* There is at least 2 blocks, so tail is always > 0 */
398
0
  if (pos == htx->head) {
399
    /* move the head forward */
400
0
    htx->head++;
401
0
  }
402
0
  else if (pos == htx->tail) {
403
    /* remove the tail. this was the last inserted block so
404
     * return NULL. */
405
0
    htx->tail--;
406
0
    blk = NULL;
407
0
    goto end;
408
0
  }
409
0
  else
410
0
    htx->flags |= HTX_FL_FRAGMENTED;
411
412
0
  blk = htx_get_blk(htx, pos+1);
413
414
0
  end:
415
0
  if (pos == htx->first)
416
0
    htx->first = (blk ? htx_get_blk_pos(htx, blk) : -1);
417
418
0
  if (htx->head == htx->tail) {
419
    /* If there is just one block in the HTX message, free space can
420
     * be adjusted. This operation could save some defrags. */
421
0
    struct htx_blk *lastblk = htx_get_blk(htx, htx->tail);
422
423
0
    htx->head_addr = 0;
424
0
    htx->end_addr = lastblk->addr;
425
0
    htx->tail_addr = lastblk->addr+htx->data;
426
0
  }
427
0
  else {
428
0
    if (addr+sz == htx->tail_addr)
429
0
      htx->tail_addr = addr;
430
0
    else if (addr+sz == htx->head_addr)
431
0
      htx->head_addr = addr;
432
0
    if (addr == htx->end_addr) {
433
0
      if (htx->tail_addr == htx->end_addr) {
434
0
        htx->tail_addr = htx->head_addr;
435
0
        htx->head_addr = htx->end_addr = 0;
436
0
      }
437
0
      else
438
0
        htx->end_addr += sz;
439
0
    }
440
0
  }
441
442
0
  BUG_ON((int32_t)htx->tail_addr < 0);
443
0
  BUG_ON((int32_t)htx->head_addr < 0);
444
0
  BUG_ON(htx->end_addr > htx->tail_addr);
445
0
  BUG_ON(htx->head_addr > htx->end_addr);
446
0
  return blk;
447
0
}
448
449
/* Looks for the HTX block containing the offset <offset>, starting at the HTX
450
 * message's head. The function returns an htx_ret with the found HTX block and
451
 * the position inside this block where the offset is. If the offset <offset> is
452
 * outside of the HTX message, htx_ret.blk is set to NULL.
453
 */
454
struct htx_ret htx_find_offset(struct htx *htx, uint32_t offset)
455
0
{
456
0
  struct htx_blk *blk;
457
0
  struct htx_ret htxret = { .blk = NULL, .ret = 0 };
458
459
0
  if (offset >= htx->data)
460
0
    return htxret;
461
462
0
  for (blk = htx_get_head_blk(htx); blk && offset; blk = htx_get_next_blk(htx, blk)) {
463
0
    uint32_t sz = htx_get_blksz(blk);
464
465
0
    if (offset < sz)
466
0
      break;
467
0
    offset -= sz;
468
0
  }
469
0
  htxret.blk = blk;
470
0
  htxret.ret = offset;
471
0
  return htxret;
472
0
}
473
474
/* Removes all blocks after the one containing the offset <offset>. This last
475
 * one may be truncated if it is a DATA block.
476
 */
477
void htx_truncate(struct htx *htx, uint32_t offset)
478
0
{
479
0
  struct htx_blk *blk;
480
0
  struct htx_ret htxret = htx_find_offset(htx, offset);
481
482
0
  blk = htxret.blk;
483
0
  if (blk && htxret.ret && htx_get_blk_type(blk) == HTX_BLK_DATA) {
484
0
    htx_change_blk_value_len(htx, blk, htxret.ret);
485
0
    blk = htx_get_next_blk(htx, blk);
486
0
  }
487
0
  while (blk)
488
0
    blk = htx_remove_blk(htx, blk);
489
0
}
490
491
/* Removes all blocks after <blk>, excluding it. if <blk> is NULL, all blocks
492
 * are removed.
493
 */
494
void htx_truncate_blk(struct htx *htx, struct htx_blk *blk)
495
0
{
496
0
  if (!blk) {
497
0
    htx_drain(htx, htx->data);
498
0
    return;
499
0
  }
500
0
  for (blk = htx_get_next_blk(htx, blk); blk; blk = htx_remove_blk(htx, blk));
501
0
}
502
503
/* Drains <count> bytes from the HTX message <htx>. If the last block is a DATA
504
 * block, it will be cut if necessary. Others blocks will be removed at once if
505
 * <count> is large enough. The function returns an htx_ret with the first block
506
 * remaining in the message and the amount of data drained. If everything is
507
 * removed, htx_ret.blk is set to NULL.
508
 */
509
struct htx_ret htx_drain(struct htx *htx, uint32_t count)
510
0
{
511
0
  struct htx_blk *blk;
512
0
  struct htx_ret htxret = { .blk = NULL, .ret = 0 };
513
514
0
  if (count == htx->data) {
515
     /* Preserve flags except FRAGMENTED and UNORDERED */
516
0
    uint32_t flags = (htx->flags & ~(HTX_FL_FRAGMENTED|HTX_FL_UNORDERED));
517
518
0
    htx_reset(htx);
519
0
    htx->flags = flags; /* restore flags */
520
0
    htxret.ret = count;
521
0
    return htxret;
522
0
  }
523
524
0
  blk = htx_get_head_blk(htx);
525
0
  while (count && blk) {
526
0
    uint32_t sz = htx_get_blksz(blk);
527
0
    enum htx_blk_type type = htx_get_blk_type(blk);
528
529
    /* Ignore unused block */
530
0
    if (type == HTX_BLK_UNUSED)
531
0
      goto next;
532
533
0
    if (sz > count) {
534
0
      if (type == HTX_BLK_DATA) {
535
0
        htx_cut_data_blk(htx, blk, count);
536
0
        htxret.ret += count;
537
0
      }
538
0
      break;
539
0
    }
540
0
    count -= sz;
541
0
    htxret.ret += sz;
542
0
    next:
543
0
    blk = htx_remove_blk(htx, blk);
544
0
  }
545
0
  htxret.blk = blk;
546
547
0
  return htxret;
548
0
}
549
550
/* Tries to append data to the last inserted block, if the type matches and if
551
 * there is enough space to take it all. If the space wraps, the buffer is
552
 * defragmented and a new block is inserted. If an error occurred, NULL is
553
 * returned. Otherwise, on success, the updated block (or the new one) is
554
 * returned. Due to its nature this function can be expensive and should be
555
 * avoided whenever possible.
556
 */
557
struct htx_blk *htx_add_data_atonce(struct htx *htx, struct ist data)
558
0
{
559
0
  struct htx_blk *blk, *tailblk;
560
0
  void *ptr;
561
0
  uint32_t sz, tailroom, headroom;
562
0
  uint32_t flags = 0;
563
564
0
  if (htx->head == -1)
565
0
    goto add_new_block;
566
567
  /* Not enough space to store data */
568
0
  if (data.len > htx_free_data_space(htx))
569
0
    return NULL;
570
571
  /* get the tail block and its size */
572
0
  tailblk = htx_get_tail_blk(htx);
573
0
  if (tailblk == NULL)
574
0
    goto add_new_block;
575
0
  sz = htx_get_blksz(tailblk);
576
577
  /* Don't try to append data if the last inserted block is not of the
578
   * same type */
579
0
  if (htx_get_blk_type(tailblk) != HTX_BLK_DATA) {
580
0
    if (htx_get_blk_type(tailblk) > HTX_BLK_DATA)
581
0
      flags |= HTX_FL_UNORDERED;
582
0
    goto add_new_block;
583
0
  }
584
585
  /*
586
   * Same type and enough space: append data
587
   */
588
0
  headroom = (htx->end_addr - htx->head_addr);
589
0
  tailroom = (htx_pos_to_addr(htx, htx->tail) - htx->tail_addr);
590
0
  BUG_ON((int32_t)headroom < 0);
591
0
  BUG_ON((int32_t)tailroom < 0);
592
593
0
  if (tailblk->addr+sz == htx->tail_addr) {
594
0
    if (data.len <= tailroom)
595
0
      goto append_data;
596
0
    else if (!htx->head_addr) {
597
      /* Not enough space in tailroom: Defrag instead of wrapping */
598
0
    }
599
0
  }
600
0
  else if (tailblk->addr+sz == htx->head_addr && data.len <= headroom)
601
0
    goto append_data;
602
603
  /* Unable to append data in the DATA block, defrag the message first and append data */
604
0
  htx_defrag(htx, NULL, 0);
605
0
  tailblk = htx_get_tail_blk(htx);
606
0
  if (tailblk == NULL)
607
0
    goto add_new_block;
608
0
  sz = htx_get_blksz(tailblk);
609
0
  if (sz + data.len >= (256 << 20))
610
0
    goto add_new_block;
611
612
0
  append_data:
613
  /* Append data and update the block itself */
614
0
  ptr = htx_get_blk_ptr(htx, tailblk);
615
0
  htx_memcpy(ptr+sz, data.ptr, data.len);
616
0
  htx_change_blk_value_len(htx, tailblk, sz+data.len);
617
0
  blk = tailblk;
618
0
  goto end;
619
620
0
  add_new_block:
621
0
  blk = htx_add_blk(htx, HTX_BLK_DATA, data.len);
622
0
  if (!blk)
623
0
    return NULL;
624
0
  blk->info += data.len;
625
0
  htx_memcpy(htx_get_blk_ptr(htx, blk), data.ptr, data.len);
626
0
  htx->flags |= flags;
627
628
0
  end:
629
0
  BUG_ON((int32_t)htx->tail_addr < 0);
630
0
  BUG_ON((int32_t)htx->head_addr < 0);
631
0
  BUG_ON(htx->end_addr > htx->tail_addr);
632
0
  BUG_ON(htx->head_addr > htx->end_addr);
633
0
  return blk;
634
0
}
635
636
/* Replaces a value part of a block by a new one. The new part can be smaller or
637
 * larger than the old one. This function works for any kind of block with
638
 * attached data. It returns the new block on success, otherwise it returns
639
 * NULL.
640
 */
641
struct htx_blk *htx_replace_blk_value(struct htx *htx, struct htx_blk *blk,
642
              const struct ist old, const struct ist new)
643
0
{
644
0
  struct ist n, v;
645
0
  int32_t delta;
646
0
  int ret;
647
648
0
  n  = htx_get_blk_name(htx, blk);
649
0
  v  = htx_get_blk_value(htx, blk);
650
0
  delta = new.len - old.len;
651
652
0
  if (unlikely(htx_get_blk_type(blk) < HTX_BLK_EOH && htx_hdrs_too_big(htx, delta)))
653
0
    return NULL;
654
655
0
  ret = htx_prepare_blk_expansion(htx, blk, delta);
656
0
  if (!ret)
657
0
    return NULL; /* not enough space */
658
659
0
  if (ret == 1) { /* Replace in place */
660
0
    if (delta <= 0) {
661
      /* compression: copy new data first then move the end */
662
0
      htx_memcpy(old.ptr, new.ptr, new.len);
663
0
      memmove(old.ptr + new.len, istend(old),
664
0
        istend(v) - istend(old));
665
0
    }
666
0
    else {
667
      /* expansion: move the end first then copy new data */
668
0
      memmove(old.ptr + new.len, istend(old),
669
0
        istend(v) - istend(old));
670
0
      htx_memcpy(old.ptr, new.ptr, new.len);
671
0
    }
672
673
    /* set the new block size and update HTX message */
674
0
    htx_set_blk_value_len(blk, v.len + delta);
675
0
    htx->data += delta;
676
0
  }
677
0
  else if (ret == 2) { /* New address but no defrag */
678
0
    void *ptr = htx_get_blk_ptr(htx, blk);
679
680
    /* Copy the name, if any */
681
0
    htx_memcpy(ptr, n.ptr, n.len);
682
0
    ptr += n.len;
683
684
    /* Copy value before old part, if any */
685
0
    htx_memcpy(ptr, v.ptr, old.ptr - v.ptr);
686
0
    ptr += old.ptr - v.ptr;
687
688
    /* Copy new value */
689
0
    htx_memcpy(ptr, new.ptr, new.len);
690
0
    ptr += new.len;
691
692
    /* Copy value after old part, if any */
693
0
    htx_memcpy(ptr, istend(old), istend(v) - istend(old));
694
695
    /* set the new block size and update HTX message */
696
0
    htx_set_blk_value_len(blk, v.len + delta);
697
0
    htx->data += delta;
698
0
    htx->flags |= HTX_FL_FRAGMENTED;
699
0
  }
700
0
  else { /* Do a defrag first (it is always an expansion) */
701
0
    struct htx_blk tmpblk;
702
0
    struct buffer *chunk = alloc_trash_chunk_sz(n.len + v.len + delta);
703
0
    void *ptr;
704
705
0
    if (!chunk)
706
0
      return NULL;
707
708
0
    ptr = b_orig(chunk);
709
0
    b_set_data(chunk, n.len + v.len + delta);
710
711
    /* Copy the name, if any */
712
0
    htx_memcpy(ptr, n.ptr, n.len);
713
0
    ptr += n.len;
714
715
    /* Copy value before old part, if any */
716
0
    htx_memcpy(ptr, v.ptr, old.ptr - v.ptr);
717
0
    ptr += old.ptr - v.ptr;
718
719
    /* Copy new value */
720
0
    htx_memcpy(ptr, new.ptr, new.len);
721
0
    ptr += new.len;
722
723
    /* Copy value after old part, if any */
724
0
    htx_memcpy(ptr, istend(old), istend(v) - istend(old));
725
726
    /* use tmpblk to set new block size before defrag and to compute
727
     * the offset after defrag
728
     */
729
0
    tmpblk.addr = blk->addr;
730
0
    tmpblk.info = blk->info;
731
0
    htx_set_blk_value_len(&tmpblk, v.len + delta);
732
733
    /* htx_defrag() will take care to update the block size and the htx message */
734
0
    blk = htx_defrag(htx, blk, tmpblk.info);
735
736
    /* finally copy data */
737
0
    htx_memcpy(htx_get_blk_ptr(htx, blk), b_orig(chunk), b_data(chunk));
738
0
    free_trash_chunk(chunk);
739
0
  }
740
741
0
  if (htx_get_blk_type(blk) < HTX_BLK_EOH)
742
0
    htx->hdrs_data += delta;
743
744
0
  return blk;
745
0
}
746
747
/* Transfer HTX blocks from <src> to <dst>, stopping if <count> bytes were
748
 * transferred (including payload and meta-data). It returns the number of bytes
749
 * copied. By default, copied blocks are removed from <src> and only full
750
 * headers and trailers part can be moved. <flags> can be set to change the
751
 * default behavior:
752
 *  - HTX_XFER_KEEP_SRC_BLKS: source blocks are not removed
753
 *  - HTX_XFER_PARTIAL_HDRS_COPY: partial headers and trailers part can be xferred
754
 *  - HTX_XFER_HDRS_ONLY: Only the headers part is xferred
755
 *  - HTX_XFER_NO_METADATA: <count> don't include meta-data, only payload
756
 */
757
size_t htx_xfer(struct htx *dst, struct htx *src, size_t count, unsigned int flags)
758
0
{
759
0
  struct htx_blk *blk, *last_dstblk;
760
0
  size_t ret = 0;
761
0
  size_t meta_sz = (flags & HTX_XFER_NO_METADATA) ? 0 : sizeof(*blk);
762
0
  uint32_t max, last_dstblk_sz;
763
0
  int dst_full = 0;
764
765
766
0
  last_dstblk = NULL;
767
0
  last_dstblk_sz = 0;
768
0
  for (blk = htx_get_head_blk(src); blk && count > meta_sz; blk = htx_get_next_blk(src, blk)) {
769
0
    struct ist v;
770
0
    enum htx_blk_type type;
771
0
    uint32_t sz;
772
773
    /* Ignore unused block */
774
0
    type = htx_get_blk_type(blk);
775
0
    if (type == HTX_BLK_UNUSED)
776
0
      continue;
777
778
0
    if ((flags & HTX_XFER_HDRS_ONLY) &&
779
0
        type != HTX_BLK_REQ_SL && type != HTX_BLK_RES_SL &&
780
0
        type != HTX_BLK_HDR && type != HTX_BLK_EOH)
781
0
      break;
782
783
0
    max = htx_free_data_space(dst);
784
0
    if (max  > count - meta_sz)
785
0
      max = count - meta_sz;
786
787
0
    if (!max)
788
0
      break;
789
790
0
    sz = htx_get_blksz(blk);
791
0
    switch (type) {
792
0
    case HTX_BLK_DATA:
793
0
      v = htx_get_blk_value(src, blk);
794
0
      v = isttrim(v, max);
795
0
      v.len = htx_add_data(dst, v);
796
0
      if (!v.len) {
797
0
        dst_full = 1;
798
0
        goto stop;
799
0
      }
800
0
      last_dstblk = htx_get_tail_blk(dst);
801
0
      last_dstblk_sz = v.len;
802
0
      count -= meta_sz + v.len;
803
0
      ret += meta_sz + v.len;
804
0
      if (v.len != sz) {
805
0
        dst_full = 1;
806
0
        goto stop;
807
0
      }
808
0
      break;
809
810
0
    default:
811
0
      if (sz > max) {
812
0
        dst_full = 1;
813
0
        goto stop;
814
0
      }
815
816
0
      last_dstblk = htx_add_blk(dst, type, sz);
817
0
      if (!last_dstblk) {
818
0
        dst_full = 1;
819
0
        goto stop;
820
0
      }
821
0
      last_dstblk->info = blk->info;
822
0
      htx_memcpy(htx_get_blk_ptr(dst, last_dstblk), htx_get_blk_ptr(src, blk), sz);
823
0
      last_dstblk_sz = sz;
824
0
      count -= meta_sz + sz;
825
0
      ret += meta_sz + sz;
826
0
      break;
827
0
    }
828
829
0
    last_dstblk = NULL; /* Reset last_dstblk because it was fully copied */
830
0
    last_dstblk_sz = 0;
831
0
  }
832
0
  stop:
833
  /* Here, if not NULL, <blk> point on the first not fully copied block in
834
   * <src>. And <last_dstblk>, if defined, is the last not fully copied
835
   * block in <dst>. So have:
836
   *   - <blk> == NULL: everything was copied. <last_dstblk> must be NULL
837
   *   - <blk> != NULL && <last_dstblk> == NULL: partial copy but the last block was fully copied
838
   *   - <blk> != NULL && <last_dstblk> != NULL: partial copy and the last block was partially copied (DATA block only)
839
   */
840
0
  if (!(flags & HTX_XFER_PARTIAL_HDRS_COPY)) {
841
    /* Partial headers/trailers copy is not supported */
842
0
    struct htx_blk *dstblk;
843
0
    enum htx_blk_type type = HTX_BLK_UNUSED;
844
845
0
    dstblk = htx_get_tail_blk(dst);
846
0
    if (dstblk)
847
0
      type = htx_get_blk_type(dstblk);
848
849
    /* the last copied block is a start-line, a header or a trailer */
850
0
    if (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL || type == HTX_BLK_HDR || type == HTX_BLK_TLR) {
851
      /* <src> cannot have partial headers or trailers part */
852
0
      BUG_ON(blk == NULL);
853
854
      /* Remove partial headers/trailers from <dst> and rollback on <src> to not remove them later */
855
0
      while (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL || type == HTX_BLK_HDR || type == HTX_BLK_TLR) {
856
0
        ret -= meta_sz + htx_get_blksz(dstblk);
857
0
        htx_remove_blk(dst, dstblk);
858
0
        dstblk = htx_get_tail_blk(dst);
859
0
        blk = htx_get_prev_blk(src, blk);
860
0
        if (!dstblk)
861
0
          break;
862
0
        type = htx_get_blk_type(dstblk);
863
0
      }
864
865
      /* Report if the xfer was interrupted because <dst> was
866
       * full but is was originally empty
867
       */
868
0
      if (dst_full && htx_is_empty(dst))
869
0
        src->flags |= HTX_FL_PARSING_ERROR;
870
0
    }
871
0
  }
872
873
0
  if (!(flags & HTX_XFER_KEEP_SRC_BLKS)) {
874
    /* True xfer performed, remove copied block from <src> */
875
0
    struct htx_blk *blk2;
876
877
    /* Remove all fully copied blocks */
878
0
    if (!blk)
879
0
      htx_drain(src, src->data);
880
0
    else {
881
0
      for (blk2 = htx_get_head_blk(src); blk2 && blk2 != blk; blk2 = htx_remove_blk(src, blk2));
882
883
      /* If copy was stopped on a DATA block and the last destination
884
       * block is not NULL, it means a partial copy was performed. So
885
       * cut the source block accordingly
886
       */
887
0
      if (last_dstblk && blk2 && htx_get_blk_type(blk2) == HTX_BLK_DATA) {
888
0
        htx_cut_data_blk(src, blk2, last_dstblk_sz);
889
0
      }
890
0
    }
891
0
  }
892
893
  /* Everything was copied, transfer terminal HTX flags too */
894
0
  if (!blk) {
895
0
    dst->flags |= (src->flags & (HTX_FL_EOM|HTX_FL_PARSING_ERROR|HTX_FL_PROCESSING_ERROR));
896
0
    src->flags = 0;
897
0
  }
898
899
0
  return ret;
900
0
}
901
902
/* Transfer HTX blocks from <src> to <dst>, stopping once the first block of the
903
 * type <mark> is transferred (typically EOH or EOT) or when <count> bytes were
904
 * moved (including payload and meta-data). It returns the number of bytes moved
905
 * and the last HTX block inserted in <dst>.
906
 *
907
 * DEPRECATED
908
 */
909
struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
910
           enum htx_blk_type mark)
911
0
{
912
0
  struct htx_blk   *blk, *dstblk;
913
0
  struct htx_blk   *srcref, *dstref;
914
0
  struct ist v;
915
0
  enum htx_blk_type type;
916
0
  uint32_t    max, sz, ret;
917
918
0
  ret = htx_used_space(dst);
919
0
  srcref = dstref = dstblk = NULL;
920
921
  /* blocks are not removed yet from <src> HTX message to be able to
922
   * rollback the transfer if all the headers/trailers are not copied.
923
   */
924
0
  for (blk = htx_get_head_blk(src); blk && count; blk = htx_get_next_blk(src, blk)) {
925
0
    type = htx_get_blk_type(blk);
926
0
    sz = htx_get_blksz(blk);
927
928
    /* Ignore unused block */
929
0
    if (type == HTX_BLK_UNUSED)
930
0
      continue;
931
932
0
    max = htx_get_max_blksz(dst, count);
933
0
    if (!max)
934
0
      break;
935
936
0
    switch (type) {
937
0
      case HTX_BLK_DATA:
938
0
        v = htx_get_blk_value(src, blk);
939
0
        v = isttrim(v, max);
940
0
        v.len = htx_add_data(dst, v);
941
0
        if (!v.len)
942
0
          goto stop;
943
0
        dstblk = htx_get_tail_blk(dst);
944
0
        count -= sizeof(*dstblk) + v.len;
945
0
        if (v.len != sz) {
946
          /* Partial xfer: don't remove <blk> from <src> but
947
           * resize its content */
948
0
          htx_cut_data_blk(src, blk, v.len);
949
0
          goto stop;
950
0
        }
951
0
        break;
952
0
      default:
953
        /* Only DATA blocks can be partially xferred */
954
0
        if (sz > max)
955
0
          goto stop;
956
0
        dstblk = htx_add_blk(dst, type, sz);
957
0
        if (!dstblk)
958
0
          goto stop;
959
0
        dstblk->info = blk->info;
960
0
        htx_memcpy(htx_get_blk_ptr(dst, dstblk), htx_get_blk_ptr(src, blk), sz);
961
0
        count -= sizeof(*dstblk) + sz;
962
0
        break;
963
0
    }
964
965
    /* Save <blk> to <srcref> and <dstblk> to <dstref> when we start
966
     * to xfer headers or trailers. When EOH/EOT block is reached,
967
     * both are reset. It is mandatory to be able to rollback a
968
     * partial transfer.
969
     */
970
0
    if (!srcref && !dstref &&
971
0
        (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL || type == HTX_BLK_TLR)) {
972
0
      srcref = blk;
973
0
      dstref = dstblk;
974
0
    }
975
0
    else if (type == HTX_BLK_EOH || type == HTX_BLK_EOT)
976
0
      srcref = dstref = NULL;
977
978
    /* <mark> allows a copy of the block which matched, then stop */
979
0
    if (type == mark) {
980
0
      blk = htx_get_next_blk(src, blk);
981
0
      break;
982
0
    }
983
0
  }
984
0
  stop:
985
0
  if (unlikely(dstref)) {
986
    /* Headers or trailers part was partially xferred, so rollback
987
     * the copy by removing all block between <dstref> and <dstblk>,
988
     * both included. <dstblk> may be NULL.
989
     */
990
0
    while (dstref && dstref != dstblk)
991
0
      dstref = htx_remove_blk(dst, dstref);
992
0
    if (dstblk)
993
0
      htx_remove_blk(dst, dstblk);
994
995
    /* <dst> HTX message is empty, it means the headers or trailers
996
     * part is too big to be copied at once.
997
     */
998
0
    if (htx_is_empty(dst))
999
0
      src->flags |= HTX_FL_PARSING_ERROR;
1000
0
  }
1001
1002
  /* Now, remove xferred blocks from <src> htx message */
1003
0
  if (!blk && !srcref) {
1004
    /* End of src reached, all blocks were consumed, drain all data */
1005
0
    htx_drain(src, src->data);
1006
0
  }
1007
0
  else {
1008
    /* Remove all block from the head to <blk>, or <srcref> if defined, excluded */
1009
0
    srcref = (srcref ? srcref : blk);
1010
0
    for (blk = htx_get_head_blk(src); blk && blk != srcref; blk = htx_remove_blk(src, blk));
1011
0
  }
1012
1013
0
  if (htx_is_empty(src))
1014
0
    dst->flags |= (src->flags & (HTX_FL_EOM|HTX_FL_PARSING_ERROR|HTX_FL_PROCESSING_ERROR));
1015
1016
0
  ret = htx_used_space(dst) - ret;
1017
0
  return (struct htx_ret){.ret = ret, .blk = dstblk};
1018
0
}
1019
1020
/* Replaces an header by a new one. The new header can be smaller or larger than
1021
 * the old one. It returns the new block on success, otherwise it returns NULL.
1022
 * The header name is always lower cased.
1023
 */
1024
struct htx_blk *htx_replace_header(struct htx *htx, struct htx_blk *blk,
1025
           const struct ist name, const struct ist value)
1026
0
{
1027
0
  enum htx_blk_type type;
1028
0
  void *ptr;
1029
0
  int32_t delta;
1030
0
  int ret;
1031
1032
0
  type = htx_get_blk_type(blk);
1033
0
  if (type != HTX_BLK_HDR)
1034
0
    return NULL;
1035
1036
0
  delta = name.len + value.len - htx_get_blksz(blk);
1037
0
  if (unlikely(htx_hdrs_too_big(htx, delta)))
1038
0
    return NULL;
1039
1040
0
  ret = htx_prepare_blk_expansion(htx, blk, delta);
1041
0
  if (!ret)
1042
0
    return NULL; /* not enough space */
1043
1044
1045
  /* Replace in place or at a new address is the same. We replace all the
1046
   * header (name+value). Only take care to defrag the message if
1047
   * necessary. */
1048
0
  if (ret == 3)
1049
0
    blk = htx_defrag(htx, blk, (type << 28) + (value.len << 8) + name.len);
1050
0
  else {
1051
0
    if (ret == 2)
1052
0
      htx->flags |= HTX_FL_FRAGMENTED;
1053
    /* Set the new block size and update HTX message */
1054
0
    blk->info = (type << 28) + (value.len << 8) + name.len;
1055
0
    htx->data += delta;
1056
0
  }
1057
1058
0
  htx->hdrs_data += delta;
1059
1060
  /* Finally, copy data. */
1061
0
  ptr = htx_get_blk_ptr(htx, blk);
1062
0
  ist2bin_lc(ptr, name);
1063
0
  htx_memcpy(ptr + name.len, value.ptr, value.len);
1064
0
  return blk;
1065
0
}
1066
1067
/* Replaces the parts of the start-line. It returns the new start-line on
1068
 * success, otherwise it returns NULL. It is the caller responsibility to update
1069
 * sl->info, if necessary.
1070
 */
1071
struct htx_sl *htx_replace_stline(struct htx *htx, struct htx_blk *blk, const struct ist p1,
1072
          const struct ist p2, const struct ist p3)
1073
0
{
1074
0
  enum htx_blk_type type;
1075
0
  struct htx_sl *sl;
1076
0
  struct htx_sl tmp; /* used to save sl->info and sl->flags */
1077
0
  uint32_t sz;
1078
0
  int32_t delta;
1079
0
  int ret;
1080
1081
0
  type = htx_get_blk_type(blk);
1082
0
  if (type != HTX_BLK_REQ_SL && type != HTX_BLK_RES_SL)
1083
0
    return NULL;
1084
1085
  /* Save start-line info and flags */
1086
0
  sl = htx_get_blk_ptr(htx, blk);
1087
0
  tmp.info = sl->info;
1088
0
  tmp.flags = sl->flags;
1089
1090
0
  sz = htx_get_blksz(blk);
1091
0
  delta = sizeof(*sl) + p1.len + p2.len + p3.len - sz;
1092
1093
0
  if (unlikely(htx_hdrs_too_big(htx, delta)))
1094
0
    return NULL;
1095
1096
0
  ret = htx_prepare_blk_expansion(htx, blk, delta);
1097
0
  if (!ret)
1098
0
    return NULL; /* not enough space */
1099
1100
  /* Replace in place or at a new address is the same. We replace all the
1101
   * start-line. Only take care to defrag the message if necessary. */
1102
0
  if (ret == 3)  {
1103
0
    blk = htx_defrag(htx, blk, (type << 28) + sz + delta);
1104
0
  }
1105
0
  else {
1106
0
    if (ret == 2)
1107
0
      htx->flags |= HTX_FL_FRAGMENTED;
1108
    /* Set the new block size and update HTX message */
1109
0
    blk->info = (type << 28) + sz + delta;
1110
0
    htx->data += delta;
1111
0
  }
1112
1113
0
  htx->hdrs_data += delta;
1114
1115
  /* Restore start-line info and flags and copy parts of the start-line */
1116
0
  sl = htx_get_blk_ptr(htx, blk);
1117
0
  sl->info = tmp.info;
1118
0
  sl->flags = tmp.flags;
1119
1120
0
  HTX_SL_P1_LEN(sl) = p1.len;
1121
0
  HTX_SL_P2_LEN(sl) = p2.len;
1122
0
  HTX_SL_P3_LEN(sl) = p3.len;
1123
1124
0
  htx_memcpy(HTX_SL_P1_PTR(sl), p1.ptr, p1.len);
1125
0
  htx_memcpy(HTX_SL_P2_PTR(sl), p2.ptr, p2.len);
1126
0
  htx_memcpy(HTX_SL_P3_PTR(sl), p3.ptr, p3.len);
1127
1128
0
  return sl;
1129
0
}
1130
1131
/* Reserves the maximum possible size for an HTX data block, by extending an
1132
 * existing one or by creating a now one. It returns a compound result with the
1133
 * HTX block and the position where new data must be inserted (0 for a new
1134
 * block). If an error occurs or if there is no space left, NULL is returned
1135
 * instead of a pointer on an HTX block.
1136
 */
1137
struct htx_ret htx_reserve_max_data(struct htx *htx)
1138
0
{
1139
0
  struct htx_blk *blk, *tailblk;
1140
0
  uint32_t sz, room;
1141
0
  int32_t len = htx_free_data_space(htx);
1142
0
  uint32_t flags = 0;
1143
1144
0
  if (htx->head == -1)
1145
0
    goto rsv_new_block;
1146
1147
0
  if (!len)
1148
0
    return (struct htx_ret){.ret = 0, .blk = NULL};
1149
1150
  /* get the tail and head block */
1151
0
  tailblk = htx_get_tail_blk(htx);
1152
0
  if (tailblk == NULL)
1153
0
    goto rsv_new_block;
1154
1155
  /* Don't try to append data if the last inserted block is not of the
1156
   * same type */
1157
0
  if (htx_get_blk_type(tailblk) != HTX_BLK_DATA) {
1158
0
    if (htx_get_blk_type(tailblk) > HTX_BLK_DATA)
1159
0
      flags |= HTX_FL_UNORDERED;
1160
0
    goto rsv_new_block;
1161
0
  }
1162
1163
0
  if (htx->flags & HTX_FL_FRAGMENTED) {
1164
0
    htx_defrag(htx, NULL, 0);
1165
0
    tailblk = htx_get_tail_blk(htx);
1166
0
    if (tailblk == NULL)
1167
0
      goto rsv_new_block;
1168
0
  }
1169
0
  sz = htx_get_blksz(tailblk);
1170
1171
  /*
1172
   * Same type and enough space: append data
1173
   */
1174
0
  if (!htx->head_addr) {
1175
0
    if (tailblk->addr+sz != htx->tail_addr)
1176
0
      goto rsv_new_block;
1177
0
    room = (htx_pos_to_addr(htx, htx->tail) - htx->tail_addr);
1178
0
  }
1179
0
  else {
1180
0
    if (tailblk->addr+sz != htx->head_addr)
1181
0
      goto rsv_new_block;
1182
0
    room = (htx->end_addr - htx->head_addr);
1183
0
  }
1184
0
  BUG_ON((int32_t)room < 0);
1185
0
  if (room < len)
1186
0
    len = room;
1187
1188
0
  htx_change_blk_value_len(htx, tailblk, sz+len);
1189
1190
0
  BUG_ON((int32_t)htx->tail_addr < 0);
1191
0
  BUG_ON((int32_t)htx->head_addr < 0);
1192
0
  BUG_ON(htx->end_addr > htx->tail_addr);
1193
0
  BUG_ON(htx->head_addr > htx->end_addr);
1194
0
  return (struct htx_ret){.ret = sz, .blk = tailblk};
1195
1196
0
rsv_new_block:
1197
0
  blk = htx_add_blk(htx, HTX_BLK_DATA, len);
1198
0
  if (!blk)
1199
0
    return (struct htx_ret){.ret = 0, .blk = NULL};
1200
0
  htx->flags |= flags;
1201
0
  blk->info += len;
1202
0
  return (struct htx_ret){.ret = 0, .blk = blk};
1203
0
}
1204
1205
/* Adds an HTX block of type DATA in <htx>. It first tries to append data if
1206
 * possible. It returns the number of bytes consumed from <data>, which may be
1207
 * zero if nothing could be copied.
1208
 */
1209
size_t htx_add_data(struct htx *htx, const struct ist data)
1210
0
{
1211
0
  struct htx_blk *blk, *tailblk;
1212
0
  void *ptr;
1213
0
  uint32_t sz, room;
1214
0
  int32_t len = data.len;
1215
0
  uint32_t flags = 0;
1216
1217
  /* Not enough space to store data */
1218
0
  if (len > htx_free_data_space(htx))
1219
0
    len = htx_free_data_space(htx);
1220
1221
0
  if (!len)
1222
0
    return 0;
1223
1224
0
  if (htx->head == -1)
1225
0
    goto add_new_block;
1226
1227
  /* get the tail and head block */
1228
0
  tailblk = htx_get_tail_blk(htx);
1229
0
  if (tailblk == NULL)
1230
0
    goto add_new_block;
1231
1232
  /* Don't try to append data if the last inserted block is not of the
1233
   * same type */
1234
0
  if (htx_get_blk_type(tailblk) != HTX_BLK_DATA) {
1235
0
    if (htx_get_blk_type(tailblk) > HTX_BLK_DATA)
1236
0
      flags |= HTX_FL_UNORDERED;
1237
0
    goto add_new_block;
1238
0
  }
1239
1240
0
  if (htx->flags & HTX_FL_FRAGMENTED) {
1241
0
    htx_defrag(htx, NULL, 0);
1242
0
    tailblk = htx_get_tail_blk(htx);
1243
0
    if (tailblk == NULL)
1244
0
      goto add_new_block;
1245
0
  }
1246
0
  sz = htx_get_blksz(tailblk);
1247
0
  if (sz + data.len >= (256 << 20))
1248
0
    goto add_new_block;
1249
  /*
1250
   * Same type and enough space: append data
1251
   */
1252
0
  if (!htx->head_addr) {
1253
0
    if (tailblk->addr+sz != htx->tail_addr)
1254
0
      goto add_new_block;
1255
0
    room = (htx_pos_to_addr(htx, htx->tail) - htx->tail_addr);
1256
0
  }
1257
0
  else {
1258
0
    if (tailblk->addr+sz != htx->head_addr)
1259
0
      goto add_new_block;
1260
0
    room = (htx->end_addr - htx->head_addr);
1261
0
  }
1262
0
  BUG_ON((int32_t)room < 0);
1263
0
  if (room < len)
1264
0
    len = room;
1265
1266
0
  append_data:
1267
  /* Append data and update the block itself */
1268
0
  ptr = htx_get_blk_ptr(htx, tailblk);
1269
0
  htx_memcpy(ptr + sz, data.ptr, len);
1270
0
  htx_change_blk_value_len(htx, tailblk, sz+len);
1271
1272
0
  BUG_ON((int32_t)htx->tail_addr < 0);
1273
0
  BUG_ON((int32_t)htx->head_addr < 0);
1274
0
  BUG_ON(htx->end_addr > htx->tail_addr);
1275
0
  BUG_ON(htx->head_addr > htx->end_addr);
1276
0
  return len;
1277
1278
0
  add_new_block:
1279
0
  blk = htx_add_blk(htx, HTX_BLK_DATA, len);
1280
0
  if (!blk)
1281
0
    return 0;
1282
1283
0
  htx->flags |= flags;
1284
0
  blk->info += len;
1285
0
  htx_memcpy(htx_get_blk_ptr(htx, blk), data.ptr, len);
1286
0
  return len;
1287
0
}
1288
1289
1290
/* Adds an HTX block of type DATA in <htx> just after all other DATA
1291
 * blocks. Because it relies on htx_add_data_atonce(), It may be happened to a
1292
 * DATA block if possible. But, if the function succeeds, it will be the last
1293
 * DATA block in all cases. If an error occurred, NULL is returned. Otherwise,
1294
 * on success, the updated block (or the new one) is returned.
1295
 */
1296
struct htx_blk *htx_add_last_data(struct htx *htx, struct ist data)
1297
0
{
1298
0
  struct htx_blk *blk, *pblk;
1299
1300
0
  blk = htx_add_data_atonce(htx, data);
1301
0
  if (!blk)
1302
0
    return NULL;
1303
1304
0
  for (pblk = htx_get_prev_blk(htx, blk); pblk; pblk = htx_get_prev_blk(htx, pblk)) {
1305
0
    if (htx_get_blk_type(pblk) <= HTX_BLK_DATA)
1306
0
      break;
1307
1308
    /* Swap .addr and .info fields */
1309
0
    blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
1310
0
    blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info;
1311
1312
0
    if (blk->addr == pblk->addr)
1313
0
      blk->addr += htx_get_blksz(pblk);
1314
0
    blk = pblk;
1315
0
  }
1316
1317
0
  return blk;
1318
0
}
1319
1320
/* Moves the block <blk> just before the block <ref>. Both blocks must be in the
1321
 * HTX message <htx> and <blk> must be placed after <ref>. pointer to these
1322
 * blocks are updated to remain valid after the move. */
1323
void htx_move_blk_before(struct htx *htx, struct htx_blk **blk, struct htx_blk **ref)
1324
0
{
1325
0
  struct htx_blk *cblk, *pblk;
1326
1327
0
  cblk = *blk;
1328
0
  for (pblk = htx_get_prev_blk(htx, cblk); pblk; pblk = htx_get_prev_blk(htx, pblk)) {
1329
0
    htx->flags |= HTX_FL_UNORDERED;
1330
1331
    /* Swap .addr and .info fields */
1332
0
    cblk->addr ^= pblk->addr; pblk->addr ^= cblk->addr; cblk->addr ^= pblk->addr;
1333
0
    cblk->info ^= pblk->info; pblk->info ^= cblk->info; cblk->info ^= pblk->info;
1334
1335
0
    if (cblk->addr == pblk->addr)
1336
0
      cblk->addr += htx_get_blksz(pblk);
1337
0
    if (pblk == *ref)
1338
0
      break;
1339
0
    cblk = pblk;
1340
0
  }
1341
0
  *blk = cblk;
1342
0
  *ref = pblk;
1343
0
}
1344
1345
/* Append the HTX message <src> to the HTX message <dst>. It returns 1 on
1346
 * success and 0 on error.  All the message or nothing is copied. If an error
1347
 * occurred, all blocks from <src> already appended to <dst> are truncated. On
1348
 * success, the EOM flag is set on <dst> if also set on <src>.
1349
 */
1350
int htx_append_msg(struct htx *dst, const struct htx *src)
1351
0
{
1352
0
  struct htx_blk *blk, *newblk;
1353
0
  enum htx_blk_type type;
1354
0
  uint32_t blksz, offset = dst->data;
1355
1356
0
  for (blk = htx_get_head_blk(src); blk; blk = htx_get_next_blk(src, blk)) {
1357
0
    type = htx_get_blk_type(blk);
1358
1359
0
    if (type == HTX_BLK_UNUSED)
1360
0
      continue;
1361
1362
0
    blksz = htx_get_blksz(blk);
1363
0
    newblk = htx_add_blk(dst, type, blksz);
1364
0
    if (!newblk)
1365
0
      goto error;
1366
0
    newblk->info = blk->info;
1367
0
    htx_memcpy(htx_get_blk_ptr(dst, newblk), htx_get_blk_ptr(src, blk), blksz);
1368
0
  }
1369
0
  dst->flags |= (src->flags & HTX_FL_EOM);
1370
0
  return 1;
1371
1372
0
  error:
1373
0
  htx_truncate(dst, offset);
1374
0
  return 0;
1375
0
}
1376
1377
1378
/* If possible, transfer HTX blocks from <src> to a small buffer. This function
1379
 * allocate the small buffer and makes <dst> point on it. If <dst> is not empty
1380
 * or if <src> contains to many data, NULL is returned. If the allocation
1381
 * failed, NULL is returned. Otherwise <dst> is returned.  <flags> instructs how
1382
 * the transfer must be performed.
1383
 */
1384
struct buffer *__htx_xfer_to_small_buffer(struct buffer *dst, struct buffer *src, unsigned int flags)
1385
0
{
1386
0
  struct htx *dst_htx;
1387
0
  struct htx *src_htx = htxbuf(src);
1388
0
  size_t sz = (sizeof(struct htx) + htx_used_space(src_htx));
1389
1390
0
  if (dst->size || sz > global.tune.bufsize_small  || !b_alloc_small(dst))
1391
0
    return NULL;
1392
0
  dst_htx = htx_from_buf(dst);
1393
0
  htx_xfer(dst_htx, src_htx, src_htx->size, flags);
1394
0
  htx_to_buf(dst_htx, dst);
1395
0
  return dst;
1396
0
}
1397
1398
/* If possible, transfer HTX blocks from <src> to a large buffer. This function
1399
 * allocates the large buffer and makes <dst> point on it. If <dst> is not empty
1400
 * or if <src> contains to many data, NULL is returned. If the allocation
1401
 * failed, NULL is returned. Otherwise <dst> is returned.  <flags> instructs how
1402
 * the transfer must be performed.
1403
 */
1404
struct buffer *__htx_xfer_to_large_buffer(struct buffer *dst, struct buffer *src, unsigned int flags)
1405
0
{
1406
0
  struct htx *dst_htx;
1407
0
  struct htx *src_htx = htxbuf(src);
1408
0
  size_t sz = (sizeof(struct htx) + htx_used_space(src_htx));
1409
1410
0
  if (dst->size || sz > global.tune.bufsize_large || !b_alloc_large(dst))
1411
0
    return NULL;
1412
0
  dst_htx = htx_from_buf(dst);
1413
0
  htx_xfer(dst_htx, src_htx, src_htx->size, flags);
1414
0
  htx_to_buf(dst_htx, dst);
1415
0
  return dst;
1416
0
}
1417
1418
/* Move HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_small_buffer() */
1419
struct buffer *htx_move_to_small_buffer(struct buffer *dst, struct buffer *src)
1420
0
{
1421
0
  return __htx_xfer_to_small_buffer(dst, src, HTX_XFER_DEFAULT);
1422
0
}
1423
1424
/* Move HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_large_buffer() */
1425
struct buffer *htx_move_to_large_buffer(struct buffer *dst, struct buffer *src)
1426
0
{
1427
0
  return __htx_xfer_to_large_buffer(dst, src, HTX_XFER_DEFAULT);
1428
0
}
1429
1430
/* Copy HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_small_buffer() */
1431
struct buffer *htx_copy_to_small_buffer(struct buffer *dst, struct buffer *src)
1432
0
{
1433
0
  return __htx_xfer_to_small_buffer(dst, src, HTX_XFER_KEEP_SRC_BLKS);
1434
0
}
1435
1436
/* Copy HTX blocks from <src> to <dst>. Relies on __htx_xfer_to_large_buffer() */
1437
struct buffer *htx_copy_to_large_buffer(struct buffer *dst, struct buffer *src)
1438
0
{
1439
0
  return __htx_xfer_to_large_buffer(dst, src, HTX_XFER_KEEP_SRC_BLKS);
1440
0
}