blob: 21a5b57f416a3684962b4aa2a8d30f7aef3c288f [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 Faulet47596d32018-10-22 09:17:28 +020023/* Finds the start line in the HTX message stopping at the first
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010024 * end-of-message. It returns NULL when not found, otherwise, it returns the
25 * pointer on the htx_sl structure. The HTX message may be updated if the
26 * start-line is returned following a lookup.
Christopher Faulet47596d32018-10-22 09:17:28 +020027 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010028struct htx_sl *http_find_stline(struct htx *htx)
Christopher Faulet47596d32018-10-22 09:17:28 +020029{
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010030 struct htx_sl *sl = NULL;
Christopher Faulet47596d32018-10-22 09:17:28 +020031 int32_t pos;
32
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010033 sl = htx_get_stline(htx);
34 if (sl)
35 return sl;
36
Christopher Fauletaa75b3d2018-12-05 16:20:40 +010037 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
38 struct htx_blk *blk = htx_get_blk(htx, pos);
39 enum htx_blk_type type = htx_get_blk_type(blk);
Christopher Faulet47596d32018-10-22 09:17:28 +020040
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010041 if (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL) {
42 sl = htx_get_blk_ptr(htx, blk);
43 htx->sl_off = blk->addr;
44 break;
Christopher Faulet47596d32018-10-22 09:17:28 +020045 }
Christopher Faulet573fe732018-11-28 16:55:12 +010046
47 if (type == HTX_BLK_EOH || type == HTX_BLK_EOM)
Christopher Faulet47596d32018-10-22 09:17:28 +020048 break;
49 }
50
Christopher Faulet47596d32018-10-22 09:17:28 +020051 return sl;
52}
53
54/* Finds the first or next occurrence of header <name> in the HTX message <htx>
55 * using the context <ctx>. This structure holds everything necessary to use the
56 * header and find next occurrence. If its <blk> member is NULL, the header is
57 * searched from the beginning. Otherwise, the next occurrence is returned. The
58 * function returns 1 when it finds a value, and 0 when there is no more. It is
59 * designed to work with headers defined as comma-separated lists. If <full> is
60 * set, it works on full-line headers in whose comma is not a delimiter but is
61 * part of the syntax. A special case, if ctx->value is NULL when searching for
62 * a new values of a header, the current header is rescanned. This allows
63 * rescanning after a header deletion.
64 */
65int http_find_header(const struct htx *htx, const struct ist name,
66 struct http_hdr_ctx *ctx, int full)
67{
68 struct htx_blk *blk = ctx->blk;
69 struct ist n, v;
70 enum htx_blk_type type;
71 uint32_t pos;
72
73 if (blk) {
74 char *p;
75
76 pos = htx_get_blk_pos(htx, blk);
77 if (!ctx->value.ptr)
78 goto rescan_hdr;
79 if (full)
80 goto next_blk;
81 v = htx_get_blk_value(htx, blk);
82 p = ctx->value.ptr + ctx->value.len + ctx->lws_after;
83 v.len -= (p - v.ptr);
84 v.ptr = p;
85 if (!v.len)
86 goto next_blk;
87 /* Skip comma */
88 if (*(v.ptr) == ',') {
89 v.ptr++;
90 v.len--;
91 }
92
93 goto return_hdr;
94 }
95
96 if (!htx->used)
97 return 0;
98
99 pos = htx_get_head(htx);
100 while (1) {
101 rescan_hdr:
102 blk = htx_get_blk(htx, pos);
103 type = htx_get_blk_type(blk);
Christopher Faulet573fe732018-11-28 16:55:12 +0100104 if (type == HTX_BLK_EOH || type == HTX_BLK_EOM)
105 break;
Christopher Faulet47596d32018-10-22 09:17:28 +0200106 if (type != HTX_BLK_HDR)
107 goto next_blk;
108 if (name.len) {
109 /* If no name was passed, we want any header. So skip the comparison */
110 n = htx_get_blk_name(htx, blk);
111 if (!isteqi(n, name))
112 goto next_blk;
113 }
114 v = htx_get_blk_value(htx, blk);
115
116 return_hdr:
117 ctx->lws_before = 0;
118 ctx->lws_after = 0;
119 while (v.len && HTTP_IS_LWS(*v.ptr)) {
120 v.ptr++;
121 v.len--;
122 ctx->lws_before++;
123 }
124 if (!full)
125 v.len = http_find_hdr_value_end(v.ptr, v.ptr + v.len) - v.ptr;
126 while (v.len && HTTP_IS_LWS(*(v.ptr + v.len - 1))) {
127 v.len--;
128 ctx->lws_after++;
129 }
130 if (!v.len)
131 goto next_blk;
132 ctx->blk = blk;
133 ctx->value = v;
134 return 1;
135
136 next_blk:
137 if (pos == htx->tail)
138 break;
139 pos++;
140 if (pos >= htx->wrap)
141 pos = 0;
142 }
143
144 ctx->blk = NULL;
145 ctx->value = ist("");
146 ctx->lws_before = ctx->lws_after = 0;
147 return 0;
148}
149
150/* Adds a header block int the HTX message <htx>, just before the EOH block. It
151 * returns 1 on success, otherwise it returns 0.
152 */
153int http_add_header(struct htx *htx, const struct ist n, const struct ist v)
154{
155 struct htx_blk *blk;
156 enum htx_blk_type type = htx_get_tail_type(htx);
157 int32_t prev;
158
159 blk = htx_add_header(htx, n, v);
160 if (!blk)
161 return 0;
162
163 if (unlikely(type < HTX_BLK_EOH))
164 return 1;
165
166 /* <blk> is the head, swap it iteratively with its predecessor to place
167 * it just before the end-of-header block. So blocks remains ordered. */
168 for (prev = htx_get_prev(htx, htx->tail); prev != -1; prev = htx_get_prev(htx, prev)) {
169 struct htx_blk *pblk = htx_get_blk(htx, prev);
170 enum htx_blk_type type = htx_get_blk_type(pblk);
171
172 /* Swap .addr and .info fields */
173 blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
174 blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info;
175
176 if (blk->addr == pblk->addr)
177 blk->addr += htx_get_blksz(pblk);
178 htx->front = prev;
179
180 /* Stop when end-of-header is reached */
181 if (type == HTX_BLK_EOH)
182 break;
183
184 blk = pblk;
185 }
Christopher Faulet05aab642019-04-11 13:43:57 +0200186
187 if (htx_get_blk_pos(htx, blk) != htx->front)
188 htx_defrag(htx, NULL);
189
Christopher Faulet47596d32018-10-22 09:17:28 +0200190 return 1;
191}
192
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100193/* Replaces parts of the start-line of the HTX message <htx>. It returns 1 on
194 * success, otherwise it returns 0. The right block is search in the HTX
Christopher Faulet47596d32018-10-22 09:17:28 +0200195 * message.
196 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100197int 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 +0200198{
199 int32_t pos;
200
Christopher Fauletaa75b3d2018-12-05 16:20:40 +0100201 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100202 struct htx_blk *blk = htx_get_blk(htx, pos);
Christopher Fauletaa75b3d2018-12-05 16:20:40 +0100203 enum htx_blk_type type = htx_get_blk_type(blk);
Christopher Faulet47596d32018-10-22 09:17:28 +0200204
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100205 if (htx->sl_off == blk->addr) {
206 if (!htx_replace_stline(htx, blk, p1, p2, p3))
Christopher Faulet47596d32018-10-22 09:17:28 +0200207 return 0;
208 return 1;
209 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200210
Christopher Faulet47596d32018-10-22 09:17:28 +0200211 if (type == HTX_BLK_EOM)
212 break;
213 }
214
215 return 0;
216}
217
Christopher Faulete010c802018-10-24 10:36:45 +0200218/* Replace the request method in the HTX message <htx> by <meth>. It returns 1
219 * on success, otherwise 0.
220 */
221int http_replace_req_meth(struct htx *htx, const struct ist meth)
222{
223 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100224 struct htx_sl *sl = http_find_stline(htx);
225 struct ist uri, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200226
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100227 if (!sl)
228 return 0;
229
Christopher Faulete010c802018-10-24 10:36:45 +0200230 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100231 chunk_memcat(temp, HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl)); /* uri */
232 uri = ist2(temp->area, HTX_SL_REQ_ULEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200233
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100234 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
235 vsn = ist2(temp->area + uri.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200236
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100237 /* create the new start line */
238 sl->info.req.meth = find_http_meth(meth.ptr, meth.len);
239 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200240}
241
242/* Replace the request uri in the HTX message <htx> by <uri>. It returns 1 on
243 * success, otherwise 0.
244 */
245int http_replace_req_uri(struct htx *htx, const struct ist uri)
246{
247 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100248 struct htx_sl *sl = http_find_stline(htx);
249 struct ist meth, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200250
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100251 if (!sl)
252 return 0;
253
Christopher Faulete010c802018-10-24 10:36:45 +0200254 /* Start by copying old method and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100255 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
256 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200257
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100258 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
259 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200260
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100261 /* create the new start line */
262 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200263}
264
265/* Replace the request path in the HTX message <htx> by <path>. The host part
266 * and the query string are preserved. It returns 1 on success, otherwise 0.
267 */
268int http_replace_req_path(struct htx *htx, const struct ist path)
269{
270 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100271 struct htx_sl *sl = http_find_stline(htx);
272 struct ist meth, uri, vsn, p;
Christopher Faulete010c802018-10-24 10:36:45 +0200273 size_t plen = 0;
274
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100275 if (!sl)
276 return 0;
277
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100278 uri = htx_sl_req_uri(sl);
279 p = http_get_path(uri);
Christopher Faulete010c802018-10-24 10:36:45 +0200280 if (!p.ptr)
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100281 p = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200282 while (plen < p.len && *(p.ptr + plen) != '?')
283 plen++;
284
285 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100286 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
287 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200288
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100289 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
290 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
291
292 chunk_memcat(temp, uri.ptr, p.ptr - uri.ptr); /* uri: host part */
Christopher Faulete010c802018-10-24 10:36:45 +0200293 chunk_memcat(temp, path.ptr, path.len); /* uri: new path */
294 chunk_memcat(temp, p.ptr + plen, p.len - plen); /* uri: QS part */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100295 uri = ist2(temp->area + meth.len + vsn.len, uri.len - plen + path.len);
Christopher Faulete010c802018-10-24 10:36:45 +0200296
297 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100298 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200299}
300
301/* Replace the request query-string in the HTX message <htx> by <query>. The
302 * host part and the path are preserved. It returns 1 on success, otherwise
303 * 0.
304 */
305int http_replace_req_query(struct htx *htx, const struct ist query)
306{
307 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100308 struct htx_sl *sl = http_find_stline(htx);
309 struct ist meth, uri, vsn, q;
Christopher Faulete010c802018-10-24 10:36:45 +0200310 int offset = 1;
311
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100312 if (!sl)
313 return 0;
314
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100315 uri = htx_sl_req_uri(sl);
316 q = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200317 while (q.len > 0 && *(q.ptr) != '?') {
318 q.ptr++;
319 q.len--;
320 }
321
322 /* skip the question mark or indicate that we must insert it
323 * (but only if the format string is not empty then).
324 */
325 if (q.len) {
326 q.ptr++;
327 q.len--;
328 }
329 else if (query.len > 1)
330 offset = 0;
331
332 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100333 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
334 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200335
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100336 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
337 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200338
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100339 chunk_memcat(temp, uri.ptr, q.ptr - uri.ptr); /* uri: host + path part */
340 chunk_memcat(temp, query.ptr + offset, query.len - offset); /* uri: new QS */
341 uri = ist2(temp->area + meth.len + vsn.len, uri.len - q.len + query.len - offset);
Christopher Faulete010c802018-10-24 10:36:45 +0200342
343 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100344 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200345}
346
347/* Replace the response status in the HTX message <htx> by <status>. It returns
348 * 1 on success, otherwise 0.
349*/
350int http_replace_res_status(struct htx *htx, const struct ist status)
351{
352 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100353 struct htx_sl *sl = http_find_stline(htx);
354 struct ist vsn, reason;
Christopher Faulete010c802018-10-24 10:36:45 +0200355
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100356 if (!sl)
357 return 0;
358
Christopher Faulete010c802018-10-24 10:36:45 +0200359 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100360 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
361 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200362
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100363 chunk_memcat(temp, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl)); /* reason */
364 reason = ist2(temp->area + vsn.len, HTX_SL_RES_RLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200365
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100366 /* create the new start line */
367 sl->info.res.status = strl2ui(status.ptr, status.len);
368 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200369}
370
371/* Replace the response reason in the HTX message <htx> by <reason>. It returns
372 * 1 on success, otherwise 0.
373*/
374int http_replace_res_reason(struct htx *htx, const struct ist reason)
375{
376 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100377 struct htx_sl *sl = http_find_stline(htx);
378 struct ist vsn, status;
Christopher Faulete010c802018-10-24 10:36:45 +0200379
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100380 if (!sl)
381 return 0;
382
Christopher Faulete010c802018-10-24 10:36:45 +0200383 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100384 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
385 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200386
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100387 chunk_memcat(temp, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl)); /* code */
388 status = ist2(temp->area + vsn.len, HTX_SL_RES_CLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200389
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100390 /* create the new start line */
391 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200392}
393
Christopher Faulet47596d32018-10-22 09:17:28 +0200394/* Replaces a part of a header value referenced in the context <ctx> by
395 * <data>. It returns 1 on success, otherwise it returns 0. The context is
396 * updated if necessary.
397 */
398int http_replace_header_value(struct htx *htx, struct http_hdr_ctx *ctx, const struct ist data)
399{
400 struct htx_blk *blk = ctx->blk;
401 char *start;
402 struct ist v;
403 uint32_t len, off;
404
405 if (!blk)
406 return 0;
407
408 v = htx_get_blk_value(htx, blk);
409 start = ctx->value.ptr - ctx->lws_before;
410 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
411 off = start - v.ptr;
412
413 blk = htx_replace_blk_value(htx, blk, ist2(start, len), data);
414 if (!blk)
415 return 0;
416
417 v = htx_get_blk_value(htx, blk);
418 ctx->blk = blk;
419 ctx->value.ptr = v.ptr + off;
420 ctx->value.len = data.len;
421 ctx->lws_before = ctx->lws_after = 0;
422
423 return 1;
424}
425
426/* Fully replaces a header referenced in the context <ctx> by the name <name>
427 * with the value <value>. It returns 1 on success, otherwise it returns 0. The
428 * context is updated if necessary.
429 */
430int http_replace_header(struct htx *htx, struct http_hdr_ctx *ctx,
431 const struct ist name, const struct ist value)
432{
433 struct htx_blk *blk = ctx->blk;
434
435 if (!blk)
436 return 0;
437
438 blk = htx_replace_header(htx, blk, name, value);
439 if (!blk)
440 return 0;
441
442 ctx->blk = blk;
443 ctx->value = ist(NULL);
444 ctx->lws_before = ctx->lws_after = 0;
445
446 return 1;
447}
448
449/* Remove one value of a header. This only works on a <ctx> returned by
450 * http_find_header function. The value is removed, as well as surrounding commas
451 * if any. If the removed value was alone, the whole header is removed. The
452 * <ctx> is always updated accordingly, as well as the HTX message <htx>. It
453 * returns 1 on success. Otherwise, it returns 0. The <ctx> is always left in a
454 * form that can be handled by http_find_header() to find next occurrence.
455 */
456int http_remove_header(struct htx *htx, struct http_hdr_ctx *ctx)
457{
458 struct htx_blk *blk = ctx->blk;
459 char *start;
460 struct ist v;
461 uint32_t len;
462
463 if (!blk)
464 return 0;
465
466 start = ctx->value.ptr - ctx->lws_before;
467 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
468
469 v = htx_get_blk_value(htx, blk);
470 if (len == v.len) {
471 blk = htx_remove_blk(htx, blk);
472 if (blk || !htx->used) {
473 ctx->blk = blk;
474 ctx->value = ist2(NULL, 0);
475 ctx->lws_before = ctx->lws_after = 0;
476 }
477 else {
478 ctx->blk = htx_get_blk(htx, htx->tail);
479 ctx->value = htx_get_blk_value(htx, ctx->blk);
480 ctx->lws_before = ctx->lws_after = 0;
481 }
482 return 1;
483 }
484
485 /* This was not the only value of this header. We have to remove the
486 * part pointed by ctx->value. If it is the last entry of the list, we
487 * remove the last separator.
488 */
489 if (start == v.ptr) {
490 /* It's the first header part but not the only one. So remove
491 * the comma after it. */
492 len++;
493 }
494 else {
495 /* There is at least one header part before the removed one. So
496 * remove the comma between them. */
497 start--;
498 len++;
499 }
500 /* Update the block content and its len */
501 memmove(start, start+len, v.len-len);
502 htx_set_blk_value_len(blk, v.len-len);
503
504 /* Update HTX msg */
505 htx->data -= len;
506
507 /* Finally update the ctx */
508 ctx->value.ptr = start;
509 ctx->value.len = 0;
510 ctx->lws_before = ctx->lws_after = 0;
511
512 return 1;
513}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200514
515
516/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
517 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
518 * performed over the whole headers. Otherwise it must contain a valid header
519 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
520 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
521 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
522 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
523 * -1. The value fetch stops at commas, so this function is suited for use with
524 * list headers.
525 * The return value is 0 if nothing was found, or non-zero otherwise.
526 */
527unsigned int http_get_htx_hdr(const struct htx *htx, const struct ist hdr,
528 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
529{
530 struct http_hdr_ctx local_ctx;
531 struct ist val_hist[MAX_HDR_HISTORY];
532 unsigned int hist_idx;
533 int found;
534
535 if (!ctx) {
536 local_ctx.blk = NULL;
537 ctx = &local_ctx;
538 }
539
540 if (occ >= 0) {
541 /* search from the beginning */
542 while (http_find_header(htx, hdr, ctx, 0)) {
543 occ--;
544 if (occ <= 0) {
545 *vptr = ctx->value.ptr;
546 *vlen = ctx->value.len;
547 return 1;
548 }
549 }
550 return 0;
551 }
552
553 /* negative occurrence, we scan all the list then walk back */
554 if (-occ > MAX_HDR_HISTORY)
555 return 0;
556
557 found = hist_idx = 0;
558 while (http_find_header(htx, hdr, ctx, 0)) {
559 val_hist[hist_idx] = ctx->value;
560 if (++hist_idx >= MAX_HDR_HISTORY)
561 hist_idx = 0;
562 found++;
563 }
564 if (-occ > found)
565 return 0;
566
567 /* OK now we have the last occurrence in [hist_idx-1], and we need to
568 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
569 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
570 * to remain in the 0..9 range.
571 */
572 hist_idx += occ + MAX_HDR_HISTORY;
573 if (hist_idx >= MAX_HDR_HISTORY)
574 hist_idx -= MAX_HDR_HISTORY;
575 *vptr = val_hist[hist_idx].ptr;
576 *vlen = val_hist[hist_idx].len;
577 return 1;
578}
579
580/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
581 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
582 * performed over the whole headers. Otherwise it must contain a valid header
583 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
584 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
585 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
586 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
587 * -1. This function differs from http_get_hdr() in that it only returns full
588 * line header values and does not stop at commas.
589 * The return value is 0 if nothing was found, or non-zero otherwise.
590 */
591unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr,
592 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
593{
594 struct http_hdr_ctx local_ctx;
595 struct ist val_hist[MAX_HDR_HISTORY];
596 unsigned int hist_idx;
597 int found;
598
599 if (!ctx) {
600 local_ctx.blk = NULL;
601 ctx = &local_ctx;
602 }
603
604 if (occ >= 0) {
605 /* search from the beginning */
606 while (http_find_header(htx, hdr, ctx, 1)) {
607 occ--;
608 if (occ <= 0) {
609 *vptr = ctx->value.ptr;
610 *vlen = ctx->value.len;
611 return 1;
612 }
613 }
614 return 0;
615 }
616
617 /* negative occurrence, we scan all the list then walk back */
618 if (-occ > MAX_HDR_HISTORY)
619 return 0;
620
621 found = hist_idx = 0;
622 while (http_find_header(htx, hdr, ctx, 1)) {
623 val_hist[hist_idx] = ctx->value;
624 if (++hist_idx >= MAX_HDR_HISTORY)
625 hist_idx = 0;
626 found++;
627 }
628 if (-occ > found)
629 return 0;
630
631 /* OK now we have the last occurrence in [hist_idx-1], and we need to
632 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
633 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
634 * to remain in the 0..9 range.
635 */
636 hist_idx += occ + MAX_HDR_HISTORY;
637 if (hist_idx >= MAX_HDR_HISTORY)
638 hist_idx -= MAX_HDR_HISTORY;
639 *vptr = val_hist[hist_idx].ptr;
640 *vlen = val_hist[hist_idx].len;
641 return 1;
642}
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100643
644static struct htx *http_str_to_htx(struct buffer *buf, struct ist raw)
645{
646 struct htx *htx;
647 struct htx_sl *sl;
648 struct h1m h1m;
649 struct http_hdr hdrs[MAX_HTTP_HDR];
650 union h1_sl h1sl;
651 unsigned int flags = HTX_SL_F_IS_RESP;
652 int ret = 0;
653
654 buf->size = global.tune.bufsize;
655 buf->area = (char *)malloc(buf->size);
656 if (!buf->area)
657 goto error;
658 b_reset(buf);
659
660 h1m_init_res(&h1m);
661 h1m.flags |= H1_MF_NO_PHDR;
662 ret = h1_headers_to_hdr_list(raw.ptr, raw.ptr + raw.len,
663 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
664 if (ret <= 0)
665 goto error;
666
667 if (unlikely(h1sl.st.v.len != 8))
668 goto error;
669 if ((*(h1sl.st.v.ptr + 5) > '1') ||
670 ((*(h1sl.st.v.ptr + 5) == '1') && (*(h1sl.st.v.ptr + 7) >= '1')))
671 h1m.flags |= H1_MF_VER_11;
672
673 if (h1m.flags & H1_MF_VER_11)
674 flags |= HTX_SL_F_VER_11;
675 if (h1m.flags & H1_MF_XFER_ENC)
676 flags |= HTX_SL_F_XFER_ENC;
677 if (h1m.flags & H1_MF_XFER_LEN) {
678 flags |= HTX_SL_F_XFER_LEN;
679 if (h1m.flags & H1_MF_CHNK)
680 goto error; /* Unsupported because there is no body parsing */
681 else if (h1m.flags & H1_MF_CLEN) {
682 flags |= HTX_SL_F_CLEN;
683 if (h1m.body_len == 0)
684 flags |= HTX_SL_F_BODYLESS;
685 }
686 }
687
688 htx = htx_from_buf(buf);
689 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, h1sl.st.v, h1sl.st.c, h1sl.st.r);
690 if (!sl || !htx_add_all_headers(htx, hdrs))
691 goto error;
692 sl->info.res.status = h1sl.st.status;
693
694 if (raw.len > ret) {
695 if (!htx_add_data(htx, ist2(raw.ptr + ret, raw.len - ret)))
696 goto error;
697 }
698 if (!htx_add_endof(htx, HTX_BLK_EOM))
699 goto error;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100700 return htx;
701
702error:
703 if (buf->size)
704 free(buf->area);
705 return NULL;
706}
707
708static int http_htx_init(void)
709{
710 struct proxy *px;
711 struct buffer chk;
712 struct ist raw;
713 int rc;
714 int err_code = 0;
715
716 for (px = proxies_list; px; px = px->next) {
717 if (!(px->options2 & PR_O2_USE_HTX))
718 continue;
719
720 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
721 if (!b_data(&px->errmsg[rc]))
722 continue;
723
724 raw = ist2(b_head(&px->errmsg[rc]), b_data(&px->errmsg[rc]));
725 if (!http_str_to_htx(&chk, raw)) {
726 ha_alert("config: %s '%s': Unable to convert message in HTX for HTTP return code %d.\n",
727 proxy_type_str(px), px->id, http_err_codes[rc]);
728 err_code |= ERR_ALERT | ERR_FATAL;
729 }
730 chunk_destroy(&px->errmsg[rc]);
731 px->errmsg[rc] = chk;
732 }
733 }
734
735 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
736 if (!http_err_msgs[rc]) {
737 ha_alert("Internal error: no message defined for HTTP return code %d", rc);
738 err_code |= ERR_ALERT | ERR_FATAL;
739 continue;
740 }
741
742 raw = ist2(http_err_msgs[rc], strlen(http_err_msgs[rc]));
743 if (!http_str_to_htx(&chk, raw)) {
744 ha_alert("Internal error: Unable to convert message in HTX for HTTP return code %d.\n",
745 http_err_codes[rc]);
746 err_code |= ERR_ALERT | ERR_FATAL;
747 }
748 htx_err_chunks[rc] = chk;
749 }
750end:
751 return err_code;
752}
753
754REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init);