blob: 23ac611332944051207c649627ae646321ff9bde [file] [log] [blame]
Christopher Fauleta3d2a162018-10-22 08:59:39 +02001/*
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 <common/chunk.h>
14#include <proto/htx.h>
15
16struct htx htx_empty = { .size = 0, .data = 0, .used = 0 };
17
18/* Defragments an HTTP message, removing unused blocks and unwrapping blocks and
19 * their contents. A temporary message is used to do so. This function never
20 * fails. if <blk> is not NULL, we replace it by the new block address, after
21 * the defragmentation. The new <blk> is returned.
22 */
23/* TODO: merge data blocks into one */
24struct htx_blk *htx_defrag(struct htx *htx, struct htx_blk *blk)
25{
26 struct buffer *chunk = get_trash_chunk();
27 struct htx *tmp = htx_from_buf(chunk);
28 struct htx_blk *newblk, *oldblk;
29 uint32_t new, old;
30 uint32_t addr, blksz;
31
32 if (!htx->used)
33 return NULL;
34
35 new = 0;
36 addr = 0;
37 tmp->size = htx->size;
38
39 /* start from the head */
40 for (old = htx_get_head(htx); old != -1; old = htx_get_next(htx, old)) {
41 oldblk = htx_get_blk(htx, old);
42 if (htx_get_blk_type(oldblk) == HTX_BLK_UNUSED) {
43 htx->used--;
44 continue;
45 }
46
47 newblk = htx_get_blk(tmp, new);
48 newblk->addr = addr;
49 newblk->info = oldblk->info;
50 blksz = htx_get_blksz(oldblk);
51
52 memcpy((void *)tmp->blocks + addr, htx_get_blk_ptr(htx, oldblk), blksz);
53 new++;
54 addr += blksz;
55
56 /* if <blk> is defined, set its new location */
57 if (blk != NULL && blk == oldblk)
58 blk = newblk;
59 } while (new < htx->used);
60
61 htx->wrap = htx->used;
62 htx->front = htx->tail = new - 1;
63 memcpy((void *)htx->blocks, (void *)tmp->blocks, htx->size);
64
65 return blk;
66}
67
68/* Reserves a new block in the HTTP message <htx> with a content of <blksz>
69 * bytes. If there is not enough space, NULL is returned. Otherwise the reserved
70 * block is returned and the HTTP message is updated. Space for this new block
71 * is reserved in the HTTP message. But it is the caller responsibility to set
72 * right info in the block to reflect the stored data.
73 */
74static struct htx_blk *htx_reserve_nxblk(struct htx *htx, uint32_t blksz)
75{
76 struct htx_blk *blk, *prevblk, *headblk, *frtblk;
77 uint32_t used;
78 uint32_t tail;
79 uint32_t prev;
80 uint32_t wrap;
81 uint32_t head;
82 int32_t headroom, tailroom;
83
84 if (blksz > htx_free_data_space(htx))
85 return NULL; /* full */
86
87 if (!htx->used) {
88 /* Empty message */
89 htx->front = htx->tail = 0;
90 htx->wrap = htx->used = 1;
91 blk = htx_get_blk(htx, htx->tail);
92 blk->addr = 0;
93 htx->data = blksz;
94 return blk;
95 }
96
97 used = htx->used + 1;
98 tail = htx->tail + 1;
99 prev = htx->tail;
100 wrap = htx->wrap;
101 head = htx_get_head(htx);
102
103 if (tail == wrap) {
104 frtblk = htx_get_blk(htx, htx->front);
105
106 /* Blocks don't wrap for now. We either need to push the new one
107 * on top of others or to defragement the table. */
108 if (sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, wrap+1) >= frtblk->addr + htx_get_blksz(frtblk))
109 wrap++;
110 else if (tail >= used) /* There is hole at the beginning */
111 tail = 0;
112 else {
113 /* No more room, tail hits data. We have to realign the
114 * whole message. */
115 goto defrag;
116 }
117 }
118 else if (used >= wrap) {
119 /* We have hit the tail, we need to reorganize the blocks. */
120 goto defrag;
121 }
122
123 /* Now we have updated tail, used and wrap, we know that there is some
124 * available room at least from the protocol's perspective. This space
125 * is split in two areas :
126 *
127 * 1: the space between the beginning of the blocks table and the
128 * front data's address. This space will only be used if data don't
129 * wrap yet.
130
131 * 2: If the previous tail was the front block, the space between the
132 * beginning of the message and the head data's address.
133 * Otherwise, the space between the tail data's address and the
134 * tail's one.
135 */
136 prevblk = htx_get_blk(htx, prev);
137 headblk = htx_get_blk(htx, head);
138 if (prevblk->addr >= headblk->addr) {
139 /* the area was contiguous */
140 frtblk = htx_get_blk(htx, htx->front);
141 tailroom = sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, wrap) - (frtblk->addr + htx_get_blksz(frtblk));
142 headroom = headblk->addr;
143
144 if (tailroom >= (int32_t)blksz) {
145 /* install upfront and update ->front */
146 blk = htx_get_blk(htx, tail);
147 blk->addr = frtblk->addr + htx_get_blksz(frtblk);
148 htx->front = tail;
149 }
150 else if (headroom >= (int32_t)blksz) {
151 blk = htx_get_blk(htx, tail);
152 blk->addr = 0;
153 }
154 else {
155 /* need to defragment the table before inserting upfront */
156 goto defrag;
157 }
158 }
159 else {
160 /* it's already wrapped so we can't store anything in the tailroom */
161 headroom = headblk->addr - (prevblk->addr + htx_get_blksz(prevblk));
162
163 if (headroom >= (int32_t)blksz) {
164 blk = htx_get_blk(htx, tail);
165 blk->addr = prevblk->addr + htx_get_blksz(prevblk);
166 }
167 else {
168 defrag:
169 /* need to defragment the table before inserting upfront */
170 htx_defrag(htx, NULL);
171 frtblk = htx_get_blk(htx, htx->front);
172 wrap = htx->wrap + 1;
173 tail = htx->tail + 1;
174 used = htx->used + 1;
175 blk = htx_get_blk(htx, tail);
176 blk->addr = frtblk->addr + htx_get_blksz(frtblk);
177 htx->front = tail;
178 }
179 }
180
181 htx->wrap = wrap;
182 htx->tail = tail;
183 htx->used = used;
184 htx->data += blksz;
185 return blk;
186}
187
188/* Adds a new block of type <type> in the HTTP message <htx>. Its content size
189 * is passed but it is the caller responsibility to do the copy.
190 */
191struct htx_blk *htx_add_blk(struct htx *htx, enum htx_blk_type type, uint32_t blksz)
192{
193 struct htx_blk *blk;
194
195 blk = htx_reserve_nxblk(htx, blksz);
196 if (!blk)
197 return NULL;
198
199 blk->info = (type << 28);
200 return blk;
201}
202
203/* Removes the block <blk> from the HTTP message <htx>. The function returns the
204 * block following <blk> or NULL if <blk> is the last block or the last
205 * inserted one.
206 */
207struct htx_blk *htx_remove_blk(struct htx *htx, struct htx_blk *blk)
208{
209 uint32_t next, head, pos;
210
211 if (htx_get_blk_type(blk) != HTX_BLK_UNUSED) {
212 /* Mark the block as unused, decrement allocated size */
213 htx->data -= htx_get_blksz(blk);
214 blk->info = ((uint32_t)HTX_BLK_UNUSED << 28);
215 }
216
217 /* This is the last block in use */
218 if (htx->used == 1/* || !htx->data */) {
219 htx->front = htx->tail = 0;
220 htx->wrap = htx->used = 0;
221 htx->data = 0;
222 return NULL;
223 }
224
225 /* There is at least 2 blocks, so tail is always >= 0 */
226 pos = htx_get_blk_pos(htx, blk);
227 head = htx_get_head(htx);
228 blk = NULL;
229 next = pos + 1; /* By default retrun the next block */
230 if (htx->tail + 1 == htx->wrap) {
231 /* The HTTP message doesn't wrap */
232 if (pos == head) {
233 /* remove the head, so just return the new head */
234 htx->used--;
235 next = htx_get_head(htx);
236 }
237 else if (pos == htx->tail) {
238 /* remove the tail. this was the last inserted block so
239 * return NULL. */
240 htx->wrap--;
241 htx->tail--;
242 htx->used--;
243 goto end;
244 }
245 }
246 else {
247 /* The HTTP message wraps */
248 if (pos == htx->tail) {
249 /* remove the tail. try to unwrap the message (pos == 0)
250 * and return NULL. */
251 htx->tail = ((pos == 0) ? htx->wrap-1 : htx->tail-1);
252 htx->used--;
253 goto end;
254 }
255 else if (pos == head) {
256 /* remove the head, try to unwrap the message (pos+1 ==
257 * wrap) and return the new head */
258 htx->used--;
259 if (pos + 1 == htx->wrap)
260 htx->wrap = htx->tail + 1;
261 next = htx_get_head(htx);
262 }
263 }
264
265 blk = htx_get_blk(htx, next);
266 end:
267 if (pos == htx->front)
268 htx->front = htx_find_front(htx);
269 return blk;
270}
271
272/* Tries to append data to the last inserted block, if the type matches and if
273 * there is enough non-wrapping space. Only DATA and TRAILERS content can be
274 * appended. If the append fails, a new block is inserted. If an error occurred,
275 * NULL is returned. Otherwise, on success, the updated block (or the new one)
276 * is returned.
277*/
278static struct htx_blk *htx_append_blk_value(struct htx *htx, enum htx_blk_type type,
279 const struct ist data)
280{
281 struct htx_blk *blk;
282 struct ist v;
283
284 if (!htx->used)
285 goto add_new_block;
286
287 /* Not enough space to store data */
288 if (data.len > htx_free_data_space(htx))
289 return NULL;
290
291 /* Append only DATA et TRAILERS data */
292 if (type != HTX_BLK_DATA && type != HTX_BLK_TLR)
293 goto add_new_block;
294
295 /* get the tail block */
296 blk = htx_get_blk(htx, htx->tail);
297
298 /* Don't try to append data if the last inserted block is not of the
299 * same type */
300 if (type != htx_get_blk_type(blk))
301 goto add_new_block;
302
303 /*
304 * Same type and enough space: append data
305 */
306 if (htx->tail + 1 == htx->wrap) {
307 struct htx_blk *frtblk = htx_get_blk(htx, htx->front);
308 int32_t tailroom = sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, htx->tail) - (frtblk->addr + htx_get_blksz(frtblk));
309 if (tailroom >= (int32_t)data.len)
310 goto append_data;
311 htx_defrag(htx, NULL);
312 blk = htx_get_blk(htx, htx->tail);
313 }
314 else {
315 struct htx_blk *headblk = htx_get_blk(htx, htx_get_head(htx));
316 int32_t headroom = headblk->addr - (blk->addr + htx_get_blksz(blk));
317 if (headroom >= (int32_t)data.len)
318 goto append_data;
319 htx_defrag(htx, NULL);
320 blk = htx_get_blk(htx, htx->tail);
321 }
322
323 append_data:
324 /* get the value of the tail block */
325 /* FIXME: check v.len + data.len < 256MB */
326 v = htx_get_blk_value(htx, blk);
327
328 /* Append data and update the block itself */
329 memcpy(v.ptr + v.len, data.ptr, data.len);
330 htx_set_blk_value_len(blk, v.len + data.len);
331
332 /* Update HTTP message */
333 htx->data += data.len;
334
335 return blk;
336
337 add_new_block:
338 /* FIXME: check tlr.len (< 256MB) */
339 blk = htx_add_blk(htx, type, data.len);
340 if (!blk)
341 return NULL;
342
343 blk->info += data.len;
344 memcpy(htx_get_blk_ptr(htx, blk), data.ptr, data.len);
345 return blk;
346}
347
348/* Replaces a value part of a block by a new one. The new part can be smaller or
349 * larger than the old one. This function works for any kind of block with
350 * attached data. It returns the new block on success, otherwise it returns
351 * NULL.
352 */
353struct htx_blk *htx_replace_blk_value(struct htx *htx, struct htx_blk *blk,
354 const struct ist old, const struct ist new)
355{
356 struct htx_blk *frtblk;
357 struct buffer *tmp;
358 struct ist n, v;
359 uint32_t info, room;
360
361 n = htx_get_blk_name(htx, blk);
362 v = htx_get_blk_value(htx, blk);
363
364 /* easy case, new data are smaller, so replace it in-place */
365 if (new.len <= old.len) {
366 memcpy(old.ptr, new.ptr, new.len);
367 if (old.len != v.len)
368 memmove(old.ptr + new.len, old.ptr + old.len, (v.ptr + v.len) - (old.ptr + old.len));
369 htx_set_blk_value_len(blk, v.len - old.len + new.len);
370 htx->data -= (old.len - new.len);
371 return blk;
372 }
373
374 /* we need to allocate more space to store the new header value */
375 if ((new.len - old.len) > htx_free_space(htx))
376 return NULL; /* not enough space */
377
378 /*
379 * Copy the new header in a temp buffer
380 */
381 tmp = get_trash_chunk();
382
383 /* 1. copy the header name */
384 chunk_memcat(tmp, n.ptr, n.len);
385
386 /* 2. copy value before old part, if any */
387 if (old.ptr != v.ptr)
388 chunk_memcat(tmp, v.ptr, old.ptr - v.ptr);
389
390 /* 3. copy new value */
391 chunk_memcat(tmp, new.ptr, new.len);
392
393 /* 4. copy value after old part if any */
394 if (old.len != v.len)
395 chunk_memcat(tmp, old.ptr + old.len, (v.ptr + v.len) - (old.ptr + old.len));
396
397 /*
398 * temporarely remove space reserved for the header
399 */
400 info = blk->info;
401 blk->info &= 0xf0000000;
402 htx->data -= (n.len + v.len);
403
404 /*
405 * Try to find right addr to copy all the data
406 */
407 if (htx->tail + 1 == htx->wrap) {
408 frtblk = htx_get_blk(htx, htx->front);
409 room = sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, htx->tail) - (frtblk->addr + htx_get_blksz(frtblk));
410 if (room >= htx->data) {
411 blk->addr = frtblk->addr + htx_get_blksz(frtblk);
412 goto replace_value;
413 }
414 }
415
416 /* HTX message need to be defragmented first */
417 blk = htx_defrag(htx, blk);
418 frtblk = htx_get_blk(htx, htx->front);
419 blk->addr = frtblk->addr + htx_get_blksz(frtblk);
420
421 replace_value:
422 blk->info = info;
423 htx_set_blk_value_len(blk, v.len - old.len + new.len);
424 memcpy(htx_get_blk_ptr(htx, blk), tmp->area, tmp->data);
425 htx->data += tmp->data;
426 htx->front = htx_get_blk_pos(htx, blk);
427
428 return blk;
429}
430
431/* Transfer HTX blocks from <src> to <dst>, stopping on the first block of the
432 * type <mark> (typically EOH, EOD or EOM) or when <count> bytes of data were
433 * moved. It returns the number of bytes of data moved and the last HTX block
434 * inserted in <dst>.
435 */
436struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
437 enum htx_blk_type mark)
438{
439 struct htx_blk *blk, *dstblk;
440 enum htx_blk_type type;
441 uint32_t info, max, sz, ret;
442
443 ret = 0;
444 blk = htx_get_blk(src, htx_get_head(src));
445 dstblk = NULL;
446 while (blk && ret <= count) {
447 type = htx_get_blk_type(blk);
448
449 /* Ingore unused block */
450 if (type == HTX_BLK_UNUSED)
451 goto next;
452
453 sz = htx_get_blksz(blk);
454 if (!sz) {
455 dstblk = htx_reserve_nxblk(dst, 0);
456 if (!dstblk)
457 break;
458 dstblk->info = blk->info;
459 goto next;
460 }
461
462 info = blk->info;
463 max = htx_free_data_space(dst);
464 if (max > count)
465 max = count;
466 if (sz > max) {
467 sz = max;
468 info = (type << 28) + sz;
469 /* Headers and pseudo headers must be fully copied */
470 if (type < HTX_BLK_DATA || !sz)
471 break;
472 }
473
474 dstblk = htx_reserve_nxblk(dst, sz);
475 if (!dstblk)
476 break;
477 dstblk->info = info;
478 memcpy(htx_get_blk_ptr(dst, dstblk), htx_get_blk_ptr(src, blk), sz);
479
480 ret += sz;
481 if (blk->info != info) {
482 /* Partial move: don't remove <blk> from <src> but
483 * resize its content */
484 blk->addr += sz;
485 htx_set_blk_value_len(blk, htx_get_blksz(blk) - sz);
486 src->data -= sz;
487 break;
488 }
489
490 next:
491 blk = htx_remove_blk(src, blk);
492 if (type == mark)
493 break;
494
495 }
496
497 return (struct htx_ret){.ret = ret, .blk = dstblk};
498}
499
500static struct htx_blk *htx_new_blk_value(struct htx *htx, struct htx_blk *blk,
501 uint32_t newsz)
502{
503 struct htx_blk *frtblk;
504 uint32_t sz, room;
505 int32_t delta;
506
507 sz = htx_get_blksz(blk);
508 delta = newsz - sz;
509
510 /* easy case, new value is smaller, so replace it in-place */
511 if (delta <= 0) {
512 /* Reset value size. It is the caller responsibility to set the new one */
513 blk->info &= 0xf0000000;
514 htx->data += delta;
515 return blk;
516 }
517
518 /* we need to allocate more space to store the new value */
519 if (delta > htx_free_space(htx))
520 return NULL; /* not enough space */
521
522 /*
523 * temporarely remove space reserved for the old value
524 */
525 /* Reset value size. It is the caller responsibility to set the new one */
526 blk->info &= 0xf0000000;
527 htx->data -= sz;
528
529 /*
530 * Try to find right addr to copy all the data
531 */
532 if (htx->tail + 1 == htx->wrap) {
533 frtblk = htx_get_blk(htx, htx->front);
534 room = sizeof(htx->blocks[0]) * htx_pos_to_idx(htx, htx->tail) - (frtblk->addr + htx_get_blksz(frtblk));
535 if (room >= newsz)
536 goto replace_value;
537 }
538
539 /* HTX message need to be defragmented first */
540 blk = htx_defrag(htx, blk);
541 frtblk = htx_get_blk(htx, htx->front);
542
543 replace_value:
544 blk->addr = frtblk->addr + htx_get_blksz(frtblk);
545 htx->data += newsz;
546 htx->front = htx_get_blk_pos(htx, blk);
547
548 return blk;
549}
550
551/* Replaces an header by a new one. The new header can be smaller or larger than
552 * the old one. It returns the new block on success, otherwise it returns NULL.
553 */
554struct htx_blk *htx_replace_header(struct htx *htx, struct htx_blk *blk,
555 const struct ist name, const struct ist value)
556{
557 enum htx_blk_type type;
558
559 type = htx_get_blk_type(blk);
560 if (type != HTX_BLK_HDR)
561 return NULL;
562
563 blk = htx_new_blk_value(htx, blk, (name.len + value.len));
564 if (!blk)
565 return NULL;
566
567 blk->info = (type << 28) + (value.len << 8) + name.len;
568 memcpy(htx_get_blk_ptr(htx, blk), name.ptr, name.len);
569 memcpy(htx_get_blk_ptr(htx, blk) + name.len, value.ptr, value.len);
570
571 return blk;
572}
573
574static void htx_set_blk_reqline(struct htx *htx, struct htx_blk *blk, const union h1_sl sl)
575{
576 union htx_sl *htx_sl;
577
578 htx_sl = htx_get_blk_ptr(htx, blk);
579 htx_sl->rq.meth = sl.rq.meth;
580
581 htx_sl->rq.m_len = sl.rq.m.len;
582 htx_sl->rq.u_len = sl.rq.u.len;
583 htx_sl->rq.v_len = sl.rq.v.len;
584
585 memcpy(htx_sl->rq.l, sl.rq.m.ptr, sl.rq.m.len);
586 memcpy(htx_sl->rq.l + sl.rq.m.len, sl.rq.u.ptr, sl.rq.u.len);
587 memcpy(htx_sl->rq.l + sl.rq.m.len + sl.rq.u.len, sl.rq.v.ptr, sl.rq.v.len);
588}
589
590
591static void htx_set_blk_resline(struct htx *htx, struct htx_blk *blk, const union h1_sl sl)
592{
593 union htx_sl *htx_sl;
594
595 htx_sl = htx_get_blk_ptr(htx, blk);
596 htx_sl->st.status = sl.st.status;
597
598 htx_sl->st.v_len = sl.st.v.len;
599 htx_sl->st.c_len = sl.st.c.len;
600 htx_sl->st.r_len = sl.st.r.len;
601
602 memcpy(htx_sl->st.l, sl.st.v.ptr, sl.st.v.len);
603 memcpy(htx_sl->st.l + sl.st.v.len, sl.st.c.ptr, sl.st.c.len);
604 memcpy(htx_sl->st.l + sl.st.v.len + sl.st.c.len, sl.st.r.ptr, sl.st.r.len);
605}
606
607/* Replaces the request start line a new one. It returns the new block on
608 * success, otherwise it returns NULL.
609 */
610struct htx_blk *htx_replace_reqline(struct htx *htx, struct htx_blk *blk,
611 const union h1_sl sl)
612{
613 enum htx_blk_type type;
614 uint32_t size;
615
616 type = htx_get_blk_type(blk);
617 if (type != HTX_BLK_REQ_SL)
618 return NULL;
619
620 size = sizeof(sl) + sl.rq.m.len + sl.rq.u.len + sl.rq.v.len;
621 blk = htx_new_blk_value(htx, blk, size);
622 if (!blk)
623 return NULL;
624
625 blk->info = (type << 28) + size;
626 htx_set_blk_reqline(htx, blk, sl);
627 return blk;
628}
629
630/* Replaces the response start line a new one. It returns the new block on
631 * success, otherwise it returns NULL.
632 */
633struct htx_blk *htx_replace_resline(struct htx *htx, struct htx_blk *blk,
634 const union h1_sl sl)
635{
636 enum htx_blk_type type;
637 uint32_t size;
638
639 type = htx_get_blk_type(blk);
640 if (type != HTX_BLK_RES_SL)
641 return NULL;
642
643 size = sizeof(sl) + sl.rq.m.len + sl.rq.u.len + sl.rq.v.len;
644 blk = htx_new_blk_value(htx, blk, size);
645 if (!blk)
646 return NULL;
647
648 blk->info = (type << 28) + size;
649 htx_set_blk_resline(htx, blk, sl);
650 return blk;
651}
652
653
654/* Adds an HTX block of type SL in <htx>. It returns the new block on
655 * success. Otherwise, it returns NULL.
656 */
657struct htx_blk *htx_add_reqline(struct htx *htx, const union h1_sl sl)
658{
659 struct htx_blk *blk;
660 uint32_t size;
661
662 size = sizeof(sl) + sl.rq.m.len + sl.rq.u.len + sl.rq.v.len;
663
664 /* FIXME: check size (< 256MB) */
665 blk = htx_add_blk(htx, HTX_BLK_REQ_SL, size);
666 if (!blk)
667 return NULL;
668
669 blk->info += size;
670 htx_set_blk_reqline(htx, blk, sl);
671 return blk;
672}
673
674/* Adds an HTX block of type SL in <htx>. It returns the new block on
675 * success. Otherwise, it returns NULL.
676 */
677struct htx_blk *htx_add_resline(struct htx *htx, const union h1_sl sl)
678{
679 struct htx_blk *blk;
680 uint32_t size;
681
682 size = sizeof(sl) + sl.st.v.len + sl.st.c.len + sl.st.r.len;
683
684 /* FIXME: check size (< 256MB) */
685 blk = htx_add_blk(htx, HTX_BLK_RES_SL, size);
686 if (!blk)
687 return NULL;
688
689 blk->info += size;
690 htx_set_blk_resline(htx, blk, sl);
691 return blk;
692}
693
694/* Adds an HTX block of type HDR in <htx>. It returns the new block on
695 * success. Otherwise, it returns NULL.
696 */
697struct htx_blk *htx_add_header(struct htx *htx, const struct ist name,
698 const struct ist value)
699{
700 struct htx_blk *blk;
701
702 /* FIXME: check name.len (< 256B) and value.len (< 1MB) */
703 blk = htx_add_blk(htx, HTX_BLK_HDR, name.len + value.len);
704 if (!blk)
705 return NULL;
706
707 blk->info += (value.len << 8) + name.len;
708 memcpy(htx_get_blk_ptr(htx, blk), name.ptr, name.len);
709 memcpy(htx_get_blk_ptr(htx, blk) + name.len, value.ptr, value.len);
710 return blk;
711}
712
713struct htx_blk *htx_add_all_headers(struct htx *htx, const struct http_hdr *hdrs)
714{
715 int i;
716
717 for (i = 0; hdrs[i].n.len; i++) {
718 if (!htx_add_header(htx, hdrs[i].n, hdrs[i].v))
719 return NULL;
720 }
721 return htx_add_endof(htx, HTX_BLK_EOH);
722}
723/* Adds an HTX block of type PHDR in <htx>. It returns the new block on
724 * success. Otherwise, it returns NULL.
725 */
726struct htx_blk *htx_add_pseudo_header(struct htx *htx, enum htx_phdr_type phdr,
727 const struct ist value)
728{
729 struct htx_blk *blk;
730
731 /* FIXME: check value.len ( < 1MB) */
732 blk = htx_add_blk(htx, HTX_BLK_PHDR, value.len);
733 if (!blk)
734 return NULL;
735
736 blk->info += (value.len << 8) + phdr;
737 memcpy(htx_get_blk_ptr(htx, blk), value.ptr, value.len);
738 return blk;
739}
740
741/* Adds an HTX block of type EOH,EOD or EOM in <htx>. It returns the new block
742 * on success. Otherwise, it returns NULL.
743 */
744struct htx_blk *htx_add_endof(struct htx *htx, enum htx_blk_type type)
745{
746 struct htx_blk *blk;
747
748 blk = htx_add_blk(htx, type, 1);
749 if (!blk)
750 return NULL;
751
752 blk->info += 1;
753 return blk;
754}
755
756
757/* Adds an HTX block of type DATA in <htx>. It first tries to append data if
758 * possible. It returns the new block on success. Otherwise, it returns NULL.
759 */
760struct htx_blk *htx_add_data(struct htx *htx, const struct ist data)
761{
762 return htx_append_blk_value(htx, HTX_BLK_DATA, data);
763}
764
765/* Adds an HTX block of type TLR in <htx>. It first tries to append trailers
766 * data if possible. It returns the new block on success. Otherwise, it returns
767 * NULL.
768 */
769struct htx_blk *htx_add_trailer(struct htx *htx, const struct ist tlr)
770{
771 return htx_append_blk_value(htx, HTX_BLK_TLR, tlr);
772}
773
774/* Adds an HTX block of type OOB in <htx>. It returns the new block on
775 * success. Otherwise, it returns NULL.
776 */
777struct htx_blk *htx_add_oob(struct htx *htx, const struct ist oob)
778{
779 struct htx_blk *blk;
780
781 /* FIXME: check oob.len (< 256MB) */
782 blk = htx_add_blk(htx, HTX_BLK_OOB, oob.len);
783 if (!blk)
784 return NULL;
785
786 blk->info += oob.len;
787 memcpy(htx_get_blk_ptr(htx, blk), oob.ptr, oob.len);
788 return blk;
789}
790
791
792/* Appends the string representation of the request line block <blk> to the
793 * chunk <chk>. It returns 1 if data are successfully appended, otherwise it
794 * returns 0.
795 */
796int htx_reqline_to_str(const union htx_sl *sl, struct buffer *chk)
797{
798 if (sl->rq.m_len + sl->rq.u_len + sl->rq.v_len + 4 > b_room(chk))
799 return 0;
800
801 chunk_memcat(chk, sl->rq.l, sl->rq.m_len);
802 chunk_memcat(chk, " ", 1);
803 chunk_memcat(chk, sl->rq.l + sl->rq.m_len, sl->rq.u_len);
804 chunk_memcat(chk, " ", 1);
805 chunk_memcat(chk, sl->rq.l + sl->rq.m_len + sl->rq.u_len, sl->rq.v_len);
806 chunk_memcat(chk, "\r\n", 2);
807
808 return 1;
809}
810
811/* Appends the string representation of the status line block <blk> to the chunk
812 * <chk>. It returns 1 if data are successfully appended, otherwise it
813 * returns 0.
814 */
815int htx_stline_to_str(const union htx_sl *sl, struct buffer *chk)
816{
817 if (sl->st.v_len + sl->st.c_len + sl->st.r_len + 4 > b_size(chk))
818 return 0;
819
820 chunk_memcat(chk, sl->st.l, sl->st.v_len);
821 chunk_memcat(chk, " ", 1);
822 chunk_memcat(chk, sl->st.l + sl->st.v_len, sl->st.c_len);
823 chunk_memcat(chk, " ", 1);
824 chunk_memcat(chk, sl->st.l + sl->st.v_len + sl->st.c_len, sl->st.r_len);
825 chunk_memcat(chk, "\r\n", 2);
826
827 return 1;
828}
829
830/* Appends the string representation of the header block <blk> to the chunk
831 * <chk>. It returns 1 if data are successfully appended, otherwise it returns
832 * 0.
833 */
834int htx_hdr_to_str(const struct ist n, const struct ist v, struct buffer *chk)
835{
836 if (n.len + v.len + 4 > b_room(chk))
837 return 0;
838
839 chunk_memcat(chk, n.ptr, n.len);
840 chunk_memcat(chk, ": ", 2);
841 chunk_memcat(chk, v.ptr, v.len);
842 chunk_memcat(chk, "\r\n", 2);
843
844 return 1;
845}
846
847/* Appends the string representation of the data block <blk> to the chunk
848 * <chk>. If <chunked> is non-zero, it emits HTTP/1 chunk-encoded data. It
849 * returns 1 if data are successfully appended, otherwise it returns 0.
850 */
851int htx_data_to_str(const struct ist data, struct buffer *chk, int chunked)
852{
853 if (chunked) {
854 uint32_t chksz;
855 char tmp[10];
856 char *beg, *end;
857
858 chksz = data.len;
859
860 beg = end = tmp+10;
861 *--beg = '\n';
862 *--beg = '\r';
863 do {
864 *--beg = hextab[chksz & 0xF];
865 } while (chksz >>= 4);
866
867 if (data.len + (end - beg) + 2 > b_room(chk))
868 return 0;
869 chunk_memcat(chk, beg, end - beg);
870 chunk_memcat(chk, data.ptr, data.len);
871 chunk_memcat(chk, "\r\n", 2);
872 }
873 else {
874 if (!chunk_memcat(chk, data.ptr, data.len))
875 return 0;
876 }
877
878 return 1;
879}
880
881/* Appends the string representation of the trailer block <blk> to the chunk
882 * <chk>. It returns 1 if data are successfully appended, otherwise it returns
883 * 0.
884 */
885int htx_trailer_to_str(const struct ist tlr, struct buffer *chk)
886{
887 /* FIXME: be sure the CRLF is here or remove it when inserted */
888 if (!chunk_memcat(chk, tlr.ptr, tlr.len))
889 return 0;
890 return 1;
891}