blob: 9a9736129402981beaddf8b659624802f5a2ae8e [file] [log] [blame]
Willy Tarreau985fc562007-04-01 09:44:10 +020012007/03/30 - Header storage in trees
2
3This documentation describes how to store headers in radix trees, providing
4fast access to any known position, while retaining the ability to grow/reduce
5any arbitrary header without having to recompute all positions.
6
7Principle :
8 We have a radix tree represented in an integer array, which represents the
9 total number of bytes used by all headers whose position is below it. This
10 ensures that we can compute any header's position in O(log(N)) where N is
11 the number of headers.
12
13Example with N=16 :
14
15 +-----------------------+
16 | |
17 +-----------+ +-----------+
18 | | | |
19 +-----+ +-----+ +-----+ +-----+
20 | | | | | | | |
21 +--+ +--+ +--+ +--+ +--+ +--+ +--+ +--+
22 | | | | | | | | | | | | | | | |
23
24 0 1 2 3 4 5 6 7 8 9 A B C D E F
25
26 To reach header 6, we have to compute hdr[0]+hdr[4]+hdr[6]
27
28 With this method, it becomes easy to grow any header and update the array.
29 To achieve this, we have to replace one after the other all bits on the
30 right with one 1 followed by zeroes, and update the position if it's higher
31 than current position, and stop when it's above number of stored headers.
32
33 For instance, if we want to grow hdr[6], we proceed like this :
34
35 6 = 0110 (BIN)
36
37 Let's consider the values to update :
38
39 (bit 0) : (0110 & ~0001) | 0001 = 0111 = 7 > 6 => update
40 (bit 1) : (0110 & ~0011) | 0010 = 0110 = 6 <= 6 => leave it
41 (bit 2) : (0110 & ~0111) | 0100 = 0100 = 4 <= 6 => leave it
42 (bit 4) : (0110 & ~1111) | 1000 = 1000 = 8 > 6 => update
43 (bit 5) : larger than array size, stop.
44
45
46It's easy to walk through the tree too. We only have one iteration per bit
47changing from X to the ancestor, and one per bit from the ancestor to Y.
48The ancestor is found while walking. To go from X to Y :
49
50 pos = pos(X)
51
52 while (Y != X) {
53 if (Y > X) {
54 // walk from Y to ancestor
55 pos += hdr[Y]
56 Y &= (Y - 1)
57 } else {
58 // walk from X to ancestor
59 pos -= hdr[X]
60 X &= (X - 1)
61 }
62 }
63
64However, it is not trivial anymore to linearly walk the tree. We have to move
65from a known place to another known place, but a jump to next entry costs the
66same as a jump to a random place.
67
68Other caveats :
69 - it is not possible to remove a header, it is only possible to empty it.
70 - it is not possible to insert a header, as that would imply a renumbering.
71 => this means that a "defrag" function is required. Headers should preferably
72 be added, then should be stuffed on top of destroyed ones, then only
73 inserted if absolutely required.
74
75
76When we have this, we can then focus on a 32-bit header descriptor which would
77look like this :
78
79{
80 unsigned line_len :13; /* total line length, including CRLF */
81 unsigned name_len :6; /* header name length, max 63 chars */
82 unsigned sp1 :5; /* max spaces before value : 31 */
83 unsigned sp2 :8; /* max spaces after value : 255 */
84}
85
86Example :
87
88 Connection: close \r\n
89 <---------+-----+-----+-------------> line_len
90 <-------->| | | name_len
91 <-----> | sp1
92 <-------------> sp2
93Rem:
94 - if there are more than 31 spaces before the value, the buffer will have to
95 be moved before being registered
96
97 - if there are more than 255 spaces after the value, the buffer will have to
98 be moved before being registered
99
100 - we can use the empty header name as an indicator for a deleted header
101
102 - it would be wise to format a new request before sending lots of random
103 spaces to the servers.
104
105 - normal clients do not send such crap, so those operations *may* reasonably
106 be more expensive than the rest provided that other ones are very fast.
107
108It would be handy to have the following macros :
109
110 hdr_eon(hdr) => end of name
111 hdr_sov(hdr) => start of value
112 hdr_eof(hdr) => end of value
113 hdr_vlen(hdr) => length of value
114 hdr_hlen(hdr) => total header length
115
116
117A 48-bit encoding would look like this :
118
119 Connection: close \r\n
120 <---------+------+---+--------------> eoh = 16 bits
121 <-------->| | | eon = 8 bits
122 <--------------->| | sov = 8 bits
123 <---> vlen = 16 bits
124