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