blob: 82f9497d951b43ff4a7cd3a3f2c70acda7fead85 [file] [log] [blame]
Christopher Faulet47596d32018-10-22 09:17:28 +02001/*
2 * Functions to manipulate HTTP messages using the internal representation.
3 *
4 * Copyright (C) 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/config.h>
14#include <common/http.h>
15
16#include <proto/http_htx.h>
17#include <proto/htx.h>
18
19/* Finds the start line in the HTX message stopping at the first
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010020 * end-of-message. It returns NULL when not found, otherwise, it returns the
21 * pointer on the htx_sl structure. The HTX message may be updated if the
22 * start-line is returned following a lookup.
Christopher Faulet47596d32018-10-22 09:17:28 +020023 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010024struct htx_sl *http_find_stline(struct htx *htx)
Christopher Faulet47596d32018-10-22 09:17:28 +020025{
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010026 struct htx_sl *sl = NULL;
Christopher Faulet47596d32018-10-22 09:17:28 +020027 int32_t pos;
28
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010029 sl = htx_get_stline(htx);
30 if (sl)
31 return sl;
32
Christopher Faulet47596d32018-10-22 09:17:28 +020033 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
34 struct htx_blk *blk = htx_get_blk(htx, pos);
35 enum htx_blk_type type = htx_get_blk_type(blk);
36
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010037 if (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL) {
38 sl = htx_get_blk_ptr(htx, blk);
39 htx->sl_off = blk->addr;
40 break;
Christopher Faulet47596d32018-10-22 09:17:28 +020041 }
Christopher Faulet573fe732018-11-28 16:55:12 +010042
43 if (type == HTX_BLK_EOH || type == HTX_BLK_EOM)
Christopher Faulet47596d32018-10-22 09:17:28 +020044 break;
45 }
46
Christopher Faulet47596d32018-10-22 09:17:28 +020047 return sl;
48}
49
50/* Finds the first or next occurrence of header <name> in the HTX message <htx>
51 * using the context <ctx>. This structure holds everything necessary to use the
52 * header and find next occurrence. If its <blk> member is NULL, the header is
53 * searched from the beginning. Otherwise, the next occurrence is returned. The
54 * function returns 1 when it finds a value, and 0 when there is no more. It is
55 * designed to work with headers defined as comma-separated lists. If <full> is
56 * set, it works on full-line headers in whose comma is not a delimiter but is
57 * part of the syntax. A special case, if ctx->value is NULL when searching for
58 * a new values of a header, the current header is rescanned. This allows
59 * rescanning after a header deletion.
60 */
61int http_find_header(const struct htx *htx, const struct ist name,
62 struct http_hdr_ctx *ctx, int full)
63{
64 struct htx_blk *blk = ctx->blk;
65 struct ist n, v;
66 enum htx_blk_type type;
67 uint32_t pos;
68
69 if (blk) {
70 char *p;
71
72 pos = htx_get_blk_pos(htx, blk);
73 if (!ctx->value.ptr)
74 goto rescan_hdr;
75 if (full)
76 goto next_blk;
77 v = htx_get_blk_value(htx, blk);
78 p = ctx->value.ptr + ctx->value.len + ctx->lws_after;
79 v.len -= (p - v.ptr);
80 v.ptr = p;
81 if (!v.len)
82 goto next_blk;
83 /* Skip comma */
84 if (*(v.ptr) == ',') {
85 v.ptr++;
86 v.len--;
87 }
88
89 goto return_hdr;
90 }
91
92 if (!htx->used)
93 return 0;
94
95 pos = htx_get_head(htx);
96 while (1) {
97 rescan_hdr:
98 blk = htx_get_blk(htx, pos);
99 type = htx_get_blk_type(blk);
Christopher Faulet573fe732018-11-28 16:55:12 +0100100 if (type == HTX_BLK_EOH || type == HTX_BLK_EOM)
101 break;
Christopher Faulet47596d32018-10-22 09:17:28 +0200102 if (type != HTX_BLK_HDR)
103 goto next_blk;
104 if (name.len) {
105 /* If no name was passed, we want any header. So skip the comparison */
106 n = htx_get_blk_name(htx, blk);
107 if (!isteqi(n, name))
108 goto next_blk;
109 }
110 v = htx_get_blk_value(htx, blk);
111
112 return_hdr:
113 ctx->lws_before = 0;
114 ctx->lws_after = 0;
115 while (v.len && HTTP_IS_LWS(*v.ptr)) {
116 v.ptr++;
117 v.len--;
118 ctx->lws_before++;
119 }
120 if (!full)
121 v.len = http_find_hdr_value_end(v.ptr, v.ptr + v.len) - v.ptr;
122 while (v.len && HTTP_IS_LWS(*(v.ptr + v.len - 1))) {
123 v.len--;
124 ctx->lws_after++;
125 }
126 if (!v.len)
127 goto next_blk;
128 ctx->blk = blk;
129 ctx->value = v;
130 return 1;
131
132 next_blk:
133 if (pos == htx->tail)
134 break;
135 pos++;
136 if (pos >= htx->wrap)
137 pos = 0;
138 }
139
140 ctx->blk = NULL;
141 ctx->value = ist("");
142 ctx->lws_before = ctx->lws_after = 0;
143 return 0;
144}
145
146/* Adds a header block int the HTX message <htx>, just before the EOH block. It
147 * returns 1 on success, otherwise it returns 0.
148 */
149int http_add_header(struct htx *htx, const struct ist n, const struct ist v)
150{
151 struct htx_blk *blk;
152 enum htx_blk_type type = htx_get_tail_type(htx);
153 int32_t prev;
154
155 blk = htx_add_header(htx, n, v);
156 if (!blk)
157 return 0;
158
159 if (unlikely(type < HTX_BLK_EOH))
160 return 1;
161
162 /* <blk> is the head, swap it iteratively with its predecessor to place
163 * it just before the end-of-header block. So blocks remains ordered. */
164 for (prev = htx_get_prev(htx, htx->tail); prev != -1; prev = htx_get_prev(htx, prev)) {
165 struct htx_blk *pblk = htx_get_blk(htx, prev);
166 enum htx_blk_type type = htx_get_blk_type(pblk);
167
168 /* Swap .addr and .info fields */
169 blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
170 blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info;
171
172 if (blk->addr == pblk->addr)
173 blk->addr += htx_get_blksz(pblk);
174 htx->front = prev;
175
176 /* Stop when end-of-header is reached */
177 if (type == HTX_BLK_EOH)
178 break;
179
180 blk = pblk;
181 }
182 return 1;
183}
184
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100185/* Replaces parts of the start-line of the HTX message <htx>. It returns 1 on
186 * success, otherwise it returns 0. The right block is search in the HTX
Christopher Faulet47596d32018-10-22 09:17:28 +0200187 * message.
188 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100189int http_replace_stline(struct htx *htx, const struct ist p1, const struct ist p2, const struct ist p3)
Christopher Faulet47596d32018-10-22 09:17:28 +0200190{
191 int32_t pos;
192
193 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100194 struct htx_blk *blk = htx_get_blk(htx, pos);
195 enum htx_blk_type type = htx_get_blk_type(blk);
Christopher Faulet47596d32018-10-22 09:17:28 +0200196
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100197 if (htx->sl_off == blk->addr) {
198 if (!htx_replace_stline(htx, blk, p1, p2, p3))
Christopher Faulet47596d32018-10-22 09:17:28 +0200199 return 0;
200 return 1;
201 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200202
Christopher Faulet47596d32018-10-22 09:17:28 +0200203 if (type == HTX_BLK_EOM)
204 break;
205 }
206
207 return 0;
208}
209
Christopher Faulete010c802018-10-24 10:36:45 +0200210/* Replace the request method in the HTX message <htx> by <meth>. It returns 1
211 * on success, otherwise 0.
212 */
213int http_replace_req_meth(struct htx *htx, const struct ist meth)
214{
215 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100216 struct htx_sl *sl = http_find_stline(htx);
217 struct ist uri, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200218
219 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100220 chunk_memcat(temp, HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl)); /* uri */
221 uri = ist2(temp->area, HTX_SL_REQ_ULEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200222
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100223 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
224 vsn = ist2(temp->area + uri.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200225
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100226 /* create the new start line */
227 sl->info.req.meth = find_http_meth(meth.ptr, meth.len);
228 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200229}
230
231/* Replace the request uri in the HTX message <htx> by <uri>. It returns 1 on
232 * success, otherwise 0.
233 */
234int http_replace_req_uri(struct htx *htx, const struct ist uri)
235{
236 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100237 struct htx_sl *sl = http_find_stline(htx);
238 struct ist meth, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200239
240 /* Start by copying old method and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100241 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
242 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200243
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100244 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
245 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200246
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100247 /* create the new start line */
248 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200249}
250
251/* Replace the request path in the HTX message <htx> by <path>. The host part
252 * and the query string are preserved. It returns 1 on success, otherwise 0.
253 */
254int http_replace_req_path(struct htx *htx, const struct ist path)
255{
256 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100257 struct htx_sl *sl = http_find_stline(htx);
258 struct ist meth, uri, vsn, p;
Christopher Faulete010c802018-10-24 10:36:45 +0200259 size_t plen = 0;
260
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100261 uri = htx_sl_req_uri(sl);
262 p = http_get_path(uri);
Christopher Faulete010c802018-10-24 10:36:45 +0200263 if (!p.ptr)
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100264 p = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200265 while (plen < p.len && *(p.ptr + plen) != '?')
266 plen++;
267
268 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100269 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
270 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200271
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100272 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
273 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
274
275 chunk_memcat(temp, uri.ptr, p.ptr - uri.ptr); /* uri: host part */
Christopher Faulete010c802018-10-24 10:36:45 +0200276 chunk_memcat(temp, path.ptr, path.len); /* uri: new path */
277 chunk_memcat(temp, p.ptr + plen, p.len - plen); /* uri: QS part */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100278 uri = ist2(temp->area + meth.len + vsn.len, uri.len - plen + path.len);
Christopher Faulete010c802018-10-24 10:36:45 +0200279
280 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100281 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200282}
283
284/* Replace the request query-string in the HTX message <htx> by <query>. The
285 * host part and the path are preserved. It returns 1 on success, otherwise
286 * 0.
287 */
288int http_replace_req_query(struct htx *htx, const struct ist query)
289{
290 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100291 struct htx_sl *sl = http_find_stline(htx);
292 struct ist meth, uri, vsn, q;
Christopher Faulete010c802018-10-24 10:36:45 +0200293 int offset = 1;
294
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100295 uri = htx_sl_req_uri(sl);
296 q = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200297 while (q.len > 0 && *(q.ptr) != '?') {
298 q.ptr++;
299 q.len--;
300 }
301
302 /* skip the question mark or indicate that we must insert it
303 * (but only if the format string is not empty then).
304 */
305 if (q.len) {
306 q.ptr++;
307 q.len--;
308 }
309 else if (query.len > 1)
310 offset = 0;
311
312 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100313 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
314 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200315
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100316 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
317 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200318
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100319 chunk_memcat(temp, uri.ptr, q.ptr - uri.ptr); /* uri: host + path part */
320 chunk_memcat(temp, query.ptr + offset, query.len - offset); /* uri: new QS */
321 uri = ist2(temp->area + meth.len + vsn.len, uri.len - q.len + query.len - offset);
Christopher Faulete010c802018-10-24 10:36:45 +0200322
323 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100324 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200325}
326
327/* Replace the response status in the HTX message <htx> by <status>. It returns
328 * 1 on success, otherwise 0.
329*/
330int http_replace_res_status(struct htx *htx, const struct ist status)
331{
332 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100333 struct htx_sl *sl = http_find_stline(htx);
334 struct ist vsn, reason;
Christopher Faulete010c802018-10-24 10:36:45 +0200335
336 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100337 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
338 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200339
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100340 chunk_memcat(temp, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl)); /* reason */
341 reason = ist2(temp->area + vsn.len, HTX_SL_RES_RLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200342
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100343 /* create the new start line */
344 sl->info.res.status = strl2ui(status.ptr, status.len);
345 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200346}
347
348/* Replace the response reason in the HTX message <htx> by <reason>. It returns
349 * 1 on success, otherwise 0.
350*/
351int http_replace_res_reason(struct htx *htx, const struct ist reason)
352{
353 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100354 struct htx_sl *sl = http_find_stline(htx);
355 struct ist vsn, status;
Christopher Faulete010c802018-10-24 10:36:45 +0200356
357 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100358 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
359 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200360
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100361 chunk_memcat(temp, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl)); /* code */
362 status = ist2(temp->area + vsn.len, HTX_SL_RES_CLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200363
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100364 /* create the new start line */
365 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200366}
367
Christopher Faulet47596d32018-10-22 09:17:28 +0200368/* Replaces a part of a header value referenced in the context <ctx> by
369 * <data>. It returns 1 on success, otherwise it returns 0. The context is
370 * updated if necessary.
371 */
372int http_replace_header_value(struct htx *htx, struct http_hdr_ctx *ctx, const struct ist data)
373{
374 struct htx_blk *blk = ctx->blk;
375 char *start;
376 struct ist v;
377 uint32_t len, off;
378
379 if (!blk)
380 return 0;
381
382 v = htx_get_blk_value(htx, blk);
383 start = ctx->value.ptr - ctx->lws_before;
384 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
385 off = start - v.ptr;
386
387 blk = htx_replace_blk_value(htx, blk, ist2(start, len), data);
388 if (!blk)
389 return 0;
390
391 v = htx_get_blk_value(htx, blk);
392 ctx->blk = blk;
393 ctx->value.ptr = v.ptr + off;
394 ctx->value.len = data.len;
395 ctx->lws_before = ctx->lws_after = 0;
396
397 return 1;
398}
399
400/* Fully replaces a header referenced in the context <ctx> by the name <name>
401 * with the value <value>. It returns 1 on success, otherwise it returns 0. The
402 * context is updated if necessary.
403 */
404int http_replace_header(struct htx *htx, struct http_hdr_ctx *ctx,
405 const struct ist name, const struct ist value)
406{
407 struct htx_blk *blk = ctx->blk;
408
409 if (!blk)
410 return 0;
411
412 blk = htx_replace_header(htx, blk, name, value);
413 if (!blk)
414 return 0;
415
416 ctx->blk = blk;
417 ctx->value = ist(NULL);
418 ctx->lws_before = ctx->lws_after = 0;
419
420 return 1;
421}
422
423/* Remove one value of a header. This only works on a <ctx> returned by
424 * http_find_header function. The value is removed, as well as surrounding commas
425 * if any. If the removed value was alone, the whole header is removed. The
426 * <ctx> is always updated accordingly, as well as the HTX message <htx>. It
427 * returns 1 on success. Otherwise, it returns 0. The <ctx> is always left in a
428 * form that can be handled by http_find_header() to find next occurrence.
429 */
430int http_remove_header(struct htx *htx, struct http_hdr_ctx *ctx)
431{
432 struct htx_blk *blk = ctx->blk;
433 char *start;
434 struct ist v;
435 uint32_t len;
436
437 if (!blk)
438 return 0;
439
440 start = ctx->value.ptr - ctx->lws_before;
441 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
442
443 v = htx_get_blk_value(htx, blk);
444 if (len == v.len) {
445 blk = htx_remove_blk(htx, blk);
446 if (blk || !htx->used) {
447 ctx->blk = blk;
448 ctx->value = ist2(NULL, 0);
449 ctx->lws_before = ctx->lws_after = 0;
450 }
451 else {
452 ctx->blk = htx_get_blk(htx, htx->tail);
453 ctx->value = htx_get_blk_value(htx, ctx->blk);
454 ctx->lws_before = ctx->lws_after = 0;
455 }
456 return 1;
457 }
458
459 /* This was not the only value of this header. We have to remove the
460 * part pointed by ctx->value. If it is the last entry of the list, we
461 * remove the last separator.
462 */
463 if (start == v.ptr) {
464 /* It's the first header part but not the only one. So remove
465 * the comma after it. */
466 len++;
467 }
468 else {
469 /* There is at least one header part before the removed one. So
470 * remove the comma between them. */
471 start--;
472 len++;
473 }
474 /* Update the block content and its len */
475 memmove(start, start+len, v.len-len);
476 htx_set_blk_value_len(blk, v.len-len);
477
478 /* Update HTX msg */
479 htx->data -= len;
480
481 /* Finally update the ctx */
482 ctx->value.ptr = start;
483 ctx->value.len = 0;
484 ctx->lws_before = ctx->lws_after = 0;
485
486 return 1;
487}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200488
489
490/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
491 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
492 * performed over the whole headers. Otherwise it must contain a valid header
493 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
494 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
495 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
496 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
497 * -1. The value fetch stops at commas, so this function is suited for use with
498 * list headers.
499 * The return value is 0 if nothing was found, or non-zero otherwise.
500 */
501unsigned int http_get_htx_hdr(const struct htx *htx, const struct ist hdr,
502 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
503{
504 struct http_hdr_ctx local_ctx;
505 struct ist val_hist[MAX_HDR_HISTORY];
506 unsigned int hist_idx;
507 int found;
508
509 if (!ctx) {
510 local_ctx.blk = NULL;
511 ctx = &local_ctx;
512 }
513
514 if (occ >= 0) {
515 /* search from the beginning */
516 while (http_find_header(htx, hdr, ctx, 0)) {
517 occ--;
518 if (occ <= 0) {
519 *vptr = ctx->value.ptr;
520 *vlen = ctx->value.len;
521 return 1;
522 }
523 }
524 return 0;
525 }
526
527 /* negative occurrence, we scan all the list then walk back */
528 if (-occ > MAX_HDR_HISTORY)
529 return 0;
530
531 found = hist_idx = 0;
532 while (http_find_header(htx, hdr, ctx, 0)) {
533 val_hist[hist_idx] = ctx->value;
534 if (++hist_idx >= MAX_HDR_HISTORY)
535 hist_idx = 0;
536 found++;
537 }
538 if (-occ > found)
539 return 0;
540
541 /* OK now we have the last occurrence in [hist_idx-1], and we need to
542 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
543 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
544 * to remain in the 0..9 range.
545 */
546 hist_idx += occ + MAX_HDR_HISTORY;
547 if (hist_idx >= MAX_HDR_HISTORY)
548 hist_idx -= MAX_HDR_HISTORY;
549 *vptr = val_hist[hist_idx].ptr;
550 *vlen = val_hist[hist_idx].len;
551 return 1;
552}
553
554/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
555 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
556 * performed over the whole headers. Otherwise it must contain a valid header
557 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
558 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
559 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
560 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
561 * -1. This function differs from http_get_hdr() in that it only returns full
562 * line header values and does not stop at commas.
563 * The return value is 0 if nothing was found, or non-zero otherwise.
564 */
565unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr,
566 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
567{
568 struct http_hdr_ctx local_ctx;
569 struct ist val_hist[MAX_HDR_HISTORY];
570 unsigned int hist_idx;
571 int found;
572
573 if (!ctx) {
574 local_ctx.blk = NULL;
575 ctx = &local_ctx;
576 }
577
578 if (occ >= 0) {
579 /* search from the beginning */
580 while (http_find_header(htx, hdr, ctx, 1)) {
581 occ--;
582 if (occ <= 0) {
583 *vptr = ctx->value.ptr;
584 *vlen = ctx->value.len;
585 return 1;
586 }
587 }
588 return 0;
589 }
590
591 /* negative occurrence, we scan all the list then walk back */
592 if (-occ > MAX_HDR_HISTORY)
593 return 0;
594
595 found = hist_idx = 0;
596 while (http_find_header(htx, hdr, ctx, 1)) {
597 val_hist[hist_idx] = ctx->value;
598 if (++hist_idx >= MAX_HDR_HISTORY)
599 hist_idx = 0;
600 found++;
601 }
602 if (-occ > found)
603 return 0;
604
605 /* OK now we have the last occurrence in [hist_idx-1], and we need to
606 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
607 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
608 * to remain in the 0..9 range.
609 */
610 hist_idx += occ + MAX_HDR_HISTORY;
611 if (hist_idx >= MAX_HDR_HISTORY)
612 hist_idx -= MAX_HDR_HISTORY;
613 *vptr = val_hist[hist_idx].ptr;
614 *vlen = val_hist[hist_idx].len;
615 return 1;
616}