blob: d35904ccff94eb227503b48c5c7a6ef096a71dc8 [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 Fauleta7b677c2018-11-29 16:48:49 +010022struct buffer htx_err_chunks[HTTP_ERR_SIZE];
23
Christopher Faulet297fbb42019-05-13 14:41:27 +020024/* Returns the next unporocessed start line in the HTX message. It returns NULL
Christopher Faulet29f17582019-05-23 11:03:26 +020025 * if the start-line is undefined (first == -1). Otherwise, it returns the
Christopher Faulet297fbb42019-05-13 14:41:27 +020026 * pointer on the htx_sl structure.
Christopher Faulet47596d32018-10-22 09:17:28 +020027 */
Christopher Faulet297fbb42019-05-13 14:41:27 +020028struct htx_sl *http_get_stline(struct htx *htx)
Christopher Faulet47596d32018-10-22 09:17:28 +020029{
Christopher Faulet297fbb42019-05-13 14:41:27 +020030 struct htx_blk *blk;
Christopher Faulet573fe732018-11-28 16:55:12 +010031
Christopher Faulet29f17582019-05-23 11:03:26 +020032 BUG_ON(htx->first == -1);
33 blk = htx_get_first_blk(htx);
Christopher Faulet297fbb42019-05-13 14:41:27 +020034 if (!blk)
35 return NULL;
Christopher Faulet29f17582019-05-23 11:03:26 +020036 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 +020037 return htx_get_blk_ptr(htx, blk);
Christopher Faulet47596d32018-10-22 09:17:28 +020038}
39
40/* Finds the first or next occurrence of header <name> in the HTX message <htx>
41 * using the context <ctx>. This structure holds everything necessary to use the
42 * header and find next occurrence. If its <blk> member is NULL, the header is
43 * searched from the beginning. Otherwise, the next occurrence is returned. The
44 * function returns 1 when it finds a value, and 0 when there is no more. It is
45 * designed to work with headers defined as comma-separated lists. If <full> is
46 * set, it works on full-line headers in whose comma is not a delimiter but is
47 * part of the syntax. A special case, if ctx->value is NULL when searching for
48 * a new values of a header, the current header is rescanned. This allows
49 * rescanning after a header deletion.
50 */
51int http_find_header(const struct htx *htx, const struct ist name,
52 struct http_hdr_ctx *ctx, int full)
53{
54 struct htx_blk *blk = ctx->blk;
55 struct ist n, v;
56 enum htx_blk_type type;
Christopher Faulet47596d32018-10-22 09:17:28 +020057
58 if (blk) {
59 char *p;
60
Christopher Faulet47596d32018-10-22 09:17:28 +020061 if (!ctx->value.ptr)
62 goto rescan_hdr;
63 if (full)
64 goto next_blk;
65 v = htx_get_blk_value(htx, blk);
66 p = ctx->value.ptr + ctx->value.len + ctx->lws_after;
67 v.len -= (p - v.ptr);
68 v.ptr = p;
69 if (!v.len)
70 goto next_blk;
71 /* Skip comma */
72 if (*(v.ptr) == ',') {
73 v.ptr++;
74 v.len--;
75 }
76
77 goto return_hdr;
78 }
79
80 if (!htx->used)
81 return 0;
82
Christopher Fauleta3f15502019-05-13 15:27:23 +020083 for (blk = htx_get_first_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
Christopher Faulet47596d32018-10-22 09:17:28 +020084 rescan_hdr:
Christopher Faulet47596d32018-10-22 09:17:28 +020085 type = htx_get_blk_type(blk);
Christopher Faulet573fe732018-11-28 16:55:12 +010086 if (type == HTX_BLK_EOH || type == HTX_BLK_EOM)
87 break;
Christopher Faulet47596d32018-10-22 09:17:28 +020088 if (type != HTX_BLK_HDR)
Christopher Faulet28f29c72019-04-30 17:55:45 +020089 continue;
Christopher Faulet47596d32018-10-22 09:17:28 +020090 if (name.len) {
91 /* If no name was passed, we want any header. So skip the comparison */
92 n = htx_get_blk_name(htx, blk);
93 if (!isteqi(n, name))
94 goto next_blk;
95 }
96 v = htx_get_blk_value(htx, blk);
97
98 return_hdr:
99 ctx->lws_before = 0;
100 ctx->lws_after = 0;
101 while (v.len && HTTP_IS_LWS(*v.ptr)) {
102 v.ptr++;
103 v.len--;
104 ctx->lws_before++;
105 }
106 if (!full)
107 v.len = http_find_hdr_value_end(v.ptr, v.ptr + v.len) - v.ptr;
108 while (v.len && HTTP_IS_LWS(*(v.ptr + v.len - 1))) {
109 v.len--;
110 ctx->lws_after++;
111 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200112 ctx->blk = blk;
113 ctx->value = v;
114 return 1;
115
116 next_blk:
Christopher Faulet28f29c72019-04-30 17:55:45 +0200117 ;
Christopher Faulet47596d32018-10-22 09:17:28 +0200118 }
119
120 ctx->blk = NULL;
121 ctx->value = ist("");
122 ctx->lws_before = ctx->lws_after = 0;
123 return 0;
124}
125
126/* Adds a header block int the HTX message <htx>, just before the EOH block. It
127 * returns 1 on success, otherwise it returns 0.
128 */
129int http_add_header(struct htx *htx, const struct ist n, const struct ist v)
130{
131 struct htx_blk *blk;
132 enum htx_blk_type type = htx_get_tail_type(htx);
133 int32_t prev;
134
135 blk = htx_add_header(htx, n, v);
136 if (!blk)
137 return 0;
138
139 if (unlikely(type < HTX_BLK_EOH))
140 return 1;
141
142 /* <blk> is the head, swap it iteratively with its predecessor to place
143 * it just before the end-of-header block. So blocks remains ordered. */
Christopher Faulet29f17582019-05-23 11:03:26 +0200144 for (prev = htx_get_prev(htx, htx->tail); prev != htx->first; prev = htx_get_prev(htx, prev)) {
Christopher Faulet47596d32018-10-22 09:17:28 +0200145 struct htx_blk *pblk = htx_get_blk(htx, prev);
146 enum htx_blk_type type = htx_get_blk_type(pblk);
147
148 /* Swap .addr and .info fields */
149 blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
150 blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info;
151
152 if (blk->addr == pblk->addr)
153 blk->addr += htx_get_blksz(pblk);
Christopher Faulet47596d32018-10-22 09:17:28 +0200154
155 /* Stop when end-of-header is reached */
156 if (type == HTX_BLK_EOH)
157 break;
158
159 blk = pblk;
160 }
Christopher Faulet05aab642019-04-11 13:43:57 +0200161
Christopher Faulet47596d32018-10-22 09:17:28 +0200162 return 1;
163}
164
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100165/* Replaces parts of the start-line of the HTX message <htx>. It returns 1 on
Christopher Faulet29f17582019-05-23 11:03:26 +0200166 * success, otherwise it returns 0.
Christopher Faulet47596d32018-10-22 09:17:28 +0200167 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100168int 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 +0200169{
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200170 struct htx_blk *blk;
Christopher Faulet47596d32018-10-22 09:17:28 +0200171
Christopher Faulet29f17582019-05-23 11:03:26 +0200172 blk = htx_get_first_blk(htx);
173 if (!blk || !htx_replace_stline(htx, blk, p1, p2, p3))
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200174 return 0;
175 return 1;
Christopher Faulet47596d32018-10-22 09:17:28 +0200176}
177
Christopher Faulete010c802018-10-24 10:36:45 +0200178/* Replace the request method in the HTX message <htx> by <meth>. It returns 1
179 * on success, otherwise 0.
180 */
181int http_replace_req_meth(struct htx *htx, const struct ist meth)
182{
183 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200184 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100185 struct ist uri, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200186
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100187 if (!sl)
188 return 0;
189
Christopher Faulete010c802018-10-24 10:36:45 +0200190 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100191 chunk_memcat(temp, HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl)); /* uri */
192 uri = ist2(temp->area, HTX_SL_REQ_ULEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200193
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100194 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
195 vsn = ist2(temp->area + uri.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200196
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100197 /* create the new start line */
198 sl->info.req.meth = find_http_meth(meth.ptr, meth.len);
199 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200200}
201
202/* Replace the request uri in the HTX message <htx> by <uri>. It returns 1 on
203 * success, otherwise 0.
204 */
205int http_replace_req_uri(struct htx *htx, const struct ist uri)
206{
207 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200208 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100209 struct ist meth, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200210
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100211 if (!sl)
212 return 0;
213
Christopher Faulete010c802018-10-24 10:36:45 +0200214 /* Start by copying old method and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100215 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
216 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200217
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100218 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
219 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200220
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100221 /* create the new start line */
222 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200223}
224
225/* Replace the request path in the HTX message <htx> by <path>. The host part
226 * and the query string are preserved. It returns 1 on success, otherwise 0.
227 */
228int http_replace_req_path(struct htx *htx, const struct ist path)
229{
230 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200231 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100232 struct ist meth, uri, vsn, p;
Christopher Faulete010c802018-10-24 10:36:45 +0200233 size_t plen = 0;
234
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100235 if (!sl)
236 return 0;
237
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100238 uri = htx_sl_req_uri(sl);
239 p = http_get_path(uri);
Christopher Faulete010c802018-10-24 10:36:45 +0200240 if (!p.ptr)
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100241 p = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200242 while (plen < p.len && *(p.ptr + plen) != '?')
243 plen++;
244
245 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100246 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
247 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200248
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100249 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
250 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
251
252 chunk_memcat(temp, uri.ptr, p.ptr - uri.ptr); /* uri: host part */
Christopher Faulete010c802018-10-24 10:36:45 +0200253 chunk_memcat(temp, path.ptr, path.len); /* uri: new path */
254 chunk_memcat(temp, p.ptr + plen, p.len - plen); /* uri: QS part */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100255 uri = ist2(temp->area + meth.len + vsn.len, uri.len - plen + path.len);
Christopher Faulete010c802018-10-24 10:36:45 +0200256
257 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100258 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200259}
260
261/* Replace the request query-string in the HTX message <htx> by <query>. The
262 * host part and the path are preserved. It returns 1 on success, otherwise
263 * 0.
264 */
265int http_replace_req_query(struct htx *htx, const struct ist query)
266{
267 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200268 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100269 struct ist meth, uri, vsn, q;
Christopher Faulete010c802018-10-24 10:36:45 +0200270 int offset = 1;
271
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100272 if (!sl)
273 return 0;
274
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100275 uri = htx_sl_req_uri(sl);
276 q = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200277 while (q.len > 0 && *(q.ptr) != '?') {
278 q.ptr++;
279 q.len--;
280 }
281
282 /* skip the question mark or indicate that we must insert it
283 * (but only if the format string is not empty then).
284 */
285 if (q.len) {
286 q.ptr++;
287 q.len--;
288 }
289 else if (query.len > 1)
290 offset = 0;
291
292 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100293 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
294 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200295
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100296 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
297 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200298
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100299 chunk_memcat(temp, uri.ptr, q.ptr - uri.ptr); /* uri: host + path part */
300 chunk_memcat(temp, query.ptr + offset, query.len - offset); /* uri: new QS */
301 uri = ist2(temp->area + meth.len + vsn.len, uri.len - q.len + query.len - offset);
Christopher Faulete010c802018-10-24 10:36:45 +0200302
303 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100304 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200305}
306
307/* Replace the response status in the HTX message <htx> by <status>. It returns
308 * 1 on success, otherwise 0.
309*/
310int http_replace_res_status(struct htx *htx, const struct ist status)
311{
312 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200313 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100314 struct ist vsn, reason;
Christopher Faulete010c802018-10-24 10:36:45 +0200315
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100316 if (!sl)
317 return 0;
318
Christopher Faulete010c802018-10-24 10:36:45 +0200319 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100320 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
321 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200322
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100323 chunk_memcat(temp, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl)); /* reason */
324 reason = ist2(temp->area + vsn.len, HTX_SL_RES_RLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200325
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100326 /* create the new start line */
327 sl->info.res.status = strl2ui(status.ptr, status.len);
328 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200329}
330
331/* Replace the response reason in the HTX message <htx> by <reason>. It returns
332 * 1 on success, otherwise 0.
333*/
334int http_replace_res_reason(struct htx *htx, const struct ist reason)
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, status;
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_CPTR(sl), HTX_SL_RES_CLEN(sl)); /* code */
348 status = ist2(temp->area + vsn.len, HTX_SL_RES_CLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200349
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100350 /* create the new start line */
351 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200352}
353
Christopher Faulet47596d32018-10-22 09:17:28 +0200354/* Replaces a part of a header value referenced in the context <ctx> by
355 * <data>. It returns 1 on success, otherwise it returns 0. The context is
356 * updated if necessary.
357 */
358int http_replace_header_value(struct htx *htx, struct http_hdr_ctx *ctx, const struct ist data)
359{
360 struct htx_blk *blk = ctx->blk;
361 char *start;
362 struct ist v;
363 uint32_t len, off;
364
365 if (!blk)
366 return 0;
367
368 v = htx_get_blk_value(htx, blk);
369 start = ctx->value.ptr - ctx->lws_before;
370 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
371 off = start - v.ptr;
372
373 blk = htx_replace_blk_value(htx, blk, ist2(start, len), data);
374 if (!blk)
375 return 0;
376
377 v = htx_get_blk_value(htx, blk);
378 ctx->blk = blk;
379 ctx->value.ptr = v.ptr + off;
380 ctx->value.len = data.len;
381 ctx->lws_before = ctx->lws_after = 0;
382
383 return 1;
384}
385
386/* Fully replaces a header referenced in the context <ctx> by the name <name>
387 * with the value <value>. It returns 1 on success, otherwise it returns 0. The
388 * context is updated if necessary.
389 */
390int http_replace_header(struct htx *htx, struct http_hdr_ctx *ctx,
391 const struct ist name, const struct ist value)
392{
393 struct htx_blk *blk = ctx->blk;
394
395 if (!blk)
396 return 0;
397
398 blk = htx_replace_header(htx, blk, name, value);
399 if (!blk)
400 return 0;
401
402 ctx->blk = blk;
403 ctx->value = ist(NULL);
404 ctx->lws_before = ctx->lws_after = 0;
405
406 return 1;
407}
408
409/* Remove one value of a header. This only works on a <ctx> returned by
410 * http_find_header function. The value is removed, as well as surrounding commas
411 * if any. If the removed value was alone, the whole header is removed. The
412 * <ctx> is always updated accordingly, as well as the HTX message <htx>. It
413 * returns 1 on success. Otherwise, it returns 0. The <ctx> is always left in a
414 * form that can be handled by http_find_header() to find next occurrence.
415 */
416int http_remove_header(struct htx *htx, struct http_hdr_ctx *ctx)
417{
418 struct htx_blk *blk = ctx->blk;
419 char *start;
420 struct ist v;
421 uint32_t len;
422
423 if (!blk)
424 return 0;
425
426 start = ctx->value.ptr - ctx->lws_before;
427 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
428
429 v = htx_get_blk_value(htx, blk);
430 if (len == v.len) {
431 blk = htx_remove_blk(htx, blk);
432 if (blk || !htx->used) {
433 ctx->blk = blk;
434 ctx->value = ist2(NULL, 0);
435 ctx->lws_before = ctx->lws_after = 0;
436 }
437 else {
438 ctx->blk = htx_get_blk(htx, htx->tail);
439 ctx->value = htx_get_blk_value(htx, ctx->blk);
440 ctx->lws_before = ctx->lws_after = 0;
441 }
442 return 1;
443 }
444
445 /* This was not the only value of this header. We have to remove the
446 * part pointed by ctx->value. If it is the last entry of the list, we
447 * remove the last separator.
448 */
449 if (start == v.ptr) {
450 /* It's the first header part but not the only one. So remove
451 * the comma after it. */
452 len++;
453 }
454 else {
455 /* There is at least one header part before the removed one. So
456 * remove the comma between them. */
457 start--;
458 len++;
459 }
460 /* Update the block content and its len */
461 memmove(start, start+len, v.len-len);
Christopher Faulet41dc8432019-06-18 09:49:16 +0200462 htx_change_blk_value_len(htx, blk, v.len-len);
Christopher Faulet47596d32018-10-22 09:17:28 +0200463
464 /* Finally update the ctx */
465 ctx->value.ptr = start;
466 ctx->value.len = 0;
467 ctx->lws_before = ctx->lws_after = 0;
468
469 return 1;
470}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200471
472
473/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
474 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
475 * performed over the whole headers. Otherwise it must contain a valid header
476 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
477 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
478 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
479 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
480 * -1. The value fetch stops at commas, so this function is suited for use with
481 * list headers.
482 * The return value is 0 if nothing was found, or non-zero otherwise.
483 */
484unsigned int http_get_htx_hdr(const struct htx *htx, const struct ist hdr,
485 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
486{
487 struct http_hdr_ctx local_ctx;
488 struct ist val_hist[MAX_HDR_HISTORY];
489 unsigned int hist_idx;
490 int found;
491
492 if (!ctx) {
493 local_ctx.blk = NULL;
494 ctx = &local_ctx;
495 }
496
497 if (occ >= 0) {
498 /* search from the beginning */
499 while (http_find_header(htx, hdr, ctx, 0)) {
500 occ--;
501 if (occ <= 0) {
502 *vptr = ctx->value.ptr;
503 *vlen = ctx->value.len;
504 return 1;
505 }
506 }
507 return 0;
508 }
509
510 /* negative occurrence, we scan all the list then walk back */
511 if (-occ > MAX_HDR_HISTORY)
512 return 0;
513
514 found = hist_idx = 0;
515 while (http_find_header(htx, hdr, ctx, 0)) {
516 val_hist[hist_idx] = ctx->value;
517 if (++hist_idx >= MAX_HDR_HISTORY)
518 hist_idx = 0;
519 found++;
520 }
521 if (-occ > found)
522 return 0;
523
524 /* OK now we have the last occurrence in [hist_idx-1], and we need to
525 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
526 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
527 * to remain in the 0..9 range.
528 */
529 hist_idx += occ + MAX_HDR_HISTORY;
530 if (hist_idx >= MAX_HDR_HISTORY)
531 hist_idx -= MAX_HDR_HISTORY;
532 *vptr = val_hist[hist_idx].ptr;
533 *vlen = val_hist[hist_idx].len;
534 return 1;
535}
536
537/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
538 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
539 * performed over the whole headers. Otherwise it must contain a valid header
540 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
541 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
542 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
543 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
544 * -1. This function differs from http_get_hdr() in that it only returns full
545 * line header values and does not stop at commas.
546 * The return value is 0 if nothing was found, or non-zero otherwise.
547 */
548unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr,
549 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
550{
551 struct http_hdr_ctx local_ctx;
552 struct ist val_hist[MAX_HDR_HISTORY];
553 unsigned int hist_idx;
554 int found;
555
556 if (!ctx) {
557 local_ctx.blk = NULL;
558 ctx = &local_ctx;
559 }
560
561 if (occ >= 0) {
562 /* search from the beginning */
563 while (http_find_header(htx, hdr, ctx, 1)) {
564 occ--;
565 if (occ <= 0) {
566 *vptr = ctx->value.ptr;
567 *vlen = ctx->value.len;
568 return 1;
569 }
570 }
571 return 0;
572 }
573
574 /* negative occurrence, we scan all the list then walk back */
575 if (-occ > MAX_HDR_HISTORY)
576 return 0;
577
578 found = hist_idx = 0;
579 while (http_find_header(htx, hdr, ctx, 1)) {
580 val_hist[hist_idx] = ctx->value;
581 if (++hist_idx >= MAX_HDR_HISTORY)
582 hist_idx = 0;
583 found++;
584 }
585 if (-occ > found)
586 return 0;
587
588 /* OK now we have the last occurrence in [hist_idx-1], and we need to
589 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
590 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
591 * to remain in the 0..9 range.
592 */
593 hist_idx += occ + MAX_HDR_HISTORY;
594 if (hist_idx >= MAX_HDR_HISTORY)
595 hist_idx -= MAX_HDR_HISTORY;
596 *vptr = val_hist[hist_idx].ptr;
597 *vlen = val_hist[hist_idx].len;
598 return 1;
599}
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100600
Christopher Faulet4c853bc2019-07-22 16:49:30 +0200601int http_str_to_htx(struct buffer *buf, struct ist raw)
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100602{
603 struct htx *htx;
604 struct htx_sl *sl;
605 struct h1m h1m;
Christopher Faulete4ab11b2019-06-11 15:05:37 +0200606 struct http_hdr hdrs[global.tune.max_http_hdr];
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100607 union h1_sl h1sl;
608 unsigned int flags = HTX_SL_F_IS_RESP;
609 int ret = 0;
610
Christopher Faulet4c853bc2019-07-22 16:49:30 +0200611 b_reset(buf);
612 if (!raw.len) {
613 buf->size = 0;
614 buf->area = malloc(raw.len);
615 return 1;
616 }
617
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100618 buf->size = global.tune.bufsize;
619 buf->area = (char *)malloc(buf->size);
620 if (!buf->area)
621 goto error;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100622
623 h1m_init_res(&h1m);
624 h1m.flags |= H1_MF_NO_PHDR;
625 ret = h1_headers_to_hdr_list(raw.ptr, raw.ptr + raw.len,
626 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
627 if (ret <= 0)
628 goto error;
629
630 if (unlikely(h1sl.st.v.len != 8))
631 goto error;
632 if ((*(h1sl.st.v.ptr + 5) > '1') ||
633 ((*(h1sl.st.v.ptr + 5) == '1') && (*(h1sl.st.v.ptr + 7) >= '1')))
634 h1m.flags |= H1_MF_VER_11;
635
Christopher Faulet9869f932019-06-26 14:23:54 +0200636 if (h1sl.st.status < 200 && (h1sl.st.status == 100 || h1sl.st.status >= 102))
637 goto error;
638
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100639 if (h1m.flags & H1_MF_VER_11)
640 flags |= HTX_SL_F_VER_11;
641 if (h1m.flags & H1_MF_XFER_ENC)
642 flags |= HTX_SL_F_XFER_ENC;
Christopher Faulet046a2e22019-10-16 09:09:04 +0200643 if (h1m.flags & H1_MF_CLEN) {
644 flags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
645 if (h1m.body_len == 0)
646 flags |= HTX_SL_F_BODYLESS;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100647 }
Christopher Faulet046a2e22019-10-16 09:09:04 +0200648 if (h1m.flags & H1_MF_CHNK)
649 goto error; /* Unsupported because there is no body parsing */
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100650
651 htx = htx_from_buf(buf);
652 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, h1sl.st.v, h1sl.st.c, h1sl.st.r);
653 if (!sl || !htx_add_all_headers(htx, hdrs))
654 goto error;
655 sl->info.res.status = h1sl.st.status;
656
Willy Tarreau0a7ef022019-05-28 10:30:11 +0200657 while (raw.len > ret) {
658 int sent = htx_add_data(htx, ist2(raw.ptr + ret, raw.len - ret));
659 if (!sent)
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100660 goto error;
Willy Tarreau0a7ef022019-05-28 10:30:11 +0200661 ret += sent;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100662 }
Christopher Faulet9869f932019-06-26 14:23:54 +0200663
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100664 if (!htx_add_endof(htx, HTX_BLK_EOM))
665 goto error;
Christopher Faulet9869f932019-06-26 14:23:54 +0200666
Christopher Faulet4c853bc2019-07-22 16:49:30 +0200667 return 1;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100668
669error:
670 if (buf->size)
671 free(buf->area);
Christopher Faulet4c853bc2019-07-22 16:49:30 +0200672 return 0;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100673}
674
675static int http_htx_init(void)
676{
677 struct proxy *px;
678 struct buffer chk;
679 struct ist raw;
680 int rc;
681 int err_code = 0;
682
683 for (px = proxies_list; px; px = px->next) {
Christopher Faulet8264f132019-07-15 14:43:38 +0200684 if (!(px->options2 & PR_O2_USE_HTX))
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100685 continue;
686
687 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
688 if (!b_data(&px->errmsg[rc]))
689 continue;
690
691 raw = ist2(b_head(&px->errmsg[rc]), b_data(&px->errmsg[rc]));
692 if (!http_str_to_htx(&chk, raw)) {
693 ha_alert("config: %s '%s': Unable to convert message in HTX for HTTP return code %d.\n",
694 proxy_type_str(px), px->id, http_err_codes[rc]);
695 err_code |= ERR_ALERT | ERR_FATAL;
696 }
697 chunk_destroy(&px->errmsg[rc]);
698 px->errmsg[rc] = chk;
699 }
700 }
701
702 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
703 if (!http_err_msgs[rc]) {
704 ha_alert("Internal error: no message defined for HTTP return code %d", rc);
705 err_code |= ERR_ALERT | ERR_FATAL;
706 continue;
707 }
708
709 raw = ist2(http_err_msgs[rc], strlen(http_err_msgs[rc]));
710 if (!http_str_to_htx(&chk, raw)) {
711 ha_alert("Internal error: Unable to convert message in HTX for HTTP return code %d.\n",
712 http_err_codes[rc]);
713 err_code |= ERR_ALERT | ERR_FATAL;
714 }
715 htx_err_chunks[rc] = chk;
716 }
717end:
718 return err_code;
719}
720
721REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init);