blob: 15865d5022a1803e6f8b13fc38f3472f58fa35f1 [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>
Christopher Fauleta7b677c2018-11-29 16:48:49 +010014#include <common/cfgparse.h>
Willy Tarreauafba57a2018-12-11 13:44:24 +010015#include <common/h1.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020016#include <common/http.h>
Willy Tarreaub96b77e2018-12-11 10:22:41 +010017#include <common/htx.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020018
19#include <proto/http_htx.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020020
Christopher Fauleta7b677c2018-11-29 16:48:49 +010021struct buffer htx_err_chunks[HTTP_ERR_SIZE];
22
Christopher Faulet297fbb42019-05-13 14:41:27 +020023/* Returns the next unporocessed start line in the HTX message. It returns NULL
24 * is the start-line is undefined (sl_pos == 1). Otherwise, it returns the
25 * pointer on the htx_sl structure.
Christopher Faulet47596d32018-10-22 09:17:28 +020026 */
Christopher Faulet297fbb42019-05-13 14:41:27 +020027struct htx_sl *http_get_stline(struct htx *htx)
Christopher Faulet47596d32018-10-22 09:17:28 +020028{
Christopher Faulet297fbb42019-05-13 14:41:27 +020029 struct htx_blk *blk;
Christopher Faulet573fe732018-11-28 16:55:12 +010030
Christopher Faulet297fbb42019-05-13 14:41:27 +020031 if (htx->sl_pos == -1)
32 return NULL;
Christopher Faulet47596d32018-10-22 09:17:28 +020033
Christopher Faulet297fbb42019-05-13 14:41:27 +020034 blk = htx_get_blk(htx, htx->sl_pos);
35 if (!blk)
36 return NULL;
37 return htx_get_blk_ptr(htx, blk);
Christopher Faulet47596d32018-10-22 09:17:28 +020038}
39
40/* Finds the first or next occurrence of header <name> in the HTX message <htx>
41 * using the context <ctx>. This structure holds everything necessary to use the
42 * header and find next occurrence. If its <blk> member is NULL, the header is
43 * searched from the beginning. Otherwise, the next occurrence is returned. The
44 * function returns 1 when it finds a value, and 0 when there is no more. It is
45 * designed to work with headers defined as comma-separated lists. If <full> is
46 * set, it works on full-line headers in whose comma is not a delimiter but is
47 * part of the syntax. A special case, if ctx->value is NULL when searching for
48 * a new values of a header, the current header is rescanned. This allows
49 * rescanning after a header deletion.
50 */
51int http_find_header(const struct htx *htx, const struct ist name,
52 struct http_hdr_ctx *ctx, int full)
53{
54 struct htx_blk *blk = ctx->blk;
55 struct ist n, v;
56 enum htx_blk_type type;
Christopher Faulet47596d32018-10-22 09:17:28 +020057
58 if (blk) {
59 char *p;
60
Christopher Faulet47596d32018-10-22 09:17:28 +020061 if (!ctx->value.ptr)
62 goto rescan_hdr;
63 if (full)
64 goto next_blk;
65 v = htx_get_blk_value(htx, blk);
66 p = ctx->value.ptr + ctx->value.len + ctx->lws_after;
67 v.len -= (p - v.ptr);
68 v.ptr = p;
69 if (!v.len)
70 goto next_blk;
71 /* Skip comma */
72 if (*(v.ptr) == ',') {
73 v.ptr++;
74 v.len--;
75 }
76
77 goto return_hdr;
78 }
79
80 if (!htx->used)
81 return 0;
82
Christopher Fauleta3f15502019-05-13 15:27:23 +020083 for (blk = htx_get_first_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
Christopher Faulet47596d32018-10-22 09:17:28 +020084 rescan_hdr:
Christopher Faulet47596d32018-10-22 09:17:28 +020085 type = htx_get_blk_type(blk);
Christopher Faulet573fe732018-11-28 16:55:12 +010086 if (type == HTX_BLK_EOH || type == HTX_BLK_EOM)
87 break;
Christopher Faulet47596d32018-10-22 09:17:28 +020088 if (type != HTX_BLK_HDR)
Christopher Faulet28f29c72019-04-30 17:55:45 +020089 continue;
Christopher Faulet47596d32018-10-22 09:17:28 +020090 if (name.len) {
91 /* If no name was passed, we want any header. So skip the comparison */
92 n = htx_get_blk_name(htx, blk);
93 if (!isteqi(n, name))
94 goto next_blk;
95 }
96 v = htx_get_blk_value(htx, blk);
97
98 return_hdr:
99 ctx->lws_before = 0;
100 ctx->lws_after = 0;
101 while (v.len && HTTP_IS_LWS(*v.ptr)) {
102 v.ptr++;
103 v.len--;
104 ctx->lws_before++;
105 }
106 if (!full)
107 v.len = http_find_hdr_value_end(v.ptr, v.ptr + v.len) - v.ptr;
108 while (v.len && HTTP_IS_LWS(*(v.ptr + v.len - 1))) {
109 v.len--;
110 ctx->lws_after++;
111 }
112 if (!v.len)
Christopher Faulet28f29c72019-04-30 17:55:45 +0200113 continue;
Christopher Faulet47596d32018-10-22 09:17:28 +0200114 ctx->blk = blk;
115 ctx->value = v;
116 return 1;
117
118 next_blk:
Christopher Faulet28f29c72019-04-30 17:55:45 +0200119 ;
Christopher Faulet47596d32018-10-22 09:17:28 +0200120 }
121
122 ctx->blk = NULL;
123 ctx->value = ist("");
124 ctx->lws_before = ctx->lws_after = 0;
125 return 0;
126}
127
128/* Adds a header block int the HTX message <htx>, just before the EOH block. It
129 * returns 1 on success, otherwise it returns 0.
130 */
131int http_add_header(struct htx *htx, const struct ist n, const struct ist v)
132{
133 struct htx_blk *blk;
134 enum htx_blk_type type = htx_get_tail_type(htx);
135 int32_t prev;
136
137 blk = htx_add_header(htx, n, v);
138 if (!blk)
139 return 0;
140
141 if (unlikely(type < HTX_BLK_EOH))
142 return 1;
143
144 /* <blk> is the head, swap it iteratively with its predecessor to place
145 * it just before the end-of-header block. So blocks remains ordered. */
Christopher Fauleta3f15502019-05-13 15:27:23 +0200146 for (prev = htx_get_prev(htx, htx->tail); prev != htx->sl_pos; prev = htx_get_prev(htx, prev)) {
Christopher Faulet47596d32018-10-22 09:17:28 +0200147 struct htx_blk *pblk = htx_get_blk(htx, prev);
148 enum htx_blk_type type = htx_get_blk_type(pblk);
149
150 /* Swap .addr and .info fields */
151 blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
152 blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info;
153
154 if (blk->addr == pblk->addr)
155 blk->addr += htx_get_blksz(pblk);
156 htx->front = prev;
157
158 /* Stop when end-of-header is reached */
159 if (type == HTX_BLK_EOH)
160 break;
161
162 blk = pblk;
163 }
Christopher Faulet05aab642019-04-11 13:43:57 +0200164
165 if (htx_get_blk_pos(htx, blk) != htx->front)
166 htx_defrag(htx, NULL);
167
Christopher Faulet47596d32018-10-22 09:17:28 +0200168 return 1;
169}
170
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100171/* Replaces parts of the start-line of the HTX message <htx>. It returns 1 on
172 * success, otherwise it returns 0. The right block is search in the HTX
Christopher Faulet47596d32018-10-22 09:17:28 +0200173 * message.
174 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100175int 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 +0200176{
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200177 struct htx_blk *blk;
Christopher Faulet47596d32018-10-22 09:17:28 +0200178
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200179 if (htx->sl_pos == -1)
180 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200181
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200182 blk = htx_get_blk(htx, htx->sl_pos);
183 if (!htx_replace_stline(htx, blk, p1, p2, p3))
184 return 0;
185 return 1;
Christopher Faulet47596d32018-10-22 09:17:28 +0200186}
187
Christopher Faulete010c802018-10-24 10:36:45 +0200188/* Replace the request method in the HTX message <htx> by <meth>. It returns 1
189 * on success, otherwise 0.
190 */
191int http_replace_req_meth(struct htx *htx, const struct ist meth)
192{
193 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200194 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100195 struct ist uri, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200196
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100197 if (!sl)
198 return 0;
199
Christopher Faulete010c802018-10-24 10:36:45 +0200200 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100201 chunk_memcat(temp, HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl)); /* uri */
202 uri = ist2(temp->area, HTX_SL_REQ_ULEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200203
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100204 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
205 vsn = ist2(temp->area + uri.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200206
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100207 /* create the new start line */
208 sl->info.req.meth = find_http_meth(meth.ptr, meth.len);
209 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200210}
211
212/* Replace the request uri in the HTX message <htx> by <uri>. It returns 1 on
213 * success, otherwise 0.
214 */
215int http_replace_req_uri(struct htx *htx, const struct ist uri)
216{
217 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200218 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100219 struct ist meth, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200220
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100221 if (!sl)
222 return 0;
223
Christopher Faulete010c802018-10-24 10:36:45 +0200224 /* Start by copying old method and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100225 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
226 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200227
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100228 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
229 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200230
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100231 /* create the new start line */
232 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200233}
234
235/* Replace the request path in the HTX message <htx> by <path>. The host part
236 * and the query string are preserved. It returns 1 on success, otherwise 0.
237 */
238int http_replace_req_path(struct htx *htx, const struct ist path)
239{
240 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200241 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100242 struct ist meth, uri, vsn, p;
Christopher Faulete010c802018-10-24 10:36:45 +0200243 size_t plen = 0;
244
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100245 if (!sl)
246 return 0;
247
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100248 uri = htx_sl_req_uri(sl);
249 p = http_get_path(uri);
Christopher Faulete010c802018-10-24 10:36:45 +0200250 if (!p.ptr)
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100251 p = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200252 while (plen < p.len && *(p.ptr + plen) != '?')
253 plen++;
254
255 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100256 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
257 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200258
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100259 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
260 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
261
262 chunk_memcat(temp, uri.ptr, p.ptr - uri.ptr); /* uri: host part */
Christopher Faulete010c802018-10-24 10:36:45 +0200263 chunk_memcat(temp, path.ptr, path.len); /* uri: new path */
264 chunk_memcat(temp, p.ptr + plen, p.len - plen); /* uri: QS part */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100265 uri = ist2(temp->area + meth.len + vsn.len, uri.len - plen + path.len);
Christopher Faulete010c802018-10-24 10:36:45 +0200266
267 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100268 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200269}
270
271/* Replace the request query-string in the HTX message <htx> by <query>. The
272 * host part and the path are preserved. It returns 1 on success, otherwise
273 * 0.
274 */
275int http_replace_req_query(struct htx *htx, const struct ist query)
276{
277 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200278 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100279 struct ist meth, uri, vsn, q;
Christopher Faulete010c802018-10-24 10:36:45 +0200280 int offset = 1;
281
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100282 if (!sl)
283 return 0;
284
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100285 uri = htx_sl_req_uri(sl);
286 q = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200287 while (q.len > 0 && *(q.ptr) != '?') {
288 q.ptr++;
289 q.len--;
290 }
291
292 /* skip the question mark or indicate that we must insert it
293 * (but only if the format string is not empty then).
294 */
295 if (q.len) {
296 q.ptr++;
297 q.len--;
298 }
299 else if (query.len > 1)
300 offset = 0;
301
302 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100303 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
304 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200305
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100306 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
307 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200308
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100309 chunk_memcat(temp, uri.ptr, q.ptr - uri.ptr); /* uri: host + path part */
310 chunk_memcat(temp, query.ptr + offset, query.len - offset); /* uri: new QS */
311 uri = ist2(temp->area + meth.len + vsn.len, uri.len - q.len + query.len - offset);
Christopher Faulete010c802018-10-24 10:36:45 +0200312
313 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100314 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200315}
316
317/* Replace the response status in the HTX message <htx> by <status>. It returns
318 * 1 on success, otherwise 0.
319*/
320int http_replace_res_status(struct htx *htx, const struct ist status)
321{
322 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200323 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100324 struct ist vsn, reason;
Christopher Faulete010c802018-10-24 10:36:45 +0200325
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100326 if (!sl)
327 return 0;
328
Christopher Faulete010c802018-10-24 10:36:45 +0200329 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100330 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
331 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200332
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100333 chunk_memcat(temp, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl)); /* reason */
334 reason = ist2(temp->area + vsn.len, HTX_SL_RES_RLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200335
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100336 /* create the new start line */
337 sl->info.res.status = strl2ui(status.ptr, status.len);
338 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200339}
340
341/* Replace the response reason in the HTX message <htx> by <reason>. It returns
342 * 1 on success, otherwise 0.
343*/
344int http_replace_res_reason(struct htx *htx, const struct ist reason)
345{
346 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200347 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100348 struct ist vsn, status;
Christopher Faulete010c802018-10-24 10:36:45 +0200349
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100350 if (!sl)
351 return 0;
352
Christopher Faulete010c802018-10-24 10:36:45 +0200353 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100354 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
355 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200356
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100357 chunk_memcat(temp, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl)); /* code */
358 status = ist2(temp->area + vsn.len, HTX_SL_RES_CLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200359
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100360 /* create the new start line */
361 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200362}
363
Christopher Faulet47596d32018-10-22 09:17:28 +0200364/* Replaces a part of a header value referenced in the context <ctx> by
365 * <data>. It returns 1 on success, otherwise it returns 0. The context is
366 * updated if necessary.
367 */
368int http_replace_header_value(struct htx *htx, struct http_hdr_ctx *ctx, const struct ist data)
369{
370 struct htx_blk *blk = ctx->blk;
371 char *start;
372 struct ist v;
373 uint32_t len, off;
374
375 if (!blk)
376 return 0;
377
378 v = htx_get_blk_value(htx, blk);
379 start = ctx->value.ptr - ctx->lws_before;
380 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
381 off = start - v.ptr;
382
383 blk = htx_replace_blk_value(htx, blk, ist2(start, len), data);
384 if (!blk)
385 return 0;
386
387 v = htx_get_blk_value(htx, blk);
388 ctx->blk = blk;
389 ctx->value.ptr = v.ptr + off;
390 ctx->value.len = data.len;
391 ctx->lws_before = ctx->lws_after = 0;
392
393 return 1;
394}
395
396/* Fully replaces a header referenced in the context <ctx> by the name <name>
397 * with the value <value>. It returns 1 on success, otherwise it returns 0. The
398 * context is updated if necessary.
399 */
400int http_replace_header(struct htx *htx, struct http_hdr_ctx *ctx,
401 const struct ist name, const struct ist value)
402{
403 struct htx_blk *blk = ctx->blk;
404
405 if (!blk)
406 return 0;
407
408 blk = htx_replace_header(htx, blk, name, value);
409 if (!blk)
410 return 0;
411
412 ctx->blk = blk;
413 ctx->value = ist(NULL);
414 ctx->lws_before = ctx->lws_after = 0;
415
416 return 1;
417}
418
419/* Remove one value of a header. This only works on a <ctx> returned by
420 * http_find_header function. The value is removed, as well as surrounding commas
421 * if any. If the removed value was alone, the whole header is removed. The
422 * <ctx> is always updated accordingly, as well as the HTX message <htx>. It
423 * returns 1 on success. Otherwise, it returns 0. The <ctx> is always left in a
424 * form that can be handled by http_find_header() to find next occurrence.
425 */
426int http_remove_header(struct htx *htx, struct http_hdr_ctx *ctx)
427{
428 struct htx_blk *blk = ctx->blk;
429 char *start;
430 struct ist v;
431 uint32_t len;
432
433 if (!blk)
434 return 0;
435
436 start = ctx->value.ptr - ctx->lws_before;
437 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
438
439 v = htx_get_blk_value(htx, blk);
440 if (len == v.len) {
441 blk = htx_remove_blk(htx, blk);
442 if (blk || !htx->used) {
443 ctx->blk = blk;
444 ctx->value = ist2(NULL, 0);
445 ctx->lws_before = ctx->lws_after = 0;
446 }
447 else {
448 ctx->blk = htx_get_blk(htx, htx->tail);
449 ctx->value = htx_get_blk_value(htx, ctx->blk);
450 ctx->lws_before = ctx->lws_after = 0;
451 }
452 return 1;
453 }
454
455 /* This was not the only value of this header. We have to remove the
456 * part pointed by ctx->value. If it is the last entry of the list, we
457 * remove the last separator.
458 */
459 if (start == v.ptr) {
460 /* It's the first header part but not the only one. So remove
461 * the comma after it. */
462 len++;
463 }
464 else {
465 /* There is at least one header part before the removed one. So
466 * remove the comma between them. */
467 start--;
468 len++;
469 }
470 /* Update the block content and its len */
471 memmove(start, start+len, v.len-len);
472 htx_set_blk_value_len(blk, v.len-len);
473
474 /* Update HTX msg */
475 htx->data -= len;
476
477 /* Finally update the ctx */
478 ctx->value.ptr = start;
479 ctx->value.len = 0;
480 ctx->lws_before = ctx->lws_after = 0;
481
482 return 1;
483}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200484
485
486/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
487 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
488 * performed over the whole headers. Otherwise it must contain a valid header
489 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
490 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
491 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
492 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
493 * -1. The value fetch stops at commas, so this function is suited for use with
494 * list headers.
495 * The return value is 0 if nothing was found, or non-zero otherwise.
496 */
497unsigned int http_get_htx_hdr(const struct htx *htx, const struct ist hdr,
498 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
499{
500 struct http_hdr_ctx local_ctx;
501 struct ist val_hist[MAX_HDR_HISTORY];
502 unsigned int hist_idx;
503 int found;
504
505 if (!ctx) {
506 local_ctx.blk = NULL;
507 ctx = &local_ctx;
508 }
509
510 if (occ >= 0) {
511 /* search from the beginning */
512 while (http_find_header(htx, hdr, ctx, 0)) {
513 occ--;
514 if (occ <= 0) {
515 *vptr = ctx->value.ptr;
516 *vlen = ctx->value.len;
517 return 1;
518 }
519 }
520 return 0;
521 }
522
523 /* negative occurrence, we scan all the list then walk back */
524 if (-occ > MAX_HDR_HISTORY)
525 return 0;
526
527 found = hist_idx = 0;
528 while (http_find_header(htx, hdr, ctx, 0)) {
529 val_hist[hist_idx] = ctx->value;
530 if (++hist_idx >= MAX_HDR_HISTORY)
531 hist_idx = 0;
532 found++;
533 }
534 if (-occ > found)
535 return 0;
536
537 /* OK now we have the last occurrence in [hist_idx-1], and we need to
538 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
539 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
540 * to remain in the 0..9 range.
541 */
542 hist_idx += occ + MAX_HDR_HISTORY;
543 if (hist_idx >= MAX_HDR_HISTORY)
544 hist_idx -= MAX_HDR_HISTORY;
545 *vptr = val_hist[hist_idx].ptr;
546 *vlen = val_hist[hist_idx].len;
547 return 1;
548}
549
550/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
551 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
552 * performed over the whole headers. Otherwise it must contain a valid header
553 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
554 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
555 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
556 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
557 * -1. This function differs from http_get_hdr() in that it only returns full
558 * line header values and does not stop at commas.
559 * The return value is 0 if nothing was found, or non-zero otherwise.
560 */
561unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr,
562 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
563{
564 struct http_hdr_ctx local_ctx;
565 struct ist val_hist[MAX_HDR_HISTORY];
566 unsigned int hist_idx;
567 int found;
568
569 if (!ctx) {
570 local_ctx.blk = NULL;
571 ctx = &local_ctx;
572 }
573
574 if (occ >= 0) {
575 /* search from the beginning */
576 while (http_find_header(htx, hdr, ctx, 1)) {
577 occ--;
578 if (occ <= 0) {
579 *vptr = ctx->value.ptr;
580 *vlen = ctx->value.len;
581 return 1;
582 }
583 }
584 return 0;
585 }
586
587 /* negative occurrence, we scan all the list then walk back */
588 if (-occ > MAX_HDR_HISTORY)
589 return 0;
590
591 found = hist_idx = 0;
592 while (http_find_header(htx, hdr, ctx, 1)) {
593 val_hist[hist_idx] = ctx->value;
594 if (++hist_idx >= MAX_HDR_HISTORY)
595 hist_idx = 0;
596 found++;
597 }
598 if (-occ > found)
599 return 0;
600
601 /* OK now we have the last occurrence in [hist_idx-1], and we need to
602 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
603 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
604 * to remain in the 0..9 range.
605 */
606 hist_idx += occ + MAX_HDR_HISTORY;
607 if (hist_idx >= MAX_HDR_HISTORY)
608 hist_idx -= MAX_HDR_HISTORY;
609 *vptr = val_hist[hist_idx].ptr;
610 *vlen = val_hist[hist_idx].len;
611 return 1;
612}
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100613
614static struct htx *http_str_to_htx(struct buffer *buf, struct ist raw)
615{
616 struct htx *htx;
617 struct htx_sl *sl;
618 struct h1m h1m;
619 struct http_hdr hdrs[MAX_HTTP_HDR];
620 union h1_sl h1sl;
621 unsigned int flags = HTX_SL_F_IS_RESP;
622 int ret = 0;
623
624 buf->size = global.tune.bufsize;
625 buf->area = (char *)malloc(buf->size);
626 if (!buf->area)
627 goto error;
628 b_reset(buf);
629
630 h1m_init_res(&h1m);
631 h1m.flags |= H1_MF_NO_PHDR;
632 ret = h1_headers_to_hdr_list(raw.ptr, raw.ptr + raw.len,
633 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
634 if (ret <= 0)
635 goto error;
636
637 if (unlikely(h1sl.st.v.len != 8))
638 goto error;
639 if ((*(h1sl.st.v.ptr + 5) > '1') ||
640 ((*(h1sl.st.v.ptr + 5) == '1') && (*(h1sl.st.v.ptr + 7) >= '1')))
641 h1m.flags |= H1_MF_VER_11;
642
643 if (h1m.flags & H1_MF_VER_11)
644 flags |= HTX_SL_F_VER_11;
645 if (h1m.flags & H1_MF_XFER_ENC)
646 flags |= HTX_SL_F_XFER_ENC;
647 if (h1m.flags & H1_MF_XFER_LEN) {
648 flags |= HTX_SL_F_XFER_LEN;
649 if (h1m.flags & H1_MF_CHNK)
650 goto error; /* Unsupported because there is no body parsing */
651 else if (h1m.flags & H1_MF_CLEN) {
652 flags |= HTX_SL_F_CLEN;
653 if (h1m.body_len == 0)
654 flags |= HTX_SL_F_BODYLESS;
655 }
656 }
657
658 htx = htx_from_buf(buf);
659 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, h1sl.st.v, h1sl.st.c, h1sl.st.r);
660 if (!sl || !htx_add_all_headers(htx, hdrs))
661 goto error;
662 sl->info.res.status = h1sl.st.status;
663
664 if (raw.len > ret) {
665 if (!htx_add_data(htx, ist2(raw.ptr + ret, raw.len - ret)))
666 goto error;
667 }
668 if (!htx_add_endof(htx, HTX_BLK_EOM))
669 goto error;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100670 return htx;
671
672error:
673 if (buf->size)
674 free(buf->area);
675 return NULL;
676}
677
678static int http_htx_init(void)
679{
680 struct proxy *px;
681 struct buffer chk;
682 struct ist raw;
683 int rc;
684 int err_code = 0;
685
686 for (px = proxies_list; px; px = px->next) {
Christopher Faulet49040582019-04-24 15:25:00 +0200687 if (px->mode != PR_MODE_HTTP || !(px->options2 & PR_O2_USE_HTX))
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100688 continue;
689
690 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
691 if (!b_data(&px->errmsg[rc]))
692 continue;
693
694 raw = ist2(b_head(&px->errmsg[rc]), b_data(&px->errmsg[rc]));
695 if (!http_str_to_htx(&chk, raw)) {
696 ha_alert("config: %s '%s': Unable to convert message in HTX for HTTP return code %d.\n",
697 proxy_type_str(px), px->id, http_err_codes[rc]);
698 err_code |= ERR_ALERT | ERR_FATAL;
699 }
700 chunk_destroy(&px->errmsg[rc]);
701 px->errmsg[rc] = chk;
702 }
703 }
704
705 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
706 if (!http_err_msgs[rc]) {
707 ha_alert("Internal error: no message defined for HTTP return code %d", rc);
708 err_code |= ERR_ALERT | ERR_FATAL;
709 continue;
710 }
711
712 raw = ist2(http_err_msgs[rc], strlen(http_err_msgs[rc]));
713 if (!http_str_to_htx(&chk, raw)) {
714 ha_alert("Internal error: Unable to convert message in HTX for HTTP return code %d.\n",
715 http_err_codes[rc]);
716 err_code |= ERR_ALERT | ERR_FATAL;
717 }
718 htx_err_chunks[rc] = chk;
719 }
720end:
721 return err_code;
722}
723
724REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init);