blob: 42a7616b93efc8b4c35e14e5007c255b638715a3 [file] [log] [blame]
Willy Tarreau79e57332018-10-02 16:01:16 +02001/*
2 * HTTP sample conversion
3 *
4 * Copyright 2000-2018 Willy Tarreau <w@1wt.eu>
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 <sys/types.h>
14
15#include <ctype.h>
16#include <string.h>
17#include <time.h>
18
19#include <common/chunk.h>
20#include <common/compat.h>
21#include <common/config.h>
22#include <common/debug.h>
23#include <common/http.h>
24#include <common/memory.h>
25#include <common/standard.h>
26#include <common/version.h>
27
28#include <types/capture.h>
29#include <types/global.h>
30
31#include <proto/arg.h>
32#include <proto/sample.h>
33#include <proto/stream.h>
34
35
36/* takes an UINT value on input supposed to represent the time since EPOCH,
37 * adds an optional offset found in args[0] and emits a string representing
38 * the date in RFC-1123/5322 format.
39 */
40static int sample_conv_http_date(const struct arg *args, struct sample *smp, void *private)
41{
42 const char day[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
43 const char mon[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
44 struct buffer *temp;
45 struct tm *tm;
46 /* With high numbers, the date returned can be negative, the 55 bits mask prevent this. */
47 time_t curr_date = smp->data.u.sint & 0x007fffffffffffffLL;
48
49 /* add offset */
50 if (args && (args[0].type == ARGT_SINT))
51 curr_date += args[0].data.sint;
52
53 tm = gmtime(&curr_date);
54 if (!tm)
55 return 0;
56
57 temp = get_trash_chunk();
58 temp->data = snprintf(temp->area, temp->size - temp->data,
59 "%s, %02d %s %04d %02d:%02d:%02d GMT",
60 day[tm->tm_wday], tm->tm_mday, mon[tm->tm_mon],
61 1900+tm->tm_year,
62 tm->tm_hour, tm->tm_min, tm->tm_sec);
63
64 smp->data.u.str = *temp;
65 smp->data.type = SMP_T_STR;
66 return 1;
67}
68
69/* Arguments: The list of expected value, the number of parts returned and the separator */
70static int sample_conv_q_preferred(const struct arg *args, struct sample *smp, void *private)
71{
72 const char *al = smp->data.u.str.area;
73 const char *end = al + smp->data.u.str.data;
74 const char *token;
75 int toklen;
76 int qvalue;
77 const char *str;
78 const char *w;
79 int best_q = 0;
80
81 /* Set the constant to the sample, because the output of the
82 * function will be peek in the constant configuration string.
83 */
84 smp->flags |= SMP_F_CONST;
85 smp->data.u.str.size = 0;
86 smp->data.u.str.area = "";
87 smp->data.u.str.data = 0;
88
89 /* Parse the accept language */
90 while (1) {
91
92 /* Jump spaces, quit if the end is detected. */
93 while (al < end && isspace((unsigned char)*al))
94 al++;
95 if (al >= end)
96 break;
97
98 /* Start of the fisrt word. */
99 token = al;
100
101 /* Look for separator: isspace(), ',' or ';'. Next value if 0 length word. */
102 while (al < end && *al != ';' && *al != ',' && !isspace((unsigned char)*al))
103 al++;
104 if (al == token)
105 goto expect_comma;
106
107 /* Length of the token. */
108 toklen = al - token;
109 qvalue = 1000;
110
111 /* Check if the token exists in the list. If the token not exists,
112 * jump to the next token.
113 */
114 str = args[0].data.str.area;
115 w = str;
116 while (1) {
117 if (*str == ';' || *str == '\0') {
118 if (http_language_range_match(token, toklen, w, str - w))
119 goto look_for_q;
120 if (*str == '\0')
121 goto expect_comma;
122 w = str + 1;
123 }
124 str++;
125 }
126 goto expect_comma;
127
128look_for_q:
129
130 /* Jump spaces, quit if the end is detected. */
131 while (al < end && isspace((unsigned char)*al))
132 al++;
133 if (al >= end)
134 goto process_value;
135
136 /* If ',' is found, process the result */
137 if (*al == ',')
138 goto process_value;
139
140 /* If the character is different from ';', look
141 * for the end of the header part in best effort.
142 */
143 if (*al != ';')
144 goto expect_comma;
145
146 /* Assumes that the char is ';', now expect "q=". */
147 al++;
148
149 /* Jump spaces, process value if the end is detected. */
150 while (al < end && isspace((unsigned char)*al))
151 al++;
152 if (al >= end)
153 goto process_value;
154
155 /* Expect 'q'. If no 'q', continue in best effort */
156 if (*al != 'q')
157 goto process_value;
158 al++;
159
160 /* Jump spaces, process value if the end is detected. */
161 while (al < end && isspace((unsigned char)*al))
162 al++;
163 if (al >= end)
164 goto process_value;
165
166 /* Expect '='. If no '=', continue in best effort */
167 if (*al != '=')
168 goto process_value;
169 al++;
170
171 /* Jump spaces, process value if the end is detected. */
172 while (al < end && isspace((unsigned char)*al))
173 al++;
174 if (al >= end)
175 goto process_value;
176
177 /* Parse the q value. */
178 qvalue = http_parse_qvalue(al, &al);
179
180process_value:
181
182 /* If the new q value is the best q value, then store the associated
183 * language in the response. If qvalue is the biggest value (1000),
184 * break the process.
185 */
186 if (qvalue > best_q) {
187 smp->data.u.str.area = (char *)w;
188 smp->data.u.str.data = str - w;
189 if (qvalue >= 1000)
190 break;
191 best_q = qvalue;
192 }
193
194expect_comma:
195
196 /* Expect comma or end. If the end is detected, quit the loop. */
197 while (al < end && *al != ',')
198 al++;
199 if (al >= end)
200 break;
201
202 /* Comma is found, jump it and restart the analyzer. */
203 al++;
204 }
205
206 /* Set default value if required. */
207 if (smp->data.u.str.data == 0 && args[1].type == ARGT_STR) {
208 smp->data.u.str.area = args[1].data.str.area;
209 smp->data.u.str.data = args[1].data.str.data;
210 }
211
212 /* Return true only if a matching language was found. */
213 return smp->data.u.str.data != 0;
214}
215
216/* This fetch url-decode any input string. */
217static int sample_conv_url_dec(const struct arg *args, struct sample *smp, void *private)
218{
219 int len;
220
221 /* If the constant flag is set or if not size is avalaible at
222 * the end of the buffer, copy the string in other buffer
223 * before decoding.
224 */
225 if (smp->flags & SMP_F_CONST || smp->data.u.str.size <= smp->data.u.str.data) {
226 struct buffer *str = get_trash_chunk();
227 memcpy(str->area, smp->data.u.str.area, smp->data.u.str.data);
228 smp->data.u.str.area = str->area;
229 smp->data.u.str.size = str->size;
230 smp->flags &= ~SMP_F_CONST;
231 }
232
233 /* Add final \0 required by url_decode(), and convert the input string. */
234 smp->data.u.str.area[smp->data.u.str.data] = '\0';
235 len = url_decode(smp->data.u.str.area);
236 if (len < 0)
237 return 0;
238 smp->data.u.str.data = len;
239 return 1;
240}
241
242static int smp_conv_req_capture(const struct arg *args, struct sample *smp, void *private)
243{
244 struct proxy *fe = strm_fe(smp->strm);
245 int idx, i;
246 struct cap_hdr *hdr;
247 int len;
248
249 if (!args || args->type != ARGT_SINT)
250 return 0;
251
252 idx = args->data.sint;
253
254 /* Check the availibity of the capture id. */
255 if (idx > fe->nb_req_cap - 1)
256 return 0;
257
258 /* Look for the original configuration. */
259 for (hdr = fe->req_cap, i = fe->nb_req_cap - 1;
260 hdr != NULL && i != idx ;
261 i--, hdr = hdr->next);
262 if (!hdr)
263 return 0;
264
265 /* check for the memory allocation */
266 if (smp->strm->req_cap[hdr->index] == NULL)
267 smp->strm->req_cap[hdr->index] = pool_alloc(hdr->pool);
268 if (smp->strm->req_cap[hdr->index] == NULL)
269 return 0;
270
271 /* Check length. */
272 len = smp->data.u.str.data;
273 if (len > hdr->len)
274 len = hdr->len;
275
276 /* Capture input data. */
277 memcpy(smp->strm->req_cap[idx], smp->data.u.str.area, len);
278 smp->strm->req_cap[idx][len] = '\0';
279
280 return 1;
281}
282
283static int smp_conv_res_capture(const struct arg *args, struct sample *smp, void *private)
284{
285 struct proxy *fe = strm_fe(smp->strm);
286 int idx, i;
287 struct cap_hdr *hdr;
288 int len;
289
290 if (!args || args->type != ARGT_SINT)
291 return 0;
292
293 idx = args->data.sint;
294
295 /* Check the availibity of the capture id. */
296 if (idx > fe->nb_rsp_cap - 1)
297 return 0;
298
299 /* Look for the original configuration. */
300 for (hdr = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
301 hdr != NULL && i != idx ;
302 i--, hdr = hdr->next);
303 if (!hdr)
304 return 0;
305
306 /* check for the memory allocation */
307 if (smp->strm->res_cap[hdr->index] == NULL)
308 smp->strm->res_cap[hdr->index] = pool_alloc(hdr->pool);
309 if (smp->strm->res_cap[hdr->index] == NULL)
310 return 0;
311
312 /* Check length. */
313 len = smp->data.u.str.data;
314 if (len > hdr->len)
315 len = hdr->len;
316
317 /* Capture input data. */
318 memcpy(smp->strm->res_cap[idx], smp->data.u.str.area, len);
319 smp->strm->res_cap[idx][len] = '\0';
320
321 return 1;
322}
323
324/************************************************************************/
325/* All supported converter keywords must be declared here. */
326/************************************************************************/
327
328/* Note: must not be declared <const> as its list will be overwritten */
329static struct sample_conv_kw_list sample_conv_kws = {ILH, {
330 { "http_date", sample_conv_http_date, ARG1(0,SINT), NULL, SMP_T_SINT, SMP_T_STR},
331 { "language", sample_conv_q_preferred, ARG2(1,STR,STR), NULL, SMP_T_STR, SMP_T_STR},
332 { "capture-req", smp_conv_req_capture, ARG1(1,SINT), NULL, SMP_T_STR, SMP_T_STR},
333 { "capture-res", smp_conv_res_capture, ARG1(1,SINT), NULL, SMP_T_STR, SMP_T_STR},
334 { "url_dec", sample_conv_url_dec, 0, NULL, SMP_T_STR, SMP_T_STR},
335 { NULL, NULL, 0, 0, 0 },
336}};
337
338__attribute__((constructor))
339static void __http_conv_init(void)
340{
341 sample_register_convs(&sample_conv_kws);
342}
343
344/*
345 * Local variables:
346 * c-indent-level: 8
347 * c-basic-offset: 8
348 * End:
349 */