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, .head  = -1, .tail = -1, .first = -1 };
17 
18 /* Defragments an HTX message. It removes unused blocks and unwraps the payloads
19  * part. A temporary buffer is used to do so. This function never fails. if
20  * <blk> is not NULL, we replace it by the new block address, after the
21  * 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 first = -1;
32 
33 	if (htx->head == -1)
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 			continue;
47 
48 		newblk = htx_get_blk(tmp, new);
49 		newblk->addr = addr;
50 		newblk->info = oldblk->info;
51 		blksz = htx_get_blksz(oldblk);
52 
53 		/* update the start-line position */
54 		if (htx->first == old)
55 			first = new;
56 
57 		/* if <blk> is defined, save its new position */
58 		if (blk != NULL && blk == oldblk)
59 			blkpos = new;
60 
61 		memcpy((void *)tmp->blocks + addr, htx_get_blk_ptr(htx, oldblk), blksz);
62 		new++;
63 		addr += blksz;
64 
65 	}
66 
67 	htx->first = first;
68 	htx->head = 0;
69 	htx->tail = new - 1;
70 	htx->head_addr = htx->end_addr = 0;
71 	htx->tail_addr = addr;
72 	memcpy((void *)htx->blocks, (void *)tmp->blocks, htx->size);
73 
74 	return ((blkpos == -1) ? NULL : htx_get_blk(htx, blkpos));
75 }
76 
77 /* Degragments HTX blocks of an HTX message. Payloads part is keep untouched
78  * here. This function will move back all blocks starting at the position 0,
79  * removing unused blocks. It must never be called with an empty message.
80  */
htx_defrag_blks(struct htx * htx)81 static void htx_defrag_blks(struct htx *htx)
82 {
83 	int32_t pos, new;
84 
85 	new = 0;
86 	for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
87 		struct htx_blk *posblk, *newblk;
88 
89 		if (pos == new) {
90 			new++;
91 			continue;
92 		}
93 
94 		posblk = htx_get_blk(htx, pos);
95 		if (htx_get_blk_type(posblk) == HTX_BLK_UNUSED)
96 			continue;
97 
98 		if (htx->first == pos)
99 			htx->first = new;
100 		newblk = htx_get_blk(htx, new++);
101 		newblk->info = posblk->info;
102 		newblk->addr = posblk->addr;
103 	}
104 	BUG_ON(!new);
105 	htx->head = 0;
106 	htx->tail = new - 1;
107 }
108 
109 /* Reserves a new block in the HTX message <htx> with a content of <blksz>
110  * bytes. If there is not enough space, NULL is returned. Otherwise the reserved
111  * block is returned and the HTX message is updated. Space for this new block is
112  * reserved in the HTX message. But it is the caller responsibility to set right
113  * info in the block to reflect the stored data.
114  */
htx_reserve_nxblk(struct htx * htx,uint32_t blksz)115 static struct htx_blk *htx_reserve_nxblk(struct htx *htx, uint32_t blksz)
116 {
117 	struct htx_blk *blk;
118 	uint32_t tail, headroom, tailroom;
119 
120 	if (blksz > htx_free_data_space(htx))
121 		return NULL; /* full */
122 
123 	if (htx->head == -1) {
124 		/* Empty message */
125 		htx->head = htx->tail = htx->first = 0;
126 		blk = htx_get_blk(htx, htx->tail);
127 		blk->addr = 0;
128 		htx->data = blksz;
129 		htx->tail_addr = blksz;
130 		return blk;
131 	}
132 
133 	/* Find the block's position. First, we try to get the next position in
134 	 * the message, increasing the tail by one. If this position is not
135 	 * available with some holes, we try to defrag the blocks without
136 	 * touching their paylood. If it is impossible, we fully defrag the
137 	 * message.
138 	 */
139 	tail = htx->tail + 1;
140 	if (htx_pos_to_addr(htx, tail) >= htx->tail_addr)
141 		;
142 	else if (htx->head > 0) {
143 		htx_defrag_blks(htx);
144 		tail = htx->tail + 1;
145 		BUG_ON(htx_pos_to_addr(htx, tail) < htx->tail_addr);
146 	}
147 	else
148 		goto defrag;
149 
150 	/* Now, we have found the block's position. Try do find where to put its
151 	 * payload. The free space is split in two areas:
152 	 *
153 	 *   * The free space in front of the blocks table. This one is used iff
154 	 *     the other one was not used yet.
155 	 *
156 	 *   * The free space at the beginning of the message. Once this one is
157          *     used, the other one is never used again, until the next defrag.
158 	 */
159 	headroom = (htx->end_addr - htx->head_addr);
160 	tailroom = (!htx->head_addr ? htx_pos_to_addr(htx, tail) - htx->tail_addr : 0);
161 	BUG_ON((int32_t)headroom < 0);
162 	BUG_ON((int32_t)tailroom < 0);
163 
164 	if (blksz <= tailroom) {
165 		blk = htx_get_blk(htx, tail);
166 		blk->addr = htx->tail_addr;
167 		htx->tail_addr += blksz;
168 	}
169 	else if (blksz <= headroom) {
170 		blk = htx_get_blk(htx, tail);
171 		blk->addr = htx->head_addr;
172 		htx->head_addr += blksz;
173 	}
174 	else {
175 	  defrag:
176 		/* need to defragment the message before inserting upfront */
177 		htx_defrag(htx, NULL);
178 		tail = htx->tail + 1;
179 		blk = htx_get_blk(htx, tail);
180 		blk->addr = htx->tail_addr;
181 		htx->tail_addr += blksz;
182 	}
183 
184 	htx->tail  = tail;
185 	htx->data += blksz;
186 	/* Set first position if not already set */
187 	if (htx->first == -1)
188 		htx->first = tail;
189 
190 	BUG_ON((int32_t)htx->tail_addr < 0);
191 	BUG_ON((int32_t)htx->head_addr < 0);
192 	BUG_ON(htx->end_addr > htx->tail_addr);
193 	BUG_ON(htx->head_addr > htx->end_addr);
194 
195 	return blk;
196 }
197 
198 /* Prepares the block to an expansion of its payload. The payload will be
199  * expanded by <delta> bytes and we need find where this expansion will be
200  * performed. It can be a compression if <delta> is negative. This function only
201  * updates all addresses. The caller have the responsibility to performe the
202  * expansion and update the block and the HTX message accordingly. No error must
203  * occurr. It returns following values:
204  *
205  *  0: The expansion cannot be performed, there is not enough space.
206  *
207  *  1: the expansion must be performed in place, there is enougth space after
208  *      the block's payload to handle it. This is especially true if it is a
209  *      compression and not an expension.
210  *
211  *  2: the block's payload must be moved at the new block address before doing
212  *     the expansion.
213  *
214  *  3: the HTX message message must be defragmented
215  */
htx_prepare_blk_expansion(struct htx * htx,struct htx_blk * blk,int32_t delta)216 static int htx_prepare_blk_expansion(struct htx *htx, struct htx_blk *blk, int32_t delta)
217 {
218 	uint32_t sz, tailroom, headroom;
219 	int ret = 3;
220 
221 	BUG_ON(htx->head == -1);
222 
223 	headroom = (htx->end_addr - htx->head_addr);
224 	tailroom = (htx_pos_to_addr(htx, htx->tail) - htx->tail_addr);
225 	BUG_ON((int32_t)headroom < 0);
226 	BUG_ON((int32_t)tailroom < 0);
227 
228 	sz = htx_get_blksz(blk);
229 	if (delta <= 0) {
230 		/* It is a compression, it can be performed in place */
231 		if (blk->addr+sz == htx->tail_addr)
232 			htx->tail_addr += delta;
233 		else if (blk->addr+sz == htx->head_addr)
234 			htx->head_addr += delta;
235 		ret = 1;
236 	}
237 	else if (delta > htx_free_space(htx)) {
238 		/* There is not enought space to handle the expansion */
239 		ret = 0;
240 	}
241 	else if (blk->addr+sz == htx->tail_addr) {
242 		/* The block's payload is just before the tail room */
243 		if (delta < tailroom) {
244 			/* Expand the block's payload */
245 			htx->tail_addr += delta;
246 			ret = 1;
247 		}
248 		else if ((sz + delta) < headroom) {
249 			uint32_t oldaddr = blk->addr;
250 
251 			/* Move the block's payload into the headroom */
252 			blk->addr = htx->head_addr;
253 			htx->tail_addr -= sz;
254 			htx->head_addr += sz + delta;
255 			if (oldaddr == htx->end_addr) {
256 				if (htx->end_addr == htx->tail_addr) {
257 					htx->tail_addr = htx->head_addr;
258 					htx->head_addr = htx->end_addr = 0;
259 				}
260 				else
261 					htx->end_addr += sz;
262 			}
263 			ret = 2;
264 		}
265 	}
266 	else if (blk->addr+sz == htx->head_addr) {
267 		/* The block's payload is just before the head room */
268 		if (delta < headroom) {
269 			/* Expand the block's payload */
270 			htx->head_addr += delta;
271 			ret = 1;
272 		}
273 	}
274 	else {
275 		/* The block's payload is not at the rooms edge */
276 		if (!htx->head_addr && sz+delta < tailroom) {
277 			/* Move the block's payload into the tailroom */
278 			if (blk->addr == htx->end_addr)
279 				htx->end_addr += sz;
280 			blk->addr = htx->tail_addr;
281 			htx->tail_addr += sz + delta;
282 			ret = 2;
283 		}
284 		else if (sz+delta < headroom) {
285 			/* Move the block's payload into the headroom */
286 			if (blk->addr == htx->end_addr)
287 				htx->end_addr += sz;
288 			blk->addr = htx->head_addr;
289 			htx->head_addr += sz + delta;
290 			ret = 2;
291 		}
292 	}
293 	/* Otherwise defrag the HTX message */
294 
295 	BUG_ON((int32_t)htx->tail_addr < 0);
296 	BUG_ON((int32_t)htx->head_addr < 0);
297 	BUG_ON(htx->end_addr > htx->tail_addr);
298 	BUG_ON(htx->head_addr > htx->end_addr);
299 	return ret;
300 }
301 
302 /* Adds a new block of type <type> in the HTX message <htx>. Its content size is
303  * passed but it is the caller responsibility to do the copy.
304  */
htx_add_blk(struct htx * htx,enum htx_blk_type type,uint32_t blksz)305 struct htx_blk *htx_add_blk(struct htx *htx, enum htx_blk_type type, uint32_t blksz)
306 {
307 	struct htx_blk *blk;
308 
309 	blk = htx_reserve_nxblk(htx, blksz);
310 	if (!blk)
311 		return NULL;
312 	BUG_ON(blk->addr > htx->size);
313 
314 	blk->info = (type << 28);
315 	return blk;
316 }
317 
318 /* Removes the block <blk> from the HTX message <htx>. The function returns the
319  * block following <blk> or NULL if <blk> is the last block or the last inserted
320  * one.
321  */
htx_remove_blk(struct htx * htx,struct htx_blk * blk)322 struct htx_blk *htx_remove_blk(struct htx *htx, struct htx_blk *blk)
323 {
324 	enum htx_blk_type type;
325 	uint32_t pos, addr, sz;
326 
327 	BUG_ON(htx->head == -1);
328 
329 	/* This is the last block in use */
330 	if (htx->head == htx->tail) {
331 		htx_reset(htx);
332 		return NULL;
333 	}
334 
335 	type = htx_get_blk_type(blk);
336 	pos  = htx_get_blk_pos(htx, blk);
337 	sz   = htx_get_blksz(blk);
338 	addr = blk->addr;
339 	if (type != HTX_BLK_UNUSED) {
340 		/* Mark the block as unused, decrement allocated size */
341 		htx->data -= htx_get_blksz(blk);
342 		blk->info = ((uint32_t)HTX_BLK_UNUSED << 28);
343 	}
344 
345 	/* There is at least 2 blocks, so tail is always > 0 */
346 	if (pos == htx->head) {
347 		/* move the head forward */
348 		htx->head++;
349 	}
350 	else if (pos == htx->tail) {
351 		/* remove the tail. this was the last inserted block so
352 		 * return NULL. */
353 		htx->tail--;
354 		blk = NULL;
355 		goto end;
356 	}
357 	blk = htx_get_blk(htx, pos+1);
358 
359   end:
360 	if (pos == htx->first)
361 		htx->first = (blk ? htx_get_blk_pos(htx, blk) : -1);
362 
363 	if (htx->head == htx->tail) {
364 		/* If there is just one block in the HTX message, free space can
365 		 * be ajusted. This operation could save some defrags. */
366 		struct htx_blk *lastblk = htx_get_blk(htx, htx->tail);
367 
368 		htx->head_addr = 0;
369 		htx->end_addr = lastblk->addr;
370 		htx->tail_addr = lastblk->addr+htx->data;
371 	}
372 	else {
373 		if (addr+sz == htx->tail_addr)
374 			htx->tail_addr = addr;
375 		else if (addr+sz == htx->head_addr)
376 			htx->head_addr = addr;
377 		if (addr == htx->end_addr) {
378 			if (htx->tail_addr == htx->end_addr) {
379 				htx->tail_addr = htx->head_addr;
380 				htx->head_addr = htx->end_addr = 0;
381 			}
382 			else
383 				htx->end_addr += sz;
384 		}
385 	}
386 
387 	BUG_ON((int32_t)htx->tail_addr < 0);
388 	BUG_ON((int32_t)htx->head_addr < 0);
389 	BUG_ON(htx->end_addr > htx->tail_addr);
390 	BUG_ON(htx->head_addr > htx->end_addr);
391 	return blk;
392 }
393 
394 /* Looks for the HTX block containing the offset <offset>, starting at the HTX
395  * message's head. The function returns an htx_ret with the found HTX block and
396  * the position inside this block where the offset is. If the offset <offset> is
397  * ouside of the HTX message, htx_ret.blk is set to NULL.
398  */
htx_find_offset(struct htx * htx,uint32_t offset)399 struct htx_ret htx_find_offset(struct htx *htx, uint32_t offset)
400 {
401 	struct htx_blk *blk;
402 	struct htx_ret htxret = { .blk = NULL, .ret = 0 };
403 
404 	if (offset >= htx->data)
405 		return htxret;
406 
407 	for (blk = htx_get_head_blk(htx); blk && offset; blk = htx_get_next_blk(htx, blk)) {
408 		uint32_t sz = htx_get_blksz(blk);
409 
410 		if (offset < sz)
411 			break;
412 		offset -= sz;
413 	}
414 	htxret.blk = blk;
415 	htxret.ret = offset;
416 	return htxret;
417 }
418 
419 /* Removes all blocks after the one containing the offset <offset>. This last
420  * one may be truncated if it is a DATA block.
421  */
htx_truncate(struct htx * htx,uint32_t offset)422 void htx_truncate(struct htx *htx, uint32_t offset)
423 {
424 	struct htx_blk *blk;
425 
426 	for (blk = htx_get_head_blk(htx); blk && offset; blk = htx_get_next_blk(htx, blk)) {
427 		uint32_t sz = htx_get_blksz(blk);
428 		enum htx_blk_type type = htx_get_blk_type(blk);
429 
430 		if (offset >= sz) {
431 			offset -= sz;
432 			continue;
433 		}
434 		if (type == HTX_BLK_DATA)
435 			htx_change_blk_value_len(htx, blk, offset);
436 		offset = 0;
437 	}
438 	while (blk)
439 		blk = htx_remove_blk(htx, blk);
440 }
441 
442 /* Drains <count> bytes from the HTX message <htx>. If the last block is a DATA
443  * block, it will be cut if necessary. Others blocks will be removed at once if
444  * <count> is large enough. The function returns an htx_ret with the first block
445  * remaing in the messsage and the amount of data drained. If everything is
446  * removed, htx_ret.blk is set to NULL.
447  */
htx_drain(struct htx * htx,uint32_t count)448 struct htx_ret htx_drain(struct htx *htx, uint32_t count)
449 {
450 	struct htx_blk *blk;
451 	struct htx_ret htxret = { .blk = NULL, .ret = 0 };
452 
453 	if (count == htx->data) {
454 		htx_reset(htx);
455 		htxret.ret = count;
456 		return htxret;
457 	}
458 
459 	blk = htx_get_head_blk(htx);
460 	while (count && blk) {
461 		uint32_t sz = htx_get_blksz(blk);
462 		enum htx_blk_type type = htx_get_blk_type(blk);
463 
464 		/* Ingore unused block */
465 		if (type == HTX_BLK_UNUSED)
466 			goto next;
467 
468 		if (sz > count) {
469 			if (type == HTX_BLK_DATA) {
470 				htx_cut_data_blk(htx, blk, count);
471 				htxret.ret += count;
472 			}
473 			break;
474 		}
475 		count -= sz;
476 		htxret.ret += sz;
477 	  next:
478 		blk = htx_remove_blk(htx, blk);
479 	}
480 	htxret.blk = blk;
481 
482 	return htxret;
483 }
484 
485 /* Tries to append data to the last inserted block, if the type matches and if
486  * there is enough space to take it all. If the space wraps, the buffer is
487  * defragmented and a new block is inserted. If an error occurred, NULL is
488  * returned. Otherwise, on success, the updated block (or the new one) is
489  * returned. Due to its nature this function can be expensive and should be
490  * avoided whenever possible.
491  */
htx_add_data_atonce(struct htx * htx,struct ist data)492 struct htx_blk *htx_add_data_atonce(struct htx *htx, struct ist data)
493 {
494 	struct htx_blk *blk, *tailblk;
495 	void *ptr;
496 	uint32_t len, sz, tailroom, headroom;
497 
498 	if (htx->head == -1)
499 		goto add_new_block;
500 
501 	/* Not enough space to store data */
502 	if (data.len > htx_free_data_space(htx))
503 		return NULL;
504 
505 	/* get the tail block and its size */
506 	tailblk = htx_get_tail_blk(htx);
507 	if (tailblk == NULL)
508 		goto add_new_block;
509 	sz = htx_get_blksz(tailblk);
510 
511 	/* Don't try to append data if the last inserted block is not of the
512 	 * same type */
513 	if (htx_get_blk_type(tailblk) != HTX_BLK_DATA)
514 		goto add_new_block;
515 
516 	/*
517 	 * Same type and enough space: append data
518 	 */
519 	headroom = (htx->end_addr - htx->head_addr);
520 	tailroom = (htx_pos_to_addr(htx, htx->tail) - htx->tail_addr);
521 	BUG_ON((int32_t)headroom < 0);
522 	BUG_ON((int32_t)tailroom < 0);
523 
524 	len = data.len;
525 	if (tailblk->addr+sz == htx->tail_addr) {
526 		if (data.len <= tailroom)
527 			goto append_data;
528 		else if (!htx->head_addr) {
529 			len = tailroom;
530 			goto append_data;
531 		}
532 	}
533 	else if (tailblk->addr+sz == htx->head_addr && data.len <= headroom)
534 		goto append_data;
535 
536 	goto add_new_block;
537 
538   append_data:
539 	/* FIXME: check v.len + data.len < 256MB */
540 	/* Append data and update the block itself */
541 	ptr = htx_get_blk_ptr(htx, tailblk);
542 	memcpy(ptr+sz, data.ptr, len);
543 	htx_change_blk_value_len(htx, tailblk, sz+len);
544 
545 	if (data.len == len) {
546 		blk = tailblk;
547 		goto end;
548 	}
549 	data.ptr += len;
550 	data.len -= len;
551 
552   add_new_block:
553 	/* FIXME: check data.len (< 256MB) */
554 	blk = htx_add_blk(htx, HTX_BLK_DATA, data.len);
555 	if (!blk)
556 		return NULL;
557 
558 	blk->info += data.len;
559 	memcpy(htx_get_blk_ptr(htx, blk), data.ptr, data.len);
560 
561   end:
562 	BUG_ON((int32_t)htx->tail_addr < 0);
563 	BUG_ON((int32_t)htx->head_addr < 0);
564 	BUG_ON(htx->end_addr > htx->tail_addr);
565 	BUG_ON(htx->head_addr > htx->end_addr);
566 	return blk;
567 }
568 
569 /* Replaces a value part of a block by a new one. The new part can be smaller or
570  * larger than the old one. This function works for any kind of block with
571  * attached data. It returns the new block on success, otherwise it returns
572  * NULL.
573  */
htx_replace_blk_value(struct htx * htx,struct htx_blk * blk,const struct ist old,const struct ist new)574 struct htx_blk *htx_replace_blk_value(struct htx *htx, struct htx_blk *blk,
575 				      const struct ist old, const struct ist new)
576 {
577 	struct ist n, v;
578 	int32_t delta;
579 	int ret;
580 
581 	n  = htx_get_blk_name(htx, blk);
582 	v  = htx_get_blk_value(htx, blk);
583 	delta = new.len - old.len;
584 	ret = htx_prepare_blk_expansion(htx, blk, delta);
585 	if (!ret)
586 		return NULL; /* not enough space */
587 
588 	/* Before updating the payload, set the new block size and update HTX
589 	 * message */
590 	htx_set_blk_value_len(blk, v.len + delta);
591 	htx->data += delta;
592 
593 	if (ret == 1) { /* Replace in place */
594 		if (delta <= 0) {
595 			/* compression: copy new data first then move the end */
596 			memcpy(old.ptr, new.ptr, new.len);
597 			memmove(old.ptr + new.len, old.ptr + old.len, (v.ptr + v.len) - (old.ptr + old.len));
598 		}
599 		else {
600 			/* expansion: move the end first then copy new data */
601 			memmove(old.ptr + new.len, old.ptr + old.len, (v.ptr + v.len) - (old.ptr + old.len));
602 			memcpy(old.ptr, new.ptr, new.len);
603 		}
604 	}
605 	else if (ret == 2) { /* New address but no defrag */
606 		void *ptr = htx_get_blk_ptr(htx, blk);
607 
608 		/* Copy the name, if any */
609 		memcpy(ptr, n.ptr, n.len);
610 		ptr += n.len;
611 
612 		/* Copy value before old part, if any */
613 		memcpy(ptr, v.ptr, old.ptr - v.ptr);
614 		ptr += old.ptr - v.ptr;
615 
616 		/* Copy new value */
617 		memcpy(ptr, new.ptr, new.len);
618 		ptr += new.len;
619 
620 		/* Copy value after old part, if any */
621 		memcpy(ptr, old.ptr + old.len, (v.ptr + v.len) - (old.ptr + old.len));
622 	}
623 	else { /* Do a degrag first */
624 		struct buffer *tmp = get_trash_chunk();
625 
626 		/* Copy the header name, if any */
627 		chunk_memcat(tmp, n.ptr, n.len);
628 
629 		/* Copy value before old part, if any */
630 		chunk_memcat(tmp, v.ptr, old.ptr - v.ptr);
631 
632 		/* Copy new value */
633 		chunk_memcat(tmp, new.ptr, new.len);
634 
635 		/* Copy value after old part if any */
636                 chunk_memcat(tmp, old.ptr + old.len, (v.ptr + v.len) - (old.ptr + old.len));
637 
638 		blk = htx_defrag(htx, blk);
639 
640 		/* Finaly, copy data. */
641 		memcpy(htx_get_blk_ptr(htx, blk), tmp->area, tmp->data);
642 	}
643 	return blk;
644 }
645 
646 /* Transfer HTX blocks from <src> to <dst>, stopping on the first block of the
647  * type <mark> (typically EOH or EOM) or when <count> bytes were moved
648  * (including payload and meta-data). It returns the number of bytes moved and
649  * the last HTX block inserted in <dst>.
650  */
htx_xfer_blks(struct htx * dst,struct htx * src,uint32_t count,enum htx_blk_type mark)651 struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
652 			     enum htx_blk_type mark)
653 {
654 	struct htx_blk   *blk, *dstblk;
655 	enum htx_blk_type type;
656 	uint32_t	  info, max, sz, ret;
657 	int inside_trailers = 0;
658 
659 	ret = htx_used_space(dst);
660 	blk = htx_get_blk(src, htx_get_head(src));
661 	dstblk = NULL;
662 
663 	while (blk && count) {
664 		type = htx_get_blk_type(blk);
665 
666 		/* Ingore unused block */
667 		if (type == HTX_BLK_UNUSED)
668 			goto next;
669 
670 		/* Be sure to have enough space to xfer all headers/trailers in
671 		 * one time. If not while <dst> is empty, we report a parsing
672 		 * error on <src>.
673 		 */
674 		if (mark >= HTX_BLK_EOH && (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL)) {
675 			struct htx_sl *sl = htx_get_blk_ptr(src, blk);
676 
677 			if (sl->hdrs_bytes != -1 && sl->hdrs_bytes > count) {
678 				if (htx_is_empty(dst))
679 					src->flags |= HTX_FL_PARSING_ERROR;
680 				break;
681 			}
682 		}
683 		else if ((type == HTX_BLK_TLR || type == HTX_BLK_EOT) &&
684 			 !inside_trailers && mark >= HTX_BLK_EOT) {
685 			inside_trailers = 1;
686 			if (htx_used_space(src) > count) {
687 				if (htx_is_empty(dst))
688 					src->flags |= HTX_FL_PARSING_ERROR;
689 				break;
690 			}
691 		}
692 
693 		sz = htx_get_blksz(blk);
694 		info = blk->info;
695 		max = htx_get_max_blksz(dst, count);
696 		if (!max)
697 			break;
698 		if (sz > max) {
699 			/* Only DATA blocks can be partially xferred */
700 			if (type != HTX_BLK_DATA)
701 				break;
702 			sz = max;
703 			info = (type << 28) + sz;
704 		}
705 
706 		dstblk = htx_reserve_nxblk(dst, sz);
707 		if (!dstblk)
708 			break;
709 		dstblk->info = info;
710 		memcpy(htx_get_blk_ptr(dst, dstblk), htx_get_blk_ptr(src, blk), sz);
711 
712 		count -= sizeof(dstblk) + sz;
713 		if (blk->info != info) {
714 			/* Partial xfer: don't remove <blk> from <src> but
715 			 * resize its content */
716 			htx_cut_data_blk(src, blk, sz);
717 			break;
718 		}
719 	  next:
720 		blk = htx_remove_blk(src, blk);
721 		if (type == mark)
722 			break;
723 	}
724 
725   end:
726 	ret = htx_used_space(dst) - ret;
727 	return (struct htx_ret){.ret = ret, .blk = dstblk};
728 }
729 
730 /* Replaces an header by a new one. The new header can be smaller or larger than
731  * the old one. It returns the new block on success, otherwise it returns NULL.
732  * The header name is always lower cased.
733  */
htx_replace_header(struct htx * htx,struct htx_blk * blk,const struct ist name,const struct ist value)734 struct htx_blk *htx_replace_header(struct htx *htx, struct htx_blk *blk,
735 				   const struct ist name, const struct ist value)
736 {
737 	enum htx_blk_type type;
738 	void *ptr;
739 	int32_t delta;
740 	int ret;
741 
742 	type = htx_get_blk_type(blk);
743 	if (type != HTX_BLK_HDR)
744 		return NULL;
745 
746 	delta = name.len + value.len - htx_get_blksz(blk);
747 	ret = htx_prepare_blk_expansion(htx, blk, delta);
748 	if (!ret)
749 		return NULL; /* not enough space */
750 
751 	/* Set the new block size and update HTX message */
752 	blk->info = (type << 28) + (value.len << 8) + name.len;
753 	htx->data += delta;
754 
755 	/* Replace in place or at a new address is the same. We replace all the
756 	 * header (name+value). Only take care to defrag the message if
757 	 * necessary. */
758 	if (ret == 3)
759 		blk = htx_defrag(htx, blk);
760 
761 	/* Finaly, copy data. */
762 	ptr = htx_get_blk_ptr(htx, blk);
763 	ist2bin_lc(ptr, name);
764 	memcpy(ptr + name.len, value.ptr, value.len);
765 	return blk;
766 }
767 
768 /* Replaces the parts of the start-line. It returns the new start-line on
769  * success, otherwise it returns NULL. It is the caller responsibility to update
770  * sl->info, if necessary.
771  */
htx_replace_stline(struct htx * htx,struct htx_blk * blk,const struct ist p1,const struct ist p2,const struct ist p3)772 struct htx_sl *htx_replace_stline(struct htx *htx, struct htx_blk *blk, const struct ist p1,
773 				  const struct ist p2, const struct ist p3)
774 {
775 	enum htx_blk_type type;
776 	struct htx_sl *sl;
777 	struct htx_sl tmp; /* used to save sl->info and sl->flags */
778 	uint32_t sz;
779 	int32_t delta;
780 	int ret;
781 
782 	type = htx_get_blk_type(blk);
783 	if (type != HTX_BLK_REQ_SL && type != HTX_BLK_RES_SL)
784 		return NULL;
785 
786 	/* Save start-line info and flags */
787 	sl = htx_get_blk_ptr(htx, blk);
788 	tmp.info = sl->info;
789 	tmp.flags = sl->flags;
790 	tmp.hdrs_bytes = sl->hdrs_bytes;
791 
792 	sz = htx_get_blksz(blk);
793 	delta = sizeof(*sl) + p1.len + p2.len + p3.len - sz;
794 	ret = htx_prepare_blk_expansion(htx, blk, delta);
795 	if (!ret)
796 		return NULL; /* not enough space */
797 
798 	/* Set the new block size and update HTX message */
799 	htx_set_blk_value_len(blk, sz+delta);
800 	htx->data += delta;
801 
802 	/* Replace in place or at a new address is the same. We replace all the
803 	 * start-line. Only take care to defrag the message if necessary. */
804 	if (ret == 3)
805 		blk = htx_defrag(htx, blk);
806 
807 	/* Restore start-line info and flags and copy parts of the start-line */
808 	sl = htx_get_blk_ptr(htx, blk);
809 	sl->info = tmp.info;
810 	sl->flags = tmp.flags;
811 	sl->hdrs_bytes = tmp.hdrs_bytes;
812 
813 	HTX_SL_P1_LEN(sl) = p1.len;
814 	HTX_SL_P2_LEN(sl) = p2.len;
815 	HTX_SL_P3_LEN(sl) = p3.len;
816 
817 	memcpy(HTX_SL_P1_PTR(sl), p1.ptr, p1.len);
818 	memcpy(HTX_SL_P2_PTR(sl), p2.ptr, p2.len);
819 	memcpy(HTX_SL_P3_PTR(sl), p3.ptr, p3.len);
820 
821 	return sl;
822 }
823 
824 /* Add a new start-line. It returns it on success, otherwise it returns NULL. It
825  * is the caller responsibility to set sl->info, if necessary.
826  */
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)827 struct htx_sl *htx_add_stline(struct htx *htx, enum htx_blk_type type, unsigned int flags,
828 			      const struct ist p1, const struct ist p2, const struct ist p3)
829 {
830 	struct htx_blk *blk;
831 	struct htx_sl  *sl;
832 	uint32_t size;
833 
834 	if (type != HTX_BLK_REQ_SL && type != HTX_BLK_RES_SL)
835 		return NULL;
836 
837 	size = sizeof(*sl) + p1.len + p2.len + p3.len;
838 
839 	/* FIXME: check size (< 256MB) */
840 	blk = htx_add_blk(htx, type, size);
841 	if (!blk)
842 		return NULL;
843 	blk->info += size;
844 
845 	sl = htx_get_blk_ptr(htx, blk);
846 	sl->hdrs_bytes = -1;
847 	sl->flags = flags;
848 
849 	HTX_SL_P1_LEN(sl) = p1.len;
850 	HTX_SL_P2_LEN(sl) = p2.len;
851 	HTX_SL_P3_LEN(sl) = p3.len;
852 
853 	memcpy(HTX_SL_P1_PTR(sl), p1.ptr, p1.len);
854 	memcpy(HTX_SL_P2_PTR(sl), p2.ptr, p2.len);
855 	memcpy(HTX_SL_P3_PTR(sl), p3.ptr, p3.len);
856 
857 	return sl;
858 }
859 
860 /* Adds an HTX block of type HDR in <htx>. It returns the new block on
861  * success. Otherwise, it returns NULL. The header name is always lower cased.
862  */
htx_add_header(struct htx * htx,const struct ist name,const struct ist value)863 struct htx_blk *htx_add_header(struct htx *htx, const struct ist name,
864 			       const struct ist value)
865 {
866 	struct htx_blk *blk;
867 
868 	/* FIXME: check name.len (< 256B) and value.len (< 1MB) */
869 	blk = htx_add_blk(htx, HTX_BLK_HDR, name.len + value.len);
870 	if (!blk)
871 		return NULL;
872 
873 	blk->info += (value.len << 8) + name.len;
874 	ist2bin_lc(htx_get_blk_ptr(htx, blk), name);
875 	memcpy(htx_get_blk_ptr(htx, blk)  + name.len, value.ptr, value.len);
876 	return blk;
877 }
878 
879 /* Adds an HTX block of type TLR in <htx>. It returns the new block on
880  * success. Otherwise, it returns NULL. The trailer name is always lower cased.
881  */
htx_add_trailer(struct htx * htx,const struct ist name,const struct ist value)882 struct htx_blk *htx_add_trailer(struct htx *htx, const struct ist name,
883 				const struct ist value)
884 {
885 	struct htx_blk *blk;
886 
887 	/* FIXME: check name.len (< 256B) and value.len (< 1MB) */
888 	blk = htx_add_blk(htx, HTX_BLK_TLR, name.len + value.len);
889 	if (!blk)
890 		return NULL;
891 
892 	blk->info += (value.len << 8) + name.len;
893 	ist2bin_lc(htx_get_blk_ptr(htx, blk), name);
894 	memcpy(htx_get_blk_ptr(htx, blk)  + name.len, value.ptr, value.len);
895 	return blk;
896 }
897 
898 /* Add all headers from the list <hdrs> into the HTX message <htx>, followed by
899  * the EOH. On sucess, it returns the last block inserted (the EOH), otherwise
900  * NULL is returned. */
htx_add_all_headers(struct htx * htx,const struct http_hdr * hdrs)901 struct htx_blk *htx_add_all_headers(struct htx *htx, const struct http_hdr *hdrs)
902 {
903 	int i;
904 
905 	for (i = 0; hdrs[i].n.len; i++) {
906 		if (!htx_add_header(htx, hdrs[i].n, hdrs[i].v))
907 			return NULL;
908 	}
909 	return htx_add_endof(htx, HTX_BLK_EOH);
910 }
911 
912 /* Add all trailers from the list <hdrs> into the HTX message <htx>, followed by
913  * the EOT. On sucess, it returns the last block inserted (the EOT), otherwise
914  * NULL is returned. */
htx_add_all_trailers(struct htx * htx,const struct http_hdr * hdrs)915 struct htx_blk *htx_add_all_trailers(struct htx *htx, const struct http_hdr *hdrs)
916 {
917 	int i;
918 
919 	for (i = 0; hdrs[i].n.len; i++) {
920 		if (!htx_add_trailer(htx, hdrs[i].n, hdrs[i].v))
921 			return NULL;
922 	}
923 	return htx_add_endof(htx, HTX_BLK_EOT);
924 }
925 
926 /* Adds an HTX block of type EOH, EOT, or EOM in <htx>. It returns the new block
927  * on success. Otherwise, it returns NULL.
928  */
htx_add_endof(struct htx * htx,enum htx_blk_type type)929 struct htx_blk *htx_add_endof(struct htx *htx, enum htx_blk_type type)
930 {
931 	struct htx_blk *blk;
932 
933 	blk = htx_add_blk(htx, type, 1);
934 	if (!blk)
935 		return NULL;
936 
937 	blk->info += 1;
938 	return blk;
939 }
940 
941 
942 /* Adds an HTX block of type DATA in <htx>. It first tries to append data if
943  * possible. It returns the number of bytes consumed from <data>, which may be
944  * zero if nothing could be copied.
945  */
htx_add_data(struct htx * htx,const struct ist data)946 size_t htx_add_data(struct htx *htx, const struct ist data)
947 {
948 	struct htx_blk *blk, *tailblk;
949 	void *ptr;
950 	uint32_t sz, room;
951 	int32_t len = data.len;
952 
953 	if (htx->head == -1)
954 		goto add_new_block;
955 
956 	/* Not enough space to store data */
957 	if (len > htx_free_data_space(htx))
958 		len = htx_free_data_space(htx);
959 
960 	if (!len)
961 		return 0;
962 
963 	/* get the tail and head block */
964 	tailblk = htx_get_tail_blk(htx);
965 	if (tailblk == NULL)
966 		goto add_new_block;
967 	sz = htx_get_blksz(tailblk);
968 
969 	/* Don't try to append data if the last inserted block is not of the
970 	 * same type */
971 	if (htx_get_blk_type(tailblk) != HTX_BLK_DATA)
972 		goto add_new_block;
973 
974 	/*
975 	 * Same type and enough space: append data
976 	 */
977 	if (!htx->head_addr) {
978 		if (tailblk->addr+sz != htx->tail_addr)
979 			goto add_new_block;
980 		room = (htx_pos_to_addr(htx, htx->tail) - htx->tail_addr);
981 	}
982 	else {
983 		if (tailblk->addr+sz != htx->head_addr)
984 			goto add_new_block;
985 		room = (htx->end_addr - htx->head_addr);
986 	}
987 	BUG_ON((int32_t)room < 0);
988 	if (room < len)
989 		len = room;
990 
991   append_data:
992 	/* FIXME: check v.len + data.len < 256MB */
993 	/* Append data and update the block itself */
994 	ptr = htx_get_blk_ptr(htx, tailblk);
995 	memcpy(ptr + sz, data.ptr, len);
996 	htx_change_blk_value_len(htx, tailblk, sz+len);
997 
998 	BUG_ON((int32_t)htx->tail_addr < 0);
999 	BUG_ON((int32_t)htx->head_addr < 0);
1000 	BUG_ON(htx->end_addr > htx->tail_addr);
1001 	BUG_ON(htx->head_addr > htx->end_addr);
1002 	return len;
1003 
1004   add_new_block:
1005 	/* FIXME: check data.len (< 256MB) */
1006 	blk = htx_add_blk(htx, HTX_BLK_DATA, len);
1007 	if (!blk)
1008 		return 0;
1009 
1010 	blk->info += len;
1011 	memcpy(htx_get_blk_ptr(htx, blk), data.ptr, len);
1012 	return len;
1013 }
1014 
1015 
1016 /* Adds an HTX block of type DATA in <htx> just after all other DATA
1017  * blocks. Because it relies on htx_add_data_atonce(), It may be happened to a
1018  * DATA block if possible. But, if the function succeeds, it will be the last
1019  * DATA block in all cases. If an error occurred, NULL is returned. Otherwise,
1020  * on success, the updated block (or the new one) is returned.
1021  */
htx_add_last_data(struct htx * htx,struct ist data)1022 struct htx_blk *htx_add_last_data(struct htx *htx, struct ist data)
1023 {
1024 	struct htx_blk *blk, *pblk;
1025 
1026 	blk = htx_add_data_atonce(htx, data);
1027 	if (!blk)
1028 		return NULL;
1029 
1030 	for (pblk = htx_get_prev_blk(htx, blk); pblk; pblk = htx_get_prev_blk(htx, pblk)) {
1031 		if (htx_get_blk_type(pblk) <= HTX_BLK_DATA)
1032 			break;
1033 
1034 		/* Swap .addr and .info fields */
1035 		blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
1036 		blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info;
1037 
1038 		if (blk->addr == pblk->addr)
1039 			blk->addr += htx_get_blksz(pblk);
1040 		blk = pblk;
1041 	}
1042 
1043 	return blk;
1044 }
1045 
1046 /* Moves the block <blk> just before the block <ref>. Both blocks must be in the
1047  * HTX message <htx> and <blk> must be placed after <ref>. pointer to these
1048  * blocks are updated to remain valid after the move. */
htx_move_blk_before(struct htx * htx,struct htx_blk ** blk,struct htx_blk ** ref)1049 void htx_move_blk_before(struct htx *htx, struct htx_blk **blk, struct htx_blk **ref)
1050 {
1051 	struct htx_blk *cblk, *pblk;
1052 
1053 	cblk = *blk;
1054 	for (pblk = htx_get_prev_blk(htx, cblk); pblk; pblk = htx_get_prev_blk(htx, pblk)) {
1055 		/* Swap .addr and .info fields */
1056 		cblk->addr ^= pblk->addr; pblk->addr ^= cblk->addr; cblk->addr ^= pblk->addr;
1057 		cblk->info ^= pblk->info; pblk->info ^= cblk->info; cblk->info ^= pblk->info;
1058 
1059 		if (cblk->addr == pblk->addr)
1060 			cblk->addr += htx_get_blksz(pblk);
1061 		if (pblk == *ref)
1062 			break;
1063 		cblk = pblk;
1064 	}
1065 	*blk = cblk;
1066 	*ref = pblk;
1067 }
1068 
1069 /* Append the HTX message <src> to the HTX message <dst>. It returns 1 on
1070  * success and 0 on error.  All the message or nothing is copied. If an error
1071  * occurred, all blocks from <src> already appended to <dst> are truncated.
1072  */
htx_append_msg(struct htx * dst,const struct htx * src)1073 int htx_append_msg(struct htx *dst, const struct htx *src)
1074 {
1075 	struct htx_blk *blk, *newblk;
1076 	enum htx_blk_type type;
1077 	uint32_t blksz, offset = dst->data;
1078 
1079 	for (blk = htx_get_head_blk(src); blk; blk = htx_get_next_blk(src, blk)) {
1080 		type = htx_get_blk_type(blk);
1081 
1082 		if (type == HTX_BLK_UNUSED)
1083 			continue;
1084 
1085 		blksz = htx_get_blksz(blk);
1086 		newblk = htx_add_blk(dst, type, blksz);
1087 		if (!newblk)
1088 			goto error;
1089 		newblk->info = blk->info;
1090 		memcpy(htx_get_blk_ptr(dst, newblk), htx_get_blk_ptr(src, blk), blksz);
1091 	}
1092 
1093 	return 1;
1094 
1095   error:
1096 	htx_truncate(dst, offset);
1097 	return 0;
1098 }
1099