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