blob: 71a952d13672efa27bf3a7a262fc777eb573dac7 [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 Faulet29f17582019-05-23 11:03:26 +020014#include <common/debug.h>
Christopher Fauleta7b677c2018-11-29 16:48:49 +010015#include <common/cfgparse.h>
Willy Tarreauafba57a2018-12-11 13:44:24 +010016#include <common/h1.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020017#include <common/http.h>
Willy Tarreaub96b77e2018-12-11 10:22:41 +010018#include <common/htx.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020019
20#include <proto/http_htx.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020021
Christopher Fauletf7346382019-07-17 22:02:08 +020022struct buffer http_err_chunks[HTTP_ERR_SIZE];
Christopher Fauleta7b677c2018-11-29 16:48:49 +010023
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +020024static int http_update_authority(struct htx *htx, struct htx_sl *sl, const struct ist host);
25static int http_update_host(struct htx *htx, struct htx_sl *sl, const struct ist uri);
26
Christopher Faulet297fbb42019-05-13 14:41:27 +020027/* Returns the next unporocessed start line in the HTX message. It returns NULL
Christopher Faulet29f17582019-05-23 11:03:26 +020028 * if the start-line is undefined (first == -1). Otherwise, it returns the
Christopher Faulet297fbb42019-05-13 14:41:27 +020029 * pointer on the htx_sl structure.
Christopher Faulet47596d32018-10-22 09:17:28 +020030 */
Christopher Faulet297fbb42019-05-13 14:41:27 +020031struct htx_sl *http_get_stline(struct htx *htx)
Christopher Faulet47596d32018-10-22 09:17:28 +020032{
Christopher Faulet297fbb42019-05-13 14:41:27 +020033 struct htx_blk *blk;
Christopher Faulet573fe732018-11-28 16:55:12 +010034
Christopher Faulet29f17582019-05-23 11:03:26 +020035 BUG_ON(htx->first == -1);
36 blk = htx_get_first_blk(htx);
Christopher Faulet297fbb42019-05-13 14:41:27 +020037 if (!blk)
38 return NULL;
Christopher Faulet29f17582019-05-23 11:03:26 +020039 BUG_ON(htx_get_blk_type(blk) != HTX_BLK_REQ_SL && htx_get_blk_type(blk) != HTX_BLK_RES_SL);
Christopher Faulet297fbb42019-05-13 14:41:27 +020040 return htx_get_blk_ptr(htx, blk);
Christopher Faulet47596d32018-10-22 09:17:28 +020041}
42
43/* Finds the first or next occurrence of header <name> in the HTX message <htx>
44 * using the context <ctx>. This structure holds everything necessary to use the
45 * header and find next occurrence. If its <blk> member is NULL, the header is
46 * searched from the beginning. Otherwise, the next occurrence is returned. The
47 * function returns 1 when it finds a value, and 0 when there is no more. It is
48 * designed to work with headers defined as comma-separated lists. If <full> is
49 * set, it works on full-line headers in whose comma is not a delimiter but is
50 * part of the syntax. A special case, if ctx->value is NULL when searching for
51 * a new values of a header, the current header is rescanned. This allows
52 * rescanning after a header deletion.
53 */
54int http_find_header(const struct htx *htx, const struct ist name,
55 struct http_hdr_ctx *ctx, int full)
56{
57 struct htx_blk *blk = ctx->blk;
58 struct ist n, v;
59 enum htx_blk_type type;
Christopher Faulet47596d32018-10-22 09:17:28 +020060
61 if (blk) {
62 char *p;
63
Christopher Faulet47596d32018-10-22 09:17:28 +020064 if (!ctx->value.ptr)
65 goto rescan_hdr;
66 if (full)
67 goto next_blk;
68 v = htx_get_blk_value(htx, blk);
69 p = ctx->value.ptr + ctx->value.len + ctx->lws_after;
70 v.len -= (p - v.ptr);
71 v.ptr = p;
72 if (!v.len)
73 goto next_blk;
74 /* Skip comma */
75 if (*(v.ptr) == ',') {
76 v.ptr++;
77 v.len--;
78 }
79
80 goto return_hdr;
81 }
82
Christopher Faulet192c6a22019-06-11 16:32:24 +020083 if (htx_is_empty(htx))
Christopher Faulet47596d32018-10-22 09:17:28 +020084 return 0;
85
Christopher Fauleta3f15502019-05-13 15:27:23 +020086 for (blk = htx_get_first_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
Christopher Faulet47596d32018-10-22 09:17:28 +020087 rescan_hdr:
Christopher Faulet47596d32018-10-22 09:17:28 +020088 type = htx_get_blk_type(blk);
Christopher Faulet573fe732018-11-28 16:55:12 +010089 if (type == HTX_BLK_EOH || type == HTX_BLK_EOM)
90 break;
Christopher Faulet47596d32018-10-22 09:17:28 +020091 if (type != HTX_BLK_HDR)
Christopher Faulet28f29c72019-04-30 17:55:45 +020092 continue;
Christopher Faulet47596d32018-10-22 09:17:28 +020093 if (name.len) {
94 /* If no name was passed, we want any header. So skip the comparison */
95 n = htx_get_blk_name(htx, blk);
96 if (!isteqi(n, name))
97 goto next_blk;
98 }
99 v = htx_get_blk_value(htx, blk);
100
101 return_hdr:
102 ctx->lws_before = 0;
103 ctx->lws_after = 0;
104 while (v.len && HTTP_IS_LWS(*v.ptr)) {
105 v.ptr++;
106 v.len--;
107 ctx->lws_before++;
108 }
109 if (!full)
110 v.len = http_find_hdr_value_end(v.ptr, v.ptr + v.len) - v.ptr;
111 while (v.len && HTTP_IS_LWS(*(v.ptr + v.len - 1))) {
112 v.len--;
113 ctx->lws_after++;
114 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200115 ctx->blk = blk;
116 ctx->value = v;
117 return 1;
118
119 next_blk:
Christopher Faulet28f29c72019-04-30 17:55:45 +0200120 ;
Christopher Faulet47596d32018-10-22 09:17:28 +0200121 }
122
123 ctx->blk = NULL;
124 ctx->value = ist("");
125 ctx->lws_before = ctx->lws_after = 0;
126 return 0;
127}
128
129/* Adds a header block int the HTX message <htx>, just before the EOH block. It
130 * returns 1 on success, otherwise it returns 0.
131 */
132int http_add_header(struct htx *htx, const struct ist n, const struct ist v)
133{
134 struct htx_blk *blk;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200135 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200136 enum htx_blk_type type = htx_get_tail_type(htx);
137 int32_t prev;
138
139 blk = htx_add_header(htx, n, v);
140 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200141 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200142
143 if (unlikely(type < HTX_BLK_EOH))
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200144 goto end;
Christopher Faulet47596d32018-10-22 09:17:28 +0200145
146 /* <blk> is the head, swap it iteratively with its predecessor to place
147 * it just before the end-of-header block. So blocks remains ordered. */
Christopher Faulet29f17582019-05-23 11:03:26 +0200148 for (prev = htx_get_prev(htx, htx->tail); prev != htx->first; prev = htx_get_prev(htx, prev)) {
Christopher Faulet47596d32018-10-22 09:17:28 +0200149 struct htx_blk *pblk = htx_get_blk(htx, prev);
150 enum htx_blk_type type = htx_get_blk_type(pblk);
151
152 /* Swap .addr and .info fields */
153 blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
154 blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info;
155
156 if (blk->addr == pblk->addr)
157 blk->addr += htx_get_blksz(pblk);
Christopher Faulet47596d32018-10-22 09:17:28 +0200158
159 /* Stop when end-of-header is reached */
160 if (type == HTX_BLK_EOH)
161 break;
162
163 blk = pblk;
164 }
Christopher Faulet05aab642019-04-11 13:43:57 +0200165
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200166 end:
167 sl = http_get_stline(htx);
168 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteq(n, ist("host"))) {
169 if (!http_update_authority(htx, sl, v))
170 goto fail;
171 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200172 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200173
174 fail:
175 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200176}
177
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100178/* Replaces parts of the start-line of the HTX message <htx>. It returns 1 on
Christopher Faulet29f17582019-05-23 11:03:26 +0200179 * success, otherwise it returns 0.
Christopher Faulet47596d32018-10-22 09:17:28 +0200180 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100181int 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 +0200182{
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200183 struct htx_blk *blk;
Christopher Faulet47596d32018-10-22 09:17:28 +0200184
Christopher Faulet29f17582019-05-23 11:03:26 +0200185 blk = htx_get_first_blk(htx);
186 if (!blk || !htx_replace_stline(htx, blk, p1, p2, p3))
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200187 return 0;
188 return 1;
Christopher Faulet47596d32018-10-22 09:17:28 +0200189}
190
Christopher Faulete010c802018-10-24 10:36:45 +0200191/* Replace the request method in the HTX message <htx> by <meth>. It returns 1
192 * on success, otherwise 0.
193 */
194int http_replace_req_meth(struct htx *htx, const struct ist meth)
195{
196 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200197 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100198 struct ist uri, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200199
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100200 if (!sl)
201 return 0;
202
Christopher Faulete010c802018-10-24 10:36:45 +0200203 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100204 chunk_memcat(temp, HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl)); /* uri */
205 uri = ist2(temp->area, HTX_SL_REQ_ULEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200206
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100207 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
208 vsn = ist2(temp->area + uri.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200209
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100210 /* create the new start line */
211 sl->info.req.meth = find_http_meth(meth.ptr, meth.len);
212 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200213}
214
215/* Replace the request uri in the HTX message <htx> by <uri>. It returns 1 on
216 * success, otherwise 0.
217 */
218int http_replace_req_uri(struct htx *htx, const struct ist uri)
219{
220 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200221 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100222 struct ist meth, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200223
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100224 if (!sl)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200225 goto fail;
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100226
Christopher Faulete010c802018-10-24 10:36:45 +0200227 /* Start by copying old method and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100228 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
229 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200230
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100231 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
232 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200233
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100234 /* create the new start line */
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200235 if (!http_replace_stline(htx, meth, uri, vsn))
236 goto fail;
237
238 sl = http_get_stline(htx);
239 if (!http_update_host(htx, sl, uri))
240 goto fail;
241
242 return 1;
243 fail:
244 return 0;
Christopher Faulete010c802018-10-24 10:36:45 +0200245}
246
247/* Replace the request path in the HTX message <htx> by <path>. The host part
248 * and the query string are preserved. It returns 1 on success, otherwise 0.
249 */
250int http_replace_req_path(struct htx *htx, const struct ist path)
251{
252 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200253 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100254 struct ist meth, uri, vsn, p;
Christopher Faulete010c802018-10-24 10:36:45 +0200255 size_t plen = 0;
256
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100257 if (!sl)
258 return 0;
259
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100260 uri = htx_sl_req_uri(sl);
261 p = http_get_path(uri);
Christopher Faulete010c802018-10-24 10:36:45 +0200262 if (!p.ptr)
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100263 p = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200264 while (plen < p.len && *(p.ptr + plen) != '?')
265 plen++;
266
267 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100268 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
269 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200270
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100271 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
272 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
273
274 chunk_memcat(temp, uri.ptr, p.ptr - uri.ptr); /* uri: host part */
Christopher Faulete010c802018-10-24 10:36:45 +0200275 chunk_memcat(temp, path.ptr, path.len); /* uri: new path */
276 chunk_memcat(temp, p.ptr + plen, p.len - plen); /* uri: QS part */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100277 uri = ist2(temp->area + meth.len + vsn.len, uri.len - plen + path.len);
Christopher Faulete010c802018-10-24 10:36:45 +0200278
279 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100280 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200281}
282
283/* Replace the request query-string in the HTX message <htx> by <query>. The
284 * host part and the path are preserved. It returns 1 on success, otherwise
285 * 0.
286 */
287int http_replace_req_query(struct htx *htx, const struct ist query)
288{
289 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200290 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100291 struct ist meth, uri, vsn, q;
Christopher Faulete010c802018-10-24 10:36:45 +0200292 int offset = 1;
293
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100294 if (!sl)
295 return 0;
296
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100297 uri = htx_sl_req_uri(sl);
298 q = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200299 while (q.len > 0 && *(q.ptr) != '?') {
300 q.ptr++;
301 q.len--;
302 }
303
304 /* skip the question mark or indicate that we must insert it
305 * (but only if the format string is not empty then).
306 */
307 if (q.len) {
308 q.ptr++;
309 q.len--;
310 }
311 else if (query.len > 1)
312 offset = 0;
313
314 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100315 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
316 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200317
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100318 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
319 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200320
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100321 chunk_memcat(temp, uri.ptr, q.ptr - uri.ptr); /* uri: host + path part */
322 chunk_memcat(temp, query.ptr + offset, query.len - offset); /* uri: new QS */
323 uri = ist2(temp->area + meth.len + vsn.len, uri.len - q.len + query.len - offset);
Christopher Faulete010c802018-10-24 10:36:45 +0200324
325 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100326 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200327}
328
329/* Replace the response status in the HTX message <htx> by <status>. It returns
330 * 1 on success, otherwise 0.
331*/
332int http_replace_res_status(struct htx *htx, const struct ist status)
333{
334 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200335 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100336 struct ist vsn, reason;
Christopher Faulete010c802018-10-24 10:36:45 +0200337
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100338 if (!sl)
339 return 0;
340
Christopher Faulete010c802018-10-24 10:36:45 +0200341 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100342 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
343 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200344
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100345 chunk_memcat(temp, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl)); /* reason */
346 reason = ist2(temp->area + vsn.len, HTX_SL_RES_RLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200347
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100348 /* create the new start line */
349 sl->info.res.status = strl2ui(status.ptr, status.len);
350 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200351}
352
353/* Replace the response reason in the HTX message <htx> by <reason>. It returns
354 * 1 on success, otherwise 0.
355*/
356int http_replace_res_reason(struct htx *htx, const struct ist reason)
357{
358 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200359 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100360 struct ist vsn, status;
Christopher Faulete010c802018-10-24 10:36:45 +0200361
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100362 if (!sl)
363 return 0;
364
Christopher Faulete010c802018-10-24 10:36:45 +0200365 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100366 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
367 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200368
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100369 chunk_memcat(temp, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl)); /* code */
370 status = ist2(temp->area + vsn.len, HTX_SL_RES_CLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200371
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100372 /* create the new start line */
373 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200374}
375
Christopher Faulet47596d32018-10-22 09:17:28 +0200376/* Replaces a part of a header value referenced in the context <ctx> by
377 * <data>. It returns 1 on success, otherwise it returns 0. The context is
378 * updated if necessary.
379 */
380int http_replace_header_value(struct htx *htx, struct http_hdr_ctx *ctx, const struct ist data)
381{
382 struct htx_blk *blk = ctx->blk;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200383 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200384 char *start;
385 struct ist v;
386 uint32_t len, off;
387
388 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200389 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200390
391 v = htx_get_blk_value(htx, blk);
392 start = ctx->value.ptr - ctx->lws_before;
393 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
394 off = start - v.ptr;
395
396 blk = htx_replace_blk_value(htx, blk, ist2(start, len), data);
397 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200398 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200399
400 v = htx_get_blk_value(htx, blk);
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200401
402 sl = http_get_stline(htx);
403 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY)) {
404 struct ist n = htx_get_blk_name(htx, blk);
405
406 if (isteq(n, ist("host"))) {
407 if (!http_update_authority(htx, sl, v))
408 goto fail;
409 ctx->blk = NULL;
410 http_find_header(htx, ist("host"), ctx, 1);
411 blk = ctx->blk;
412 v = htx_get_blk_value(htx, blk);
413 }
414 }
415
Christopher Faulet47596d32018-10-22 09:17:28 +0200416 ctx->blk = blk;
417 ctx->value.ptr = v.ptr + off;
418 ctx->value.len = data.len;
419 ctx->lws_before = ctx->lws_after = 0;
420
421 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200422 fail:
423 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200424}
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;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200434 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200435
436 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200437 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200438
439 blk = htx_replace_header(htx, blk, name, value);
440 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200441 goto fail;
442
443 sl = http_get_stline(htx);
444 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteq(name, ist("host"))) {
445 if (!http_update_authority(htx, sl, value))
446 goto fail;
447 ctx->blk = NULL;
448 http_find_header(htx, ist("host"), ctx, 1);
449 blk = ctx->blk;
450 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200451
452 ctx->blk = blk;
453 ctx->value = ist(NULL);
454 ctx->lws_before = ctx->lws_after = 0;
455
456 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200457 fail:
458 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200459}
460
461/* Remove one value of a header. This only works on a <ctx> returned by
462 * http_find_header function. The value is removed, as well as surrounding commas
463 * if any. If the removed value was alone, the whole header is removed. The
464 * <ctx> is always updated accordingly, as well as the HTX message <htx>. It
465 * returns 1 on success. Otherwise, it returns 0. The <ctx> is always left in a
466 * form that can be handled by http_find_header() to find next occurrence.
467 */
468int http_remove_header(struct htx *htx, struct http_hdr_ctx *ctx)
469{
470 struct htx_blk *blk = ctx->blk;
471 char *start;
472 struct ist v;
473 uint32_t len;
474
475 if (!blk)
476 return 0;
477
478 start = ctx->value.ptr - ctx->lws_before;
479 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
480
481 v = htx_get_blk_value(htx, blk);
482 if (len == v.len) {
483 blk = htx_remove_blk(htx, blk);
Christopher Faulet192c6a22019-06-11 16:32:24 +0200484 if (blk || htx_is_empty(htx)) {
Christopher Faulet47596d32018-10-22 09:17:28 +0200485 ctx->blk = blk;
486 ctx->value = ist2(NULL, 0);
487 ctx->lws_before = ctx->lws_after = 0;
488 }
489 else {
490 ctx->blk = htx_get_blk(htx, htx->tail);
491 ctx->value = htx_get_blk_value(htx, ctx->blk);
492 ctx->lws_before = ctx->lws_after = 0;
493 }
494 return 1;
495 }
496
497 /* This was not the only value of this header. We have to remove the
498 * part pointed by ctx->value. If it is the last entry of the list, we
499 * remove the last separator.
500 */
501 if (start == v.ptr) {
502 /* It's the first header part but not the only one. So remove
503 * the comma after it. */
504 len++;
505 }
506 else {
507 /* There is at least one header part before the removed one. So
508 * remove the comma between them. */
509 start--;
510 len++;
511 }
512 /* Update the block content and its len */
513 memmove(start, start+len, v.len-len);
Christopher Faulet3e2638e2019-06-18 09:49:16 +0200514 htx_change_blk_value_len(htx, blk, v.len-len);
Christopher Faulet47596d32018-10-22 09:17:28 +0200515
516 /* Finally update the ctx */
517 ctx->value.ptr = start;
518 ctx->value.len = 0;
519 ctx->lws_before = ctx->lws_after = 0;
520
521 return 1;
522}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200523
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200524/* Updates the authority part of the uri with the value <host>. It happens when
525 * the header host is modified. It returns 0 on failure and 1 on success. It is
526 * the caller responsibility to provide the start-line and to be sure the uri
527 * contains an authority. Thus, if no authority is found in the uri, an error is
528 * returned.
529 */
530static int http_update_authority(struct htx *htx, struct htx_sl *sl, const struct ist host)
531{
532 struct buffer *temp = get_trash_chunk();
533 struct ist meth, vsn, uri, authority;
534
535 uri = htx_sl_req_uri(sl);
536 authority = http_get_authority(uri, 1);
537 if (!authority.len || isteq(host, authority))
538 return 0;
539
540 /* Start by copying old method and version */
541 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
542 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
543
544 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
545 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
546
547 chunk_memcat(temp, uri.ptr, authority.ptr - uri.ptr);
548 chunk_memcat(temp, host.ptr, host.len);
549 chunk_memcat(temp, authority.ptr + authority.len, uri.ptr + uri.len - (authority.ptr + authority.len));
550 uri = ist2(temp->area + meth.len + vsn.len, host.len + uri.len - authority.len); /* uri */
551
552 return http_replace_stline(htx, meth, uri, vsn);
553
554}
555
556/* Update the header host by extracting the authority of the uri <uri>. flags of
557 * the start-line are also updated accordingly. For orgin-form and asterisk-form
558 * uri, the header host is not changed and the flag HTX_SL_F_HAS_AUTHORITY is
559 * removed from the flags of the start-line. Otherwise, this flag is set and the
560 * authority is used to set the value of the header host. This function returns
561 * 0 on failure and 1 on success.
562*/
563static int http_update_host(struct htx *htx, struct htx_sl *sl, const struct ist uri)
564{
565 struct ist authority;
566 struct http_hdr_ctx ctx;
567
568 if (!uri.len || uri.ptr[0] == '/' || uri.ptr[0] == '*') {
569 // origin-form or a asterisk-form (RFC7320 #5.3.1 and #5.3.4)
570 sl->flags &= ~HTX_SL_F_HAS_AUTHORITY;
571 }
572 else {
573 sl->flags |= HTX_SL_F_HAS_AUTHORITY;
574 if (sl->info.req.meth != HTTP_METH_CONNECT) {
575 // absolute-form (RFC7320 #5.3.2)
576 sl->flags |= HTX_SL_F_HAS_SCHM;
577 if (uri.len > 4 && (uri.ptr[0] | 0x20) == 'h')
578 sl->flags |= ((uri.ptr[4] == ':') ? HTX_SL_F_SCHM_HTTP : HTX_SL_F_SCHM_HTTPS);
579
580 authority = http_get_authority(uri, 1);
581 if (!authority.len)
582 goto fail;
583 }
584 else {
585 // authority-form (RFC7320 #5.3.3)
586 authority = uri;
587 }
588
589 /* Replace header host value */
590 ctx.blk = NULL;
591 while (http_find_header(htx, ist("host"), &ctx, 1)) {
592 if (!http_replace_header_value(htx, &ctx, authority))
593 goto fail;
594 }
595
596 }
597 return 1;
598 fail:
599 return 0;
600}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200601
602/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
603 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
604 * performed over the whole headers. Otherwise it must contain a valid header
605 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
606 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
607 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
608 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
609 * -1. The value fetch stops at commas, so this function is suited for use with
610 * list headers.
611 * The return value is 0 if nothing was found, or non-zero otherwise.
612 */
613unsigned int http_get_htx_hdr(const struct htx *htx, const struct ist hdr,
614 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
615{
616 struct http_hdr_ctx local_ctx;
617 struct ist val_hist[MAX_HDR_HISTORY];
618 unsigned int hist_idx;
619 int found;
620
621 if (!ctx) {
622 local_ctx.blk = NULL;
623 ctx = &local_ctx;
624 }
625
626 if (occ >= 0) {
627 /* search from the beginning */
628 while (http_find_header(htx, hdr, ctx, 0)) {
629 occ--;
630 if (occ <= 0) {
631 *vptr = ctx->value.ptr;
632 *vlen = ctx->value.len;
633 return 1;
634 }
635 }
636 return 0;
637 }
638
639 /* negative occurrence, we scan all the list then walk back */
640 if (-occ > MAX_HDR_HISTORY)
641 return 0;
642
643 found = hist_idx = 0;
644 while (http_find_header(htx, hdr, ctx, 0)) {
645 val_hist[hist_idx] = ctx->value;
646 if (++hist_idx >= MAX_HDR_HISTORY)
647 hist_idx = 0;
648 found++;
649 }
650 if (-occ > found)
651 return 0;
652
653 /* OK now we have the last occurrence in [hist_idx-1], and we need to
654 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
655 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
656 * to remain in the 0..9 range.
657 */
658 hist_idx += occ + MAX_HDR_HISTORY;
659 if (hist_idx >= MAX_HDR_HISTORY)
660 hist_idx -= MAX_HDR_HISTORY;
661 *vptr = val_hist[hist_idx].ptr;
662 *vlen = val_hist[hist_idx].len;
663 return 1;
664}
665
666/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
667 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
668 * performed over the whole headers. Otherwise it must contain a valid header
669 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
670 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
671 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
672 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
673 * -1. This function differs from http_get_hdr() in that it only returns full
674 * line header values and does not stop at commas.
675 * The return value is 0 if nothing was found, or non-zero otherwise.
676 */
677unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr,
678 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
679{
680 struct http_hdr_ctx local_ctx;
681 struct ist val_hist[MAX_HDR_HISTORY];
682 unsigned int hist_idx;
683 int found;
684
685 if (!ctx) {
686 local_ctx.blk = NULL;
687 ctx = &local_ctx;
688 }
689
690 if (occ >= 0) {
691 /* search from the beginning */
692 while (http_find_header(htx, hdr, ctx, 1)) {
693 occ--;
694 if (occ <= 0) {
695 *vptr = ctx->value.ptr;
696 *vlen = ctx->value.len;
697 return 1;
698 }
699 }
700 return 0;
701 }
702
703 /* negative occurrence, we scan all the list then walk back */
704 if (-occ > MAX_HDR_HISTORY)
705 return 0;
706
707 found = hist_idx = 0;
708 while (http_find_header(htx, hdr, ctx, 1)) {
709 val_hist[hist_idx] = ctx->value;
710 if (++hist_idx >= MAX_HDR_HISTORY)
711 hist_idx = 0;
712 found++;
713 }
714 if (-occ > found)
715 return 0;
716
717 /* OK now we have the last occurrence in [hist_idx-1], and we need to
718 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
719 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
720 * to remain in the 0..9 range.
721 */
722 hist_idx += occ + MAX_HDR_HISTORY;
723 if (hist_idx >= MAX_HDR_HISTORY)
724 hist_idx -= MAX_HDR_HISTORY;
725 *vptr = val_hist[hist_idx].ptr;
726 *vlen = val_hist[hist_idx].len;
727 return 1;
728}
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100729
Christopher Faulet90cc4812019-07-22 16:49:30 +0200730int http_str_to_htx(struct buffer *buf, struct ist raw)
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100731{
732 struct htx *htx;
733 struct htx_sl *sl;
734 struct h1m h1m;
Christopher Faulete4ab11b2019-06-11 15:05:37 +0200735 struct http_hdr hdrs[global.tune.max_http_hdr];
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100736 union h1_sl h1sl;
737 unsigned int flags = HTX_SL_F_IS_RESP;
738 int ret = 0;
739
Christopher Faulet90cc4812019-07-22 16:49:30 +0200740 b_reset(buf);
741 if (!raw.len) {
742 buf->size = 0;
743 buf->area = malloc(raw.len);
744 return 1;
745 }
746
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100747 buf->size = global.tune.bufsize;
748 buf->area = (char *)malloc(buf->size);
749 if (!buf->area)
750 goto error;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100751
752 h1m_init_res(&h1m);
753 h1m.flags |= H1_MF_NO_PHDR;
754 ret = h1_headers_to_hdr_list(raw.ptr, raw.ptr + raw.len,
755 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
756 if (ret <= 0)
757 goto error;
758
759 if (unlikely(h1sl.st.v.len != 8))
760 goto error;
761 if ((*(h1sl.st.v.ptr + 5) > '1') ||
762 ((*(h1sl.st.v.ptr + 5) == '1') && (*(h1sl.st.v.ptr + 7) >= '1')))
763 h1m.flags |= H1_MF_VER_11;
764
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200765 if (h1sl.st.status < 200 && (h1sl.st.status == 100 || h1sl.st.status >= 102))
766 goto error;
767
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100768 if (h1m.flags & H1_MF_VER_11)
769 flags |= HTX_SL_F_VER_11;
770 if (h1m.flags & H1_MF_XFER_ENC)
771 flags |= HTX_SL_F_XFER_ENC;
Christopher Faulet0d4ce932019-10-16 09:09:04 +0200772 if (h1m.flags & H1_MF_CLEN) {
773 flags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
774 if (h1m.body_len == 0)
775 flags |= HTX_SL_F_BODYLESS;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100776 }
Christopher Faulet0d4ce932019-10-16 09:09:04 +0200777 if (h1m.flags & H1_MF_CHNK)
778 goto error; /* Unsupported because there is no body parsing */
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100779
780 htx = htx_from_buf(buf);
781 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, h1sl.st.v, h1sl.st.c, h1sl.st.r);
782 if (!sl || !htx_add_all_headers(htx, hdrs))
783 goto error;
784 sl->info.res.status = h1sl.st.status;
785
Willy Tarreau0a7ef022019-05-28 10:30:11 +0200786 while (raw.len > ret) {
787 int sent = htx_add_data(htx, ist2(raw.ptr + ret, raw.len - ret));
788 if (!sent)
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100789 goto error;
Willy Tarreau0a7ef022019-05-28 10:30:11 +0200790 ret += sent;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100791 }
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200792
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100793 if (!htx_add_endof(htx, HTX_BLK_EOM))
794 goto error;
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200795
Christopher Faulet90cc4812019-07-22 16:49:30 +0200796 return 1;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100797
798error:
799 if (buf->size)
800 free(buf->area);
Christopher Faulet90cc4812019-07-22 16:49:30 +0200801 return 0;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100802}
803
804static int http_htx_init(void)
805{
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100806 struct buffer chk;
807 struct ist raw;
808 int rc;
809 int err_code = 0;
810
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100811 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
812 if (!http_err_msgs[rc]) {
813 ha_alert("Internal error: no message defined for HTTP return code %d", rc);
814 err_code |= ERR_ALERT | ERR_FATAL;
815 continue;
816 }
817
818 raw = ist2(http_err_msgs[rc], strlen(http_err_msgs[rc]));
819 if (!http_str_to_htx(&chk, raw)) {
820 ha_alert("Internal error: Unable to convert message in HTX for HTTP return code %d.\n",
821 http_err_codes[rc]);
822 err_code |= ERR_ALERT | ERR_FATAL;
823 }
Christopher Fauletf7346382019-07-17 22:02:08 +0200824 http_err_chunks[rc] = chk;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100825 }
826end:
827 return err_code;
828}
829
830REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init);