blob: 7489f628847aa845ae1034ec51d3650d84ff0f8b [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 }
115 if (!v.len)
Christopher Faulet28f29c72019-04-30 17:55:45 +0200116 continue;
Christopher Faulet47596d32018-10-22 09:17:28 +0200117 ctx->blk = blk;
118 ctx->value = v;
119 return 1;
120
121 next_blk:
Christopher Faulet28f29c72019-04-30 17:55:45 +0200122 ;
Christopher Faulet47596d32018-10-22 09:17:28 +0200123 }
124
125 ctx->blk = NULL;
126 ctx->value = ist("");
127 ctx->lws_before = ctx->lws_after = 0;
128 return 0;
129}
130
131/* Adds a header block int the HTX message <htx>, just before the EOH block. It
132 * returns 1 on success, otherwise it returns 0.
133 */
134int http_add_header(struct htx *htx, const struct ist n, const struct ist v)
135{
136 struct htx_blk *blk;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200137 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200138 enum htx_blk_type type = htx_get_tail_type(htx);
139 int32_t prev;
140
141 blk = htx_add_header(htx, n, v);
142 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200143 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200144
145 if (unlikely(type < HTX_BLK_EOH))
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200146 goto end;
Christopher Faulet47596d32018-10-22 09:17:28 +0200147
148 /* <blk> is the head, swap it iteratively with its predecessor to place
149 * it just before the end-of-header block. So blocks remains ordered. */
Christopher Faulet29f17582019-05-23 11:03:26 +0200150 for (prev = htx_get_prev(htx, htx->tail); prev != htx->first; prev = htx_get_prev(htx, prev)) {
Christopher Faulet47596d32018-10-22 09:17:28 +0200151 struct htx_blk *pblk = htx_get_blk(htx, prev);
152 enum htx_blk_type type = htx_get_blk_type(pblk);
153
154 /* Swap .addr and .info fields */
155 blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
156 blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info;
157
158 if (blk->addr == pblk->addr)
159 blk->addr += htx_get_blksz(pblk);
Christopher Faulet47596d32018-10-22 09:17:28 +0200160
161 /* Stop when end-of-header is reached */
162 if (type == HTX_BLK_EOH)
163 break;
164
165 blk = pblk;
166 }
Christopher Faulet05aab642019-04-11 13:43:57 +0200167
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200168 end:
169 sl = http_get_stline(htx);
170 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteq(n, ist("host"))) {
171 if (!http_update_authority(htx, sl, v))
172 goto fail;
173 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200174 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200175
176 fail:
177 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200178}
179
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100180/* Replaces parts of the start-line of the HTX message <htx>. It returns 1 on
Christopher Faulet29f17582019-05-23 11:03:26 +0200181 * success, otherwise it returns 0.
Christopher Faulet47596d32018-10-22 09:17:28 +0200182 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100183int 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 +0200184{
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200185 struct htx_blk *blk;
Christopher Faulet47596d32018-10-22 09:17:28 +0200186
Christopher Faulet29f17582019-05-23 11:03:26 +0200187 blk = htx_get_first_blk(htx);
188 if (!blk || !htx_replace_stline(htx, blk, p1, p2, p3))
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200189 return 0;
190 return 1;
Christopher Faulet47596d32018-10-22 09:17:28 +0200191}
192
Christopher Faulete010c802018-10-24 10:36:45 +0200193/* Replace the request method in the HTX message <htx> by <meth>. It returns 1
194 * on success, otherwise 0.
195 */
196int http_replace_req_meth(struct htx *htx, const struct ist meth)
197{
198 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200199 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100200 struct ist uri, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200201
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100202 if (!sl)
203 return 0;
204
Christopher Faulete010c802018-10-24 10:36:45 +0200205 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100206 chunk_memcat(temp, HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl)); /* uri */
207 uri = ist2(temp->area, HTX_SL_REQ_ULEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200208
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100209 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
210 vsn = ist2(temp->area + uri.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200211
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100212 /* create the new start line */
213 sl->info.req.meth = find_http_meth(meth.ptr, meth.len);
214 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200215}
216
217/* Replace the request uri in the HTX message <htx> by <uri>. It returns 1 on
218 * success, otherwise 0.
219 */
220int http_replace_req_uri(struct htx *htx, const struct ist uri)
221{
222 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200223 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100224 struct ist meth, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200225
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100226 if (!sl)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200227 goto fail;
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100228
Christopher Faulete010c802018-10-24 10:36:45 +0200229 /* Start by copying old method and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100230 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
231 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200232
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100233 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
234 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200235
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100236 /* create the new start line */
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200237 if (!http_replace_stline(htx, meth, uri, vsn))
238 goto fail;
239
240 sl = http_get_stline(htx);
241 if (!http_update_host(htx, sl, uri))
242 goto fail;
243
244 return 1;
245 fail:
246 return 0;
Christopher Faulete010c802018-10-24 10:36:45 +0200247}
248
249/* Replace the request path in the HTX message <htx> by <path>. The host part
250 * and the query string are preserved. It returns 1 on success, otherwise 0.
251 */
252int http_replace_req_path(struct htx *htx, const struct ist path)
253{
254 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200255 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100256 struct ist meth, uri, vsn, p;
Christopher Faulete010c802018-10-24 10:36:45 +0200257 size_t plen = 0;
258
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100259 if (!sl)
260 return 0;
261
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100262 uri = htx_sl_req_uri(sl);
263 p = http_get_path(uri);
Christopher Faulete010c802018-10-24 10:36:45 +0200264 if (!p.ptr)
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100265 p = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200266 while (plen < p.len && *(p.ptr + plen) != '?')
267 plen++;
268
269 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100270 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
271 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200272
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100273 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
274 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
275
276 chunk_memcat(temp, uri.ptr, p.ptr - uri.ptr); /* uri: host part */
Christopher Faulete010c802018-10-24 10:36:45 +0200277 chunk_memcat(temp, path.ptr, path.len); /* uri: new path */
278 chunk_memcat(temp, p.ptr + plen, p.len - plen); /* uri: QS part */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100279 uri = ist2(temp->area + meth.len + vsn.len, uri.len - plen + path.len);
Christopher Faulete010c802018-10-24 10:36:45 +0200280
281 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100282 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200283}
284
285/* Replace the request query-string in the HTX message <htx> by <query>. The
286 * host part and the path are preserved. It returns 1 on success, otherwise
287 * 0.
288 */
289int http_replace_req_query(struct htx *htx, const struct ist query)
290{
291 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200292 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100293 struct ist meth, uri, vsn, q;
Christopher Faulete010c802018-10-24 10:36:45 +0200294 int offset = 1;
295
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100296 if (!sl)
297 return 0;
298
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100299 uri = htx_sl_req_uri(sl);
300 q = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200301 while (q.len > 0 && *(q.ptr) != '?') {
302 q.ptr++;
303 q.len--;
304 }
305
306 /* skip the question mark or indicate that we must insert it
307 * (but only if the format string is not empty then).
308 */
309 if (q.len) {
310 q.ptr++;
311 q.len--;
312 }
313 else if (query.len > 1)
314 offset = 0;
315
316 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100317 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
318 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200319
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100320 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
321 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200322
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100323 chunk_memcat(temp, uri.ptr, q.ptr - uri.ptr); /* uri: host + path part */
324 chunk_memcat(temp, query.ptr + offset, query.len - offset); /* uri: new QS */
325 uri = ist2(temp->area + meth.len + vsn.len, uri.len - q.len + query.len - offset);
Christopher Faulete010c802018-10-24 10:36:45 +0200326
327 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100328 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200329}
330
331/* Replace the response status in the HTX message <htx> by <status>. It returns
332 * 1 on success, otherwise 0.
333*/
334int http_replace_res_status(struct htx *htx, const struct ist status)
335{
336 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200337 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100338 struct ist vsn, reason;
Christopher Faulete010c802018-10-24 10:36:45 +0200339
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100340 if (!sl)
341 return 0;
342
Christopher Faulete010c802018-10-24 10:36:45 +0200343 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100344 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
345 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200346
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100347 chunk_memcat(temp, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl)); /* reason */
348 reason = ist2(temp->area + vsn.len, HTX_SL_RES_RLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200349
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100350 /* create the new start line */
351 sl->info.res.status = strl2ui(status.ptr, status.len);
352 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200353}
354
355/* Replace the response reason in the HTX message <htx> by <reason>. It returns
356 * 1 on success, otherwise 0.
357*/
358int http_replace_res_reason(struct htx *htx, const struct ist reason)
359{
360 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200361 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100362 struct ist vsn, status;
Christopher Faulete010c802018-10-24 10:36:45 +0200363
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100364 if (!sl)
365 return 0;
366
Christopher Faulete010c802018-10-24 10:36:45 +0200367 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100368 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
369 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200370
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100371 chunk_memcat(temp, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl)); /* code */
372 status = ist2(temp->area + vsn.len, HTX_SL_RES_CLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200373
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100374 /* create the new start line */
375 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200376}
377
Christopher Faulet47596d32018-10-22 09:17:28 +0200378/* Replaces a part of a header value referenced in the context <ctx> by
379 * <data>. It returns 1 on success, otherwise it returns 0. The context is
380 * updated if necessary.
381 */
382int http_replace_header_value(struct htx *htx, struct http_hdr_ctx *ctx, const struct ist data)
383{
384 struct htx_blk *blk = ctx->blk;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200385 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200386 char *start;
387 struct ist v;
388 uint32_t len, off;
389
390 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200391 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200392
393 v = htx_get_blk_value(htx, blk);
394 start = ctx->value.ptr - ctx->lws_before;
395 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
396 off = start - v.ptr;
397
398 blk = htx_replace_blk_value(htx, blk, ist2(start, len), data);
399 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200400 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200401
402 v = htx_get_blk_value(htx, blk);
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200403
404 sl = http_get_stline(htx);
405 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY)) {
406 struct ist n = htx_get_blk_name(htx, blk);
407
408 if (isteq(n, ist("host"))) {
409 if (!http_update_authority(htx, sl, v))
410 goto fail;
411 ctx->blk = NULL;
412 http_find_header(htx, ist("host"), ctx, 1);
413 blk = ctx->blk;
414 v = htx_get_blk_value(htx, blk);
415 }
416 }
417
Christopher Faulet47596d32018-10-22 09:17:28 +0200418 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;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200424 fail:
425 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200426}
427
428/* Fully replaces a header referenced in the context <ctx> by the name <name>
429 * with the value <value>. It returns 1 on success, otherwise it returns 0. The
430 * context is updated if necessary.
431 */
432int http_replace_header(struct htx *htx, struct http_hdr_ctx *ctx,
433 const struct ist name, const struct ist value)
434{
435 struct htx_blk *blk = ctx->blk;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200436 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200437
438 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200439 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200440
441 blk = htx_replace_header(htx, blk, name, value);
442 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200443 goto fail;
444
445 sl = http_get_stline(htx);
446 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteq(name, ist("host"))) {
447 if (!http_update_authority(htx, sl, value))
448 goto fail;
449 ctx->blk = NULL;
450 http_find_header(htx, ist("host"), ctx, 1);
451 blk = ctx->blk;
452 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200453
454 ctx->blk = blk;
455 ctx->value = ist(NULL);
456 ctx->lws_before = ctx->lws_after = 0;
457
458 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200459 fail:
460 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200461}
462
463/* Remove one value of a header. This only works on a <ctx> returned by
464 * http_find_header function. The value is removed, as well as surrounding commas
465 * if any. If the removed value was alone, the whole header is removed. The
466 * <ctx> is always updated accordingly, as well as the HTX message <htx>. It
467 * returns 1 on success. Otherwise, it returns 0. The <ctx> is always left in a
468 * form that can be handled by http_find_header() to find next occurrence.
469 */
470int http_remove_header(struct htx *htx, struct http_hdr_ctx *ctx)
471{
472 struct htx_blk *blk = ctx->blk;
473 char *start;
474 struct ist v;
475 uint32_t len;
476
477 if (!blk)
478 return 0;
479
480 start = ctx->value.ptr - ctx->lws_before;
481 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
482
483 v = htx_get_blk_value(htx, blk);
484 if (len == v.len) {
485 blk = htx_remove_blk(htx, blk);
Christopher Faulet192c6a22019-06-11 16:32:24 +0200486 if (blk || htx_is_empty(htx)) {
Christopher Faulet47596d32018-10-22 09:17:28 +0200487 ctx->blk = blk;
488 ctx->value = ist2(NULL, 0);
489 ctx->lws_before = ctx->lws_after = 0;
490 }
491 else {
492 ctx->blk = htx_get_blk(htx, htx->tail);
493 ctx->value = htx_get_blk_value(htx, ctx->blk);
494 ctx->lws_before = ctx->lws_after = 0;
495 }
496 return 1;
497 }
498
499 /* This was not the only value of this header. We have to remove the
500 * part pointed by ctx->value. If it is the last entry of the list, we
501 * remove the last separator.
502 */
503 if (start == v.ptr) {
504 /* It's the first header part but not the only one. So remove
505 * the comma after it. */
506 len++;
507 }
508 else {
509 /* There is at least one header part before the removed one. So
510 * remove the comma between them. */
511 start--;
512 len++;
513 }
514 /* Update the block content and its len */
515 memmove(start, start+len, v.len-len);
Christopher Faulet3e2638e2019-06-18 09:49:16 +0200516 htx_change_blk_value_len(htx, blk, v.len-len);
Christopher Faulet47596d32018-10-22 09:17:28 +0200517
518 /* Finally update the ctx */
519 ctx->value.ptr = start;
520 ctx->value.len = 0;
521 ctx->lws_before = ctx->lws_after = 0;
522
523 return 1;
524}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200525
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200526/* Updates the authority part of the uri with the value <host>. It happens when
527 * the header host is modified. It returns 0 on failure and 1 on success. It is
528 * the caller responsibility to provide the start-line and to be sure the uri
529 * contains an authority. Thus, if no authority is found in the uri, an error is
530 * returned.
531 */
532static int http_update_authority(struct htx *htx, struct htx_sl *sl, const struct ist host)
533{
534 struct buffer *temp = get_trash_chunk();
535 struct ist meth, vsn, uri, authority;
536
537 uri = htx_sl_req_uri(sl);
538 authority = http_get_authority(uri, 1);
539 if (!authority.len || isteq(host, authority))
540 return 0;
541
542 /* Start by copying old method and version */
543 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
544 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
545
546 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
547 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
548
549 chunk_memcat(temp, uri.ptr, authority.ptr - uri.ptr);
550 chunk_memcat(temp, host.ptr, host.len);
551 chunk_memcat(temp, authority.ptr + authority.len, uri.ptr + uri.len - (authority.ptr + authority.len));
552 uri = ist2(temp->area + meth.len + vsn.len, host.len + uri.len - authority.len); /* uri */
553
554 return http_replace_stline(htx, meth, uri, vsn);
555
556}
557
558/* Update the header host by extracting the authority of the uri <uri>. flags of
559 * the start-line are also updated accordingly. For orgin-form and asterisk-form
560 * uri, the header host is not changed and the flag HTX_SL_F_HAS_AUTHORITY is
561 * removed from the flags of the start-line. Otherwise, this flag is set and the
562 * authority is used to set the value of the header host. This function returns
563 * 0 on failure and 1 on success.
564*/
565static int http_update_host(struct htx *htx, struct htx_sl *sl, const struct ist uri)
566{
567 struct ist authority;
568 struct http_hdr_ctx ctx;
569
570 if (!uri.len || uri.ptr[0] == '/' || uri.ptr[0] == '*') {
571 // origin-form or a asterisk-form (RFC7320 #5.3.1 and #5.3.4)
572 sl->flags &= ~HTX_SL_F_HAS_AUTHORITY;
573 }
574 else {
575 sl->flags |= HTX_SL_F_HAS_AUTHORITY;
576 if (sl->info.req.meth != HTTP_METH_CONNECT) {
577 // absolute-form (RFC7320 #5.3.2)
578 sl->flags |= HTX_SL_F_HAS_SCHM;
579 if (uri.len > 4 && (uri.ptr[0] | 0x20) == 'h')
580 sl->flags |= ((uri.ptr[4] == ':') ? HTX_SL_F_SCHM_HTTP : HTX_SL_F_SCHM_HTTPS);
581
582 authority = http_get_authority(uri, 1);
583 if (!authority.len)
584 goto fail;
585 }
586 else {
587 // authority-form (RFC7320 #5.3.3)
588 authority = uri;
589 }
590
591 /* Replace header host value */
592 ctx.blk = NULL;
593 while (http_find_header(htx, ist("host"), &ctx, 1)) {
594 if (!http_replace_header_value(htx, &ctx, authority))
595 goto fail;
596 }
597
598 }
599 return 1;
600 fail:
601 return 0;
602}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200603
604/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
605 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
606 * performed over the whole headers. Otherwise it must contain a valid header
607 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
608 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
609 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
610 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
611 * -1. The value fetch stops at commas, so this function is suited for use with
612 * list headers.
613 * The return value is 0 if nothing was found, or non-zero otherwise.
614 */
615unsigned int http_get_htx_hdr(const struct htx *htx, const struct ist hdr,
616 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
617{
618 struct http_hdr_ctx local_ctx;
619 struct ist val_hist[MAX_HDR_HISTORY];
620 unsigned int hist_idx;
621 int found;
622
623 if (!ctx) {
624 local_ctx.blk = NULL;
625 ctx = &local_ctx;
626 }
627
628 if (occ >= 0) {
629 /* search from the beginning */
630 while (http_find_header(htx, hdr, ctx, 0)) {
631 occ--;
632 if (occ <= 0) {
633 *vptr = ctx->value.ptr;
634 *vlen = ctx->value.len;
635 return 1;
636 }
637 }
638 return 0;
639 }
640
641 /* negative occurrence, we scan all the list then walk back */
642 if (-occ > MAX_HDR_HISTORY)
643 return 0;
644
645 found = hist_idx = 0;
646 while (http_find_header(htx, hdr, ctx, 0)) {
647 val_hist[hist_idx] = ctx->value;
648 if (++hist_idx >= MAX_HDR_HISTORY)
649 hist_idx = 0;
650 found++;
651 }
652 if (-occ > found)
653 return 0;
654
655 /* OK now we have the last occurrence in [hist_idx-1], and we need to
656 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
657 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
658 * to remain in the 0..9 range.
659 */
660 hist_idx += occ + MAX_HDR_HISTORY;
661 if (hist_idx >= MAX_HDR_HISTORY)
662 hist_idx -= MAX_HDR_HISTORY;
663 *vptr = val_hist[hist_idx].ptr;
664 *vlen = val_hist[hist_idx].len;
665 return 1;
666}
667
668/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
669 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
670 * performed over the whole headers. Otherwise it must contain a valid header
671 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
672 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
673 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
674 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
675 * -1. This function differs from http_get_hdr() in that it only returns full
676 * line header values and does not stop at commas.
677 * The return value is 0 if nothing was found, or non-zero otherwise.
678 */
679unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr,
680 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
681{
682 struct http_hdr_ctx local_ctx;
683 struct ist val_hist[MAX_HDR_HISTORY];
684 unsigned int hist_idx;
685 int found;
686
687 if (!ctx) {
688 local_ctx.blk = NULL;
689 ctx = &local_ctx;
690 }
691
692 if (occ >= 0) {
693 /* search from the beginning */
694 while (http_find_header(htx, hdr, ctx, 1)) {
695 occ--;
696 if (occ <= 0) {
697 *vptr = ctx->value.ptr;
698 *vlen = ctx->value.len;
699 return 1;
700 }
701 }
702 return 0;
703 }
704
705 /* negative occurrence, we scan all the list then walk back */
706 if (-occ > MAX_HDR_HISTORY)
707 return 0;
708
709 found = hist_idx = 0;
710 while (http_find_header(htx, hdr, ctx, 1)) {
711 val_hist[hist_idx] = ctx->value;
712 if (++hist_idx >= MAX_HDR_HISTORY)
713 hist_idx = 0;
714 found++;
715 }
716 if (-occ > found)
717 return 0;
718
719 /* OK now we have the last occurrence in [hist_idx-1], and we need to
720 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
721 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
722 * to remain in the 0..9 range.
723 */
724 hist_idx += occ + MAX_HDR_HISTORY;
725 if (hist_idx >= MAX_HDR_HISTORY)
726 hist_idx -= MAX_HDR_HISTORY;
727 *vptr = val_hist[hist_idx].ptr;
728 *vlen = val_hist[hist_idx].len;
729 return 1;
730}
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100731
Christopher Faulet90cc4812019-07-22 16:49:30 +0200732int http_str_to_htx(struct buffer *buf, struct ist raw)
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100733{
734 struct htx *htx;
735 struct htx_sl *sl;
736 struct h1m h1m;
Christopher Faulete4ab11b2019-06-11 15:05:37 +0200737 struct http_hdr hdrs[global.tune.max_http_hdr];
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100738 union h1_sl h1sl;
739 unsigned int flags = HTX_SL_F_IS_RESP;
740 int ret = 0;
741
Christopher Faulet90cc4812019-07-22 16:49:30 +0200742 b_reset(buf);
743 if (!raw.len) {
744 buf->size = 0;
745 buf->area = malloc(raw.len);
746 return 1;
747 }
748
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100749 buf->size = global.tune.bufsize;
750 buf->area = (char *)malloc(buf->size);
751 if (!buf->area)
752 goto error;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100753
754 h1m_init_res(&h1m);
755 h1m.flags |= H1_MF_NO_PHDR;
756 ret = h1_headers_to_hdr_list(raw.ptr, raw.ptr + raw.len,
757 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
758 if (ret <= 0)
759 goto error;
760
761 if (unlikely(h1sl.st.v.len != 8))
762 goto error;
763 if ((*(h1sl.st.v.ptr + 5) > '1') ||
764 ((*(h1sl.st.v.ptr + 5) == '1') && (*(h1sl.st.v.ptr + 7) >= '1')))
765 h1m.flags |= H1_MF_VER_11;
766
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200767 if (h1sl.st.status < 200 && (h1sl.st.status == 100 || h1sl.st.status >= 102))
768 goto error;
769
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100770 if (h1m.flags & H1_MF_VER_11)
771 flags |= HTX_SL_F_VER_11;
772 if (h1m.flags & H1_MF_XFER_ENC)
773 flags |= HTX_SL_F_XFER_ENC;
Christopher Faulet0d4ce932019-10-16 09:09:04 +0200774 if (h1m.flags & H1_MF_CLEN) {
775 flags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
776 if (h1m.body_len == 0)
777 flags |= HTX_SL_F_BODYLESS;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100778 }
Christopher Faulet0d4ce932019-10-16 09:09:04 +0200779 if (h1m.flags & H1_MF_CHNK)
780 goto error; /* Unsupported because there is no body parsing */
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100781
782 htx = htx_from_buf(buf);
783 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, h1sl.st.v, h1sl.st.c, h1sl.st.r);
784 if (!sl || !htx_add_all_headers(htx, hdrs))
785 goto error;
786 sl->info.res.status = h1sl.st.status;
787
Willy Tarreau0a7ef022019-05-28 10:30:11 +0200788 while (raw.len > ret) {
789 int sent = htx_add_data(htx, ist2(raw.ptr + ret, raw.len - ret));
790 if (!sent)
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100791 goto error;
Willy Tarreau0a7ef022019-05-28 10:30:11 +0200792 ret += sent;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100793 }
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200794
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100795 if (!htx_add_endof(htx, HTX_BLK_EOM))
796 goto error;
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200797
Christopher Faulet90cc4812019-07-22 16:49:30 +0200798 return 1;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100799
800error:
801 if (buf->size)
802 free(buf->area);
Christopher Faulet90cc4812019-07-22 16:49:30 +0200803 return 0;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100804}
805
806static int http_htx_init(void)
807{
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100808 struct buffer chk;
809 struct ist raw;
810 int rc;
811 int err_code = 0;
812
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100813 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
814 if (!http_err_msgs[rc]) {
815 ha_alert("Internal error: no message defined for HTTP return code %d", rc);
816 err_code |= ERR_ALERT | ERR_FATAL;
817 continue;
818 }
819
820 raw = ist2(http_err_msgs[rc], strlen(http_err_msgs[rc]));
821 if (!http_str_to_htx(&chk, raw)) {
822 ha_alert("Internal error: Unable to convert message in HTX for HTTP return code %d.\n",
823 http_err_codes[rc]);
824 err_code |= ERR_ALERT | ERR_FATAL;
825 }
Christopher Fauletf7346382019-07-17 22:02:08 +0200826 http_err_chunks[rc] = chk;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100827 }
828end:
829 return err_code;
830}
831
832REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init);