/src/haproxy/src/http_ana.c
Line | Count | Source |
1 | | /* |
2 | | * HTTP protocol analyzer |
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 <haproxy/acl.h> |
14 | | #include <haproxy/action-t.h> |
15 | | #include <haproxy/api.h> |
16 | | #include <haproxy/applet.h> |
17 | | #include <haproxy/backend.h> |
18 | | #include <haproxy/base64.h> |
19 | | #include <haproxy/capture-t.h> |
20 | | #include <haproxy/cfgparse.h> |
21 | | #include <haproxy/channel.h> |
22 | | #include <haproxy/check.h> |
23 | | #include <haproxy/connection.h> |
24 | | #include <haproxy/errors.h> |
25 | | #include <haproxy/filters.h> |
26 | | #include <haproxy/http.h> |
27 | | #include <haproxy/http_ana.h> |
28 | | #include <haproxy/http_htx.h> |
29 | | #include <haproxy/http_ext.h> |
30 | | #include <haproxy/htx.h> |
31 | | #include <haproxy/log.h> |
32 | | #include <haproxy/net_helper.h> |
33 | | #include <haproxy/proxy.h> |
34 | | #include <haproxy/regex.h> |
35 | | #include <haproxy/sc_strm.h> |
36 | | #include <haproxy/server-t.h> |
37 | | #include <haproxy/stats.h> |
38 | | #include <haproxy/stats-html.h> |
39 | | #include <haproxy/stconn.h> |
40 | | #include <haproxy/stream.h> |
41 | | #include <haproxy/trace.h> |
42 | | #include <haproxy/uri_auth-t.h> |
43 | | #include <haproxy/vars.h> |
44 | | |
45 | | |
46 | | #define TRACE_SOURCE &trace_strm |
47 | | |
48 | | extern const char *stat_status_codes[]; |
49 | | |
50 | | struct pool_head *pool_head_requri __read_mostly = NULL; |
51 | | struct pool_head *pool_head_capture __read_mostly = NULL; |
52 | | |
53 | | |
54 | | static void http_end_request(struct stream *s); |
55 | | static void http_end_response(struct stream *s); |
56 | | |
57 | | static void http_capture_headers(struct htx *htx, char **cap, struct cap_hdr *cap_hdr); |
58 | | static int http_del_hdr_value(char *start, char *end, char **from, char *next); |
59 | | static size_t http_fmt_req_line(const struct htx_sl *sl, char *str, size_t len); |
60 | | static void http_debug_stline(const char *dir, struct stream *s, const struct htx_sl *sl); |
61 | | static void http_debug_hdr(const char *dir, struct stream *s, const struct ist n, const struct ist v); |
62 | | |
63 | | static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct list *def_rules, struct list *rules, struct stream *s); |
64 | | static enum rule_result http_res_get_intercept_rule(struct proxy *px, struct list *def_rules, struct list *rules, struct stream *s, uint8_t final); |
65 | | static enum rule_result http_req_restrict_header_names(struct stream *s, struct htx *htx, struct proxy *px); |
66 | | |
67 | | static void http_manage_client_side_cookies(struct stream *s, struct channel *req); |
68 | | static void http_manage_server_side_cookies(struct stream *s, struct channel *res); |
69 | | |
70 | | static int http_stats_check_uri(struct stream *s, struct http_txn *txn, struct proxy *px); |
71 | | static int http_handle_stats(struct stream *s, struct channel *req, struct proxy *px); |
72 | | |
73 | | static int http_handle_expect_hdr(struct stream *s, struct htx *htx, struct http_msg *msg); |
74 | | static int http_reply_100_continue(struct stream *s); |
75 | | |
76 | | /* This stream analyser waits for a complete HTTP request. It returns 1 if the |
77 | | * processing can continue on next analysers, or zero if it either needs more |
78 | | * data or wants to immediately abort the request (eg: timeout, error, ...). It |
79 | | * is tied to AN_REQ_WAIT_HTTP and may may remove itself from s->req.analysers |
80 | | * when it has nothing left to do, and may remove any analyser when it wants to |
81 | | * abort. |
82 | | */ |
83 | | int http_wait_for_request(struct stream *s, struct channel *req, int an_bit) |
84 | 0 | { |
85 | 0 | uint8_t do_log = 0; |
86 | | |
87 | | /* |
88 | | * We will analyze a complete HTTP request to check the its syntax. |
89 | | * |
90 | | * Once the start line and all headers are received, we may perform a |
91 | | * capture of the error (if any), and we will set a few fields. We also |
92 | | * check for monitor-uri, logging and finally headers capture. |
93 | | */ |
94 | 0 | struct session *sess = s->sess; |
95 | 0 | struct http_txn *txn = s->txn.http; |
96 | 0 | struct http_msg *msg = &txn->req; |
97 | 0 | struct htx *htx; |
98 | 0 | struct htx_sl *sl; |
99 | 0 | char http_ver; |
100 | |
|
101 | 0 | DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn, msg); |
102 | |
|
103 | 0 | if (unlikely(!IS_HTX_STRM(s))) { |
104 | | /* It is only possible when a TCP stream is upgrade to HTTP. |
105 | | * There is a transition period during which there is no |
106 | | * data. The stream is still in raw mode and SF_IGNORE flag is |
107 | | * still set. When this happens, the new mux is responsible to |
108 | | * handle all errors. Thus we may leave immediately. |
109 | | */ |
110 | 0 | BUG_ON(!(s->flags & SF_IGNORE) || !c_empty(&s->req)); |
111 | | |
112 | | /* Don't connect for now */ |
113 | 0 | channel_dont_connect(req); |
114 | | |
115 | | /* An abort at this stage means we are performing a "destructive" |
116 | | * HTTP upgrade (TCP>H2). In this case, we can leave. |
117 | | */ |
118 | 0 | if (s->scf->flags & (SC_FL_ABRT_DONE|SC_FL_EOS)) { |
119 | 0 | s->logs.logwait = 0; |
120 | 0 | s->logs.level = 0; |
121 | 0 | stream_abort(s); |
122 | 0 | req->analysers &= AN_REQ_FLT_END; |
123 | 0 | req->analyse_exp = TICK_ETERNITY; |
124 | 0 | DBG_TRACE_LEAVE(STRM_EV_STRM_ANA, s); |
125 | 0 | return 1; |
126 | 0 | } |
127 | 0 | DBG_TRACE_LEAVE(STRM_EV_STRM_ANA, s); |
128 | 0 | return 0; |
129 | 0 | } |
130 | | |
131 | 0 | htx = htxbuf(&req->buf); |
132 | 0 | sl = http_get_stline(htx); |
133 | 0 | if ((sl->flags & HTX_SL_F_NOT_HTTP) || HTX_SL_REQ_VLEN(sl) != 8) { |
134 | | /* Not an HTTP request */ |
135 | 0 | http_ver = 0; |
136 | 0 | msg->vsn = 0; |
137 | 0 | } |
138 | 0 | else { |
139 | 0 | char *ptr; |
140 | |
|
141 | 0 | ptr = HTX_SL_REQ_VPTR(sl); |
142 | 0 | msg->vsn = ((ptr[5] - '0') << 4) + (ptr[7] - '0'); |
143 | 0 | http_ver = ptr[5] - '0'; |
144 | 0 | } |
145 | | |
146 | | /* Parsing errors are caught here */ |
147 | 0 | if (htx->flags & (HTX_FL_PARSING_ERROR|HTX_FL_PROCESSING_ERROR)) { |
148 | 0 | stream_inc_http_req_ctr(s); |
149 | 0 | proxy_inc_fe_req_ctr(sess->listener, sess->fe, http_ver); |
150 | 0 | if (htx->flags & HTX_FL_PARSING_ERROR) { |
151 | 0 | stream_inc_http_err_ctr(s); |
152 | 0 | goto return_bad_req; |
153 | 0 | } |
154 | 0 | else |
155 | 0 | goto return_int_err; |
156 | 0 | } |
157 | | |
158 | | /* we're speaking HTTP here, so let's speak HTTP to the client */ |
159 | 0 | s->srv_error = http_return_srv_error; |
160 | |
|
161 | 0 | msg->msg_state = HTTP_MSG_BODY; |
162 | 0 | stream_inc_http_req_ctr(s); |
163 | 0 | proxy_inc_fe_req_ctr(sess->listener, sess->fe, http_ver); /* one more valid request for this FE */ |
164 | | |
165 | | /* kill the pending keep-alive timeout */ |
166 | 0 | req->analyse_exp = TICK_ETERNITY; |
167 | |
|
168 | 0 | BUG_ON(htx_get_first_type(htx) != HTX_BLK_REQ_SL); |
169 | | |
170 | | /* 0: we might have to print this header in debug mode */ |
171 | 0 | if (unlikely((global.mode & MODE_DEBUG) && |
172 | 0 | (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)))) { |
173 | 0 | int32_t pos; |
174 | |
|
175 | 0 | http_debug_stline("clireq", s, sl); |
176 | |
|
177 | 0 | for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) { |
178 | 0 | struct htx_blk *blk = htx_get_blk(htx, pos); |
179 | 0 | enum htx_blk_type type = htx_get_blk_type(blk); |
180 | |
|
181 | 0 | if (type == HTX_BLK_EOH) |
182 | 0 | break; |
183 | 0 | if (type != HTX_BLK_HDR) |
184 | 0 | continue; |
185 | | |
186 | 0 | http_debug_hdr("clihdr", s, |
187 | 0 | htx_get_blk_name(htx, blk), |
188 | 0 | htx_get_blk_value(htx, blk)); |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | | /* |
193 | | * 1: identify the method and the version. Also set HTTP flags |
194 | | */ |
195 | 0 | txn->meth = sl->info.req.meth; |
196 | 0 | if (sl->flags & HTX_SL_F_VER_11) |
197 | 0 | msg->flags |= HTTP_MSGF_VER_11; |
198 | 0 | msg->flags |= HTTP_MSGF_XFER_LEN; |
199 | 0 | if (sl->flags & HTX_SL_F_CLEN) |
200 | 0 | msg->flags |= HTTP_MSGF_CNT_LEN; |
201 | 0 | else if (sl->flags & HTX_SL_F_CHNK) |
202 | 0 | msg->flags |= HTTP_MSGF_TE_CHNK; |
203 | 0 | if (sl->flags & HTX_SL_F_BODYLESS) |
204 | 0 | msg->flags |= HTTP_MSGF_BODYLESS; |
205 | 0 | if (sl->flags & HTX_SL_F_CONN_UPG) |
206 | 0 | msg->flags |= HTTP_MSGF_CONN_UPG; |
207 | | |
208 | | /* we can make use of server redirect on GET and HEAD */ |
209 | 0 | if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD) |
210 | 0 | s->flags |= SF_REDIRECTABLE; |
211 | 0 | else if (txn->meth == HTTP_METH_OTHER && isteqi(htx_sl_req_meth(sl), ist("PRI"))) { |
212 | | /* PRI is reserved for the HTTP/2 preface */ |
213 | 0 | goto return_bad_req; |
214 | 0 | } |
215 | | |
216 | | /* |
217 | | * 2: check if the URI matches the monitor_uri. We have to do this for |
218 | | * every request which gets in, because the monitor-uri is defined by |
219 | | * the frontend. If the monitor-uri starts with a '/', the matching is |
220 | | * done against the request's path. Otherwise, the request's uri is |
221 | | * used. It is a workaround to let HTTP/2 health-checks work as |
222 | | * expected. |
223 | | */ |
224 | 0 | if (unlikely(isttest(sess->fe->monitor_uri))) { |
225 | 0 | const struct ist monitor_uri = sess->fe->monitor_uri; |
226 | 0 | struct http_uri_parser parser = http_uri_parser_init(htx_sl_req_uri(sl)); |
227 | |
|
228 | 0 | if ((istptr(monitor_uri)[0] == '/' && |
229 | 0 | isteq(http_parse_path(&parser), monitor_uri)) || |
230 | 0 | isteq(htx_sl_req_uri(sl), monitor_uri)) { |
231 | | /* |
232 | | * We have found the monitor URI |
233 | | */ |
234 | 0 | struct acl_cond *cond; |
235 | |
|
236 | 0 | s->flags |= SF_MONITOR; |
237 | 0 | if (sess->fe_tgcounters) |
238 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->intercepted_req); |
239 | | |
240 | | /* Check if we want to fail this monitor request or not */ |
241 | 0 | list_for_each_entry(cond, &sess->fe->mon_fail_cond, list) { |
242 | 0 | if (!acl_match_cond(cond, sess->fe, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL)) |
243 | 0 | continue; |
244 | | |
245 | | /* we fail this request, let's return 503 service unavail */ |
246 | 0 | txn->status = 503; |
247 | 0 | if (!(s->flags & SF_ERR_MASK)) |
248 | 0 | s->flags |= SF_ERR_LOCAL; /* we don't want a real error here */ |
249 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_intercepted); |
250 | 0 | goto return_prx_cond; |
251 | 0 | } |
252 | | |
253 | | /* nothing to fail, let's reply normally */ |
254 | 0 | txn->status = 200; |
255 | 0 | if (!(s->flags & SF_ERR_MASK)) |
256 | 0 | s->flags |= SF_ERR_LOCAL; /* we don't want a real error here */ |
257 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_intercepted); |
258 | 0 | goto return_prx_cond; |
259 | 0 | } |
260 | 0 | } |
261 | | |
262 | | /* |
263 | | * 3: Maybe we have to copy the original REQURI for the logs ? |
264 | | * Note: we cannot log anymore if the request has been |
265 | | * classified as invalid. |
266 | | */ |
267 | 0 | if (unlikely(sess->fe->to_log == LW_LOGSTEPS)) { |
268 | 0 | if (log_orig_proxy(LOG_ORIG_TXN_REQUEST, sess->fe)) |
269 | 0 | do_log = 1; /* forced log (through "log-steps") */ |
270 | 0 | } |
271 | 0 | else if (unlikely(s->logs.logwait & LW_REQ)) { |
272 | | /* default behavior when log-steps isn't specified: |
273 | | * don't log systematically, but only if conditions are |
274 | | * met, ie: not log emitted yet for this txn, all data available |
275 | | * (unless "option logasap" is used) |
276 | | */ |
277 | 0 | do_log = 2; |
278 | 0 | } |
279 | |
|
280 | 0 | if (do_log) { |
281 | | /* we have a complete HTTP request that we must log */ |
282 | 0 | if ((txn->uri = pool_alloc(pool_head_requri)) != NULL) { |
283 | 0 | size_t len; |
284 | |
|
285 | 0 | len = http_fmt_req_line(sl, txn->uri, global.tune.requri_len - 1); |
286 | 0 | txn->uri[len] = 0; |
287 | |
|
288 | 0 | if (do_log == 1 || !(s->logs.logwait &= ~(LW_REQ|LW_INIT))) |
289 | 0 | s->do_log(s, log_orig(LOG_ORIG_TXN_REQUEST, LOG_ORIG_FL_NONE)); |
290 | 0 | } else { |
291 | 0 | ha_alert("HTTP logging : out of memory.\n"); |
292 | 0 | } |
293 | 0 | } |
294 | | |
295 | | /* if the frontend has "option http-use-proxy-header", we'll check if |
296 | | * we have what looks like a proxied connection instead of a connection, |
297 | | * and in this case set the TX_USE_PX_CONN flag to use Proxy-connection. |
298 | | * Note that this is *not* RFC-compliant, however browsers and proxies |
299 | | * happen to do that despite being non-standard :-( |
300 | | * We consider that a request not beginning with either '/' or '*' is |
301 | | * a proxied connection, which covers both "scheme://location" and |
302 | | * CONNECT ip:port. |
303 | | */ |
304 | 0 | if ((sess->fe->options2 & PR_O2_USE_PXHDR) && |
305 | 0 | *HTX_SL_REQ_UPTR(sl) != '/' && *HTX_SL_REQ_UPTR(sl) != '*') |
306 | 0 | txn->flags |= TX_USE_PX_CONN; |
307 | | |
308 | | /* 5: we may need to capture headers */ |
309 | 0 | if (unlikely((s->logs.logwait & LW_REQHDR) && s->req_cap)) |
310 | 0 | http_capture_headers(htx, s->req_cap, sess->fe->req_cap); |
311 | | |
312 | | /* we may have to wait for the request's body */ |
313 | 0 | if (s->be->options & PR_O_WREQ_BODY) |
314 | 0 | req->analysers |= AN_REQ_HTTP_BODY; |
315 | | |
316 | | /* |
317 | | * RFC7234#4: |
318 | | * A cache MUST write through requests with methods |
319 | | * that are unsafe (Section 4.2.1 of [RFC7231]) to |
320 | | * the origin server; i.e., a cache is not allowed |
321 | | * to generate a reply to such a request before |
322 | | * having forwarded the request and having received |
323 | | * a corresponding response. |
324 | | * |
325 | | * RFC7231#4.2.1: |
326 | | * Of the request methods defined by this |
327 | | * specification, the GET, HEAD, OPTIONS, and TRACE |
328 | | * methods are defined to be safe. |
329 | | */ |
330 | 0 | if (likely(txn->meth == HTTP_METH_GET || |
331 | 0 | txn->meth == HTTP_METH_HEAD || |
332 | 0 | txn->meth == HTTP_METH_OPTIONS || |
333 | 0 | txn->meth == HTTP_METH_TRACE)) |
334 | 0 | txn->flags |= TX_CACHEABLE | TX_CACHE_COOK; |
335 | | |
336 | | /* end of job, return OK */ |
337 | 0 | req->analysers &= ~an_bit; |
338 | 0 | req->analyse_exp = TICK_ETERNITY; |
339 | |
|
340 | 0 | DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
341 | 0 | return 1; |
342 | | |
343 | 0 | return_int_err: |
344 | 0 | txn->status = 500; |
345 | 0 | if (!(s->flags & SF_ERR_MASK)) |
346 | 0 | s->flags |= SF_ERR_INTERNAL; |
347 | 0 | if (sess->fe_tgcounters) |
348 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->internal_errors); |
349 | 0 | if (sess->li_tgcounters) |
350 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->internal_errors); |
351 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_internal_err); |
352 | 0 | goto return_prx_cond; |
353 | | |
354 | 0 | return_bad_req: |
355 | 0 | txn->status = 400; |
356 | 0 | if (sess->fe_tgcounters) |
357 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->failed_req); |
358 | 0 | if (sess->li_tgcounters) |
359 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->failed_req); |
360 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_proto_err); |
361 | | /* fall through */ |
362 | |
|
363 | 0 | return_prx_cond: |
364 | 0 | http_set_term_flags(s); |
365 | 0 | http_reply_and_close(s, txn->status, http_error_message(s)); |
366 | |
|
367 | 0 | DBG_TRACE_DEVEL("leaving on error", |
368 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); |
369 | 0 | return 0; |
370 | 0 | } |
371 | | |
372 | | |
373 | | /* This stream analyser runs all HTTP request processing which is common to |
374 | | * frontends and backends, which means blocking ACLs, filters, connection-close, |
375 | | * reqadd, stats and redirects. This is performed for the designated proxy. |
376 | | * It returns 1 if the processing can continue on next analysers, or zero if it |
377 | | * either needs more data or wants to immediately abort the request (eg: deny, |
378 | | * error, ...). |
379 | | */ |
380 | | int http_process_req_common(struct stream *s, struct channel *req, int an_bit, struct proxy *px) |
381 | 0 | { |
382 | 0 | struct list *def_rules, *rules; |
383 | 0 | struct session *sess = s->sess; |
384 | 0 | struct http_txn *txn = s->txn.http; |
385 | 0 | struct http_msg *msg = &txn->req; |
386 | 0 | struct htx *htx; |
387 | 0 | struct redirect_rule *rule; |
388 | 0 | enum rule_result verdict; |
389 | 0 | struct connection *conn = objt_conn(sess->origin); |
390 | 0 | int stats_rules = 0; |
391 | |
|
392 | 0 | DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn, msg); |
393 | |
|
394 | 0 | htx = htxbuf(&req->buf); |
395 | | |
396 | | /* just in case we have some per-backend tracking. Only called the first |
397 | | * execution of the analyser. */ |
398 | 0 | if (!s->current_rule && !s->current_rule_list) |
399 | 0 | stream_inc_be_http_req_ctr(s); |
400 | |
|
401 | 0 | def_rules = ((px->defpx && (an_bit == AN_REQ_HTTP_PROCESS_FE || px != sess->fe)) ? &px->defpx->http_req_rules : NULL); |
402 | 0 | rules = &px->http_req_rules; |
403 | |
|
404 | 0 | verdict = HTTP_RULE_RES_CONT; |
405 | |
|
406 | 0 | if (px->uri_auth && s->current_rule_list == &px->uri_auth->http_req_rules) |
407 | 0 | stats_rules = 1; |
408 | | |
409 | | /* resume stats http-request rules if needed */ |
410 | 0 | if (stats_rules) |
411 | 0 | verdict = http_req_get_intercept_rule(px, NULL, &px->uri_auth->http_req_rules, s); |
412 | | /* evaluate http-request rules */ |
413 | 0 | else if ((def_rules && !LIST_ISEMPTY(def_rules)) || !LIST_ISEMPTY(rules)) |
414 | 0 | verdict = http_req_get_intercept_rule(px, def_rules, rules, s); |
415 | |
|
416 | 0 | rule_verdict: |
417 | |
|
418 | 0 | switch (verdict) { |
419 | 0 | case HTTP_RULE_RES_YIELD: /* some data miss, call the function later. */ |
420 | 0 | goto return_prx_yield; |
421 | | |
422 | 0 | case HTTP_RULE_RES_FYIELD: /* we must try again after context-switch */ |
423 | 0 | goto return_prx_fyield; |
424 | | |
425 | 0 | case HTTP_RULE_RES_CONT: |
426 | 0 | case HTTP_RULE_RES_STOP: /* nothing to do */ |
427 | 0 | break; |
428 | | |
429 | 0 | case HTTP_RULE_RES_DENY: /* deny or tarpit */ |
430 | 0 | if (txn->flags & TX_CLTARPIT) |
431 | 0 | goto tarpit; |
432 | 0 | goto deny; |
433 | | |
434 | 0 | case HTTP_RULE_RES_ABRT: /* abort request, response already sent. Eg: auth */ |
435 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_intercepted); |
436 | 0 | goto return_prx_cond; |
437 | | |
438 | 0 | case HTTP_RULE_RES_DONE: /* OK, but terminate request processing (eg: redirect) */ |
439 | 0 | goto done; |
440 | | |
441 | 0 | case HTTP_RULE_RES_BADREQ: /* failed with a bad request */ |
442 | 0 | goto return_bad_req; |
443 | | |
444 | 0 | case HTTP_RULE_RES_ERROR: /* failed with a bad request */ |
445 | 0 | goto return_int_err; |
446 | 0 | } |
447 | | |
448 | 0 | if (stats_rules) |
449 | 0 | goto resume_stats_rules; |
450 | | |
451 | 0 | if (px->options2 & (PR_O2_RSTRICT_REQ_HDR_NAMES_BLK|PR_O2_RSTRICT_REQ_HDR_NAMES_DEL)) { |
452 | 0 | verdict = http_req_restrict_header_names(s, htx, px); |
453 | 0 | if (verdict == HTTP_RULE_RES_DENY) |
454 | 0 | goto deny; |
455 | 0 | } |
456 | | |
457 | 0 | if (conn && (conn->flags & CO_FL_EARLY_DATA) && |
458 | 0 | (conn->flags & (CO_FL_EARLY_SSL_HS | CO_FL_SSL_WAIT_HS))) { |
459 | 0 | struct http_hdr_ctx ctx; |
460 | |
|
461 | 0 | ctx.blk = NULL; |
462 | 0 | if (!http_find_header(htx, ist("Early-Data"), &ctx, 0)) { |
463 | 0 | if (unlikely(!http_add_header(htx, ist("Early-Data"), ist("1"), 0))) |
464 | 0 | goto return_fail_rewrite; |
465 | 0 | } |
466 | 0 | } |
467 | | |
468 | | /* OK at this stage, we know that the request was accepted according to |
469 | | * the http-request rules, we can check for the stats. Note that the |
470 | | * URI is detected *before* the req* rules in order not to be affected |
471 | | * by a possible reqrep, while they are processed *after* so that a |
472 | | * reqdeny can still block them. This clearly needs to change in 1.6! |
473 | | */ |
474 | 0 | if (!s->target && http_stats_check_uri(s, txn, px)) { |
475 | 0 | s->target = &http_stats_applet.obj_type; |
476 | 0 | if (unlikely(!sc_applet_create(s->scb, objt_applet(s->target)))) { |
477 | 0 | s->logs.request_ts = now_ns; |
478 | 0 | if (!(s->flags & SF_ERR_MASK)) |
479 | 0 | s->flags |= SF_ERR_RESOURCE; |
480 | 0 | goto return_int_err; |
481 | 0 | } |
482 | | |
483 | | /* parse the whole stats request and extract the relevant information */ |
484 | 0 | http_handle_stats(s, req, px); |
485 | 0 | verdict = http_req_get_intercept_rule(px, NULL, &px->uri_auth->http_req_rules, s); |
486 | 0 | stats_rules = 1; |
487 | 0 | goto rule_verdict; |
488 | 0 | } |
489 | | |
490 | 0 | resume_stats_rules: |
491 | | |
492 | | /* Proceed with the applets now. */ |
493 | 0 | if (unlikely(objt_applet(s->target))) { |
494 | | /* report it if the request was intercepted by the frontend */ |
495 | 0 | if (sess->fe == s->be && sess->fe_tgcounters) |
496 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->intercepted_req); |
497 | |
|
498 | 0 | if (http_handle_expect_hdr(s, htx, msg) == -1) |
499 | 0 | goto return_int_err; |
500 | | |
501 | 0 | if (!(s->flags & SF_ERR_MASK)) // this is not really an error but it is |
502 | 0 | s->flags |= SF_ERR_LOCAL; // to mark that it comes from the proxy |
503 | 0 | http_set_term_flags(s); |
504 | |
|
505 | 0 | if (HAS_FILTERS(s)) |
506 | 0 | req->analysers |= AN_REQ_FLT_HTTP_HDRS; |
507 | | |
508 | | /* enable the minimally required analyzers to handle keep-alive and compression on the HTTP response */ |
509 | 0 | req->analysers &= (AN_REQ_HTTP_BODY | AN_REQ_FLT_HTTP_HDRS | AN_REQ_FLT_END); |
510 | 0 | req->analysers &= ~AN_REQ_FLT_XFER_DATA; |
511 | 0 | req->analysers |= AN_REQ_HTTP_XFER_BODY; |
512 | |
|
513 | 0 | s->scb->flags |= SC_FL_SND_ASAP; |
514 | 0 | s->flags |= SF_ASSIGNED; |
515 | 0 | goto done; |
516 | 0 | } |
517 | | |
518 | | /* check whether we have some ACLs set to redirect this request */ |
519 | 0 | list_for_each_entry(rule, &px->redirect_rules, list) { |
520 | 0 | if (!acl_match_cond(rule->cond, px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL)) |
521 | 0 | continue; |
522 | 0 | if (!http_apply_redirect_rule(rule, s, txn)) { |
523 | 0 | goto return_int_err; |
524 | 0 | } |
525 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_intercepted); |
526 | 0 | goto done; |
527 | 0 | } |
528 | | |
529 | | /* POST requests may be accompanied with an "Expect: 100-Continue" header. |
530 | | * If this happens, then the data will not come immediately, so we must |
531 | | * send all what we have without waiting. Note that due to the small gain |
532 | | * in waiting for the body of the request, it's easier to simply put the |
533 | | * SC_FL_SND_ASAP flag on the back SC any time. It's a one-shot flag so it |
534 | | * will remove itself once used. |
535 | | */ |
536 | 0 | s->scb->flags |= SC_FL_SND_ASAP; |
537 | |
|
538 | 0 | done: /* done with this analyser, continue with next ones that the calling |
539 | | * points will have set, if any. |
540 | | */ |
541 | 0 | req->analyse_exp = TICK_ETERNITY; |
542 | 0 | done_without_exp: /* done with this analyser, but don't reset the analyse_exp. */ |
543 | 0 | req->analysers &= ~an_bit; |
544 | 0 | s->current_rule = s->current_rule_list = NULL; |
545 | 0 | DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
546 | 0 | return 1; |
547 | | |
548 | 0 | tarpit: |
549 | | /* Allow cookie logging |
550 | | */ |
551 | 0 | if (s->be->cookie_name || sess->fe->capture_name) |
552 | 0 | http_manage_client_side_cookies(s, req); |
553 | | |
554 | | /* When a connection is tarpitted, we use the tarpit timeout, |
555 | | * which may be the same as the connect timeout if unspecified. |
556 | | * If unset, then set it to zero because we really want it to |
557 | | * eventually expire. We build the tarpit as an analyser. |
558 | | */ |
559 | 0 | channel_htx_erase(&s->req, htx); |
560 | | |
561 | | /* wipe the request out so that we can drop the connection early |
562 | | * if the client closes first. |
563 | | */ |
564 | 0 | channel_dont_connect(req); |
565 | |
|
566 | 0 | req->analysers &= AN_REQ_FLT_END; /* remove switching rules etc... */ |
567 | 0 | req->analysers |= AN_REQ_HTTP_TARPIT; |
568 | 0 | req->analyse_exp = tick_add_ifset(now_ms, s->tarpit_timeout ? s->tarpit_timeout : s->be->timeout.tarpit); |
569 | 0 | if (!req->analyse_exp) |
570 | 0 | req->analyse_exp = tick_add(now_ms, 0); |
571 | 0 | stream_inc_http_err_ctr(s); |
572 | 0 | if (sess->fe_tgcounters) |
573 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->denied_req); |
574 | 0 | if ((s->flags & SF_BE_ASSIGNED) && s->be_tgcounters) |
575 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->denied_req); |
576 | 0 | if (sess->li_tgcounters) |
577 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->denied_req); |
578 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_intercepted); |
579 | 0 | goto done_without_exp; |
580 | | |
581 | 0 | deny: /* this request was blocked (denied) */ |
582 | | |
583 | | /* Allow cookie logging |
584 | | */ |
585 | 0 | if (s->be->cookie_name || sess->fe->capture_name) |
586 | 0 | http_manage_client_side_cookies(s, req); |
587 | |
|
588 | 0 | s->logs.request_ts = now_ns; |
589 | 0 | stream_inc_http_err_ctr(s); |
590 | 0 | if (sess->fe_tgcounters) |
591 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->denied_req); |
592 | 0 | if ((s->flags & SF_BE_ASSIGNED) && s->be_tgcounters) |
593 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->denied_req); |
594 | 0 | if (sess->li_tgcounters) |
595 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->denied_req); |
596 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_intercepted); |
597 | 0 | goto return_prx_err; |
598 | | |
599 | 0 | return_fail_rewrite: |
600 | 0 | if (!(s->flags & SF_ERR_MASK)) |
601 | 0 | s->flags |= SF_ERR_PRXCOND; |
602 | 0 | if (sess->fe_tgcounters) |
603 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->failed_rewrites); |
604 | 0 | if ((s->flags & SF_BE_ASSIGNED) && s->be_tgcounters) |
605 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->failed_rewrites); |
606 | 0 | if (sess->li_tgcounters) |
607 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->failed_rewrites); |
608 | 0 | if (s->sv_tgcounters) |
609 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->failed_rewrites); |
610 | | /* fall through */ |
611 | |
|
612 | 0 | return_int_err: |
613 | 0 | txn->status = 500; |
614 | 0 | if (!(s->flags & SF_ERR_MASK)) |
615 | 0 | s->flags |= SF_ERR_INTERNAL; |
616 | 0 | if (sess->fe_tgcounters) |
617 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->internal_errors); |
618 | 0 | if ((s->flags & SF_BE_ASSIGNED) && s->be_tgcounters) |
619 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->internal_errors); |
620 | 0 | if (sess->li_tgcounters) |
621 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->internal_errors); |
622 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_internal_err); |
623 | 0 | goto return_prx_err; |
624 | | |
625 | 0 | return_bad_req: |
626 | 0 | txn->status = 400; |
627 | 0 | if (sess->fe_tgcounters) |
628 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->failed_req); |
629 | 0 | if (sess->li_tgcounters) |
630 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->failed_req); |
631 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_proto_err); |
632 | | /* fall through */ |
633 | |
|
634 | 0 | return_prx_err: |
635 | 0 | http_set_term_flags(s); |
636 | 0 | http_reply_and_close(s, txn->status, http_error_message(s)); |
637 | | /* fall through */ |
638 | |
|
639 | 0 | return_prx_cond: |
640 | 0 | http_set_term_flags(s); |
641 | 0 | req->analysers &= AN_REQ_FLT_END; |
642 | 0 | req->analyse_exp = TICK_ETERNITY; |
643 | 0 | s->current_rule = s->current_rule_list = NULL; |
644 | 0 | DBG_TRACE_DEVEL("leaving on error", |
645 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); |
646 | 0 | return 0; |
647 | | |
648 | 0 | return_prx_yield: |
649 | 0 | channel_dont_connect(req); |
650 | 0 | DBG_TRACE_DEVEL("waiting for more data", |
651 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
652 | 0 | return 0; |
653 | | |
654 | 0 | return_prx_fyield: |
655 | 0 | channel_dont_connect(req); |
656 | 0 | DBG_TRACE_DEVEL("forced yield", |
657 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
658 | 0 | return 0; |
659 | 0 | } |
660 | | |
661 | | /* This function performs all the processing enabled for the current request. |
662 | | * It returns 1 if the processing can continue on next analysers, or zero if it |
663 | | * needs more data, encounters an error, or wants to immediately abort the |
664 | | * request. It relies on buffers flags, and updates s->req.analysers. |
665 | | */ |
666 | | int http_process_request(struct stream *s, struct channel *req, int an_bit) |
667 | 0 | { |
668 | 0 | struct session *sess = s->sess; |
669 | 0 | struct http_txn *txn = s->txn.http; |
670 | 0 | struct htx *htx; |
671 | 0 | struct connection *cli_conn = objt_conn(strm_sess(s)->origin); |
672 | |
|
673 | 0 | DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
674 | | |
675 | | /* |
676 | | * Right now, we know that we have processed the entire headers |
677 | | * and that unwanted requests have been filtered out. We can do |
678 | | * whatever we want with the remaining request. Also, now we |
679 | | * may have separate values for ->fe, ->be. |
680 | | */ |
681 | 0 | htx = htxbuf(&req->buf); |
682 | | |
683 | | /* |
684 | | * 7: Now we can work with the cookies. |
685 | | * Note that doing so might move headers in the request, but |
686 | | * the fields will stay coherent and the URI will not move. |
687 | | * This should only be performed in the backend. |
688 | | */ |
689 | 0 | if (s->be->cookie_name || sess->fe->capture_name) |
690 | 0 | http_manage_client_side_cookies(s, req); |
691 | | |
692 | | /* 8: Generate unique ID if a "unique-id-format" is defined. |
693 | | * |
694 | | * A unique ID is generated even when it is not sent to ensure that the ID can make use of |
695 | | * fetches only available in the HTTP request processing stage. |
696 | | */ |
697 | 0 | if (!lf_expr_isempty(&sess->fe->format_unique_id)) { |
698 | 0 | struct ist unique_id = stream_generate_unique_id(s, &sess->fe->format_unique_id); |
699 | |
|
700 | 0 | if (!isttest(unique_id)) { |
701 | 0 | if (!(s->flags & SF_ERR_MASK)) |
702 | 0 | s->flags |= SF_ERR_RESOURCE; |
703 | 0 | goto return_int_err; |
704 | 0 | } |
705 | | |
706 | | /* send unique ID if a "unique-id-header" is defined */ |
707 | 0 | if (isttest(sess->fe->header_unique_id) && |
708 | 0 | unlikely(!http_add_header(htx, sess->fe->header_unique_id, unique_id, 1))) |
709 | 0 | goto return_fail_rewrite; |
710 | 0 | } |
711 | | |
712 | | /* handle http extensions (if configured) */ |
713 | 0 | if (unlikely(!http_handle_7239_header(s, req))) |
714 | 0 | goto return_fail_rewrite; |
715 | 0 | if (unlikely(!http_handle_xff_header(s, req))) |
716 | 0 | goto return_fail_rewrite; |
717 | 0 | if (unlikely(!http_handle_xot_header(s, req))) |
718 | 0 | goto return_fail_rewrite; |
719 | | |
720 | | /* Filter the request headers if there are filters attached to the |
721 | | * stream. |
722 | | */ |
723 | 0 | if (HAS_FILTERS(s)) |
724 | 0 | req->analysers |= AN_REQ_FLT_HTTP_HDRS; |
725 | | |
726 | | /* If we have no server assigned yet and we're balancing on url_param |
727 | | * with a POST request, we may be interested in checking the body for |
728 | | * that parameter. This will be done in another analyser. |
729 | | */ |
730 | 0 | if (!(s->flags & (SF_ASSIGNED|SF_DIRECT)) && |
731 | 0 | s->txn.http->meth == HTTP_METH_POST && |
732 | 0 | (s->be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_PH) { |
733 | 0 | channel_dont_connect(req); |
734 | 0 | req->analysers |= AN_REQ_HTTP_BODY; |
735 | 0 | } |
736 | |
|
737 | 0 | req->analysers &= ~AN_REQ_FLT_XFER_DATA; |
738 | 0 | req->analysers |= AN_REQ_HTTP_XFER_BODY; |
739 | | |
740 | | /* We expect some data from the client. Unless we know for sure |
741 | | * we already have a full request, we have to re-enable quick-ack |
742 | | * in case we previously disabled it, otherwise we might cause |
743 | | * the client to delay further data. |
744 | | */ |
745 | 0 | if ((sess->listener && (sess->listener->bind_conf->options & BC_O_NOQUICKACK)) && !(htx->flags & HTX_FL_EOM)) |
746 | 0 | conn_set_quickack(cli_conn, 1); |
747 | | |
748 | | /************************************************************* |
749 | | * OK, that's finished for the headers. We have done what we * |
750 | | * could. Let's switch to the DATA state. * |
751 | | ************************************************************/ |
752 | 0 | req->analyse_exp = TICK_ETERNITY; |
753 | 0 | req->analysers &= ~an_bit; |
754 | |
|
755 | 0 | s->logs.request_ts = now_ns; |
756 | | /* OK let's go on with the BODY now */ |
757 | 0 | DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
758 | 0 | return 1; |
759 | | |
760 | 0 | return_fail_rewrite: |
761 | 0 | if (!(s->flags & SF_ERR_MASK)) |
762 | 0 | s->flags |= SF_ERR_PRXCOND; |
763 | 0 | if (sess->fe_tgcounters) |
764 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->failed_rewrites); |
765 | 0 | if ((s->flags & SF_BE_ASSIGNED) && s->be_tgcounters) |
766 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->failed_rewrites); |
767 | 0 | if (sess->li_tgcounters) |
768 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->failed_rewrites); |
769 | 0 | if (s->sv_tgcounters) |
770 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->failed_rewrites); |
771 | | /* fall through */ |
772 | |
|
773 | 0 | return_int_err: |
774 | 0 | txn->status = 500; |
775 | 0 | if (!(s->flags & SF_ERR_MASK)) |
776 | 0 | s->flags |= SF_ERR_INTERNAL; |
777 | 0 | if (sess->fe_tgcounters) |
778 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->internal_errors); |
779 | 0 | if ((s->flags & SF_BE_ASSIGNED) && s->be_tgcounters) |
780 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->internal_errors); |
781 | 0 | if (sess->li_tgcounters) |
782 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->internal_errors); |
783 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_internal_err); |
784 | |
|
785 | 0 | http_set_term_flags(s); |
786 | 0 | http_reply_and_close(s, txn->status, http_error_message(s)); |
787 | |
|
788 | 0 | DBG_TRACE_DEVEL("leaving on error", |
789 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); |
790 | 0 | return 0; |
791 | 0 | } |
792 | | |
793 | | /* This function is an analyser which processes the HTTP tarpit. It always |
794 | | * returns zero, at the beginning because it prevents any other processing |
795 | | * from occurring, and at the end because it terminates the request. |
796 | | */ |
797 | | int http_process_tarpit(struct stream *s, struct channel *req, int an_bit) |
798 | 0 | { |
799 | 0 | struct http_txn *txn = s->txn.http; |
800 | |
|
801 | 0 | DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn, &txn->req); |
802 | | /* This connection is being tarpitted. The CLIENT side has |
803 | | * already set the connect expiration date to the right |
804 | | * timeout. We just have to check that the client is still |
805 | | * there and that the timeout has not expired. |
806 | | */ |
807 | 0 | channel_dont_connect(req); |
808 | 0 | if (!(s->scf->flags & (SC_FL_ABRT_DONE|SC_FL_EOS)) && |
809 | 0 | !tick_is_expired(req->analyse_exp, now_ms)) { |
810 | | /* Be sure to drain all data from the request channel */ |
811 | 0 | channel_htx_erase(req, htxbuf(&req->buf)); |
812 | 0 | DBG_TRACE_DEVEL("waiting for tarpit timeout expiry", |
813 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
814 | 0 | return 0; |
815 | 0 | } |
816 | | |
817 | | |
818 | | /* We will set the queue timer to the time spent, just for |
819 | | * logging purposes. We fake a 500 server error, so that the |
820 | | * attacker will not suspect his connection has been tarpitted. |
821 | | * It will not cause trouble to the logs because we can exclude |
822 | | * the tarpitted connections by filtering on the 'PT' status flags. |
823 | | */ |
824 | 0 | s->logs.t_queue = ns_to_ms(now_ns - s->logs.accept_ts); |
825 | |
|
826 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_intercepted); |
827 | 0 | http_set_term_flags(s); |
828 | 0 | http_reply_and_close(s, txn->status, (!(s->scf->flags & SC_FL_ERROR) ? http_error_message(s) : NULL)); |
829 | |
|
830 | 0 | DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
831 | 0 | return 0; |
832 | 0 | } |
833 | | |
834 | | /* This function is an analyser which waits for the HTTP request body. It waits |
835 | | * for either the buffer to be full, or the full advertised contents to have |
836 | | * reached the buffer. It must only be called after the standard HTTP request |
837 | | * processing has occurred, because it expects the request to be parsed and will |
838 | | * look for the Expect header. It may send a 100-Continue interim response. It |
839 | | * returns zero if it needs to read more data, or 1 once it has completed its |
840 | | * analysis. |
841 | | */ |
842 | | int http_wait_for_request_body(struct stream *s, struct channel *req, int an_bit) |
843 | 0 | { |
844 | 0 | struct session *sess = s->sess; |
845 | 0 | struct http_txn *txn = s->txn.http; |
846 | |
|
847 | 0 | DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn, |
848 | 0 | &s->txn.http->req); |
849 | | |
850 | |
|
851 | 0 | switch (http_wait_for_msg_body(s, req, s->be->timeout.httpreq, 0, 0)) { |
852 | 0 | case HTTP_RULE_RES_CONT: |
853 | 0 | s->waiting_entity.type = STRM_ENTITY_NONE; |
854 | 0 | s->waiting_entity.ptr = NULL; |
855 | 0 | goto http_end; |
856 | 0 | case HTTP_RULE_RES_YIELD: |
857 | 0 | s->waiting_entity.type = STRM_ENTITY_WREQ_BODY; |
858 | 0 | s->waiting_entity.ptr = NULL; |
859 | 0 | goto missing_data_or_waiting; |
860 | 0 | case HTTP_RULE_RES_BADREQ: |
861 | 0 | goto return_bad_req; |
862 | 0 | case HTTP_RULE_RES_ERROR: |
863 | 0 | goto return_int_err; |
864 | 0 | case HTTP_RULE_RES_ABRT: |
865 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_intercepted); |
866 | 0 | goto return_prx_cond; |
867 | 0 | default: |
868 | 0 | goto return_int_err; |
869 | 0 | } |
870 | | |
871 | 0 | http_end: |
872 | | /* The situation will not evolve, so let's give up on the analysis. */ |
873 | 0 | s->logs.request_ts = now_ns; /* update the request timer to reflect full request */ |
874 | 0 | req->analysers &= ~an_bit; |
875 | 0 | req->analyse_exp = TICK_ETERNITY; |
876 | 0 | DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
877 | 0 | return 1; |
878 | | |
879 | 0 | missing_data_or_waiting: |
880 | 0 | channel_dont_connect(req); |
881 | 0 | DBG_TRACE_DEVEL("waiting for more data", |
882 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
883 | 0 | return 0; |
884 | | |
885 | 0 | return_int_err: |
886 | 0 | txn->status = 500; |
887 | 0 | if (!(s->flags & SF_ERR_MASK)) |
888 | 0 | s->flags |= SF_ERR_INTERNAL; |
889 | 0 | if (sess->fe_tgcounters) |
890 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->internal_errors); |
891 | 0 | if ((s->flags & SF_BE_ASSIGNED) && s->be_tgcounters) |
892 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->internal_errors); |
893 | 0 | if (sess->li_tgcounters) |
894 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->internal_errors); |
895 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_internal_err); |
896 | 0 | goto return_prx_err; |
897 | | |
898 | 0 | return_bad_req: /* let's centralize all bad requests */ |
899 | 0 | txn->status = 400; |
900 | 0 | if (sess->fe_tgcounters) |
901 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->failed_req); |
902 | 0 | if (sess->li_tgcounters) |
903 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->failed_req); |
904 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_proto_err); |
905 | | /* fall through */ |
906 | |
|
907 | 0 | return_prx_err: |
908 | 0 | http_set_term_flags(s); |
909 | 0 | http_reply_and_close(s, txn->status, http_error_message(s)); |
910 | | /* fall through */ |
911 | |
|
912 | 0 | return_prx_cond: |
913 | 0 | http_set_term_flags(s); |
914 | |
|
915 | 0 | req->analysers &= AN_REQ_FLT_END; |
916 | 0 | req->analyse_exp = TICK_ETERNITY; |
917 | 0 | DBG_TRACE_DEVEL("leaving on error", |
918 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); |
919 | 0 | return 0; |
920 | 0 | } |
921 | | |
922 | | /* This function is an analyser which forwards request body (including chunk |
923 | | * sizes if any). It is called as soon as we must forward, even if we forward |
924 | | * zero byte. The only situation where it must not be called is when we're in |
925 | | * tunnel mode and we want to forward till the close. It's used both to forward |
926 | | * remaining data and to resync after end of body. It expects the msg_state to |
927 | | * be between MSG_BODY and MSG_DONE (inclusive). It returns zero if it needs to |
928 | | * read more data, or 1 once we can go on with next request or end the stream. |
929 | | * When in MSG_DATA or MSG_TRAILERS, it will automatically forward chunk_len |
930 | | * bytes of pending data + the headers if not already done. |
931 | | */ |
932 | | int http_request_forward_body(struct stream *s, struct channel *req, int an_bit) |
933 | 0 | { |
934 | 0 | struct session *sess = s->sess; |
935 | 0 | struct http_txn *txn = s->txn.http; |
936 | 0 | struct http_msg *msg = &txn->req; |
937 | 0 | struct htx *htx; |
938 | 0 | short status = 0; |
939 | 0 | int ret; |
940 | |
|
941 | 0 | DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn, msg); |
942 | |
|
943 | 0 | htx = htxbuf(&req->buf); |
944 | |
|
945 | 0 | if (htx->flags & HTX_FL_PARSING_ERROR) |
946 | 0 | goto return_bad_req; |
947 | 0 | if (htx->flags & HTX_FL_PROCESSING_ERROR) |
948 | 0 | goto return_int_err; |
949 | | |
950 | | /* Note that we don't have to send 100-continue back because we don't |
951 | | * need the data to complete our job, and it's up to the server to |
952 | | * decide whether to return 100, 417 or anything else in return of |
953 | | * an "Expect: 100-continue" header. |
954 | | */ |
955 | 0 | if (msg->msg_state == HTTP_MSG_BODY) |
956 | 0 | msg->msg_state = HTTP_MSG_DATA; |
957 | | |
958 | | /* in most states, we should abort in case of early close */ |
959 | 0 | channel_auto_close(req); |
960 | |
|
961 | 0 | if (req->to_forward) { |
962 | 0 | if (req->to_forward == CHN_INFINITE_FORWARD) { |
963 | 0 | if (s->scf->flags & SC_FL_EOI) |
964 | 0 | msg->msg_state = HTTP_MSG_ENDING; |
965 | 0 | } |
966 | 0 | else { |
967 | | /* We can't process the buffer's contents yet */ |
968 | 0 | req->flags |= CF_WAKE_WRITE; |
969 | 0 | goto missing_data_or_waiting; |
970 | 0 | } |
971 | 0 | } |
972 | | |
973 | 0 | if (msg->msg_state >= HTTP_MSG_ENDING) |
974 | 0 | goto ending; |
975 | | |
976 | 0 | if (txn->meth == HTTP_METH_CONNECT) { |
977 | 0 | msg->msg_state = HTTP_MSG_ENDING; |
978 | 0 | goto ending; |
979 | 0 | } |
980 | | |
981 | | /* Forward input data. We get it by removing all outgoing data not |
982 | | * forwarded yet from HTX data size. If there are some data filters, we |
983 | | * let them decide the amount of data to forward. |
984 | | */ |
985 | 0 | if (HAS_REQ_DATA_FILTERS(s)) { |
986 | 0 | ret = flt_http_payload(s, msg, htx->data); |
987 | 0 | if (ret < 0) |
988 | 0 | goto return_bad_req; |
989 | 0 | c_adv(req, ret); |
990 | 0 | } |
991 | 0 | else { |
992 | 0 | c_adv(req, htx->data - co_data(req)); |
993 | 0 | if ((global.tune.options & GTUNE_USE_FAST_FWD) && (msg->flags & HTTP_MSGF_XFER_LEN) && |
994 | 0 | (!(msg->flags & HTTP_MSGF_CONN_UPG) || (htx->flags & HTX_FL_EOM))) |
995 | 0 | channel_htx_forward_forever(req, htx); |
996 | 0 | } |
997 | | |
998 | 0 | if (htx->data != co_data(req)) |
999 | 0 | goto missing_data_or_waiting; |
1000 | | |
1001 | | /* Check if the end-of-message is reached and if so, switch the message |
1002 | | * in HTTP_MSG_ENDING state. Then if all data was marked to be |
1003 | | * forwarded, set the state to HTTP_MSG_DONE. |
1004 | | */ |
1005 | 0 | if (!(htx->flags & HTX_FL_EOM)) |
1006 | 0 | goto missing_data_or_waiting; |
1007 | | |
1008 | 0 | msg->msg_state = HTTP_MSG_ENDING; |
1009 | |
|
1010 | 0 | ending: |
1011 | 0 | s->scb->flags &= ~SC_FL_SND_EXP_MORE; /* no more data are expected to be send */ |
1012 | | |
1013 | | /* other states, ENDING...TUNNEL */ |
1014 | 0 | if (msg->msg_state >= HTTP_MSG_DONE) |
1015 | 0 | goto done; |
1016 | | |
1017 | 0 | if (HAS_REQ_DATA_FILTERS(s)) { |
1018 | 0 | ret = flt_http_end(s, msg); |
1019 | 0 | if (ret <= 0) { |
1020 | 0 | if (!ret) |
1021 | 0 | goto missing_data_or_waiting; |
1022 | 0 | goto return_bad_req; |
1023 | 0 | } |
1024 | 0 | } |
1025 | | |
1026 | 0 | if (txn->meth == HTTP_METH_CONNECT) |
1027 | 0 | msg->msg_state = HTTP_MSG_TUNNEL; |
1028 | 0 | else { |
1029 | 0 | msg->msg_state = HTTP_MSG_DONE; |
1030 | 0 | req->to_forward = 0; |
1031 | 0 | } |
1032 | |
|
1033 | 0 | done: |
1034 | | /* we don't want to forward closes on DONE except in tunnel mode. */ |
1035 | 0 | if (!(txn->flags & TX_CON_WANT_TUN)) |
1036 | 0 | channel_dont_close(req); |
1037 | |
|
1038 | 0 | if ((s->scb->flags & SC_FL_SHUT_DONE) && co_data(req)) { |
1039 | | /* request errors are most likely due to the server aborting the |
1040 | | * transfer. But handle server aborts only if the response was |
1041 | | * not received yet. Otherwise, let the response analyzer the |
1042 | | * responsibility to handle the error. It is especially |
1043 | | * important to properly handle L7-retries but also K/A silent close. |
1044 | | */ |
1045 | 0 | if (txn->rsp.msg_state >= HTTP_MSG_BODY && htx_is_empty(htxbuf(&s->res.buf))) |
1046 | 0 | goto return_srv_abort; |
1047 | 0 | } |
1048 | | |
1049 | 0 | http_end_request(s); |
1050 | 0 | if (!(req->analysers & an_bit)) { |
1051 | 0 | DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
1052 | 0 | return 1; |
1053 | 0 | } |
1054 | | |
1055 | | /* If "option abortonclose" is set on the backend, we want to monitor |
1056 | | * the client's connection and forward any shutdown notification to the |
1057 | | * server, which will decide whether to close or to go on processing the |
1058 | | * request. We only do that in tunnel mode, and not in other modes since |
1059 | | * it can be abused to exhaust source ports. */ |
1060 | 0 | if (proxy_abrt_close_def(s->be, 1)) { |
1061 | 0 | channel_auto_read(req); |
1062 | 0 | if ((s->scf->flags & (SC_FL_ABRT_DONE|SC_FL_EOS)) && !(txn->flags & TX_CON_WANT_TUN)) |
1063 | 0 | s->scb->flags |= SC_FL_NOLINGER; |
1064 | 0 | channel_auto_close(req); |
1065 | 0 | } |
1066 | 0 | else if (s->txn.http->meth == HTTP_METH_POST) { |
1067 | | /* POST requests may require to read extra CRLF sent by broken |
1068 | | * browsers and which could cause an RST to be sent upon close |
1069 | | * on some systems (eg: Linux). */ |
1070 | 0 | channel_auto_read(req); |
1071 | 0 | } |
1072 | 0 | DBG_TRACE_DEVEL("waiting for the end of the HTTP txn", |
1073 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
1074 | 0 | return 0; |
1075 | | |
1076 | 0 | missing_data_or_waiting: |
1077 | | /* stop waiting for data if the input is closed before the end */ |
1078 | 0 | if (msg->msg_state < HTTP_MSG_ENDING && (s->scf->flags & (SC_FL_ABRT_DONE|SC_FL_EOS))) |
1079 | 0 | goto return_cli_abort; |
1080 | | |
1081 | 0 | waiting: |
1082 | | /* waiting for the last bits to leave the buffer */ |
1083 | 0 | if (s->scb->flags & SC_FL_SHUT_DONE) { |
1084 | | /* Handle server aborts only if the response was not received |
1085 | | * yet. Otherwise, let the response analyzer the responsibility |
1086 | | * to handle the error. It is especially important to properly |
1087 | | * handle L7-retries but also K/A silent close. |
1088 | | */ |
1089 | 0 | if (txn->rsp.msg_state >= HTTP_MSG_BODY && htx_is_empty(htxbuf(&s->res.buf))) |
1090 | 0 | goto return_srv_abort; |
1091 | 0 | } |
1092 | | |
1093 | | /* When TE: chunked is used, we need to get there again to parse remaining |
1094 | | * chunks even if the client has closed, so we don't want to set CF_DONTCLOSE. |
1095 | | * And when content-length is used, we never want to let the possible |
1096 | | * shutdown be forwarded to the other side, as the state machine will |
1097 | | * take care of it once the client responds. It's also important to |
1098 | | * prevent TIME_WAITs from accumulating on the backend side, and for |
1099 | | * HTTP/2 where the last frame comes with a shutdown. |
1100 | | */ |
1101 | 0 | if (msg->flags & HTTP_MSGF_XFER_LEN) |
1102 | 0 | channel_dont_close(req); |
1103 | | |
1104 | | /* We know that more data are expected, but we couldn't send more that |
1105 | | * what we did. So we always set the SC_FL_SND_EXP_MORE flag so that the |
1106 | | * system knows it must not set a PUSH on this first part. Interactive |
1107 | | * modes are already handled by the stream sock layer. We must not do |
1108 | | * this in content-length mode because it could present the MSG_MORE |
1109 | | * flag with the last block of forwarded data, which would cause an |
1110 | | * additional delay to be observed by the receiver. |
1111 | | */ |
1112 | 0 | if (HAS_REQ_DATA_FILTERS(s)) |
1113 | 0 | s->scb->flags |= SC_FL_SND_EXP_MORE; |
1114 | |
|
1115 | 0 | DBG_TRACE_DEVEL("waiting for more data to forward", |
1116 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
1117 | 0 | return 0; |
1118 | | |
1119 | 0 | return_cli_abort: |
1120 | 0 | if (sess->fe_tgcounters) |
1121 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->cli_aborts); |
1122 | 0 | if (s->be_tgcounters) |
1123 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->cli_aborts); |
1124 | 0 | if (sess->li_tgcounters) |
1125 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->cli_aborts); |
1126 | 0 | if (s->sv_tgcounters) |
1127 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->cli_aborts); |
1128 | 0 | if (!(s->flags & SF_ERR_MASK)) |
1129 | 0 | s->flags |= ((req->flags & CF_READ_TIMEOUT) ? SF_ERR_CLITO : SF_ERR_CLICL); |
1130 | 0 | status = 400; |
1131 | 0 | goto return_prx_cond; |
1132 | | |
1133 | 0 | return_srv_abort: |
1134 | 0 | if (sess->fe_tgcounters) |
1135 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->srv_aborts); |
1136 | 0 | if (s->be_tgcounters) |
1137 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->srv_aborts); |
1138 | 0 | if (sess->li_tgcounters) |
1139 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->srv_aborts); |
1140 | 0 | if (s->sv_tgcounters) |
1141 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->srv_aborts); |
1142 | 0 | if (!(s->flags & SF_ERR_MASK)) |
1143 | 0 | s->flags |= ((req->flags & CF_WRITE_TIMEOUT) ? SF_ERR_SRVTO : SF_ERR_SRVCL); |
1144 | 0 | status = 502; |
1145 | 0 | goto return_prx_cond; |
1146 | | |
1147 | 0 | return_int_err: |
1148 | 0 | if (!(s->flags & SF_ERR_MASK)) |
1149 | 0 | s->flags |= SF_ERR_INTERNAL; |
1150 | 0 | if (sess->fe_tgcounters) |
1151 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->internal_errors); |
1152 | 0 | if (s->be_tgcounters) |
1153 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->internal_errors); |
1154 | 0 | if (sess->li_tgcounters) |
1155 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->internal_errors); |
1156 | 0 | if (s->sv_tgcounters) |
1157 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->internal_errors); |
1158 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_internal_err); |
1159 | 0 | status = 500; |
1160 | 0 | goto return_prx_cond; |
1161 | | |
1162 | 0 | return_bad_req: |
1163 | 0 | if (sess->fe_tgcounters) |
1164 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->failed_req); |
1165 | 0 | if (sess->li_tgcounters) |
1166 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->failed_req); |
1167 | 0 | stream_report_term_evt(s->scf, strm_tevt_type_proto_err); |
1168 | 0 | status = 400; |
1169 | | /* fall through */ |
1170 | |
|
1171 | 0 | return_prx_cond: |
1172 | 0 | http_set_term_flags(s); |
1173 | 0 | if (txn->status > 0) { |
1174 | | /* Note: we don't send any error if some data were already sent */ |
1175 | 0 | http_reply_and_close(s, txn->status, NULL); |
1176 | 0 | } else { |
1177 | 0 | txn->status = status; |
1178 | 0 | http_reply_and_close(s, txn->status, http_error_message(s)); |
1179 | 0 | } |
1180 | 0 | DBG_TRACE_DEVEL("leaving on error ", |
1181 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); |
1182 | 0 | return 0; |
1183 | 0 | } |
1184 | | |
1185 | | /* Reset the stream and the backend stream connector to a situation suitable for attemption connection */ |
1186 | | /* Returns 0 if we can attempt to retry, -1 otherwise */ |
1187 | | static __inline int do_l7_retry(struct stream *s, struct stconn *sc) |
1188 | 0 | { |
1189 | 0 | struct channel *req, *res; |
1190 | 0 | int co_data; |
1191 | |
|
1192 | 0 | if (s->conn_retries >= s->max_retries) |
1193 | 0 | return -1; |
1194 | 0 | s->conn_retries++; |
1195 | 0 | if (objt_server(s->target)) { |
1196 | 0 | if (s->flags & SF_CURR_SESS) { |
1197 | 0 | s->flags &= ~SF_CURR_SESS; |
1198 | 0 | _HA_ATOMIC_DEC(&__objt_server(s->target)->cur_sess); |
1199 | 0 | } |
1200 | 0 | if (s->sv_tgcounters) |
1201 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->retries); |
1202 | 0 | } |
1203 | 0 | if (s->be_tgcounters) |
1204 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->retries); |
1205 | |
|
1206 | 0 | req = &s->req; |
1207 | 0 | res = &s->res; |
1208 | | |
1209 | | /* Remove any write error from the request, and read error from the response */ |
1210 | 0 | s->scf->flags &= ~(SC_FL_EOS|SC_FL_ABRT_DONE|SC_FL_ABRT_WANTED); |
1211 | 0 | req->flags &= ~(CF_WRITE_TIMEOUT|CF_WROTE_DATA); |
1212 | 0 | res->flags &= ~(CF_READ_TIMEOUT | CF_READ_EVENT); |
1213 | 0 | res->analysers &= AN_RES_FLT_END; |
1214 | 0 | s->conn_err_type = STRM_ET_NONE; |
1215 | 0 | s->flags &= ~(SF_CONN_EXP | SF_ERR_MASK | SF_FINST_MASK); |
1216 | 0 | s->conn_exp = TICK_ETERNITY; |
1217 | 0 | stream_choose_redispatch(s); |
1218 | 0 | res->to_forward = 0; |
1219 | 0 | res->analyse_exp = TICK_ETERNITY; |
1220 | |
|
1221 | 0 | s->scb->flags &= ~(SC_FL_ERROR|SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED); |
1222 | 0 | if (sc_reset_endp(s->scb) < 0) { |
1223 | 0 | s->flags |= SF_ERR_INTERNAL; |
1224 | 0 | return -1; |
1225 | 0 | } |
1226 | | |
1227 | 0 | b_free(&req->buf); |
1228 | | |
1229 | | /* Swap the L7 buffer with the channel buffer */ |
1230 | | /* We know we stored the co_data as b_data, so get it there */ |
1231 | 0 | co_data = b_data(&s->txn.http->l7_buffer); |
1232 | 0 | b_set_data(&s->txn.http->l7_buffer, b_size(&s->txn.http->l7_buffer)); |
1233 | |
|
1234 | 0 | req->buf = s->txn.http->l7_buffer; |
1235 | 0 | s->txn.http->l7_buffer = BUF_NULL; |
1236 | 0 | co_set_data(req, co_data); |
1237 | |
|
1238 | 0 | DBG_TRACE_DEVEL("perform a L7 retry", STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, |
1239 | 0 | s->txn.http); |
1240 | |
|
1241 | 0 | b_reset(&res->buf); |
1242 | 0 | co_set_data(res, 0); |
1243 | 0 | return 0; |
1244 | 0 | } |
1245 | | |
1246 | | /* This stream analyser waits for a complete HTTP response. It returns 1 if the |
1247 | | * processing can continue on next analysers, or zero if it either needs more |
1248 | | * data or wants to immediately abort the response (eg: timeout, error, ...). It |
1249 | | * is tied to AN_RES_WAIT_HTTP and may may remove itself from s->res.analysers |
1250 | | * when it has nothing left to do, and may remove any analyser when it wants to |
1251 | | * abort. |
1252 | | */ |
1253 | | int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit) |
1254 | 0 | { |
1255 | | /* |
1256 | | * We will analyze a complete HTTP response to check the its syntax. |
1257 | | * |
1258 | | * Once the start line and all headers are received, we may perform a |
1259 | | * capture of the error (if any), and we will set a few fields. We also |
1260 | | * logging and finally headers capture. |
1261 | | */ |
1262 | 0 | struct session *sess = s->sess; |
1263 | 0 | struct http_txn *txn = s->txn.http; |
1264 | 0 | struct http_msg *msg = &txn->rsp; |
1265 | 0 | struct htx *htx; |
1266 | 0 | struct connection *srv_conn; |
1267 | 0 | struct htx_sl *sl; |
1268 | 0 | int n, l7_retry_failed = 0; |
1269 | |
|
1270 | 0 | DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn, msg); |
1271 | |
|
1272 | 0 | htx = htxbuf(&rep->buf); |
1273 | | |
1274 | | /* Parsing errors are caught here */ |
1275 | 0 | if (htx->flags & HTX_FL_PARSING_ERROR) { |
1276 | 0 | if (objt_server(s->target)) |
1277 | 0 | health_adjust(__objt_server(s->target), HANA_STATUS_HTTP_HDRRSP); |
1278 | 0 | goto return_bad_res; |
1279 | 0 | } |
1280 | 0 | if (htx->flags & HTX_FL_PROCESSING_ERROR) |
1281 | 0 | goto return_int_err; |
1282 | | |
1283 | | /* |
1284 | | * Now we quickly check if we have found a full valid response. |
1285 | | * If not so, we check the FD and buffer states before leaving. |
1286 | | * A full response is indicated by the fact that we have seen |
1287 | | * the double LF/CRLF, so the state is >= HTTP_MSG_BODY. Invalid |
1288 | | * responses are checked first. |
1289 | | * |
1290 | | * Depending on whether the client is still there or not, we |
1291 | | * may send an error response back or not. Note that normally |
1292 | | * we should only check for HTTP status there, and check I/O |
1293 | | * errors somewhere else. |
1294 | | */ |
1295 | 0 | next_one: |
1296 | 0 | if (unlikely(htx_is_empty(htx) || htx->first == -1)) { |
1297 | | /* 1: have we encountered a read error ? */ |
1298 | 0 | if (s->scb->flags & SC_FL_ERROR) { |
1299 | 0 | struct connection *conn = sc_conn(s->scb); |
1300 | |
|
1301 | 0 | if (!(s->flags & SF_SRV_REUSED) && objt_server(s->target)) |
1302 | 0 | health_adjust(__objt_server(s->target), HANA_STATUS_HTTP_READ_ERROR); |
1303 | |
|
1304 | 0 | if ((txn->flags & TX_L7_RETRY) && |
1305 | 0 | (s->be->retry_type & PR_RE_DISCONNECTED) && |
1306 | 0 | (!conn || conn->err_code != CO_ER_SSL_EARLY_FAILED)) { |
1307 | 0 | if (co_data(rep) || do_l7_retry(s, s->scb) == 0) |
1308 | 0 | return 0; |
1309 | 0 | l7_retry_failed = 1; |
1310 | 0 | } |
1311 | | |
1312 | | /* Perform a L7 retry on empty response or because server refuses the early data. */ |
1313 | 0 | if ((txn->flags & TX_L7_RETRY) && |
1314 | 0 | (s->be->retry_type & PR_RE_EARLY_ERROR) && |
1315 | 0 | conn && conn->err_code == CO_ER_SSL_EARLY_FAILED) { |
1316 | 0 | if (do_l7_retry(s, s->scb) == 0) { |
1317 | 0 | DBG_TRACE_DEVEL("leaving on L7 retry", |
1318 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
1319 | 0 | return 0; |
1320 | 0 | } |
1321 | 0 | l7_retry_failed = 1; |
1322 | 0 | } |
1323 | | |
1324 | 0 | if (!l7_retry_failed && (s->flags & SF_SRV_REUSED)) |
1325 | 0 | goto abort_keep_alive; |
1326 | | |
1327 | 0 | if (s->be_tgcounters) |
1328 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->failed_resp); |
1329 | 0 | if (s->sv_tgcounters) |
1330 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->failed_resp); |
1331 | | |
1332 | | /* if the server refused the early data, just send a 425 */ |
1333 | 0 | if (conn && conn->err_code == CO_ER_SSL_EARLY_FAILED) |
1334 | 0 | txn->status = 425; |
1335 | 0 | else { |
1336 | 0 | txn->status = 502; |
1337 | 0 | stream_inc_http_fail_ctr(s); |
1338 | 0 | } |
1339 | |
|
1340 | 0 | s->scb->flags |= SC_FL_NOLINGER; |
1341 | |
|
1342 | 0 | if (!(s->flags & SF_ERR_MASK)) |
1343 | 0 | s->flags |= SF_ERR_SRVCL; |
1344 | 0 | http_set_term_flags(s); |
1345 | |
|
1346 | 0 | http_reply_and_close(s, txn->status, http_error_message(s)); |
1347 | 0 | DBG_TRACE_DEVEL("leaving on error", |
1348 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); |
1349 | 0 | return 0; |
1350 | 0 | } |
1351 | | |
1352 | | /* 2: read timeout : return a 504 to the client. */ |
1353 | 0 | else if (rep->flags & CF_READ_TIMEOUT) { |
1354 | 0 | if (objt_server(s->target)) |
1355 | 0 | health_adjust(__objt_server(s->target), HANA_STATUS_HTTP_READ_TIMEOUT); |
1356 | |
|
1357 | 0 | if ((txn->flags & TX_L7_RETRY) && |
1358 | 0 | (s->be->retry_type & PR_RE_TIMEOUT)) { |
1359 | 0 | if (co_data(rep) || do_l7_retry(s, s->scb) == 0) { |
1360 | 0 | DBG_TRACE_DEVEL("leaving on L7 retry", |
1361 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
1362 | 0 | return 0; |
1363 | 0 | } |
1364 | 0 | } |
1365 | 0 | if (s->be_tgcounters) |
1366 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->failed_resp); |
1367 | 0 | if (s->sv_tgcounters) |
1368 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->failed_resp); |
1369 | |
|
1370 | 0 | txn->status = 504; |
1371 | 0 | stream_inc_http_fail_ctr(s); |
1372 | 0 | s->scb->flags |= SC_FL_NOLINGER; |
1373 | |
|
1374 | 0 | if (!(s->flags & SF_ERR_MASK)) |
1375 | 0 | s->flags |= SF_ERR_SRVTO; |
1376 | 0 | http_set_term_flags(s); |
1377 | |
|
1378 | 0 | http_reply_and_close(s, txn->status, http_error_message(s)); |
1379 | 0 | DBG_TRACE_DEVEL("leaving on error", |
1380 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); |
1381 | 0 | return 0; |
1382 | 0 | } |
1383 | | |
1384 | | /* 3: client abort with an abortonclose */ |
1385 | 0 | else if ((s->scb->flags & (SC_FL_EOS|SC_FL_ABRT_DONE)) && (s->scb->flags & SC_FL_SHUT_DONE) && |
1386 | 0 | (s->scf->flags & (SC_FL_EOS|SC_FL_ABRT_DONE))) { |
1387 | 0 | if (sess->fe_tgcounters) |
1388 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->cli_aborts); |
1389 | 0 | if (s->be_tgcounters) |
1390 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->cli_aborts); |
1391 | 0 | if (sess->li_tgcounters) |
1392 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->cli_aborts); |
1393 | 0 | if (s->sv_tgcounters) |
1394 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->cli_aborts); |
1395 | |
|
1396 | 0 | txn->status = 400; |
1397 | |
|
1398 | 0 | if (!(s->flags & SF_ERR_MASK)) |
1399 | 0 | s->flags |= SF_ERR_CLICL; |
1400 | 0 | http_set_term_flags(s); |
1401 | |
|
1402 | 0 | http_reply_and_close(s, txn->status, http_error_message(s)); |
1403 | | |
1404 | | /* process_stream() will take care of the error */ |
1405 | 0 | DBG_TRACE_DEVEL("leaving on error", |
1406 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); |
1407 | 0 | return 0; |
1408 | 0 | } |
1409 | | |
1410 | | /* 4: close from server, capture the response if the server has started to respond */ |
1411 | 0 | else if (s->scb->flags & (SC_FL_EOS|SC_FL_ABRT_DONE)) { |
1412 | 0 | if (!(s->flags & SF_SRV_REUSED) && objt_server(s->target)) |
1413 | 0 | health_adjust(__objt_server(s->target), HANA_STATUS_HTTP_BROKEN_PIPE); |
1414 | |
|
1415 | 0 | if ((txn->flags & TX_L7_RETRY) && |
1416 | 0 | (s->be->retry_type & PR_RE_DISCONNECTED)) { |
1417 | 0 | if (co_data(rep) || do_l7_retry(s, s->scb) == 0) { |
1418 | 0 | DBG_TRACE_DEVEL("leaving on L7 retry", |
1419 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
1420 | 0 | return 0; |
1421 | 0 | } |
1422 | 0 | l7_retry_failed = 1; |
1423 | 0 | } |
1424 | | |
1425 | 0 | if (!l7_retry_failed && (s->flags & SF_SRV_REUSED)) |
1426 | 0 | goto abort_keep_alive; |
1427 | | |
1428 | 0 | if (s->be_tgcounters) |
1429 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->failed_resp); |
1430 | 0 | if (s->sv_tgcounters) |
1431 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->failed_resp); |
1432 | |
|
1433 | 0 | txn->status = 502; |
1434 | 0 | stream_inc_http_fail_ctr(s); |
1435 | 0 | s->scb->flags |= SC_FL_NOLINGER; |
1436 | |
|
1437 | 0 | if (!(s->flags & SF_ERR_MASK)) |
1438 | 0 | s->flags |= SF_ERR_SRVCL; |
1439 | 0 | http_set_term_flags(s); |
1440 | |
|
1441 | 0 | http_reply_and_close(s, txn->status, http_error_message(s)); |
1442 | 0 | DBG_TRACE_DEVEL("leaving on error", |
1443 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); |
1444 | 0 | return 0; |
1445 | 0 | } |
1446 | | |
1447 | | /* 5: write error to client (we don't send any message then) */ |
1448 | 0 | else if (sc_ep_test(s->scf, SE_FL_ERR_PENDING)) { |
1449 | 0 | if (s->flags & SF_SRV_REUSED) |
1450 | 0 | goto abort_keep_alive; |
1451 | | |
1452 | 0 | if (s->be_tgcounters) |
1453 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->failed_resp); |
1454 | 0 | if (s->sv_tgcounters) |
1455 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->failed_resp); |
1456 | 0 | rep->analysers &= AN_RES_FLT_END; |
1457 | |
|
1458 | 0 | if (!(s->flags & SF_ERR_MASK)) |
1459 | 0 | s->flags |= SF_ERR_CLICL; |
1460 | 0 | http_set_term_flags(s); |
1461 | | |
1462 | | /* process_stream() will take care of the error */ |
1463 | 0 | http_reply_and_close(s, txn->status, NULL); |
1464 | 0 | DBG_TRACE_DEVEL("leaving on error", |
1465 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); |
1466 | 0 | return 0; |
1467 | 0 | } |
1468 | | |
1469 | 0 | channel_dont_close(rep); |
1470 | 0 | s->scb->flags |= SC_FL_RCV_ONCE; /* try to get back here ASAP */ |
1471 | 0 | DBG_TRACE_DEVEL("waiting for more data", |
1472 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
1473 | 0 | return 0; |
1474 | 0 | } |
1475 | | |
1476 | | /* More interesting part now : we know that we have a complete |
1477 | | * response which at least looks like HTTP. We have an indicator |
1478 | | * of each header's length, so we can parse them quickly. |
1479 | | */ |
1480 | 0 | BUG_ON(htx_get_first_type(htx) != HTX_BLK_RES_SL); |
1481 | 0 | sl = http_get_stline(htx); |
1482 | |
|
1483 | 0 | if ((sl->flags & HTX_SL_F_NOT_HTTP) || HTX_SL_RES_VLEN(sl) != 8) { |
1484 | | /* Not an HTTP response */ |
1485 | 0 | msg->vsn = 0; |
1486 | 0 | } |
1487 | 0 | else if (objt_server(s->target)) { |
1488 | | /* HTTP response from a server, use it to set the response version */ |
1489 | 0 | char *ptr; |
1490 | |
|
1491 | 0 | ptr = HTX_SL_RES_VPTR(sl); |
1492 | 0 | msg->vsn = ((ptr[5] - '0') << 4) + (ptr[7] - '0'); |
1493 | | |
1494 | | /* If front endpoint is an applet, use the server version for the request */ |
1495 | 0 | if (sc_ep_test(s->scf, SE_FL_T_APPLET)) |
1496 | 0 | txn->req.vsn = msg->vsn; |
1497 | 0 | } |
1498 | 0 | else { |
1499 | | /* HTTP response from an applet, use the request version for the response */ |
1500 | 0 | msg->vsn = txn->req.vsn; |
1501 | 0 | } |
1502 | | |
1503 | | /* Adjust server's health based on status code. Note: status codes 501 |
1504 | | * and 505 are triggered on demand by client request, so we must not |
1505 | | * count them as server failures. |
1506 | | */ |
1507 | 0 | if (objt_server(s->target)) { |
1508 | 0 | if (sl->info.res.status >= 100 && (sl->info.res.status < 500 || sl->info.res.status == 501 || sl->info.res.status == 505)) |
1509 | 0 | health_adjust(__objt_server(s->target), HANA_STATUS_HTTP_OK); |
1510 | 0 | else |
1511 | 0 | health_adjust(__objt_server(s->target), HANA_STATUS_HTTP_STS); |
1512 | 0 | } |
1513 | | |
1514 | | /* Perform a L7 retry because of the status code */ |
1515 | 0 | if ((txn->flags & TX_L7_RETRY) && |
1516 | 0 | l7_status_match(s->be, sl->info.res.status) && |
1517 | 0 | do_l7_retry(s, s->scb) == 0) { |
1518 | 0 | DBG_TRACE_DEVEL("leaving on L7 retry", STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
1519 | 0 | return 0; |
1520 | 0 | } |
1521 | | |
1522 | 0 | msg->msg_state = HTTP_MSG_BODY; |
1523 | | |
1524 | | |
1525 | | /* 0: we might have to print this header in debug mode */ |
1526 | 0 | if (unlikely((global.mode & MODE_DEBUG) && |
1527 | 0 | (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)))) { |
1528 | 0 | int32_t pos; |
1529 | |
|
1530 | 0 | http_debug_stline("srvrep", s, sl); |
1531 | |
|
1532 | 0 | for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) { |
1533 | 0 | struct htx_blk *blk = htx_get_blk(htx, pos); |
1534 | 0 | enum htx_blk_type type = htx_get_blk_type(blk); |
1535 | |
|
1536 | 0 | if (type == HTX_BLK_EOH) |
1537 | 0 | break; |
1538 | 0 | if (type != HTX_BLK_HDR) |
1539 | 0 | continue; |
1540 | | |
1541 | 0 | http_debug_hdr("srvhdr", s, |
1542 | 0 | htx_get_blk_name(htx, blk), |
1543 | 0 | htx_get_blk_value(htx, blk)); |
1544 | 0 | } |
1545 | 0 | } |
1546 | | |
1547 | | /* 1: get the status code and the version. Also set HTTP flags */ |
1548 | 0 | txn->server_status = txn->status = sl->info.res.status; |
1549 | 0 | if (sl->flags & HTX_SL_F_VER_11) |
1550 | 0 | msg->flags |= HTTP_MSGF_VER_11; |
1551 | 0 | if (sl->flags & HTX_SL_F_XFER_LEN) { |
1552 | 0 | msg->flags |= HTTP_MSGF_XFER_LEN; |
1553 | 0 | if (sl->flags & HTX_SL_F_CLEN) |
1554 | 0 | msg->flags |= HTTP_MSGF_CNT_LEN; |
1555 | 0 | else if (sl->flags & HTX_SL_F_CHNK) |
1556 | 0 | msg->flags |= HTTP_MSGF_TE_CHNK; |
1557 | 0 | } |
1558 | 0 | if (sl->flags & HTX_SL_F_BODYLESS) |
1559 | 0 | msg->flags |= HTTP_MSGF_BODYLESS; |
1560 | 0 | if (sl->flags & HTX_SL_F_CONN_UPG) |
1561 | 0 | msg->flags |= HTTP_MSGF_CONN_UPG; |
1562 | | |
1563 | | /* when the client triggers a 4xx from the server, it's most often due |
1564 | | * to a missing object or permission. These events should be tracked |
1565 | | * because if they happen often, it may indicate a brute force or a |
1566 | | * vulnerability scan. |
1567 | | */ |
1568 | 0 | if (http_status_matches(http_err_status_codes, txn->status)) |
1569 | 0 | stream_inc_http_err_ctr(s); |
1570 | |
|
1571 | 0 | if (http_status_matches(http_fail_status_codes, txn->status)) |
1572 | 0 | stream_inc_http_fail_ctr(s); |
1573 | |
|
1574 | 0 | if (s->sv_tgcounters) { |
1575 | 0 | n = txn->status / 100; |
1576 | 0 | if (n < 1 || n > 5) |
1577 | 0 | n = 0; |
1578 | |
|
1579 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->p.http.rsp[n]); |
1580 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->p.http.cum_req); |
1581 | 0 | } |
1582 | | |
1583 | | /* |
1584 | | * We may be facing a 100-continue response, or any other informational |
1585 | | * 1xx response which is non-final, in which case this is not the right |
1586 | | * response, and we're waiting for the next one. Let's allow this response |
1587 | | * to go to the client and wait for the next one. There's an exception for |
1588 | | * 101 which is used later in the code to switch protocols. |
1589 | | */ |
1590 | 0 | if (txn->status < 200 && |
1591 | 0 | (txn->status == 100 || txn->status >= 102)) { |
1592 | 0 | FLT_STRM_CB(s, flt_http_reset(s, msg)); |
1593 | 0 | htx->first = channel_htx_fwd_headers(rep, htx); |
1594 | 0 | msg->msg_state = HTTP_MSG_RPBEFORE; |
1595 | 0 | msg->flags = 0; |
1596 | 0 | txn->server_status = txn->status = 0; |
1597 | 0 | s->logs.t_data = -1; /* was not a response yet */ |
1598 | 0 | s->scf->flags |= SC_FL_SND_ASAP; /* Send ASAP informational messages */ |
1599 | 0 | goto next_one; |
1600 | 0 | } |
1601 | | |
1602 | | /* A 101-switching-protocols must contains a Connection header with the |
1603 | | * "upgrade" option and the request too. It means both are agree to |
1604 | | * upgrade. It is not so strict because there is no test on the Upgrade |
1605 | | * header content. But it is probably stronger enough for now. |
1606 | | */ |
1607 | 0 | if (txn->status == 101 && |
1608 | 0 | (!(txn->req.flags & HTTP_MSGF_CONN_UPG) || !(txn->rsp.flags & HTTP_MSGF_CONN_UPG))) { |
1609 | 0 | if (objt_server(s->target)) |
1610 | 0 | health_adjust(__objt_server(s->target), HANA_STATUS_HTTP_HDRRSP); |
1611 | 0 | goto return_bad_res; |
1612 | 0 | } |
1613 | | |
1614 | | /* |
1615 | | * 2: check for cacheability. |
1616 | | */ |
1617 | | |
1618 | 0 | switch (txn->status) { |
1619 | 0 | case 200: |
1620 | 0 | case 203: |
1621 | 0 | case 204: |
1622 | 0 | case 206: |
1623 | 0 | case 300: |
1624 | 0 | case 301: |
1625 | 0 | case 404: |
1626 | 0 | case 405: |
1627 | 0 | case 410: |
1628 | 0 | case 414: |
1629 | 0 | case 501: |
1630 | 0 | break; |
1631 | 0 | default: |
1632 | | /* RFC7231#6.1: |
1633 | | * Responses with status codes that are defined as |
1634 | | * cacheable by default (e.g., 200, 203, 204, 206, |
1635 | | * 300, 301, 404, 405, 410, 414, and 501 in this |
1636 | | * specification) can be reused by a cache with |
1637 | | * heuristic expiration unless otherwise indicated |
1638 | | * by the method definition or explicit cache |
1639 | | * controls [RFC7234]; all other status codes are |
1640 | | * not cacheable by default. |
1641 | | */ |
1642 | 0 | txn->flags &= ~(TX_CACHEABLE | TX_CACHE_COOK); |
1643 | 0 | break; |
1644 | 0 | } |
1645 | | |
1646 | | /* |
1647 | | * 3: we may need to capture headers |
1648 | | */ |
1649 | 0 | s->logs.logwait &= ~LW_RESP; |
1650 | 0 | if (unlikely((s->logs.logwait & LW_RSPHDR) && s->res_cap)) |
1651 | 0 | http_capture_headers(htx, s->res_cap, sess->fe->rsp_cap); |
1652 | | |
1653 | | /* Skip parsing if no content length is possible. */ |
1654 | 0 | if (unlikely((txn->meth == HTTP_METH_CONNECT && txn->status >= 200 && txn->status < 300) || |
1655 | 0 | txn->status == 101)) { |
1656 | | /* Either we've established an explicit tunnel, or we're |
1657 | | * switching the protocol. In both cases, we're very unlikely |
1658 | | * to understand the next protocols. We have to switch to tunnel |
1659 | | * mode, so that we transfer the request and responses then let |
1660 | | * this protocol pass unmodified. When we later implement specific |
1661 | | * parsers for such protocols, we'll want to check the Upgrade |
1662 | | * header which contains information about that protocol for |
1663 | | * responses with status 101 (eg: see RFC2817 about TLS). |
1664 | | */ |
1665 | 0 | txn->flags |= TX_CON_WANT_TUN; |
1666 | 0 | } |
1667 | | |
1668 | | /* Check for NTML authentication headers in 401 (WWW-Authenticate) and |
1669 | | * 407 (Proxy-Authenticate) responses and set the connection to |
1670 | | * private. |
1671 | | * |
1672 | | * Note that this is not performed when using a true multiplexer unless |
1673 | | * connection is already attached to the session as nothing prevents it |
1674 | | * from being shared already by several sessions here. |
1675 | | */ |
1676 | 0 | srv_conn = sc_conn(s->scb); |
1677 | 0 | if (srv_conn && |
1678 | 0 | (LIST_INLIST(&srv_conn->sess_el) || strcmp(srv_conn->mux->name, "H1") == 0)) { |
1679 | 0 | struct ist hdr; |
1680 | 0 | struct http_hdr_ctx ctx; |
1681 | |
|
1682 | 0 | if (txn->status == 401) |
1683 | 0 | hdr = ist("WWW-Authenticate"); |
1684 | 0 | else if (txn->status == 407) |
1685 | 0 | hdr = ist("Proxy-Authenticate"); |
1686 | 0 | else |
1687 | 0 | goto end; |
1688 | | |
1689 | 0 | ctx.blk = NULL; |
1690 | 0 | while (http_find_header(htx, hdr, &ctx, 0)) { |
1691 | | /* If www-authenticate contains "Negotiate", "Nego2", or "NTLM", |
1692 | | * possibly followed by blanks and a base64 string, the connection |
1693 | | * is private. Since it's a mess to deal with, we only check for |
1694 | | * values starting with "NTLM" or "Nego". Note that often multiple |
1695 | | * headers are sent by the server there. |
1696 | | */ |
1697 | 0 | if ((ctx.value.len >= 4 && strncasecmp(ctx.value.ptr, "Nego", 4) == 0) || |
1698 | 0 | (ctx.value.len >= 4 && strncasecmp(ctx.value.ptr, "NTLM", 4) == 0)) { |
1699 | 0 | sess->flags |= SESS_FL_PREFER_LAST; |
1700 | 0 | conn_set_private(srv_conn); |
1701 | | /* If it fail now, the same will be done in mux->detach() callback */ |
1702 | 0 | session_add_conn(sess, srv_conn); |
1703 | 0 | break; |
1704 | 0 | } |
1705 | 0 | } |
1706 | 0 | } |
1707 | | |
1708 | 0 | end: |
1709 | | /* Now, L7 buffer is useless, it can be released */ |
1710 | 0 | b_free(&txn->l7_buffer); |
1711 | | |
1712 | | /* we want to have the response time before we start processing it */ |
1713 | 0 | s->logs.t_data = ns_to_ms(now_ns - s->logs.accept_ts); |
1714 | | |
1715 | | /* end of job, return OK */ |
1716 | 0 | rep->analysers &= ~an_bit; |
1717 | 0 | rep->analyse_exp = TICK_ETERNITY; |
1718 | 0 | channel_auto_close(rep); |
1719 | 0 | DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
1720 | 0 | return 1; |
1721 | | |
1722 | 0 | return_int_err: |
1723 | 0 | if (sess->fe_tgcounters) |
1724 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->internal_errors); |
1725 | 0 | if (s->be_tgcounters) |
1726 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->internal_errors); |
1727 | 0 | if (sess->li_tgcounters) |
1728 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->internal_errors); |
1729 | 0 | if (s->sv_tgcounters) |
1730 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->internal_errors); |
1731 | 0 | txn->status = 500; |
1732 | 0 | if (!(s->flags & SF_ERR_MASK)) |
1733 | 0 | s->flags |= SF_ERR_INTERNAL; |
1734 | 0 | stream_report_term_evt(s->scb, strm_tevt_type_internal_err); |
1735 | 0 | goto return_prx_cond; |
1736 | | |
1737 | 0 | return_bad_res: |
1738 | 0 | if ((s->be->retry_type & PR_RE_JUNK_REQUEST) && |
1739 | 0 | (txn->flags & TX_L7_RETRY) && |
1740 | 0 | do_l7_retry(s, s->scb) == 0) { |
1741 | 0 | DBG_TRACE_DEVEL("leaving on L7 retry", |
1742 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
1743 | 0 | return 0; |
1744 | 0 | } |
1745 | | |
1746 | 0 | if (s->be_tgcounters) |
1747 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->failed_resp); |
1748 | 0 | if (s->sv_tgcounters) |
1749 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->failed_resp); |
1750 | |
|
1751 | 0 | txn->status = 502; |
1752 | 0 | stream_inc_http_fail_ctr(s); |
1753 | 0 | stream_report_term_evt(s->scb, strm_tevt_type_proto_err); |
1754 | | /* fall through */ |
1755 | |
|
1756 | 0 | return_prx_cond: |
1757 | 0 | http_set_term_flags(s); |
1758 | 0 | http_reply_and_close(s, txn->status, http_error_message(s)); |
1759 | |
|
1760 | 0 | s->scb->flags |= SC_FL_NOLINGER; |
1761 | 0 | DBG_TRACE_DEVEL("leaving on error", |
1762 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); |
1763 | 0 | return 0; |
1764 | | |
1765 | 0 | abort_keep_alive: |
1766 | | /* A keep-alive request to the server failed on a network error. |
1767 | | * The client is required to retry. We need to close without returning |
1768 | | * any other information so that the client retries. |
1769 | | */ |
1770 | 0 | txn->status = 0; |
1771 | 0 | s->logs.logwait = 0; |
1772 | 0 | s->logs.level = 0; |
1773 | 0 | s->scf->flags &= ~SC_FL_SND_EXP_MORE; /* speed up sending a previous response */ |
1774 | 0 | http_reply_and_close(s, txn->status, NULL); |
1775 | 0 | DBG_TRACE_DEVEL("leaving by closing K/A connection", |
1776 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
1777 | 0 | return 0; |
1778 | 0 | } |
1779 | | |
1780 | | /* This function performs all the processing enabled for the current response. |
1781 | | * It normally returns 1 unless it wants to break. It relies on buffers flags, |
1782 | | * and updates s->res.analysers. It might make sense to explode it into several |
1783 | | * other functions. It works like process_request (see indications above). |
1784 | | */ |
1785 | | int http_process_res_common(struct stream *s, struct channel *rep, int an_bit, struct proxy *px) |
1786 | 0 | { |
1787 | 0 | struct session *sess = s->sess; |
1788 | 0 | struct http_txn *txn = s->txn.http; |
1789 | 0 | struct http_msg *msg = &txn->rsp; |
1790 | 0 | struct htx *htx; |
1791 | 0 | struct proxy *cur_proxy; |
1792 | 0 | enum rule_result ret = HTTP_RULE_RES_CONT; |
1793 | 0 | uint8_t do_log = 0; |
1794 | |
|
1795 | 0 | if (unlikely(msg->msg_state < HTTP_MSG_BODY)) /* we need more data */ |
1796 | 0 | return 0; |
1797 | | |
1798 | 0 | DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn, msg); |
1799 | |
|
1800 | 0 | htx = htxbuf(&rep->buf); |
1801 | | |
1802 | | /* The stats applet needs to adjust the Connection header but we don't |
1803 | | * apply any filter there. |
1804 | | */ |
1805 | 0 | if (unlikely(objt_applet(s->target) == &http_stats_applet)) { |
1806 | 0 | rep->analysers &= ~an_bit; |
1807 | 0 | rep->analyse_exp = TICK_ETERNITY; |
1808 | 0 | goto end; |
1809 | 0 | } |
1810 | | |
1811 | | /* |
1812 | | * We will have to evaluate the filters. |
1813 | | * As opposed to version 1.2, now they will be evaluated in the |
1814 | | * filters order and not in the header order. This means that |
1815 | | * each filter has to be validated among all headers. |
1816 | | * |
1817 | | * Filters are tried with ->be first, then with ->fe if it is |
1818 | | * different from ->be. |
1819 | | * |
1820 | | * Maybe we are in resume condiion. In this case I choose the |
1821 | | * "struct proxy" which contains the rule list matching the resume |
1822 | | * pointer. If none of these "struct proxy" match, I initialise |
1823 | | * the process with the first one. |
1824 | | * |
1825 | | * In fact, I check only correspondence between the current list |
1826 | | * pointer and the ->fe rule list. If it doesn't match, I initialize |
1827 | | * the loop with the ->be. |
1828 | | */ |
1829 | 0 | if (s->current_rule_list == &sess->fe->http_res_rules || |
1830 | 0 | (sess->fe->defpx && s->current_rule_list == &sess->fe->defpx->http_res_rules)) |
1831 | 0 | cur_proxy = sess->fe; |
1832 | 0 | else |
1833 | 0 | cur_proxy = s->be; |
1834 | |
|
1835 | 0 | while (1) { |
1836 | | /* evaluate http-response rules */ |
1837 | 0 | if (ret == HTTP_RULE_RES_CONT || ret == HTTP_RULE_RES_STOP) { |
1838 | 0 | struct list *def_rules, *rules; |
1839 | |
|
1840 | 0 | def_rules = ((cur_proxy->defpx && (cur_proxy == s->be || cur_proxy->defpx != s->be->defpx)) ? &cur_proxy->defpx->http_res_rules : NULL); |
1841 | 0 | rules = &cur_proxy->http_res_rules; |
1842 | |
|
1843 | 0 | ret = http_res_get_intercept_rule(cur_proxy, def_rules, rules, s, 0); |
1844 | |
|
1845 | 0 | switch (ret) { |
1846 | 0 | case HTTP_RULE_RES_YIELD: /* some data miss, call the function later. */ |
1847 | 0 | goto return_prx_yield; |
1848 | | |
1849 | 0 | case HTTP_RULE_RES_FYIELD: /* we must try again after context-switch */ |
1850 | 0 | goto return_prx_fyield; |
1851 | | |
1852 | 0 | case HTTP_RULE_RES_CONT: |
1853 | 0 | case HTTP_RULE_RES_STOP: /* nothing to do */ |
1854 | 0 | break; |
1855 | | |
1856 | 0 | case HTTP_RULE_RES_DENY: /* deny or tarpit */ |
1857 | 0 | goto deny; |
1858 | | |
1859 | 0 | case HTTP_RULE_RES_ABRT: /* abort request, response already sent */ |
1860 | 0 | stream_report_term_evt(s->scb, strm_tevt_type_intercepted); |
1861 | 0 | goto return_prx_cond; |
1862 | | |
1863 | 0 | case HTTP_RULE_RES_DONE: /* OK, but terminate request processing (eg: redirect) */ |
1864 | 0 | goto done; |
1865 | | |
1866 | 0 | case HTTP_RULE_RES_BADREQ: /* failed with a bad request */ |
1867 | 0 | goto return_bad_res; |
1868 | | |
1869 | 0 | case HTTP_RULE_RES_ERROR: /* failed with a bad request */ |
1870 | 0 | goto return_int_err; |
1871 | 0 | } |
1872 | |
|
1873 | 0 | } |
1874 | | |
1875 | | /* check whether we're already working on the frontend */ |
1876 | 0 | if (cur_proxy == sess->fe) |
1877 | 0 | break; |
1878 | 0 | cur_proxy = sess->fe; |
1879 | 0 | } |
1880 | | |
1881 | | /* OK that's all we can do for 1xx responses */ |
1882 | 0 | if (unlikely(txn->status < 200 && txn->status != 101)) |
1883 | 0 | goto end; |
1884 | | |
1885 | | /* |
1886 | | * Now check for a server cookie. |
1887 | | */ |
1888 | 0 | if (s->be->cookie_name || sess->fe->capture_name || (s->be->options & PR_O_CHK_CACHE)) |
1889 | 0 | http_manage_server_side_cookies(s, rep); |
1890 | | |
1891 | | /* |
1892 | | * Check for cache-control or pragma headers if required. |
1893 | | */ |
1894 | 0 | if ((s->be->options & PR_O_CHK_CACHE) || (s->be->ck_opts & PR_CK_NOC)) |
1895 | 0 | http_check_response_for_cacheability(s, rep); |
1896 | | |
1897 | | /* |
1898 | | * Add server cookie in the response if needed |
1899 | | */ |
1900 | 0 | if (objt_server(s->target) && (s->be->ck_opts & PR_CK_INS) && |
1901 | 0 | !((txn->flags & TX_SCK_FOUND) && (s->be->ck_opts & PR_CK_PSV)) && |
1902 | 0 | (!(s->flags & SF_DIRECT) || |
1903 | 0 | ((s->be->cookie_maxidle || txn->cookie_last_date) && |
1904 | 0 | (!txn->cookie_last_date || (txn->cookie_last_date - date.tv_sec) < 0)) || |
1905 | 0 | (s->be->cookie_maxlife && !txn->cookie_first_date) || // set the first_date |
1906 | 0 | (!s->be->cookie_maxlife && txn->cookie_first_date)) && // remove the first_date |
1907 | 0 | (!(s->be->ck_opts & PR_CK_POST) || (txn->meth == HTTP_METH_POST)) && |
1908 | 0 | !(s->flags & SF_IGNORE_PRST)) { |
1909 | | /* the server is known, it's not the one the client requested, or the |
1910 | | * cookie's last seen date needs to be refreshed. We have to |
1911 | | * insert a set-cookie here, except if we want to insert only on POST |
1912 | | * requests and this one isn't. Note that servers which don't have cookies |
1913 | | * (eg: some backup servers) will return a full cookie removal request. |
1914 | | */ |
1915 | 0 | if (!__objt_server(s->target)->cookie) { |
1916 | 0 | chunk_printf(&trash, |
1917 | 0 | "%s=; Expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/", |
1918 | 0 | s->be->cookie_name); |
1919 | 0 | } |
1920 | 0 | else { |
1921 | 0 | chunk_printf(&trash, "%s=%s", s->be->cookie_name, __objt_server(s->target)->cookie); |
1922 | |
|
1923 | 0 | if (s->be->cookie_maxidle || s->be->cookie_maxlife) { |
1924 | | /* emit last_date, which is mandatory */ |
1925 | 0 | trash.area[trash.data++] = COOKIE_DELIM_DATE; |
1926 | 0 | s30tob64((date.tv_sec+3) >> 2, |
1927 | 0 | trash.area + trash.data); |
1928 | 0 | trash.data += 5; |
1929 | |
|
1930 | 0 | if (s->be->cookie_maxlife) { |
1931 | | /* emit first_date, which is either the original one or |
1932 | | * the current date. |
1933 | | */ |
1934 | 0 | trash.area[trash.data++] = COOKIE_DELIM_DATE; |
1935 | 0 | s30tob64(txn->cookie_first_date ? |
1936 | 0 | txn->cookie_first_date >> 2 : |
1937 | 0 | (date.tv_sec+3) >> 2, |
1938 | 0 | trash.area + trash.data); |
1939 | 0 | trash.data += 5; |
1940 | 0 | } |
1941 | 0 | } |
1942 | 0 | chunk_appendf(&trash, "; path=/"); |
1943 | 0 | } |
1944 | |
|
1945 | 0 | if (s->be->cookie_domain) |
1946 | 0 | chunk_appendf(&trash, "; domain=%s", s->be->cookie_domain); |
1947 | |
|
1948 | 0 | if (s->be->ck_opts & PR_CK_HTTPONLY) |
1949 | 0 | chunk_appendf(&trash, "; HttpOnly"); |
1950 | |
|
1951 | 0 | if (s->be->ck_opts & PR_CK_SECURE) |
1952 | 0 | chunk_appendf(&trash, "; Secure"); |
1953 | |
|
1954 | 0 | if (s->be->cookie_attrs) |
1955 | 0 | chunk_appendf(&trash, "; %s", s->be->cookie_attrs); |
1956 | |
|
1957 | 0 | if (unlikely(!http_add_header(htx, ist("Set-Cookie"), ist2(trash.area, trash.data), 0))) |
1958 | 0 | goto return_fail_rewrite; |
1959 | | |
1960 | 0 | txn->flags &= ~TX_SCK_MASK; |
1961 | 0 | if (__objt_server(s->target)->cookie && (s->flags & SF_DIRECT)) |
1962 | | /* the server did not change, only the date was updated */ |
1963 | 0 | txn->flags |= TX_SCK_UPDATED; |
1964 | 0 | else |
1965 | 0 | txn->flags |= TX_SCK_INSERTED; |
1966 | | |
1967 | | /* Here, we will tell an eventual cache on the client side that we don't |
1968 | | * want it to cache this reply because HTTP/1.0 caches also cache cookies ! |
1969 | | * Some caches understand the correct form: 'no-cache="set-cookie"', but |
1970 | | * others don't (eg: apache <= 1.3.26). So we use 'private' instead. |
1971 | | */ |
1972 | 0 | if ((s->be->ck_opts & PR_CK_NOC) && (txn->flags & TX_CACHEABLE)) { |
1973 | |
|
1974 | 0 | txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK; |
1975 | |
|
1976 | 0 | if (unlikely(!http_add_header(htx, ist("Cache-control"), ist("private"), 0))) |
1977 | 0 | goto return_fail_rewrite; |
1978 | 0 | } |
1979 | 0 | } |
1980 | | |
1981 | | /* |
1982 | | * Check if result will be cacheable with a cookie. |
1983 | | * We'll block the response if security checks have caught |
1984 | | * nasty things such as a cacheable cookie. |
1985 | | */ |
1986 | 0 | if (((txn->flags & (TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_PRESENT)) == |
1987 | 0 | (TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_PRESENT)) && |
1988 | 0 | (s->be->options & PR_O_CHK_CACHE)) { |
1989 | | /* we're in presence of a cacheable response containing |
1990 | | * a set-cookie header. We'll block it as requested by |
1991 | | * the 'checkcache' option, and send an alert. |
1992 | | */ |
1993 | 0 | ha_alert("Blocking cacheable cookie in response from instance %s, server %s.\n", |
1994 | 0 | s->be->id, objt_server(s->target) ? __objt_server(s->target)->id : "<dispatch>"); |
1995 | 0 | send_log(s->be, LOG_ALERT, |
1996 | 0 | "Blocking cacheable cookie in response from instance %s, server %s.\n", |
1997 | 0 | s->be->id, objt_server(s->target) ? __objt_server(s->target)->id : "<dispatch>"); |
1998 | 0 | goto deny; |
1999 | 0 | } |
2000 | | |
2001 | 0 | end: |
2002 | | /* |
2003 | | * Evaluate after-response rules before forwarding the response. rules |
2004 | | * from the backend are evaluated first, then one from the frontend if |
2005 | | * it differs. |
2006 | | */ |
2007 | 0 | if (!http_eval_after_res_rules(s)) |
2008 | 0 | goto return_int_err; |
2009 | | |
2010 | | /* Filter the response headers if there are filters attached to the |
2011 | | * stream. |
2012 | | */ |
2013 | 0 | if (HAS_FILTERS(s)) |
2014 | 0 | rep->analysers |= AN_RES_FLT_HTTP_HDRS; |
2015 | | |
2016 | | /* Always enter in the body analyzer */ |
2017 | 0 | rep->analysers &= ~AN_RES_FLT_XFER_DATA; |
2018 | 0 | rep->analysers |= AN_RES_HTTP_XFER_BODY; |
2019 | |
|
2020 | 0 | if (sess->fe->to_log == LW_LOGSTEPS) { |
2021 | 0 | if (log_orig_proxy(LOG_ORIG_TXN_RESPONSE, sess->fe)) |
2022 | 0 | do_log = 1; |
2023 | 0 | } |
2024 | 0 | else if ((!lf_expr_isempty(&sess->fe->logformat) && !(s->logs.logwait & LW_BYTES))) |
2025 | 0 | do_log = 1; |
2026 | | |
2027 | | /* if the user wants to log as soon as possible, without counting |
2028 | | * bytes from the server, then this is the right moment. We have |
2029 | | * to temporarily assign req_in to log what we currently have. |
2030 | | */ |
2031 | 0 | if (do_log) { |
2032 | 0 | s->logs.t_close = s->logs.t_data; /* to get a valid end date */ |
2033 | 0 | s->logs.res_in = htx->data; |
2034 | 0 | s->do_log(s, log_orig(LOG_ORIG_TXN_RESPONSE, LOG_ORIG_FL_NONE)); |
2035 | 0 | s->logs.res_in = 0; |
2036 | 0 | } |
2037 | |
|
2038 | 0 | done: |
2039 | 0 | DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
2040 | 0 | rep->analysers &= ~an_bit; |
2041 | 0 | rep->analyse_exp = TICK_ETERNITY; |
2042 | 0 | s->current_rule = s->current_rule_list = NULL; |
2043 | 0 | return 1; |
2044 | | |
2045 | 0 | deny: |
2046 | 0 | if (sess->fe_tgcounters) |
2047 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->denied_resp); |
2048 | 0 | if (s->be_tgcounters) |
2049 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->denied_resp); |
2050 | 0 | if (sess->li_tgcounters) |
2051 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->denied_resp); |
2052 | 0 | if (s->sv_tgcounters) |
2053 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->denied_resp); |
2054 | 0 | stream_report_term_evt(s->scb, strm_tevt_type_intercepted); |
2055 | 0 | goto return_prx_err; |
2056 | | |
2057 | 0 | return_fail_rewrite: |
2058 | 0 | if (!(s->flags & SF_ERR_MASK)) |
2059 | 0 | s->flags |= SF_ERR_PRXCOND; |
2060 | 0 | if (sess->fe_tgcounters) |
2061 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->failed_rewrites); |
2062 | 0 | if (s->be_tgcounters) |
2063 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->failed_rewrites); |
2064 | 0 | if (sess->li_tgcounters) |
2065 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->failed_rewrites); |
2066 | 0 | if (s->sv_tgcounters) |
2067 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->failed_rewrites); |
2068 | | /* fall through */ |
2069 | |
|
2070 | 0 | return_int_err: |
2071 | 0 | txn->status = 500; |
2072 | 0 | if (!(s->flags & SF_ERR_MASK)) |
2073 | 0 | s->flags |= SF_ERR_INTERNAL; |
2074 | 0 | if (sess->fe_tgcounters) |
2075 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->internal_errors); |
2076 | 0 | if (s->be_tgcounters) |
2077 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->internal_errors); |
2078 | 0 | if (sess->li_tgcounters) |
2079 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->internal_errors); |
2080 | 0 | if (s->sv_tgcounters) |
2081 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->internal_errors); |
2082 | 0 | stream_report_term_evt(s->scb, strm_tevt_type_internal_err); |
2083 | 0 | goto return_prx_err; |
2084 | | |
2085 | 0 | return_bad_res: |
2086 | 0 | s->logs.t_data = -1; /* was not a valid response */ |
2087 | 0 | txn->status = 502; |
2088 | 0 | stream_inc_http_fail_ctr(s); |
2089 | 0 | if (s->be_tgcounters) |
2090 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->failed_resp); |
2091 | 0 | if (objt_server(s->target)) { |
2092 | 0 | if (s->sv_tgcounters) |
2093 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->failed_resp); |
2094 | 0 | health_adjust(__objt_server(s->target), HANA_STATUS_HTTP_RSP); |
2095 | 0 | } |
2096 | 0 | stream_report_term_evt(s->scb, strm_tevt_type_proto_err); |
2097 | | /* fall through */ |
2098 | |
|
2099 | 0 | return_prx_err: |
2100 | 0 | http_set_term_flags(s); |
2101 | 0 | http_reply_and_close(s, txn->status, http_error_message(s)); |
2102 | | /* fall through */ |
2103 | |
|
2104 | 0 | return_prx_cond: |
2105 | 0 | s->scb->flags |= SC_FL_NOLINGER; |
2106 | 0 | http_set_term_flags(s); |
2107 | |
|
2108 | 0 | rep->analysers &= AN_RES_FLT_END; |
2109 | 0 | s->req.analysers &= AN_REQ_FLT_END; |
2110 | 0 | rep->analyse_exp = TICK_ETERNITY; |
2111 | 0 | s->current_rule = s->current_rule_list = NULL; |
2112 | 0 | DBG_TRACE_DEVEL("leaving on error", |
2113 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); |
2114 | 0 | return 0; |
2115 | | |
2116 | 0 | return_prx_yield: |
2117 | 0 | channel_dont_close(rep); |
2118 | 0 | DBG_TRACE_DEVEL("waiting for more data", |
2119 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
2120 | 0 | return 0; |
2121 | | |
2122 | 0 | return_prx_fyield: |
2123 | 0 | channel_dont_close(rep); |
2124 | 0 | DBG_TRACE_DEVEL("forced yield", |
2125 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
2126 | 0 | return 0; |
2127 | |
|
2128 | 0 | } |
2129 | | |
2130 | | /* This function is an analyser which forwards response body (including chunk |
2131 | | * sizes if any). It is called as soon as we must forward, even if we forward |
2132 | | * zero byte. The only situation where it must not be called is when we're in |
2133 | | * tunnel mode and we want to forward till the close. It's used both to forward |
2134 | | * remaining data and to resync after end of body. It expects the msg_state to |
2135 | | * be between MSG_BODY and MSG_DONE (inclusive). It returns zero if it needs to |
2136 | | * read more data, or 1 once we can go on with next request or end the stream. |
2137 | | * |
2138 | | * It is capable of compressing response data both in content-length mode and |
2139 | | * in chunked mode. The state machines follows different flows depending on |
2140 | | * whether content-length and chunked modes are used, since there are no |
2141 | | * trailers in content-length : |
2142 | | * |
2143 | | * chk-mode cl-mode |
2144 | | * ,----- BODY -----. |
2145 | | * / \ |
2146 | | * V size > 0 V chk-mode |
2147 | | * .--> SIZE -------------> DATA -------------> CRLF |
2148 | | * | | size == 0 | last byte | |
2149 | | * | v final crlf v inspected | |
2150 | | * | TRAILERS -----------> DONE | |
2151 | | * | | |
2152 | | * `----------------------------------------------' |
2153 | | * |
2154 | | * Compression only happens in the DATA state, and must be flushed in final |
2155 | | * states (TRAILERS/DONE) or when leaving on missing data. Normal forwarding |
2156 | | * is performed at once on final states for all bytes parsed, or when leaving |
2157 | | * on missing data. |
2158 | | */ |
2159 | | int http_response_forward_body(struct stream *s, struct channel *res, int an_bit) |
2160 | 0 | { |
2161 | 0 | struct session *sess = s->sess; |
2162 | 0 | struct http_txn *txn = s->txn.http; |
2163 | 0 | struct http_msg *msg = &s->txn.http->rsp; |
2164 | 0 | struct htx *htx; |
2165 | 0 | int ret; |
2166 | |
|
2167 | 0 | DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn, msg); |
2168 | |
|
2169 | 0 | htx = htxbuf(&res->buf); |
2170 | |
|
2171 | 0 | if (htx->flags & HTX_FL_PARSING_ERROR) |
2172 | 0 | goto return_bad_res; |
2173 | 0 | if (htx->flags & HTX_FL_PROCESSING_ERROR) |
2174 | 0 | goto return_int_err; |
2175 | | |
2176 | 0 | if (msg->msg_state == HTTP_MSG_BODY) |
2177 | 0 | msg->msg_state = HTTP_MSG_DATA; |
2178 | | |
2179 | | /* in most states, we should abort in case of early close */ |
2180 | 0 | channel_auto_close(res); |
2181 | |
|
2182 | 0 | if (res->to_forward) { |
2183 | 0 | if (res->to_forward == CHN_INFINITE_FORWARD) { |
2184 | 0 | if (s->scb->flags & SC_FL_EOI) |
2185 | 0 | msg->msg_state = HTTP_MSG_ENDING; |
2186 | 0 | } |
2187 | 0 | else { |
2188 | | /* We can't process the buffer's contents yet */ |
2189 | 0 | res->flags |= CF_WAKE_WRITE; |
2190 | 0 | goto missing_data_or_waiting; |
2191 | 0 | } |
2192 | 0 | } |
2193 | | |
2194 | 0 | if (msg->msg_state >= HTTP_MSG_ENDING) |
2195 | 0 | goto ending; |
2196 | | |
2197 | 0 | if ((txn->meth == HTTP_METH_CONNECT && txn->status >= 200 && txn->status < 300) || txn->status == 101 || |
2198 | 0 | (!(msg->flags & HTTP_MSGF_XFER_LEN) && !HAS_RSP_DATA_FILTERS(s))) { |
2199 | 0 | msg->msg_state = HTTP_MSG_ENDING; |
2200 | 0 | goto ending; |
2201 | 0 | } |
2202 | | |
2203 | | /* Forward input data. We get it by removing all outgoing data not |
2204 | | * forwarded yet from HTX data size. If there are some data filters, we |
2205 | | * let them decide the amount of data to forward. |
2206 | | */ |
2207 | 0 | if (HAS_RSP_DATA_FILTERS(s)) { |
2208 | 0 | ret = flt_http_payload(s, msg, htx->data); |
2209 | 0 | if (ret < 0) |
2210 | 0 | goto return_bad_res; |
2211 | 0 | c_adv(res, ret); |
2212 | 0 | } |
2213 | 0 | else { |
2214 | 0 | c_adv(res, htx->data - co_data(res)); |
2215 | 0 | if ((global.tune.options & GTUNE_USE_FAST_FWD) && (msg->flags & HTTP_MSGF_XFER_LEN)) |
2216 | 0 | channel_htx_forward_forever(res, htx); |
2217 | 0 | } |
2218 | | |
2219 | 0 | if (htx->data != co_data(res)) |
2220 | 0 | goto missing_data_or_waiting; |
2221 | | |
2222 | 0 | if (!(msg->flags & HTTP_MSGF_XFER_LEN) && (s->scb->flags & (SC_FL_EOS|SC_FL_ABRT_DONE))) { |
2223 | 0 | msg->msg_state = HTTP_MSG_ENDING; |
2224 | 0 | goto ending; |
2225 | 0 | } |
2226 | | |
2227 | | /* Check if the end-of-message is reached and if so, switch the message |
2228 | | * in HTTP_MSG_ENDING state. Then if all data was marked to be |
2229 | | * forwarded, set the state to HTTP_MSG_DONE. |
2230 | | */ |
2231 | 0 | if (!(htx->flags & HTX_FL_EOM)) |
2232 | 0 | goto missing_data_or_waiting; |
2233 | | |
2234 | 0 | msg->msg_state = HTTP_MSG_ENDING; |
2235 | |
|
2236 | 0 | ending: |
2237 | 0 | s->scf->flags &= ~SC_FL_SND_EXP_MORE; /* no more data are expected to be sent */ |
2238 | | |
2239 | | /* other states, ENDING...TUNNEL */ |
2240 | 0 | if (msg->msg_state >= HTTP_MSG_DONE) |
2241 | 0 | goto done; |
2242 | | |
2243 | 0 | if (HAS_RSP_DATA_FILTERS(s)) { |
2244 | 0 | ret = flt_http_end(s, msg); |
2245 | 0 | if (ret <= 0) { |
2246 | 0 | if (!ret) |
2247 | 0 | goto missing_data_or_waiting; |
2248 | 0 | goto return_bad_res; |
2249 | 0 | } |
2250 | 0 | } |
2251 | | |
2252 | 0 | if (!(txn->flags & TX_CON_WANT_TUN) && !(msg->flags & HTTP_MSGF_XFER_LEN)) { |
2253 | | /* One-side tunnel */ |
2254 | 0 | msg->msg_state = HTTP_MSG_TUNNEL; |
2255 | 0 | } |
2256 | 0 | else { |
2257 | 0 | msg->msg_state = HTTP_MSG_DONE; |
2258 | 0 | res->to_forward = 0; |
2259 | 0 | } |
2260 | |
|
2261 | 0 | done: |
2262 | |
|
2263 | 0 | channel_dont_close(res); |
2264 | |
|
2265 | 0 | if ((s->scf->flags & SC_FL_SHUT_DONE) && co_data(res)) { |
2266 | | /* response errors are most likely due to the client aborting |
2267 | | * the transfer. */ |
2268 | 0 | goto return_cli_abort; |
2269 | 0 | } |
2270 | | |
2271 | 0 | http_end_response(s); |
2272 | 0 | if (!(res->analysers & an_bit)) { |
2273 | 0 | DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
2274 | 0 | return 1; |
2275 | 0 | } |
2276 | 0 | DBG_TRACE_DEVEL("waiting for the end of the HTTP txn", |
2277 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
2278 | 0 | return 0; |
2279 | | |
2280 | 0 | missing_data_or_waiting: |
2281 | 0 | if (s->scf->flags & SC_FL_SHUT_DONE) |
2282 | 0 | goto return_cli_abort; |
2283 | | |
2284 | | /* stop waiting for data if the input is closed before the end. If the |
2285 | | * client side was already closed, it means that the client has aborted, |
2286 | | * so we don't want to count this as a server abort. Otherwise it's a |
2287 | | * server abort. |
2288 | | */ |
2289 | 0 | if (msg->msg_state < HTTP_MSG_ENDING && (s->scb->flags & (SC_FL_EOS|SC_FL_ABRT_DONE))) { |
2290 | 0 | if ((s->scf->flags & SC_FL_EOS) && (s->scb->flags & (SC_FL_ABRT_DONE|SC_FL_SHUT_DONE)) == (SC_FL_ABRT_DONE|SC_FL_SHUT_DONE)) |
2291 | 0 | goto return_cli_abort; |
2292 | | /* If we have some pending data, we continue the processing */ |
2293 | 0 | if (htx_is_empty(htx)) |
2294 | 0 | goto return_srv_abort; |
2295 | 0 | } |
2296 | | |
2297 | | /* When TE: chunked is used, we need to get there again to parse |
2298 | | * remaining chunks even if the server has closed, so we don't want to |
2299 | | * set CF_DONTCLOSE. Similarly when there is a content-leng or if there |
2300 | | * are filters registered on the stream, we don't want to forward a |
2301 | | * close |
2302 | | */ |
2303 | 0 | if ((msg->flags & HTTP_MSGF_XFER_LEN) || HAS_RSP_DATA_FILTERS(s)) |
2304 | 0 | channel_dont_close(res); |
2305 | | |
2306 | | /* We know that more data are expected, but we couldn't send more that |
2307 | | * what we did. So we always set the SC_FL_SND_EXP_MORE flag so that the |
2308 | | * system knows it must not set a PUSH on this first part. Interactive |
2309 | | * modes are already handled by the stream sock layer. We must not do |
2310 | | * this in content-length mode because it could present the MSG_MORE |
2311 | | * flag with the last block of forwarded data, which would cause an |
2312 | | * additional delay to be observed by the receiver. |
2313 | | */ |
2314 | 0 | if (HAS_RSP_DATA_FILTERS(s)) |
2315 | 0 | s->scf->flags |= SC_FL_SND_EXP_MORE; |
2316 | | |
2317 | | /* the stream handler will take care of timeouts and errors */ |
2318 | 0 | DBG_TRACE_DEVEL("waiting for more data to forward", |
2319 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn); |
2320 | 0 | return 0; |
2321 | | |
2322 | 0 | return_srv_abort: |
2323 | 0 | if (sess->fe_tgcounters) |
2324 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->srv_aborts); |
2325 | 0 | if (s->be_tgcounters) |
2326 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->srv_aborts); |
2327 | 0 | if (sess->li_tgcounters) |
2328 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->srv_aborts); |
2329 | 0 | if (s->sv_tgcounters) |
2330 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->srv_aborts); |
2331 | 0 | stream_inc_http_fail_ctr(s); |
2332 | 0 | if (!(s->flags & SF_ERR_MASK)) |
2333 | 0 | s->flags |= ((res->flags & CF_READ_TIMEOUT) ? SF_ERR_SRVTO : SF_ERR_SRVCL); |
2334 | 0 | goto return_error; |
2335 | | |
2336 | 0 | return_cli_abort: |
2337 | 0 | if (sess->fe_tgcounters) |
2338 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->cli_aborts); |
2339 | 0 | if (s->be_tgcounters) |
2340 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->cli_aborts); |
2341 | 0 | if (sess->li_tgcounters) |
2342 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->cli_aborts); |
2343 | 0 | if (s->sv_tgcounters) |
2344 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->cli_aborts); |
2345 | 0 | if (!(s->flags & SF_ERR_MASK)) |
2346 | 0 | s->flags |= ((res->flags & CF_WRITE_TIMEOUT) ? SF_ERR_CLITO : SF_ERR_CLICL); |
2347 | 0 | goto return_error; |
2348 | | |
2349 | 0 | return_int_err: |
2350 | 0 | if (sess->fe_tgcounters) |
2351 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->internal_errors); |
2352 | 0 | if (s->be_tgcounters) |
2353 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->internal_errors); |
2354 | 0 | if (sess->li_tgcounters) |
2355 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->internal_errors); |
2356 | 0 | if (s->sv_tgcounters) |
2357 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->internal_errors); |
2358 | 0 | if (!(s->flags & SF_ERR_MASK)) |
2359 | 0 | s->flags |= SF_ERR_INTERNAL; |
2360 | 0 | stream_report_term_evt(s->scb, strm_tevt_type_internal_err); |
2361 | 0 | goto return_error; |
2362 | | |
2363 | 0 | return_bad_res: |
2364 | 0 | if (s->be_tgcounters) |
2365 | 0 | _HA_ATOMIC_INC(&s->be_tgcounters->failed_resp); |
2366 | 0 | if (objt_server(s->target)) { |
2367 | 0 | if (s->sv_tgcounters) |
2368 | 0 | _HA_ATOMIC_INC(&s->sv_tgcounters->failed_resp); |
2369 | 0 | health_adjust(__objt_server(s->target), HANA_STATUS_HTTP_RSP); |
2370 | 0 | } |
2371 | 0 | stream_inc_http_fail_ctr(s); |
2372 | 0 | stream_report_term_evt(s->scb, strm_tevt_type_proto_err); |
2373 | | /* fall through */ |
2374 | |
|
2375 | 0 | return_error: |
2376 | | /* don't send any error message as we're in the body */ |
2377 | 0 | http_set_term_flags(s); |
2378 | 0 | http_reply_and_close(s, txn->status, NULL); |
2379 | 0 | stream_inc_http_fail_ctr(s); |
2380 | 0 | DBG_TRACE_DEVEL("leaving on error", |
2381 | 0 | STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); |
2382 | 0 | return 0; |
2383 | 0 | } |
2384 | | |
2385 | | /* Perform an HTTP redirect based on the information in <rule>. The function |
2386 | | * returns zero in case of an irrecoverable error such as too large a request |
2387 | | * to build a valid response, 1 in case of successful redirect (hence the rule |
2388 | | * is final), or 2 if the rule has to be silently skipped. |
2389 | | */ |
2390 | | int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s, struct http_txn *txn) |
2391 | 0 | { |
2392 | 0 | struct channel *req = &s->req; |
2393 | 0 | struct channel *res = &s->res; |
2394 | 0 | struct htx *htx; |
2395 | 0 | struct htx_sl *sl; |
2396 | 0 | struct buffer *chunk; |
2397 | 0 | struct ist status, reason, location; |
2398 | 0 | unsigned int flags; |
2399 | 0 | int ret = 1; |
2400 | |
|
2401 | 0 | chunk = alloc_trash_chunk(); |
2402 | 0 | if (!chunk) { |
2403 | 0 | if (!(s->flags & SF_ERR_MASK)) |
2404 | 0 | s->flags |= SF_ERR_RESOURCE; |
2405 | 0 | goto fail; |
2406 | 0 | } |
2407 | | |
2408 | | /* |
2409 | | * Create the location |
2410 | | */ |
2411 | 0 | htx = htxbuf(&req->buf); |
2412 | 0 | switch(rule->type) { |
2413 | 0 | case REDIRECT_TYPE_SCHEME: { |
2414 | 0 | struct http_hdr_ctx ctx; |
2415 | 0 | struct ist path, host; |
2416 | 0 | struct http_uri_parser parser; |
2417 | |
|
2418 | 0 | host = ist(""); |
2419 | 0 | ctx.blk = NULL; |
2420 | 0 | if (http_find_header(htx, ist("Host"), &ctx, 0)) |
2421 | 0 | host = ctx.value; |
2422 | |
|
2423 | 0 | sl = http_get_stline(htx); |
2424 | 0 | parser = http_uri_parser_init(htx_sl_req_uri(sl)); |
2425 | 0 | path = http_parse_path(&parser); |
2426 | | /* build message using path */ |
2427 | 0 | if (isttest(path)) { |
2428 | 0 | if (rule->flags & REDIRECT_FLAG_DROP_QS) { |
2429 | 0 | int qs = 0; |
2430 | 0 | while (qs < path.len) { |
2431 | 0 | if (*(path.ptr + qs) == '?') { |
2432 | 0 | path.len = qs; |
2433 | 0 | break; |
2434 | 0 | } |
2435 | 0 | qs++; |
2436 | 0 | } |
2437 | 0 | } |
2438 | 0 | } |
2439 | 0 | else |
2440 | 0 | path = ist("/"); |
2441 | |
|
2442 | 0 | if (rule->rdr_str) { /* this is an old "redirect" rule */ |
2443 | | /* add scheme */ |
2444 | 0 | if (!chunk_memcat(chunk, rule->rdr_str, rule->rdr_len)) |
2445 | 0 | goto fail; |
2446 | 0 | } |
2447 | 0 | else { |
2448 | | /* add scheme with executing log format */ |
2449 | 0 | chunk->data += build_logline(s, chunk->area + chunk->data, |
2450 | 0 | chunk->size - chunk->data, |
2451 | 0 | &rule->rdr_fmt); |
2452 | 0 | } |
2453 | | /* add "://" + host + path */ |
2454 | 0 | if (!chunk_memcat(chunk, "://", 3) || |
2455 | 0 | !chunk_memcat(chunk, host.ptr, host.len) || |
2456 | 0 | !chunk_memcat(chunk, path.ptr, path.len)) |
2457 | 0 | goto fail; |
2458 | | |
2459 | | /* append a slash at the end of the location if needed and missing */ |
2460 | 0 | if (chunk->data && chunk->area[chunk->data - 1] != '/' && |
2461 | 0 | (rule->flags & REDIRECT_FLAG_APPEND_SLASH)) { |
2462 | 0 | if (chunk->data + 1 >= chunk->size) |
2463 | 0 | goto fail; |
2464 | 0 | chunk->area[chunk->data++] = '/'; |
2465 | 0 | } |
2466 | 0 | break; |
2467 | 0 | } |
2468 | | |
2469 | 0 | case REDIRECT_TYPE_PREFIX: { |
2470 | 0 | struct ist path; |
2471 | 0 | struct http_uri_parser parser; |
2472 | |
|
2473 | 0 | sl = http_get_stline(htx); |
2474 | 0 | parser = http_uri_parser_init(htx_sl_req_uri(sl)); |
2475 | 0 | path = http_parse_path(&parser); |
2476 | | /* build message using path */ |
2477 | 0 | if (isttest(path)) { |
2478 | 0 | if (rule->flags & REDIRECT_FLAG_DROP_QS) { |
2479 | 0 | int qs = 0; |
2480 | 0 | while (qs < path.len) { |
2481 | 0 | if (*(path.ptr + qs) == '?') { |
2482 | 0 | path.len = qs; |
2483 | 0 | break; |
2484 | 0 | } |
2485 | 0 | qs++; |
2486 | 0 | } |
2487 | 0 | } |
2488 | 0 | } |
2489 | 0 | else |
2490 | 0 | path = ist("/"); |
2491 | |
|
2492 | 0 | if (rule->rdr_str) { /* this is an old "redirect" rule */ |
2493 | | /* add prefix. Note that if prefix == "/", we don't want to |
2494 | | * add anything, otherwise it makes it hard for the user to |
2495 | | * configure a self-redirection. |
2496 | | */ |
2497 | 0 | if (rule->rdr_len != 1 || *rule->rdr_str != '/') { |
2498 | 0 | if (!chunk_memcat(chunk, rule->rdr_str, rule->rdr_len)) |
2499 | 0 | goto fail; |
2500 | 0 | } |
2501 | 0 | } |
2502 | 0 | else { |
2503 | | /* add prefix with executing log format */ |
2504 | 0 | chunk->data += build_logline(s, chunk->area + chunk->data, |
2505 | 0 | chunk->size - chunk->data, |
2506 | 0 | &rule->rdr_fmt); |
2507 | 0 | } |
2508 | | |
2509 | | /* add path */ |
2510 | 0 | if (!chunk_memcat(chunk, path.ptr, path.len)) |
2511 | 0 | goto fail; |
2512 | | |
2513 | | /* append a slash at the end of the location if needed and missing */ |
2514 | 0 | if (chunk->data && chunk->area[chunk->data - 1] != '/' && |
2515 | 0 | (rule->flags & REDIRECT_FLAG_APPEND_SLASH)) { |
2516 | 0 | if (chunk->data + 1 >= chunk->size) |
2517 | 0 | goto fail; |
2518 | 0 | chunk->area[chunk->data++] = '/'; |
2519 | 0 | } |
2520 | 0 | break; |
2521 | 0 | } |
2522 | 0 | case REDIRECT_TYPE_LOCATION: |
2523 | 0 | default: |
2524 | 0 | if (rule->rdr_str) { /* this is an old "redirect" rule */ |
2525 | | /* add location */ |
2526 | 0 | if (!chunk_memcat(chunk, rule->rdr_str, rule->rdr_len)) |
2527 | 0 | goto fail; |
2528 | 0 | } |
2529 | 0 | else { |
2530 | | /* add location with executing log format */ |
2531 | 0 | size_t len = build_logline(s, chunk->area + chunk->data, |
2532 | 0 | chunk->size - chunk->data, |
2533 | 0 | &rule->rdr_fmt); |
2534 | 0 | if (!len && (rule->flags & REDIRECT_FLAG_IGNORE_EMPTY)) { |
2535 | 0 | ret = 2; |
2536 | 0 | goto out; |
2537 | 0 | } |
2538 | | |
2539 | 0 | chunk->data += len; |
2540 | 0 | } |
2541 | | |
2542 | 0 | if (rule->flags & REDIRECT_FLAG_KEEP_QS) { |
2543 | 0 | struct ist path; |
2544 | 0 | struct http_uri_parser parser; |
2545 | 0 | char *ptr, *end; |
2546 | 0 | char sep = '?'; |
2547 | |
|
2548 | 0 | ptr = memchr(chunk->area, '?', chunk->data); |
2549 | 0 | if (ptr != NULL) |
2550 | 0 | sep = ((ptr+1 != b_tail(chunk)) ? '&' : '\0'); |
2551 | |
|
2552 | 0 | sl = http_get_stline(htx); |
2553 | 0 | parser = http_uri_parser_init(htx_sl_req_uri(sl)); |
2554 | 0 | path = http_parse_path(&parser); |
2555 | 0 | ptr = istptr(path); |
2556 | 0 | end = istend(path); |
2557 | | |
2558 | | /* look up the '?' */ |
2559 | 0 | do { |
2560 | 0 | if (ptr == end) |
2561 | 0 | break; |
2562 | 0 | } while (*ptr++ != '?'); |
2563 | |
|
2564 | 0 | if (ptr == end) |
2565 | 0 | break; |
2566 | 0 | if (sep != '\0' && !chunk_memcat(chunk, &sep, 1)) |
2567 | 0 | goto fail; |
2568 | 0 | if (!chunk_memcat(chunk, ptr, end-ptr)) |
2569 | 0 | goto fail; |
2570 | 0 | } |
2571 | 0 | break; |
2572 | 0 | } |
2573 | 0 | location = ist2(chunk->area, chunk->data); |
2574 | | |
2575 | | /* |
2576 | | * Create the 30x response |
2577 | | */ |
2578 | 0 | switch (rule->code) { |
2579 | 0 | case 308: |
2580 | 0 | status = ist("308"); |
2581 | 0 | reason = ist("Permanent Redirect"); |
2582 | 0 | break; |
2583 | 0 | case 307: |
2584 | 0 | status = ist("307"); |
2585 | 0 | reason = ist("Temporary Redirect"); |
2586 | 0 | break; |
2587 | 0 | case 303: |
2588 | 0 | status = ist("303"); |
2589 | 0 | reason = ist("See Other"); |
2590 | 0 | break; |
2591 | 0 | case 301: |
2592 | 0 | status = ist("301"); |
2593 | 0 | reason = ist("Moved Permanently"); |
2594 | 0 | break; |
2595 | 0 | case 302: |
2596 | 0 | default: |
2597 | 0 | status = ist("302"); |
2598 | 0 | reason = ist("Found"); |
2599 | 0 | break; |
2600 | 0 | } |
2601 | | |
2602 | 0 | htx = htx_from_buf(&res->buf); |
2603 | | /* Trim any possible response */ |
2604 | 0 | channel_htx_truncate(&s->res, htx); |
2605 | 0 | flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN|HTX_SL_F_BODYLESS); |
2606 | 0 | sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.1"), status, reason); |
2607 | 0 | if (!sl) |
2608 | 0 | goto fail; |
2609 | 0 | sl->info.res.status = rule->code; |
2610 | 0 | s->txn.http->status = rule->code; |
2611 | |
|
2612 | 0 | if (!htx_add_header(htx, ist("Content-length"), ist("0")) || |
2613 | 0 | !htx_add_header(htx, ist("Location"), location)) |
2614 | 0 | goto fail; |
2615 | | |
2616 | 0 | if (rule->code == 302 || rule->code == 303 || rule->code == 307) { |
2617 | 0 | if (!htx_add_header(htx, ist("Cache-Control"), ist("no-cache"))) |
2618 | 0 | goto fail; |
2619 | 0 | } |
2620 | | |
2621 | 0 | if (rule->flags & REDIRECT_FLAG_COOKIE_FMT) { |
2622 | 0 | trash.data = build_logline(s, trash.area, trash.size, &rule->cookie.fmt); |
2623 | 0 | if (!htx_add_header(htx, ist("Set-Cookie"), ist2(trash.area, trash.data))) |
2624 | 0 | goto fail; |
2625 | 0 | } |
2626 | 0 | else if (isttest(rule->cookie.str)) { |
2627 | 0 | if (!htx_add_header(htx, ist("Set-Cookie"), rule->cookie.str)) |
2628 | 0 | goto fail; |
2629 | 0 | } |
2630 | | |
2631 | 0 | if (!htx_add_endof(htx, HTX_BLK_EOH)) |
2632 | 0 | goto fail; |
2633 | | |
2634 | 0 | htx->flags |= HTX_FL_EOM; |
2635 | 0 | htx_to_buf(htx, &res->buf); |
2636 | |
|
2637 | 0 | if (!(s->flags & SF_ERR_MASK)) |
2638 | 0 | s->flags |= SF_ERR_LOCAL; |
2639 | 0 | http_set_term_flags(s); |
2640 | |
|
2641 | 0 | if (!http_forward_proxy_resp(s, 1)) |
2642 | 0 | goto fail; |
2643 | | |
2644 | 0 | if (rule->flags & REDIRECT_FLAG_FROM_REQ) { |
2645 | | /* let's log the request time */ |
2646 | 0 | s->logs.request_ts = now_ns; |
2647 | 0 | req->analysers &= AN_REQ_FLT_END; |
2648 | | |
2649 | | /* report it if the request was intercepted by the frontend */ |
2650 | 0 | if (s->sess->fe == s->be && s->sess->fe_tgcounters) |
2651 | 0 | _HA_ATOMIC_INC(&s->sess->fe_tgcounters->intercepted_req); |
2652 | 0 | } |
2653 | |
|
2654 | 0 | out: |
2655 | 0 | free_trash_chunk(chunk); |
2656 | 0 | return ret; |
2657 | | |
2658 | 0 | fail: |
2659 | | /* If an error occurred, remove the incomplete HTTP response from the |
2660 | | * buffer */ |
2661 | 0 | channel_htx_truncate(res, htxbuf(&res->buf)); |
2662 | 0 | ret = 0; |
2663 | 0 | goto out; |
2664 | 0 | } |
2665 | | |
2666 | | /* This function filters the request header names to only allow [0-9a-zA-Z-] |
2667 | | * characters. Depending on the proxy configuration, headers with a name not |
2668 | | * matching this charset are removed or the request is rejected with a |
2669 | | * 403-Forbidden response if such name are found. It returns HTTP_RULE_RES_CONT |
2670 | | * to continue the request processing or HTTP_RULE_RES_DENY if the request is |
2671 | | * rejected. |
2672 | | */ |
2673 | | static enum rule_result http_req_restrict_header_names(struct stream *s, struct htx *htx, struct proxy *px) |
2674 | 0 | { |
2675 | 0 | struct htx_blk *blk; |
2676 | 0 | enum rule_result rule_ret = HTTP_RULE_RES_CONT; |
2677 | |
|
2678 | 0 | blk = htx_get_first_blk(htx); |
2679 | 0 | while (blk) { |
2680 | 0 | enum htx_blk_type type = htx_get_blk_type(blk); |
2681 | |
|
2682 | 0 | if (type == HTX_BLK_HDR) { |
2683 | 0 | struct ist n = htx_get_blk_name(htx, blk); |
2684 | 0 | int i, end = istlen(n); |
2685 | |
|
2686 | 0 | for (i = 0; i < end; i++) { |
2687 | 0 | if (!isalnum((unsigned char)n.ptr[i]) && n.ptr[i] != '-') { |
2688 | 0 | break; |
2689 | 0 | } |
2690 | 0 | } |
2691 | |
|
2692 | 0 | if (i < end) { |
2693 | | /* Disallowed character found - block the request or remove the header */ |
2694 | 0 | if (px->options2 & PR_O2_RSTRICT_REQ_HDR_NAMES_BLK) |
2695 | 0 | goto block; |
2696 | 0 | blk = htx_remove_blk(htx, blk); |
2697 | 0 | continue; |
2698 | 0 | } |
2699 | 0 | } |
2700 | 0 | if (type == HTX_BLK_EOH) |
2701 | 0 | break; |
2702 | | |
2703 | 0 | blk = htx_get_next_blk(htx, blk); |
2704 | 0 | } |
2705 | 0 | out: |
2706 | 0 | return rule_ret; |
2707 | 0 | block: |
2708 | | /* Block the request returning a 403-Forbidden response */ |
2709 | 0 | s->txn.http->status = 403; |
2710 | 0 | rule_ret = HTTP_RULE_RES_DENY; |
2711 | 0 | goto out; |
2712 | 0 | } |
2713 | | |
2714 | | /* Replace all headers matching the name <name>. The header value is replaced if |
2715 | | * it matches the regex <re>. <str> is used for the replacement. If <full> is |
2716 | | * set to 1, the full-line is matched and replaced. Otherwise, comma-separated |
2717 | | * values are evaluated one by one. It returns 0 on success and -1 on error. |
2718 | | */ |
2719 | | int http_replace_hdrs(struct stream* s, struct htx *htx, struct ist name, |
2720 | | const char *str, struct my_regex *re, int full) |
2721 | 0 | { |
2722 | 0 | struct http_hdr_ctx ctx; |
2723 | |
|
2724 | 0 | ctx.blk = NULL; |
2725 | 0 | while (http_find_header(htx, name, &ctx, full)) { |
2726 | 0 | struct buffer *output = get_trash_chunk(); |
2727 | |
|
2728 | 0 | if (!regex_exec_match2(re, ctx.value.ptr, ctx.value.len, MAX_MATCH, pmatch, 0)) |
2729 | 0 | continue; |
2730 | | |
2731 | 0 | output->data = exp_replace(output->area, output->size, ctx.value.ptr, str, pmatch); |
2732 | 0 | if (output->data == -1) |
2733 | 0 | return -1; |
2734 | 0 | if (!http_replace_header_value(htx, &ctx, ist2(output->area, output->data), 1)) |
2735 | 0 | return -1; |
2736 | 0 | } |
2737 | 0 | return 0; |
2738 | 0 | } |
2739 | | |
2740 | | /* This function executes one of the set-{method,path,query,uri} actions. It |
2741 | | * takes the string from the variable 'replace' with length 'len', then modifies |
2742 | | * the relevant part of the request line accordingly. Then it updates various |
2743 | | * pointers to the next elements which were moved, and the total buffer length. |
2744 | | * It finds the action to be performed in p[2], previously filled by function |
2745 | | * parse_set_req_line(). It returns 0 in case of success, -1 in case of internal |
2746 | | * error, though this can be revisited when this code is finally exploited. |
2747 | | * |
2748 | | * 'action' can be '0' to replace method, '1' to replace path, '2' to replace |
2749 | | * query string, 3 to replace uri or 4 to replace the path+query. |
2750 | | * |
2751 | | * In query string case, the mark question '?' must be set at the start of the |
2752 | | * string by the caller, event if the replacement query string is empty. |
2753 | | */ |
2754 | | int http_req_replace_stline(int action, const char *replace, int len, |
2755 | | struct proxy *px, struct stream *s) |
2756 | 0 | { |
2757 | 0 | struct htx *htx = htxbuf(&s->req.buf); |
2758 | |
|
2759 | 0 | switch (action) { |
2760 | 0 | case 0: // method |
2761 | 0 | if (!http_replace_req_meth(htx, ist2(replace, len))) |
2762 | 0 | return -1; |
2763 | 0 | break; |
2764 | | |
2765 | 0 | case 1: // path |
2766 | 0 | if (!http_replace_req_path(htx, ist2(replace, len), 0)) |
2767 | 0 | return -1; |
2768 | 0 | break; |
2769 | | |
2770 | 0 | case 2: // query |
2771 | 0 | if (!http_replace_req_query(htx, ist2(replace, len))) |
2772 | 0 | return -1; |
2773 | 0 | break; |
2774 | | |
2775 | 0 | case 3: // uri |
2776 | 0 | if (!http_replace_req_uri(htx, ist2(replace, len))) |
2777 | 0 | return -1; |
2778 | 0 | break; |
2779 | | |
2780 | 0 | case 4: // path + query |
2781 | 0 | if (!http_replace_req_path(htx, ist2(replace, len), 1)) |
2782 | 0 | return -1; |
2783 | 0 | break; |
2784 | | |
2785 | 0 | default: |
2786 | 0 | return -1; |
2787 | 0 | } |
2788 | 0 | return 0; |
2789 | 0 | } |
2790 | | |
2791 | | /* This function replace the HTTP status code and the associated message. The |
2792 | | * variable <status> contains the new status code. This function never fails. It |
2793 | | * returns 0 in case of success, -1 in case of internal error. |
2794 | | */ |
2795 | | int http_res_set_status(unsigned int status, struct ist reason, struct stream *s) |
2796 | 0 | { |
2797 | 0 | struct htx *htx = htxbuf(&s->res.buf); |
2798 | 0 | char *res; |
2799 | |
|
2800 | 0 | chunk_reset(&trash); |
2801 | 0 | res = ultoa_o(status, trash.area, trash.size); |
2802 | 0 | trash.data = res - trash.area; |
2803 | | |
2804 | | /* Do we have a custom reason format string? */ |
2805 | 0 | if (!isttest(reason)) { |
2806 | 0 | const char *str = http_get_reason(status); |
2807 | 0 | reason = ist(str); |
2808 | 0 | } |
2809 | |
|
2810 | 0 | if (!http_replace_res_status(htx, ist2(trash.area, trash.data), reason)) |
2811 | 0 | return -1; |
2812 | 0 | s->txn.http->status = status; |
2813 | 0 | return 0; |
2814 | 0 | } |
2815 | | |
2816 | | /* Executes the http-request rules <rules> for stream <s>, proxy <px> and |
2817 | | * transaction <txn>. Returns the verdict of the first rule that prevents |
2818 | | * further processing of the request (auth, deny, ...), and defaults to |
2819 | | * HTTP_RULE_RES_STOP if it executed all rules or stopped on an allow, or |
2820 | | * HTTP_RULE_RES_CONT if the last rule was reached. It may set the TX_CLTARPIT |
2821 | | * on txn->flags if it encounters a tarpit rule. If <deny_status> is not NULL |
2822 | | * and a deny/tarpit rule is matched, it will be filled with this rule's deny |
2823 | | * status. |
2824 | | */ |
2825 | | static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct list *def_rules, |
2826 | | struct list *rules, struct stream *s) |
2827 | 0 | { |
2828 | 0 | struct session *sess = strm_sess(s); |
2829 | 0 | struct http_txn *txn = s->txn.http; |
2830 | 0 | struct act_rule *rule; |
2831 | 0 | enum rule_result rule_ret = HTTP_RULE_RES_CONT; |
2832 | 0 | int act_opts = 0; |
2833 | |
|
2834 | 0 | if ((s->scf->flags & SC_FL_ERROR) || |
2835 | 0 | ((s->scf->flags & SC_FL_EOS) && proxy_abrt_close_def(px, 1))) |
2836 | 0 | act_opts |= ACT_OPT_FINAL | ACT_OPT_FINAL_EARLY; |
2837 | | |
2838 | | /* If "the current_rule_list" match the executed rule list, we are in |
2839 | | * resume condition. If a resume is needed it is always in the action |
2840 | | * and never in the ACL or converters. In this case, we initialise the |
2841 | | * current rule, and go to the action execution point. |
2842 | | */ |
2843 | 0 | if (s->current_rule) { |
2844 | 0 | int forced = s->flags & SF_RULE_FYIELD; |
2845 | |
|
2846 | 0 | rule = s->current_rule; |
2847 | 0 | s->flags &= ~SF_RULE_FYIELD; |
2848 | 0 | if (s->current_rule_list == rules || (def_rules && s->current_rule_list == def_rules)) { |
2849 | 0 | if (forced) |
2850 | 0 | goto resume_rule; |
2851 | 0 | goto resume_execution; |
2852 | 0 | } |
2853 | 0 | } |
2854 | 0 | s->current_rule_list = ((!def_rules || s->current_rule_list == def_rules) ? rules : def_rules); |
2855 | |
|
2856 | 0 | restart: |
2857 | | /* start the ruleset evaluation in strict mode */ |
2858 | 0 | txn->req.flags &= ~HTTP_MSGF_SOFT_RW; |
2859 | |
|
2860 | 0 | list_for_each_entry(rule, s->current_rule_list, list) { |
2861 | 0 | resume_rule: |
2862 | 0 | s->current_rule = rule; |
2863 | | |
2864 | | /* check if budget is exceeded and we need to continue on the next |
2865 | | * polling loop, unless we know that we cannot yield |
2866 | | */ |
2867 | 0 | if (s->rules_bcount++ >= global.tune.max_rules_at_once && !(act_opts & ACT_OPT_FINAL)) { |
2868 | 0 | s->flags |= SF_RULE_FYIELD; |
2869 | 0 | rule_ret = HTTP_RULE_RES_FYIELD; |
2870 | 0 | task_wakeup(s->task, TASK_WOKEN_MSG); |
2871 | 0 | goto end; |
2872 | 0 | } |
2873 | | |
2874 | | /* check optional condition */ |
2875 | 0 | if (!acl_match_cond(rule->cond, px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL)) |
2876 | 0 | continue; |
2877 | | |
2878 | 0 | act_opts |= ACT_OPT_FIRST; |
2879 | 0 | resume_execution: |
2880 | 0 | if (rule->kw->flags & KWF_EXPERIMENTAL) |
2881 | 0 | mark_tainted(TAINTED_ACTION_EXP_EXECUTED); |
2882 | | |
2883 | | /* Always call the action function if defined */ |
2884 | 0 | if (rule->action_ptr) { |
2885 | 0 | if (!(s->scf->flags & SC_FL_ERROR) && !(s->req.flags & (CF_READ_TIMEOUT|CF_WRITE_TIMEOUT))) { |
2886 | 0 | s->waiting_entity.type = STRM_ENTITY_NONE; |
2887 | 0 | s->waiting_entity.ptr = NULL; |
2888 | 0 | } |
2889 | |
|
2890 | 0 | switch (EXEC_CTX_WITH_RET(rule->exec_ctx, |
2891 | 0 | rule->action_ptr(rule, px, sess, s, act_opts))) { |
2892 | 0 | case ACT_RET_CONT: |
2893 | 0 | break; |
2894 | 0 | case ACT_RET_STOP: |
2895 | 0 | rule_ret = HTTP_RULE_RES_STOP; |
2896 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
2897 | 0 | s->last_entity.ptr = rule; |
2898 | 0 | goto end; |
2899 | 0 | case ACT_RET_YIELD: |
2900 | 0 | if (act_opts & ACT_OPT_FINAL) { |
2901 | 0 | send_log(s->be, LOG_WARNING, |
2902 | 0 | "Internal error: action yields while it is no long allowed " |
2903 | 0 | "for the http-request actions."); |
2904 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
2905 | 0 | s->last_entity.ptr = rule; |
2906 | 0 | rule_ret = HTTP_RULE_RES_ERROR; |
2907 | 0 | goto end; |
2908 | 0 | } |
2909 | 0 | s->waiting_entity.type = STRM_ENTITY_RULE; |
2910 | 0 | s->waiting_entity.ptr = rule; |
2911 | 0 | rule_ret = HTTP_RULE_RES_YIELD; |
2912 | 0 | goto end; |
2913 | 0 | case ACT_RET_ERR: |
2914 | 0 | rule_ret = HTTP_RULE_RES_ERROR; |
2915 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
2916 | 0 | s->last_entity.ptr = rule; |
2917 | 0 | goto end; |
2918 | 0 | case ACT_RET_DONE: |
2919 | 0 | rule_ret = HTTP_RULE_RES_DONE; |
2920 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
2921 | 0 | s->last_entity.ptr = rule; |
2922 | 0 | goto end; |
2923 | 0 | case ACT_RET_DENY: |
2924 | 0 | if (txn->status == -1) |
2925 | 0 | txn->status = 403; |
2926 | 0 | rule_ret = HTTP_RULE_RES_DENY; |
2927 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
2928 | 0 | s->last_entity.ptr = rule; |
2929 | 0 | goto end; |
2930 | 0 | case ACT_RET_ABRT: |
2931 | 0 | rule_ret = HTTP_RULE_RES_ABRT; |
2932 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
2933 | 0 | s->last_entity.ptr = rule; |
2934 | 0 | goto end; |
2935 | 0 | case ACT_RET_INV: |
2936 | 0 | rule_ret = HTTP_RULE_RES_BADREQ; |
2937 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
2938 | 0 | s->last_entity.ptr = rule; |
2939 | 0 | goto end; |
2940 | 0 | } |
2941 | 0 | continue; /* eval the next rule */ |
2942 | 0 | } |
2943 | | |
2944 | | /* If not action function defined, check for known actions */ |
2945 | 0 | switch (rule->action) { |
2946 | 0 | case ACT_ACTION_ALLOW: |
2947 | 0 | rule_ret = HTTP_RULE_RES_STOP; |
2948 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
2949 | 0 | s->last_entity.ptr = rule; |
2950 | 0 | goto end; |
2951 | | |
2952 | 0 | case ACT_ACTION_DENY: |
2953 | 0 | txn->status = rule->arg.http_reply->status; |
2954 | 0 | txn->http_reply = rule->arg.http_reply; |
2955 | 0 | rule_ret = HTTP_RULE_RES_DENY; |
2956 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
2957 | 0 | s->last_entity.ptr = rule; |
2958 | 0 | goto end; |
2959 | | |
2960 | 0 | case ACT_HTTP_REQ_TARPIT: |
2961 | 0 | txn->flags |= TX_CLTARPIT; |
2962 | 0 | txn->status = rule->arg.http_reply->status; |
2963 | 0 | txn->http_reply = rule->arg.http_reply; |
2964 | 0 | rule_ret = HTTP_RULE_RES_DENY; |
2965 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
2966 | 0 | s->last_entity.ptr = rule; |
2967 | 0 | goto end; |
2968 | | |
2969 | 0 | case ACT_HTTP_REDIR: { |
2970 | 0 | int ret = http_apply_redirect_rule(rule->arg.redir, s, txn); |
2971 | |
|
2972 | 0 | if (ret == 2) // 2 == skip |
2973 | 0 | break; |
2974 | | |
2975 | 0 | rule_ret = ret ? HTTP_RULE_RES_ABRT : HTTP_RULE_RES_ERROR; |
2976 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
2977 | 0 | s->last_entity.ptr = rule; |
2978 | 0 | goto end; |
2979 | 0 | } |
2980 | | |
2981 | | /* other flags exists, but normally, they never be matched. */ |
2982 | 0 | default: |
2983 | 0 | break; |
2984 | 0 | } |
2985 | 0 | } |
2986 | | |
2987 | 0 | if (def_rules && s->current_rule_list == def_rules) { |
2988 | 0 | s->current_rule_list = rules; |
2989 | 0 | s->current_rule = NULL; |
2990 | 0 | goto restart; |
2991 | 0 | } |
2992 | | |
2993 | 0 | end: |
2994 | | /* if the ruleset evaluation is finished reset the strict mode */ |
2995 | 0 | if (rule_ret != HTTP_RULE_RES_YIELD && rule_ret != HTTP_RULE_RES_FYIELD) { |
2996 | 0 | s->current_rule_list = NULL; |
2997 | 0 | s->current_rule = NULL; |
2998 | 0 | txn->req.flags &= ~HTTP_MSGF_SOFT_RW; |
2999 | 0 | } |
3000 | | |
3001 | | /* we reached the end of the rules, nothing to report */ |
3002 | 0 | return rule_ret; |
3003 | 0 | } |
3004 | | |
3005 | | /* Executes the http-response rules <rules> for stream <s> and proxy <px>. It |
3006 | | * returns one of 6 possible statuses: HTTP_RULE_RES_CONT, HTTP_RULE_RES_STOP, |
3007 | | * HTTP_RULE_RES_DONE, HTTP_RULE_RES_(F)YIELD, or HTTP_RULE_RES_BADREQ. If *CONT |
3008 | | * is returned, the process can continue the evaluation of next rule list. If |
3009 | | * *STOP or *DONE is returned, the process must stop the evaluation. If *BADREQ |
3010 | | * is returned, it means the operation could not be processed and a server error |
3011 | | * must be returned. If *YIELD is returned, the caller must call again the |
3012 | | * function with the same context. |
3013 | | */ |
3014 | | static enum rule_result http_res_get_intercept_rule(struct proxy *px, struct list *def_rules, |
3015 | | struct list *rules, struct stream *s, uint8_t final) |
3016 | 0 | { |
3017 | 0 | struct session *sess = strm_sess(s); |
3018 | 0 | struct http_txn *txn = s->txn.http; |
3019 | 0 | struct act_rule *rule; |
3020 | 0 | enum rule_result rule_ret = HTTP_RULE_RES_CONT; |
3021 | 0 | int act_opts = 0; |
3022 | |
|
3023 | 0 | if (final) |
3024 | 0 | act_opts |= ACT_OPT_FINAL; |
3025 | 0 | if ((s->scf->flags & SC_FL_ERROR) || |
3026 | 0 | ((s->scf->flags & SC_FL_EOS) && proxy_abrt_close_def(px, 1))) |
3027 | 0 | act_opts |= ACT_OPT_FINAL | ACT_OPT_FINAL_EARLY; |
3028 | | |
3029 | | /* If "the current_rule_list" match the executed rule list, we are in |
3030 | | * resume condition. If a resume is needed it is always in the action |
3031 | | * and never in the ACL or converters. In this case, we initialise the |
3032 | | * current rule, and go to the action execution point. |
3033 | | */ |
3034 | 0 | if (s->current_rule) { |
3035 | 0 | int forced = s->flags & SF_RULE_FYIELD; |
3036 | |
|
3037 | 0 | rule = s->current_rule; |
3038 | 0 | s->flags &= ~SF_RULE_FYIELD; |
3039 | 0 | if (s->current_rule_list == rules || (def_rules && s->current_rule_list == def_rules)) { |
3040 | 0 | if (forced) |
3041 | 0 | goto resume_rule; |
3042 | 0 | goto resume_execution; |
3043 | 0 | } |
3044 | 0 | } |
3045 | 0 | s->current_rule_list = ((!def_rules || s->current_rule_list == def_rules) ? rules : def_rules); |
3046 | |
|
3047 | 0 | restart: |
3048 | | |
3049 | | /* start the ruleset evaluation in strict mode */ |
3050 | 0 | txn->rsp.flags &= ~HTTP_MSGF_SOFT_RW; |
3051 | |
|
3052 | 0 | list_for_each_entry(rule, s->current_rule_list, list) { |
3053 | 0 | resume_rule: |
3054 | 0 | s->current_rule = rule; |
3055 | | |
3056 | | /* check if budget is exceeded and we need to continue on the next |
3057 | | * polling loop, unless we know that we cannot yield |
3058 | | */ |
3059 | 0 | if (s->rules_bcount++ >= global.tune.max_rules_at_once && !(act_opts & ACT_OPT_FINAL)) { |
3060 | 0 | s->flags |= SF_RULE_FYIELD; |
3061 | 0 | rule_ret = HTTP_RULE_RES_FYIELD; |
3062 | 0 | task_wakeup(s->task, TASK_WOKEN_MSG); |
3063 | 0 | goto end; |
3064 | 0 | } |
3065 | | |
3066 | | /* check optional condition */ |
3067 | 0 | if (!acl_match_cond(rule->cond, px, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL)) |
3068 | 0 | continue; |
3069 | | |
3070 | 0 | act_opts |= ACT_OPT_FIRST; |
3071 | 0 | resume_execution: |
3072 | 0 | if (rule->kw->flags & KWF_EXPERIMENTAL) |
3073 | 0 | mark_tainted(TAINTED_ACTION_EXP_EXECUTED); |
3074 | | |
3075 | | /* Always call the action function if defined */ |
3076 | 0 | if (rule->action_ptr) { |
3077 | 0 | if (!(s->scb->flags & SC_FL_ERROR) && !(s->res.flags & (CF_READ_TIMEOUT|CF_WRITE_TIMEOUT))) { |
3078 | 0 | s->waiting_entity.type = STRM_ENTITY_NONE; |
3079 | 0 | s->waiting_entity.ptr = NULL; |
3080 | 0 | } |
3081 | |
|
3082 | 0 | switch (EXEC_CTX_WITH_RET(rule->exec_ctx, |
3083 | 0 | rule->action_ptr(rule, px, sess, s, act_opts))) { |
3084 | 0 | case ACT_RET_CONT: |
3085 | 0 | break; |
3086 | 0 | case ACT_RET_STOP: |
3087 | 0 | rule_ret = HTTP_RULE_RES_STOP; |
3088 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
3089 | 0 | s->last_entity.ptr = rule; |
3090 | 0 | goto end; |
3091 | 0 | case ACT_RET_YIELD: |
3092 | 0 | if (act_opts & ACT_OPT_FINAL) { |
3093 | 0 | send_log(s->be, LOG_WARNING, |
3094 | 0 | "Internal error: action yields while it is no long allowed " |
3095 | 0 | "for the http-response/http-after-response actions."); |
3096 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
3097 | 0 | s->last_entity.ptr = rule; |
3098 | 0 | rule_ret = HTTP_RULE_RES_ERROR; |
3099 | 0 | goto end; |
3100 | 0 | } |
3101 | 0 | s->waiting_entity.type = STRM_ENTITY_RULE; |
3102 | 0 | s->waiting_entity.ptr = rule; |
3103 | 0 | rule_ret = HTTP_RULE_RES_YIELD; |
3104 | 0 | goto end; |
3105 | 0 | case ACT_RET_ERR: |
3106 | 0 | rule_ret = HTTP_RULE_RES_ERROR; |
3107 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
3108 | 0 | s->last_entity.ptr = rule; |
3109 | 0 | goto end; |
3110 | 0 | case ACT_RET_DONE: |
3111 | 0 | rule_ret = HTTP_RULE_RES_DONE; |
3112 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
3113 | 0 | s->last_entity.ptr = rule; |
3114 | 0 | goto end; |
3115 | 0 | case ACT_RET_DENY: |
3116 | 0 | if (txn->status == -1) |
3117 | 0 | txn->status = 502; |
3118 | 0 | rule_ret = HTTP_RULE_RES_DENY; |
3119 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
3120 | 0 | s->last_entity.ptr = rule; |
3121 | 0 | goto end; |
3122 | 0 | case ACT_RET_ABRT: |
3123 | 0 | rule_ret = HTTP_RULE_RES_ABRT; |
3124 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
3125 | 0 | s->last_entity.ptr = rule; |
3126 | 0 | goto end; |
3127 | 0 | case ACT_RET_INV: |
3128 | 0 | rule_ret = HTTP_RULE_RES_BADREQ; |
3129 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
3130 | 0 | s->last_entity.ptr = rule; |
3131 | 0 | goto end; |
3132 | 0 | } |
3133 | 0 | continue; /* eval the next rule */ |
3134 | 0 | } |
3135 | | |
3136 | | /* If not action function defined, check for known actions */ |
3137 | 0 | switch (rule->action) { |
3138 | 0 | case ACT_ACTION_ALLOW: |
3139 | 0 | rule_ret = HTTP_RULE_RES_STOP; /* "allow" rules are OK */ |
3140 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
3141 | 0 | s->last_entity.ptr = rule; |
3142 | 0 | goto end; |
3143 | | |
3144 | 0 | case ACT_ACTION_DENY: |
3145 | 0 | txn->status = rule->arg.http_reply->status; |
3146 | 0 | txn->http_reply = rule->arg.http_reply; |
3147 | 0 | rule_ret = HTTP_RULE_RES_DENY; |
3148 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
3149 | 0 | s->last_entity.ptr = rule; |
3150 | 0 | goto end; |
3151 | | |
3152 | 0 | case ACT_HTTP_REDIR: { |
3153 | 0 | int ret = http_apply_redirect_rule(rule->arg.redir, s, txn); |
3154 | |
|
3155 | 0 | if (ret == 2) // 2 == skip |
3156 | 0 | break; |
3157 | | |
3158 | 0 | rule_ret = ret ? HTTP_RULE_RES_ABRT : HTTP_RULE_RES_ERROR; |
3159 | 0 | s->last_entity.type = STRM_ENTITY_RULE; |
3160 | 0 | s->last_entity.ptr = rule; |
3161 | 0 | goto end; |
3162 | 0 | } |
3163 | | /* other flags exists, but normally, they never be matched. */ |
3164 | 0 | default: |
3165 | 0 | break; |
3166 | 0 | } |
3167 | 0 | } |
3168 | | |
3169 | 0 | if (def_rules && s->current_rule_list == def_rules) { |
3170 | 0 | s->current_rule_list = rules; |
3171 | 0 | s->current_rule = NULL; |
3172 | 0 | goto restart; |
3173 | 0 | } |
3174 | | |
3175 | 0 | end: |
3176 | | /* if the ruleset evaluation is finished reset the strict mode */ |
3177 | 0 | if (rule_ret != HTTP_RULE_RES_YIELD && rule_ret != HTTP_RULE_RES_FYIELD) { |
3178 | 0 | s->current_rule_list = NULL; |
3179 | 0 | s->current_rule = NULL; |
3180 | 0 | txn->rsp.flags &= ~HTTP_MSGF_SOFT_RW; |
3181 | 0 | } |
3182 | | |
3183 | | /* we reached the end of the rules, nothing to report */ |
3184 | 0 | return rule_ret; |
3185 | 0 | } |
3186 | | |
3187 | | /* Executes backend and frontend http-after-response rules for the stream <s>, |
3188 | | * in that order. it return 1 on success and 0 on error. It is the caller |
3189 | | * responsibility to catch error or ignore it. If it catches it, this function |
3190 | | * may be called a second time, for the internal error. |
3191 | | */ |
3192 | | int http_eval_after_res_rules(struct stream *s) |
3193 | 0 | { |
3194 | 0 | struct list *def_rules, *rules; |
3195 | 0 | struct session *sess = s->sess; |
3196 | 0 | enum rule_result ret = HTTP_RULE_RES_CONT; |
3197 | | |
3198 | | /* Eval after-response ruleset only if the reply is not const */ |
3199 | 0 | if (s->txn.http->flags & TX_CONST_REPLY) |
3200 | 0 | goto end; |
3201 | | |
3202 | | /* prune the request variables if not already done and swap to the response variables. */ |
3203 | 0 | if (s->vars_reqres.scope != SCOPE_RES) { |
3204 | 0 | vars_prune(&s->vars_reqres, s->sess, s); |
3205 | 0 | vars_init_head(&s->vars_reqres, SCOPE_RES); |
3206 | 0 | } |
3207 | |
|
3208 | 0 | def_rules = (s->be->defpx ? &s->be->defpx->http_after_res_rules : NULL); |
3209 | 0 | rules = &s->be->http_after_res_rules; |
3210 | |
|
3211 | 0 | ret = http_res_get_intercept_rule(s->be, def_rules, rules, s, 1); |
3212 | 0 | if ((ret == HTTP_RULE_RES_CONT || ret == HTTP_RULE_RES_STOP) && sess->fe != s->be) { |
3213 | 0 | def_rules = ((sess->fe->defpx && sess->fe->defpx != s->be->defpx) ? &sess->fe->defpx->http_after_res_rules : NULL); |
3214 | 0 | rules = &sess->fe->http_after_res_rules; |
3215 | 0 | ret = http_res_get_intercept_rule(sess->fe, def_rules, rules, s, 1); |
3216 | 0 | } |
3217 | |
|
3218 | 0 | end: |
3219 | | /* All other codes than CONTINUE, STOP or DONE are forbidden */ |
3220 | 0 | return (ret == HTTP_RULE_RES_CONT || ret == HTTP_RULE_RES_STOP || ret == HTTP_RULE_RES_DONE); |
3221 | 0 | } |
3222 | | |
3223 | | /* |
3224 | | * Manage client-side cookie. It can impact performance by about 2% so it is |
3225 | | * desirable to call it only when needed. This code is quite complex because |
3226 | | * of the multiple very crappy and ambiguous syntaxes we have to support. it |
3227 | | * highly recommended not to touch this part without a good reason ! |
3228 | | */ |
3229 | | static void http_manage_client_side_cookies(struct stream *s, struct channel *req) |
3230 | 0 | { |
3231 | 0 | struct session *sess = s->sess; |
3232 | 0 | struct http_txn *txn = s->txn.http; |
3233 | 0 | struct htx *htx; |
3234 | 0 | struct http_hdr_ctx ctx; |
3235 | 0 | char *hdr_beg, *hdr_end, *del_from; |
3236 | 0 | char *prev, *att_beg, *att_end, *equal, *val_beg, *val_end, *next; |
3237 | 0 | int preserve_hdr; |
3238 | |
|
3239 | 0 | htx = htxbuf(&req->buf); |
3240 | 0 | ctx.blk = NULL; |
3241 | 0 | while (http_find_header(htx, ist("Cookie"), &ctx, 1)) { |
3242 | 0 | int is_first = 1; |
3243 | 0 | del_from = NULL; /* nothing to be deleted */ |
3244 | 0 | preserve_hdr = 0; /* assume we may kill the whole header */ |
3245 | | |
3246 | | /* Now look for cookies. Conforming to RFC2109, we have to support |
3247 | | * attributes whose name begin with a '$', and associate them with |
3248 | | * the right cookie, if we want to delete this cookie. |
3249 | | * So there are 3 cases for each cookie read : |
3250 | | * 1) it's a special attribute, beginning with a '$' : ignore it. |
3251 | | * 2) it's a server id cookie that we *MAY* want to delete : save |
3252 | | * some pointers on it (last semi-colon, beginning of cookie...) |
3253 | | * 3) it's an application cookie : we *MAY* have to delete a previous |
3254 | | * "special" cookie. |
3255 | | * At the end of loop, if a "special" cookie remains, we may have to |
3256 | | * remove it. If no application cookie persists in the header, we |
3257 | | * *MUST* delete it. |
3258 | | * |
3259 | | * Note: RFC2965 is unclear about the processing of spaces around |
3260 | | * the equal sign in the ATTR=VALUE form. A careful inspection of |
3261 | | * the RFC explicitly allows spaces before it, and not within the |
3262 | | * tokens (attrs or values). An inspection of RFC2109 allows that |
3263 | | * too but section 10.1.3 lets one think that spaces may be allowed |
3264 | | * after the equal sign too, resulting in some (rare) buggy |
3265 | | * implementations trying to do that. So let's do what servers do. |
3266 | | * Latest ietf draft forbids spaces all around. Also, earlier RFCs |
3267 | | * allowed quoted strings in values, with any possible character |
3268 | | * after a backslash, including control chars and delimiters, which |
3269 | | * causes parsing to become ambiguous. Browsers also allow spaces |
3270 | | * within values even without quotes. |
3271 | | * |
3272 | | * We have to keep multiple pointers in order to support cookie |
3273 | | * removal at the beginning, middle or end of header without |
3274 | | * corrupting the header. All of these headers are valid : |
3275 | | * |
3276 | | * hdr_beg hdr_end |
3277 | | * | | |
3278 | | * v | |
3279 | | * NAME1=VALUE1;NAME2=VALUE2;NAME3=VALUE3 | |
3280 | | * NAME1=VALUE1;NAME2_ONLY ;NAME3=VALUE3 v |
3281 | | * NAME1 = VALUE 1 ; NAME2 = VALUE2 ; NAME3 = VALUE3 |
3282 | | * | | | | | | | |
3283 | | * | | | | | | | |
3284 | | * | | | | | | +--> next |
3285 | | * | | | | | +----> val_end |
3286 | | * | | | | +-----------> val_beg |
3287 | | * | | | +--------------> equal |
3288 | | * | | +----------------> att_end |
3289 | | * | +---------------------> att_beg |
3290 | | * +--------------------------> prev |
3291 | | * |
3292 | | */ |
3293 | 0 | hdr_beg = ctx.value.ptr; |
3294 | 0 | hdr_end = hdr_beg + ctx.value.len; |
3295 | 0 | for (prev = hdr_beg; prev < hdr_end; prev = next) { |
3296 | | /* Iterate through all cookies on this line */ |
3297 | | |
3298 | | /* find att_beg */ |
3299 | 0 | att_beg = prev; |
3300 | 0 | if (!is_first) |
3301 | 0 | att_beg++; |
3302 | 0 | is_first = 0; |
3303 | |
|
3304 | 0 | while (att_beg < hdr_end && HTTP_IS_SPHT(*att_beg)) |
3305 | 0 | att_beg++; |
3306 | | |
3307 | | /* find att_end : this is the first character after the last non |
3308 | | * space before the equal. It may be equal to hdr_end. |
3309 | | */ |
3310 | 0 | equal = att_end = att_beg; |
3311 | 0 | while (equal < hdr_end) { |
3312 | 0 | if (*equal == '=' || *equal == ',' || *equal == ';') |
3313 | 0 | break; |
3314 | 0 | if (HTTP_IS_SPHT(*equal++)) |
3315 | 0 | continue; |
3316 | 0 | att_end = equal; |
3317 | 0 | } |
3318 | | |
3319 | | /* here, <equal> points to '=', a delimiter or the end. <att_end> |
3320 | | * is between <att_beg> and <equal>, both may be identical. |
3321 | | */ |
3322 | | /* look for end of cookie if there is an equal sign */ |
3323 | 0 | if (equal < hdr_end && *equal == '=') { |
3324 | | /* look for the beginning of the value */ |
3325 | 0 | val_beg = equal + 1; |
3326 | 0 | while (val_beg < hdr_end && HTTP_IS_SPHT(*val_beg)) |
3327 | 0 | val_beg++; |
3328 | | |
3329 | | /* find the end of the value, respecting quotes */ |
3330 | 0 | next = http_find_cookie_value_end(val_beg, hdr_end); |
3331 | | |
3332 | | /* make val_end point to the first white space or delimiter after the value */ |
3333 | 0 | val_end = next; |
3334 | 0 | while (val_end > val_beg && HTTP_IS_SPHT(*(val_end - 1))) |
3335 | 0 | val_end--; |
3336 | 0 | } |
3337 | 0 | else |
3338 | 0 | val_beg = val_end = next = equal; |
3339 | | |
3340 | | /* We have nothing to do with attributes beginning with |
3341 | | * '$'. However, they will automatically be removed if a |
3342 | | * header before them is removed, since they're supposed |
3343 | | * to be linked together. |
3344 | | */ |
3345 | 0 | if (*att_beg == '$') |
3346 | 0 | continue; |
3347 | | |
3348 | | /* Ignore cookies with no equal sign */ |
3349 | 0 | if (equal == next) { |
3350 | | /* This is not our cookie, so we must preserve it. But if we already |
3351 | | * scheduled another cookie for removal, we cannot remove the |
3352 | | * complete header, but we can remove the previous block itself. |
3353 | | */ |
3354 | 0 | preserve_hdr = 1; |
3355 | 0 | if (del_from != NULL) { |
3356 | 0 | int delta = http_del_hdr_value(hdr_beg, hdr_end, &del_from, prev); |
3357 | 0 | val_end += delta; |
3358 | 0 | next += delta; |
3359 | 0 | hdr_end += delta; |
3360 | 0 | prev = del_from; |
3361 | 0 | del_from = NULL; |
3362 | 0 | } |
3363 | 0 | continue; |
3364 | 0 | } |
3365 | | |
3366 | | /* if there are spaces around the equal sign, we need to |
3367 | | * strip them otherwise we'll get trouble for cookie captures, |
3368 | | * or even for rewrites. Since this happens extremely rarely, |
3369 | | * it does not hurt performance. |
3370 | | */ |
3371 | 0 | if (unlikely(att_end != equal || val_beg > equal + 1)) { |
3372 | 0 | int stripped_before = 0; |
3373 | 0 | int stripped_after = 0; |
3374 | |
|
3375 | 0 | if (att_end != equal) { |
3376 | 0 | memmove(att_end, equal, hdr_end - equal); |
3377 | 0 | stripped_before = (att_end - equal); |
3378 | 0 | equal += stripped_before; |
3379 | 0 | val_beg += stripped_before; |
3380 | 0 | } |
3381 | |
|
3382 | 0 | if (val_beg > equal + 1) { |
3383 | 0 | memmove(equal + 1, val_beg, hdr_end + stripped_before - val_beg); |
3384 | 0 | stripped_after = (equal + 1) - val_beg; |
3385 | 0 | val_beg += stripped_after; |
3386 | 0 | stripped_before += stripped_after; |
3387 | 0 | } |
3388 | |
|
3389 | 0 | val_end += stripped_before; |
3390 | 0 | next += stripped_before; |
3391 | 0 | hdr_end += stripped_before; |
3392 | 0 | } |
3393 | | /* now everything is as on the diagram above */ |
3394 | | |
3395 | | /* First, let's see if we want to capture this cookie. We check |
3396 | | * that we don't already have a client side cookie, because we |
3397 | | * can only capture one. Also as an optimisation, we ignore |
3398 | | * cookies shorter than the declared name. |
3399 | | */ |
3400 | 0 | if (sess->fe->capture_name != NULL && txn->cli_cookie == NULL && |
3401 | 0 | (val_end - att_beg >= sess->fe->capture_namelen) && |
3402 | 0 | memcmp(att_beg, sess->fe->capture_name, sess->fe->capture_namelen) == 0) { |
3403 | 0 | int log_len = val_end - att_beg; |
3404 | |
|
3405 | 0 | if ((txn->cli_cookie = pool_alloc(pool_head_capture)) == NULL) { |
3406 | 0 | ha_alert("HTTP logging : out of memory.\n"); |
3407 | 0 | } else { |
3408 | 0 | if (log_len > sess->fe->capture_len) |
3409 | 0 | log_len = sess->fe->capture_len; |
3410 | 0 | memcpy(txn->cli_cookie, att_beg, log_len); |
3411 | 0 | txn->cli_cookie[log_len] = 0; |
3412 | 0 | } |
3413 | 0 | } |
3414 | | |
3415 | | /* Persistence cookies in passive, rewrite or insert mode have the |
3416 | | * following form : |
3417 | | * |
3418 | | * Cookie: NAME=SRV[|<lastseen>[|<firstseen>]] |
3419 | | * |
3420 | | * For cookies in prefix mode, the form is : |
3421 | | * |
3422 | | * Cookie: NAME=SRV~VALUE |
3423 | | */ |
3424 | 0 | if ((att_end - att_beg == s->be->cookie_len) && (s->be->cookie_name != NULL) && |
3425 | 0 | (memcmp(att_beg, s->be->cookie_name, att_end - att_beg) == 0)) { |
3426 | 0 | struct server *srv = s->be->srv; |
3427 | 0 | char *delim; |
3428 | | |
3429 | | /* if we're in cookie prefix mode, we'll search the delimiter so that we |
3430 | | * have the server ID between val_beg and delim, and the original cookie between |
3431 | | * delim+1 and val_end. Otherwise, delim==val_end : |
3432 | | * |
3433 | | * hdr_beg |
3434 | | * | |
3435 | | * v |
3436 | | * NAME=SRV; # in all but prefix modes |
3437 | | * NAME=SRV~OPAQUE ; # in prefix mode |
3438 | | * || || | |+-> next |
3439 | | * || || | +--> val_end |
3440 | | * || || +---------> delim |
3441 | | * || |+------------> val_beg |
3442 | | * || +-------------> att_end = equal |
3443 | | * |+-----------------> att_beg |
3444 | | * +------------------> prev |
3445 | | * |
3446 | | */ |
3447 | 0 | if (s->be->ck_opts & PR_CK_PFX) { |
3448 | 0 | for (delim = val_beg; delim < val_end; delim++) |
3449 | 0 | if (*delim == COOKIE_DELIM) |
3450 | 0 | break; |
3451 | 0 | } |
3452 | 0 | else { |
3453 | 0 | char *vbar1; |
3454 | 0 | delim = val_end; |
3455 | | /* Now check if the cookie contains a date field, which would |
3456 | | * appear after a vertical bar ('|') just after the server name |
3457 | | * and before the delimiter. |
3458 | | */ |
3459 | 0 | vbar1 = memchr(val_beg, COOKIE_DELIM_DATE, val_end - val_beg); |
3460 | 0 | if (vbar1) { |
3461 | | /* OK, so left of the bar is the server's cookie and |
3462 | | * right is the last seen date. It is a base64 encoded |
3463 | | * 30-bit value representing the UNIX date since the |
3464 | | * epoch in 4-second quantities. |
3465 | | */ |
3466 | 0 | int val; |
3467 | 0 | delim = vbar1++; |
3468 | 0 | if (val_end - vbar1 >= 5) { |
3469 | 0 | val = b64tos30(vbar1); |
3470 | 0 | if (val > 0) |
3471 | 0 | txn->cookie_last_date = val << 2; |
3472 | 0 | } |
3473 | | /* look for a second vertical bar */ |
3474 | 0 | vbar1 = memchr(vbar1, COOKIE_DELIM_DATE, val_end - vbar1); |
3475 | 0 | if (vbar1 && (val_end - vbar1 > 5)) { |
3476 | 0 | val = b64tos30(vbar1 + 1); |
3477 | 0 | if (val > 0) |
3478 | 0 | txn->cookie_first_date = val << 2; |
3479 | 0 | } |
3480 | 0 | } |
3481 | 0 | } |
3482 | | |
3483 | | /* if the cookie has an expiration date and the proxy wants to check |
3484 | | * it, then we do that now. We first check if the cookie is too old, |
3485 | | * then only if it has expired. We detect strict overflow because the |
3486 | | * time resolution here is not great (4 seconds). Cookies with dates |
3487 | | * in the future are ignored if their offset is beyond one day. This |
3488 | | * allows an admin to fix timezone issues without expiring everyone |
3489 | | * and at the same time avoids keeping unwanted side effects for too |
3490 | | * long. |
3491 | | */ |
3492 | 0 | if (txn->cookie_first_date && s->be->cookie_maxlife && |
3493 | 0 | (((signed)(date.tv_sec - txn->cookie_first_date) > (signed)s->be->cookie_maxlife) || |
3494 | 0 | ((signed)(txn->cookie_first_date - date.tv_sec) > 86400))) { |
3495 | 0 | txn->flags &= ~TX_CK_MASK; |
3496 | 0 | txn->flags |= TX_CK_OLD; |
3497 | 0 | delim = val_beg; // let's pretend we have not found the cookie |
3498 | 0 | txn->cookie_first_date = 0; |
3499 | 0 | txn->cookie_last_date = 0; |
3500 | 0 | } |
3501 | 0 | else if (txn->cookie_last_date && s->be->cookie_maxidle && |
3502 | 0 | (((signed)(date.tv_sec - txn->cookie_last_date) > (signed)s->be->cookie_maxidle) || |
3503 | 0 | ((signed)(txn->cookie_last_date - date.tv_sec) > 86400))) { |
3504 | 0 | txn->flags &= ~TX_CK_MASK; |
3505 | 0 | txn->flags |= TX_CK_EXPIRED; |
3506 | 0 | delim = val_beg; // let's pretend we have not found the cookie |
3507 | 0 | txn->cookie_first_date = 0; |
3508 | 0 | txn->cookie_last_date = 0; |
3509 | 0 | } |
3510 | | |
3511 | | /* Here, we'll look for the first running server which supports the cookie. |
3512 | | * This allows to share a same cookie between several servers, for example |
3513 | | * to dedicate backup servers to specific servers only. |
3514 | | * However, to prevent clients from sticking to cookie-less backup server |
3515 | | * when they have incidentely learned an empty cookie, we simply ignore |
3516 | | * empty cookies and mark them as invalid. |
3517 | | * The same behaviour is applied when persistence must be ignored. |
3518 | | */ |
3519 | 0 | if ((delim == val_beg) || (s->flags & (SF_IGNORE_PRST | SF_ASSIGNED))) |
3520 | 0 | srv = NULL; |
3521 | |
|
3522 | 0 | while (srv) { |
3523 | 0 | if (srv->cookie && (srv->cklen == delim - val_beg) && |
3524 | 0 | !memcmp(val_beg, srv->cookie, delim - val_beg)) { |
3525 | 0 | if ((srv->cur_state != SRV_ST_STOPPED) || |
3526 | 0 | (s->be->options & PR_O_PERSIST) || |
3527 | 0 | (s->flags & SF_FORCE_PRST)) { |
3528 | | /* we found the server and we can use it */ |
3529 | 0 | txn->flags &= ~TX_CK_MASK; |
3530 | 0 | txn->flags |= (srv->cur_state != SRV_ST_STOPPED) ? TX_CK_VALID : TX_CK_DOWN; |
3531 | 0 | s->flags |= SF_DIRECT | SF_ASSIGNED; |
3532 | 0 | stream_set_srv_target(s, srv); |
3533 | 0 | break; |
3534 | 0 | } else { |
3535 | | /* we found a server, but it's down, |
3536 | | * mark it as such and go on in case |
3537 | | * another one is available. |
3538 | | */ |
3539 | 0 | txn->flags &= ~TX_CK_MASK; |
3540 | 0 | txn->flags |= TX_CK_DOWN; |
3541 | 0 | } |
3542 | 0 | } |
3543 | 0 | srv = srv->next; |
3544 | 0 | } |
3545 | |
|
3546 | 0 | if (!srv && !(txn->flags & (TX_CK_DOWN|TX_CK_EXPIRED|TX_CK_OLD))) { |
3547 | | /* no server matched this cookie or we deliberately skipped it */ |
3548 | 0 | txn->flags &= ~TX_CK_MASK; |
3549 | 0 | if ((s->flags & (SF_IGNORE_PRST | SF_ASSIGNED))) |
3550 | 0 | txn->flags |= TX_CK_UNUSED; |
3551 | 0 | else |
3552 | 0 | txn->flags |= TX_CK_INVALID; |
3553 | 0 | } |
3554 | | |
3555 | | /* depending on the cookie mode, we may have to either : |
3556 | | * - delete the complete cookie if we're in insert+indirect mode, so that |
3557 | | * the server never sees it ; |
3558 | | * - remove the server id from the cookie value, and tag the cookie as an |
3559 | | * application cookie so that it does not get accidentally removed later, |
3560 | | * if we're in cookie prefix mode |
3561 | | */ |
3562 | 0 | if ((s->be->ck_opts & PR_CK_PFX) && (delim != val_end)) { |
3563 | 0 | int delta; /* negative */ |
3564 | |
|
3565 | 0 | memmove(val_beg, delim + 1, hdr_end - (delim + 1)); |
3566 | 0 | delta = val_beg - (delim + 1); |
3567 | 0 | val_end += delta; |
3568 | 0 | next += delta; |
3569 | 0 | hdr_end += delta; |
3570 | 0 | del_from = NULL; |
3571 | 0 | preserve_hdr = 1; /* we want to keep this cookie */ |
3572 | 0 | } |
3573 | 0 | else if (del_from == NULL && |
3574 | 0 | (s->be->ck_opts & (PR_CK_INS | PR_CK_IND)) == (PR_CK_INS | PR_CK_IND)) { |
3575 | 0 | del_from = prev; |
3576 | 0 | } |
3577 | 0 | } |
3578 | 0 | else { |
3579 | | /* This is not our cookie, so we must preserve it. But if we already |
3580 | | * scheduled another cookie for removal, we cannot remove the |
3581 | | * complete header, but we can remove the previous block itself. |
3582 | | */ |
3583 | 0 | preserve_hdr = 1; |
3584 | |
|
3585 | 0 | if (del_from != NULL) { |
3586 | 0 | int delta = http_del_hdr_value(hdr_beg, hdr_end, &del_from, prev); |
3587 | 0 | if (att_beg >= del_from) |
3588 | 0 | att_beg += delta; |
3589 | 0 | if (att_end >= del_from) |
3590 | 0 | att_end += delta; |
3591 | 0 | val_beg += delta; |
3592 | 0 | val_end += delta; |
3593 | 0 | next += delta; |
3594 | 0 | hdr_end += delta; |
3595 | 0 | prev = del_from; |
3596 | 0 | del_from = NULL; |
3597 | 0 | } |
3598 | 0 | } |
3599 | |
|
3600 | 0 | } /* for each cookie */ |
3601 | | |
3602 | | |
3603 | | /* There are no more cookies on this line. |
3604 | | * We may still have one (or several) marked for deletion at the |
3605 | | * end of the line. We must do this now in two ways : |
3606 | | * - if some cookies must be preserved, we only delete from the |
3607 | | * mark to the end of line ; |
3608 | | * - if nothing needs to be preserved, simply delete the whole header |
3609 | | */ |
3610 | 0 | if (del_from) { |
3611 | 0 | hdr_end = (preserve_hdr ? del_from : hdr_beg); |
3612 | 0 | } |
3613 | 0 | if ((hdr_end - hdr_beg) != ctx.value.len) { |
3614 | 0 | if (hdr_beg != hdr_end) |
3615 | 0 | htx_change_blk_value_len(htx, ctx.blk, hdr_end - hdr_beg); |
3616 | 0 | else |
3617 | 0 | http_remove_header(htx, &ctx); |
3618 | 0 | } |
3619 | 0 | } /* for each "Cookie header */ |
3620 | 0 | } |
3621 | | |
3622 | | /* |
3623 | | * Manage server-side cookies. It can impact performance by about 2% so it is |
3624 | | * desirable to call it only when needed. This function is also used when we |
3625 | | * just need to know if there is a cookie (eg: for check-cache). |
3626 | | */ |
3627 | | static void http_manage_server_side_cookies(struct stream *s, struct channel *res) |
3628 | 0 | { |
3629 | 0 | struct session *sess = s->sess; |
3630 | 0 | struct http_txn *txn = s->txn.http; |
3631 | 0 | struct htx *htx; |
3632 | 0 | struct http_hdr_ctx ctx; |
3633 | 0 | struct server *srv; |
3634 | 0 | char *hdr_beg, *hdr_end; |
3635 | 0 | char *prev, *att_beg, *att_end, *equal, *val_beg, *val_end, *next; |
3636 | |
|
3637 | 0 | htx = htxbuf(&res->buf); |
3638 | |
|
3639 | 0 | ctx.blk = NULL; |
3640 | 0 | while (http_find_header(htx, ist("Set-Cookie"), &ctx, 1)) { |
3641 | 0 | int is_first = 1; |
3642 | | |
3643 | | /* OK, right now we know we have a Set-Cookie* at hdr_beg, and |
3644 | | * <prev> points to the colon. |
3645 | | */ |
3646 | 0 | txn->flags |= TX_SCK_PRESENT; |
3647 | | |
3648 | | /* Maybe we only wanted to see if there was a Set-Cookie (eg: |
3649 | | * check-cache is enabled) and we are not interested in checking |
3650 | | * them. Warning, the cookie capture is declared in the frontend. |
3651 | | */ |
3652 | 0 | if (s->be->cookie_name == NULL && sess->fe->capture_name == NULL) |
3653 | 0 | break; |
3654 | | |
3655 | | /* OK so now we know we have to process this response cookie. |
3656 | | * The format of the Set-Cookie header is slightly different |
3657 | | * from the format of the Cookie header in that it does not |
3658 | | * support the comma as a cookie delimiter (thus the header |
3659 | | * cannot be folded) because the Expires attribute described in |
3660 | | * the original Netscape's spec may contain an unquoted date |
3661 | | * with a comma inside. We have to live with this because |
3662 | | * many browsers don't support Max-Age and some browsers don't |
3663 | | * support quoted strings. However the Set-Cookie2 header is |
3664 | | * clean but basically nobody supports it. |
3665 | | * |
3666 | | * We have to keep multiple pointers in order to support cookie |
3667 | | * removal at the beginning, middle or end of header without |
3668 | | * corrupting the header (in case of set-cookie2). A special |
3669 | | * pointer, <scav> points to the beginning of the set-cookie-av |
3670 | | * fields after the first semi-colon. The <next> pointer points |
3671 | | * either to the end of line (set-cookie) or next unquoted comma |
3672 | | * (set-cookie2). All of these headers are valid : |
3673 | | * |
3674 | | * hdr_beg hdr_end |
3675 | | * | | |
3676 | | * v | |
3677 | | * NAME1 = VALUE 1 ; Secure; Path="/" | |
3678 | | * NAME=VALUE; Secure; Expires=Thu, 01-Jan-1970 00:00:01 GMT v |
3679 | | * NAME = VALUE ; Secure; Expires=Thu, 01-Jan-1970 00:00:01 GMT |
3680 | | * NAME1 = VALUE 1 ; Max-Age=0, NAME2=VALUE2; Discard |
3681 | | * | | | | | | | | |
3682 | | * | | | | | | | +-> next |
3683 | | * | | | | | | +------------> scav |
3684 | | * | | | | | +--------------> val_end |
3685 | | * | | | | +--------------------> val_beg |
3686 | | * | | | +----------------------> equal |
3687 | | * | | +------------------------> att_end |
3688 | | * | +----------------------------> att_beg |
3689 | | * +------------------------------> prev |
3690 | | * -------------------------------> hdr_beg |
3691 | | */ |
3692 | 0 | hdr_beg = ctx.value.ptr; |
3693 | 0 | hdr_end = hdr_beg + ctx.value.len; |
3694 | 0 | for (prev = hdr_beg; prev < hdr_end; prev = next) { |
3695 | | |
3696 | | /* Iterate through all cookies on this line */ |
3697 | | |
3698 | | /* find att_beg */ |
3699 | 0 | att_beg = prev; |
3700 | 0 | if (!is_first) |
3701 | 0 | att_beg++; |
3702 | 0 | is_first = 0; |
3703 | |
|
3704 | 0 | while (att_beg < hdr_end && HTTP_IS_SPHT(*att_beg)) |
3705 | 0 | att_beg++; |
3706 | | |
3707 | | /* find att_end : this is the first character after the last non |
3708 | | * space before the equal. It may be equal to hdr_end. |
3709 | | */ |
3710 | 0 | equal = att_end = att_beg; |
3711 | |
|
3712 | 0 | while (equal < hdr_end) { |
3713 | 0 | if (*equal == '=' || *equal == ';') |
3714 | 0 | break; |
3715 | 0 | if (HTTP_IS_SPHT(*equal++)) |
3716 | 0 | continue; |
3717 | 0 | att_end = equal; |
3718 | 0 | } |
3719 | | |
3720 | | /* here, <equal> points to '=', a delimiter or the end. <att_end> |
3721 | | * is between <att_beg> and <equal>, both may be identical. |
3722 | | */ |
3723 | | |
3724 | | /* look for end of cookie if there is an equal sign */ |
3725 | 0 | if (equal < hdr_end && *equal == '=') { |
3726 | | /* look for the beginning of the value */ |
3727 | 0 | val_beg = equal + 1; |
3728 | 0 | while (val_beg < hdr_end && HTTP_IS_SPHT(*val_beg)) |
3729 | 0 | val_beg++; |
3730 | | |
3731 | | /* find the end of the value, respecting quotes */ |
3732 | 0 | next = http_find_cookie_value_end(val_beg, hdr_end); |
3733 | | |
3734 | | /* make val_end point to the first white space or delimiter after the value */ |
3735 | 0 | val_end = next; |
3736 | 0 | while (val_end > val_beg && HTTP_IS_SPHT(*(val_end - 1))) |
3737 | 0 | val_end--; |
3738 | 0 | } |
3739 | 0 | else { |
3740 | | /* <equal> points to next comma, semi-colon or EOL */ |
3741 | 0 | val_beg = val_end = next = equal; |
3742 | 0 | } |
3743 | |
|
3744 | 0 | if (next < hdr_end) { |
3745 | | /* For Set-Cookie, since commas are permitted |
3746 | | * in values, skip to the end. |
3747 | | */ |
3748 | 0 | next = hdr_end; |
3749 | 0 | } |
3750 | | |
3751 | | /* Now everything is as on the diagram above */ |
3752 | | |
3753 | | /* Ignore cookies with no equal sign */ |
3754 | 0 | if (equal == val_end) |
3755 | 0 | continue; |
3756 | | |
3757 | | /* If there are spaces around the equal sign, we need to |
3758 | | * strip them otherwise we'll get trouble for cookie captures, |
3759 | | * or even for rewrites. Since this happens extremely rarely, |
3760 | | * it does not hurt performance. |
3761 | | */ |
3762 | 0 | if (unlikely(att_end != equal || val_beg > equal + 1)) { |
3763 | 0 | int stripped_before = 0; |
3764 | 0 | int stripped_after = 0; |
3765 | |
|
3766 | 0 | if (att_end != equal) { |
3767 | 0 | memmove(att_end, equal, hdr_end - equal); |
3768 | 0 | stripped_before = (att_end - equal); |
3769 | 0 | equal += stripped_before; |
3770 | 0 | val_beg += stripped_before; |
3771 | 0 | } |
3772 | |
|
3773 | 0 | if (val_beg > equal + 1) { |
3774 | 0 | memmove(equal + 1, val_beg, hdr_end + stripped_before - val_beg); |
3775 | 0 | stripped_after = (equal + 1) - val_beg; |
3776 | 0 | val_beg += stripped_after; |
3777 | 0 | stripped_before += stripped_after; |
3778 | 0 | } |
3779 | |
|
3780 | 0 | val_end += stripped_before; |
3781 | 0 | next += stripped_before; |
3782 | 0 | hdr_end += stripped_before; |
3783 | |
|
3784 | 0 | htx_change_blk_value_len(htx, ctx.blk, hdr_end - hdr_beg); |
3785 | 0 | ctx.value.len = hdr_end - hdr_beg; |
3786 | 0 | } |
3787 | | |
3788 | | /* First, let's see if we want to capture this cookie. We check |
3789 | | * that we don't already have a server side cookie, because we |
3790 | | * can only capture one. Also as an optimisation, we ignore |
3791 | | * cookies shorter than the declared name. |
3792 | | */ |
3793 | 0 | if (sess->fe->capture_name != NULL && |
3794 | 0 | txn->srv_cookie == NULL && |
3795 | 0 | (val_end - att_beg >= sess->fe->capture_namelen) && |
3796 | 0 | memcmp(att_beg, sess->fe->capture_name, sess->fe->capture_namelen) == 0) { |
3797 | 0 | int log_len = val_end - att_beg; |
3798 | 0 | if ((txn->srv_cookie = pool_alloc(pool_head_capture)) == NULL) { |
3799 | 0 | ha_alert("HTTP logging : out of memory.\n"); |
3800 | 0 | } |
3801 | 0 | else { |
3802 | 0 | if (log_len > sess->fe->capture_len) |
3803 | 0 | log_len = sess->fe->capture_len; |
3804 | 0 | memcpy(txn->srv_cookie, att_beg, log_len); |
3805 | 0 | txn->srv_cookie[log_len] = 0; |
3806 | 0 | } |
3807 | 0 | } |
3808 | |
|
3809 | 0 | srv = objt_server(s->target); |
3810 | | /* now check if we need to process it for persistence */ |
3811 | 0 | if (!(s->flags & SF_IGNORE_PRST) && |
3812 | 0 | (att_end - att_beg == s->be->cookie_len) && (s->be->cookie_name != NULL) && |
3813 | 0 | (memcmp(att_beg, s->be->cookie_name, att_end - att_beg) == 0)) { |
3814 | | /* assume passive cookie by default */ |
3815 | 0 | txn->flags &= ~TX_SCK_MASK; |
3816 | 0 | txn->flags |= TX_SCK_FOUND; |
3817 | | |
3818 | | /* If the cookie is in insert mode on a known server, we'll delete |
3819 | | * this occurrence because we'll insert another one later. |
3820 | | * We'll delete it too if the "indirect" option is set and we're in |
3821 | | * a direct access. |
3822 | | */ |
3823 | 0 | if (s->be->ck_opts & PR_CK_PSV) { |
3824 | | /* The "preserve" flag was set, we don't want to touch the |
3825 | | * server's cookie. |
3826 | | */ |
3827 | 0 | } |
3828 | 0 | else if ((srv && (s->be->ck_opts & PR_CK_INS)) || |
3829 | 0 | ((s->flags & SF_DIRECT) && (s->be->ck_opts & PR_CK_IND))) { |
3830 | | /* this cookie must be deleted */ |
3831 | 0 | if (prev == hdr_beg && next == hdr_end) { |
3832 | | /* whole header */ |
3833 | 0 | http_remove_header(htx, &ctx); |
3834 | | /* note: while both invalid now, <next> and <hdr_end> |
3835 | | * are still equal, so the for() will stop as expected. |
3836 | | */ |
3837 | 0 | } else { |
3838 | | /* just remove the value */ |
3839 | 0 | int delta = http_del_hdr_value(hdr_beg, hdr_end, &prev, next); |
3840 | 0 | next = prev; |
3841 | 0 | hdr_end += delta; |
3842 | 0 | } |
3843 | 0 | txn->flags &= ~TX_SCK_MASK; |
3844 | 0 | txn->flags |= TX_SCK_DELETED; |
3845 | | /* and go on with next cookie */ |
3846 | 0 | } |
3847 | 0 | else if (srv && srv->cookie && (s->be->ck_opts & PR_CK_RW)) { |
3848 | | /* replace bytes val_beg->val_end with the cookie name associated |
3849 | | * with this server since we know it. |
3850 | | */ |
3851 | 0 | int sliding, delta; |
3852 | |
|
3853 | 0 | ctx.value = ist2(val_beg, val_end - val_beg); |
3854 | 0 | ctx.lws_before = ctx.lws_after = 0; |
3855 | 0 | http_replace_header_value(htx, &ctx, ist2(srv->cookie, srv->cklen), 0); |
3856 | 0 | delta = srv->cklen - (val_end - val_beg); |
3857 | 0 | sliding = (ctx.value.ptr - val_beg); |
3858 | 0 | hdr_beg += sliding; |
3859 | 0 | val_beg += sliding; |
3860 | 0 | next += sliding + delta; |
3861 | 0 | hdr_end += sliding + delta; |
3862 | |
|
3863 | 0 | txn->flags &= ~TX_SCK_MASK; |
3864 | 0 | txn->flags |= TX_SCK_REPLACED; |
3865 | 0 | } |
3866 | 0 | else if (srv && srv->cookie && (s->be->ck_opts & PR_CK_PFX)) { |
3867 | | /* insert the cookie name associated with this server |
3868 | | * before existing cookie, and insert a delimiter between them.. |
3869 | | */ |
3870 | 0 | int sliding, delta; |
3871 | 0 | ctx.value = ist2(val_beg, 0); |
3872 | 0 | ctx.lws_before = ctx.lws_after = 0; |
3873 | 0 | http_replace_header_value(htx, &ctx, ist2(srv->cookie, srv->cklen + 1), 0); |
3874 | 0 | delta = srv->cklen + 1; |
3875 | 0 | sliding = (ctx.value.ptr - val_beg); |
3876 | 0 | hdr_beg += sliding; |
3877 | 0 | val_beg += sliding; |
3878 | 0 | next += sliding + delta; |
3879 | 0 | hdr_end += sliding + delta; |
3880 | |
|
3881 | 0 | val_beg[srv->cklen] = COOKIE_DELIM; |
3882 | 0 | txn->flags &= ~TX_SCK_MASK; |
3883 | 0 | txn->flags |= TX_SCK_REPLACED; |
3884 | 0 | } |
3885 | 0 | } |
3886 | | /* that's done for this cookie, check the next one on the same |
3887 | | * line when next != hdr_end (which should normally not happen |
3888 | | * with set-cookie2 support removed). |
3889 | | */ |
3890 | 0 | } |
3891 | 0 | } |
3892 | 0 | } |
3893 | | |
3894 | | /* |
3895 | | * Parses the Cache-Control and Pragma request header fields to determine if |
3896 | | * the request may be served from the cache and/or if it is cacheable. Updates |
3897 | | * s->txn.http->flags. |
3898 | | */ |
3899 | | void http_check_request_for_cacheability(struct stream *s, struct channel *req) |
3900 | 0 | { |
3901 | 0 | struct http_txn *txn = s->txn.http; |
3902 | 0 | struct htx *htx; |
3903 | 0 | struct http_hdr_ctx ctx = { .blk = NULL }; |
3904 | 0 | int pragma_found, cc_found; |
3905 | |
|
3906 | 0 | if ((txn->flags & (TX_CACHEABLE|TX_CACHE_IGNORE)) == TX_CACHE_IGNORE) |
3907 | 0 | return; /* nothing more to do here */ |
3908 | | |
3909 | 0 | htx = htxbuf(&req->buf); |
3910 | 0 | pragma_found = cc_found = 0; |
3911 | | |
3912 | | /* Check "pragma" header for HTTP/1.0 compatibility. */ |
3913 | 0 | if (http_find_header(htx, ist("pragma"), &ctx, 1)) { |
3914 | 0 | if (isteqi(ctx.value, ist("no-cache"))) { |
3915 | 0 | pragma_found = 1; |
3916 | 0 | } |
3917 | 0 | } |
3918 | |
|
3919 | 0 | ctx.blk = NULL; |
3920 | | /* Don't use the cache and don't try to store if we found the |
3921 | | * Authorization header */ |
3922 | 0 | if (http_find_header(htx, ist("authorization"), &ctx, 1)) { |
3923 | 0 | txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK; |
3924 | 0 | txn->flags |= TX_CACHE_IGNORE; |
3925 | 0 | } |
3926 | | |
3927 | | |
3928 | | /* Look for "cache-control" header and iterate over all the values |
3929 | | * until we find one that specifies that caching is possible or not. */ |
3930 | 0 | ctx.blk = NULL; |
3931 | 0 | while (http_find_header(htx, ist("cache-control"), &ctx, 0)) { |
3932 | 0 | cc_found = 1; |
3933 | | /* We don't check the values after max-age, max-stale nor min-fresh, |
3934 | | * we simply don't use the cache when they're specified. */ |
3935 | 0 | if (istmatchi(ctx.value, ist("max-age")) || |
3936 | 0 | istmatchi(ctx.value, ist("no-cache")) || |
3937 | 0 | istmatchi(ctx.value, ist("max-stale")) || |
3938 | 0 | istmatchi(ctx.value, ist("min-fresh"))) { |
3939 | 0 | txn->flags |= TX_CACHE_IGNORE; |
3940 | 0 | continue; |
3941 | 0 | } |
3942 | 0 | if (istmatchi(ctx.value, ist("no-store"))) { |
3943 | 0 | txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK; |
3944 | 0 | continue; |
3945 | 0 | } |
3946 | 0 | } |
3947 | | |
3948 | | /* RFC7234#5.4: |
3949 | | * When the Cache-Control header field is also present and |
3950 | | * understood in a request, Pragma is ignored. |
3951 | | * When the Cache-Control header field is not present in a |
3952 | | * request, caches MUST consider the no-cache request |
3953 | | * pragma-directive as having the same effect as if |
3954 | | * "Cache-Control: no-cache" were present. |
3955 | | */ |
3956 | 0 | if (!cc_found && pragma_found) |
3957 | 0 | txn->flags |= TX_CACHE_IGNORE; |
3958 | 0 | } |
3959 | | |
3960 | | /* |
3961 | | * Check if response is cacheable or not. Updates s->txn.http->flags. |
3962 | | */ |
3963 | | void http_check_response_for_cacheability(struct stream *s, struct channel *res) |
3964 | 0 | { |
3965 | 0 | struct http_txn *txn = s->txn.http; |
3966 | 0 | struct http_hdr_ctx ctx = { .blk = NULL }; |
3967 | 0 | struct htx *htx; |
3968 | 0 | int has_freshness_info = 0; |
3969 | 0 | int has_validator = 0; |
3970 | 0 | int has_null_maxage = 0; |
3971 | |
|
3972 | 0 | if (txn->status < 200) { |
3973 | | /* do not try to cache interim responses! */ |
3974 | 0 | txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK; |
3975 | 0 | return; |
3976 | 0 | } |
3977 | | |
3978 | 0 | htx = htxbuf(&res->buf); |
3979 | | /* Check "pragma" header for HTTP/1.0 compatibility. */ |
3980 | 0 | if (http_find_header(htx, ist("pragma"), &ctx, 1)) { |
3981 | 0 | if (isteqi(ctx.value, ist("no-cache"))) { |
3982 | 0 | txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK; |
3983 | 0 | return; |
3984 | 0 | } |
3985 | 0 | } |
3986 | | |
3987 | | /* Look for "cache-control" header and iterate over all the values |
3988 | | * until we find one that specifies that caching is possible or not. */ |
3989 | 0 | ctx.blk = NULL; |
3990 | 0 | while (http_find_header(htx, ist("cache-control"), &ctx, 0)) { |
3991 | 0 | if (isteqi(ctx.value, ist("public"))) { |
3992 | 0 | txn->flags |= TX_CACHEABLE | TX_CACHE_COOK; |
3993 | 0 | continue; |
3994 | 0 | } |
3995 | | /* This max-age might be overridden by a s-maxage directive, do |
3996 | | * not unset the TX_CACHEABLE yet. */ |
3997 | 0 | if (isteqi(ctx.value, ist("max-age=0"))) { |
3998 | 0 | has_null_maxage = 1; |
3999 | 0 | continue; |
4000 | 0 | } |
4001 | | |
4002 | | /* We might have a no-cache="set-cookie" form. */ |
4003 | 0 | if (isteqi(ctx.value, ist("no-cache=\"set-cookie\""))) { |
4004 | 0 | txn->flags &= ~TX_CACHE_COOK; |
4005 | 0 | continue; |
4006 | 0 | } |
4007 | | |
4008 | 0 | if (isteqi(ctx.value, ist("private")) || istmatchi(ctx.value, ist("private=")) || |
4009 | 0 | isteqi(ctx.value, ist("no-cache")) || istmatchi(ctx.value, ist("no-cache=")) || |
4010 | 0 | isteqi(ctx.value, ist("no-store")) || istmatchi(ctx.value, ist("no-store=")) || |
4011 | 0 | isteqi(ctx.value, ist("s-maxage=0"))) { |
4012 | 0 | txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK; |
4013 | 0 | continue; |
4014 | 0 | } |
4015 | 0 | if (istmatchi(ctx.value, ist("s-maxage"))) { |
4016 | 0 | has_freshness_info = 1; |
4017 | 0 | has_null_maxage = 0; /* The null max-age is overridden, ignore it */ |
4018 | 0 | continue; |
4019 | 0 | } |
4020 | 0 | if (istmatchi(ctx.value, ist("max-age"))) { |
4021 | 0 | has_freshness_info = 1; |
4022 | 0 | continue; |
4023 | 0 | } |
4024 | 0 | } |
4025 | | |
4026 | | /* We had a 'max-age=0' directive but no extra s-maxage, do not cache |
4027 | | * the response. */ |
4028 | 0 | if (has_null_maxage) { |
4029 | 0 | txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK; |
4030 | 0 | } |
4031 | | |
4032 | | /* If no freshness information could be found in Cache-Control values, |
4033 | | * look for an Expires header. */ |
4034 | 0 | if (!has_freshness_info) { |
4035 | 0 | ctx.blk = NULL; |
4036 | 0 | has_freshness_info = http_find_header(htx, ist("expires"), &ctx, 0); |
4037 | 0 | } |
4038 | | |
4039 | | /* If no freshness information could be found in Cache-Control or Expires |
4040 | | * values, look for an explicit validator. */ |
4041 | 0 | if (!has_freshness_info) { |
4042 | 0 | ctx.blk = NULL; |
4043 | 0 | has_validator = 1; |
4044 | 0 | if (!http_find_header(htx, ist("etag"), &ctx, 0)) { |
4045 | 0 | ctx.blk = NULL; |
4046 | 0 | if (!http_find_header(htx, ist("last-modified"), &ctx, 0)) |
4047 | 0 | has_validator = 0; |
4048 | 0 | } |
4049 | 0 | } |
4050 | | |
4051 | | /* We won't store an entry that has neither a cache validator nor an |
4052 | | * explicit expiration time, as suggested in RFC 7234#3. */ |
4053 | 0 | if (!has_freshness_info && !has_validator) |
4054 | 0 | txn->flags &= ~TX_CACHEABLE; |
4055 | 0 | } |
4056 | | |
4057 | | /* |
4058 | | * In a GET, HEAD or POST request, check if the requested URI matches the stats uri |
4059 | | * for the current proxy. |
4060 | | * |
4061 | | * It is assumed that the request is either a HEAD, GET, or POST and that the |
4062 | | * uri_auth field is valid. |
4063 | | * |
4064 | | * Returns 1 if stats should be provided, otherwise 0. |
4065 | | */ |
4066 | | static int http_stats_check_uri(struct stream *s, struct http_txn *txn, struct proxy *px) |
4067 | 0 | { |
4068 | 0 | struct uri_auth *uri_auth = px->uri_auth; |
4069 | 0 | struct htx *htx; |
4070 | 0 | struct htx_sl *sl; |
4071 | 0 | struct ist uri; |
4072 | |
|
4073 | 0 | if (!uri_auth) |
4074 | 0 | return 0; |
4075 | | |
4076 | 0 | if (txn->meth != HTTP_METH_GET && txn->meth != HTTP_METH_HEAD && txn->meth != HTTP_METH_POST) |
4077 | 0 | return 0; |
4078 | | |
4079 | 0 | htx = htxbuf(&s->req.buf); |
4080 | 0 | sl = http_get_stline(htx); |
4081 | 0 | uri = htx_sl_req_uri(sl); |
4082 | 0 | if (*uri_auth->uri_prefix == '/') { |
4083 | 0 | struct http_uri_parser parser = http_uri_parser_init(uri); |
4084 | 0 | uri = http_parse_path(&parser); |
4085 | 0 | } |
4086 | | |
4087 | | /* check URI size */ |
4088 | 0 | if (uri_auth->uri_len > uri.len) |
4089 | 0 | return 0; |
4090 | | |
4091 | 0 | if (memcmp(uri.ptr, uri_auth->uri_prefix, uri_auth->uri_len) != 0) |
4092 | 0 | return 0; |
4093 | | |
4094 | 0 | return 1; |
4095 | 0 | } |
4096 | | |
4097 | | /* This function prepares an applet to handle the stats. It can deal with the |
4098 | | * "100-continue" expectation, check that admin rules are met for POST requests, |
4099 | | * and program a response message if something was unexpected. It cannot fail |
4100 | | * and always relies on the stats applet to complete the job. It does not touch |
4101 | | * analysers nor counters, which are left to the caller. It does not touch |
4102 | | * s->target which is supposed to already point to the stats applet. The caller |
4103 | | * is expected to have already assigned an appctx to the stream. |
4104 | | */ |
4105 | | static int http_handle_stats(struct stream *s, struct channel *req, struct proxy *px) |
4106 | 0 | { |
4107 | 0 | struct stats_admin_rule *stats_admin_rule; |
4108 | 0 | struct session *sess = s->sess; |
4109 | 0 | struct http_txn *txn = s->txn.http; |
4110 | 0 | struct http_msg *msg = &txn->req; |
4111 | 0 | struct uri_auth *uri_auth = px->uri_auth; |
4112 | 0 | const char *h, *lookup, *end; |
4113 | 0 | struct appctx *appctx = __sc_appctx(s->scb); |
4114 | 0 | struct show_stat_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx)); |
4115 | 0 | struct htx *htx; |
4116 | 0 | struct htx_sl *sl; |
4117 | |
|
4118 | 0 | appctx->st1 = 0; |
4119 | 0 | ctx->state = STAT_STATE_INIT; |
4120 | 0 | ctx->st_code = STAT_STATUS_INIT; |
4121 | 0 | ctx->http_px = px; |
4122 | 0 | ctx->flags |= uri_auth->flags; |
4123 | 0 | ctx->flags |= STAT_F_FMT_HTML; /* assume HTML mode by default */ |
4124 | 0 | if ((msg->flags & HTTP_MSGF_VER_11) && (txn->meth != HTTP_METH_HEAD)) |
4125 | 0 | ctx->flags |= STAT_F_CHUNKED; |
4126 | |
|
4127 | 0 | watcher_init(&ctx->px_watch, &ctx->obj1, offsetof(struct proxy, watcher_list)); |
4128 | 0 | watcher_init(&ctx->srv_watch, &ctx->obj2, offsetof(struct server, watcher_list)); |
4129 | |
|
4130 | 0 | htx = htxbuf(&req->buf); |
4131 | 0 | sl = http_get_stline(htx); |
4132 | 0 | lookup = HTX_SL_REQ_UPTR(sl) + uri_auth->uri_len; |
4133 | 0 | end = HTX_SL_REQ_UPTR(sl) + HTX_SL_REQ_ULEN(sl); |
4134 | |
|
4135 | 0 | for (h = lookup; h <= end - 3; h++) { |
4136 | 0 | if (memcmp(h, ";up", 3) == 0) { |
4137 | 0 | ctx->flags |= STAT_F_HIDE_DOWN; |
4138 | 0 | break; |
4139 | 0 | } |
4140 | 0 | } |
4141 | |
|
4142 | 0 | for (h = lookup; h <= end - 9; h++) { |
4143 | 0 | if (memcmp(h, ";no-maint", 9) == 0) { |
4144 | 0 | ctx->flags |= STAT_F_HIDE_MAINT; |
4145 | 0 | break; |
4146 | 0 | } |
4147 | 0 | } |
4148 | |
|
4149 | 0 | if (uri_auth->refresh) { |
4150 | 0 | for (h = lookup; h <= end - 10; h++) { |
4151 | 0 | if (memcmp(h, ";norefresh", 10) == 0) { |
4152 | 0 | ctx->flags |= STAT_F_NO_REFRESH; |
4153 | 0 | break; |
4154 | 0 | } |
4155 | 0 | } |
4156 | 0 | } |
4157 | |
|
4158 | 0 | for (h = lookup; h <= end - 4; h++) { |
4159 | 0 | if (memcmp(h, ";csv", 4) == 0) { |
4160 | 0 | ctx->flags &= ~(STAT_F_FMT_MASK|STAT_F_JSON_SCHM); |
4161 | 0 | break; |
4162 | 0 | } |
4163 | 0 | } |
4164 | |
|
4165 | 0 | for (h = lookup; h <= end - 6; h++) { |
4166 | 0 | if (memcmp(h, ";typed", 6) == 0) { |
4167 | 0 | ctx->flags &= ~(STAT_F_FMT_MASK|STAT_F_JSON_SCHM); |
4168 | 0 | ctx->flags |= STAT_F_FMT_TYPED; |
4169 | 0 | break; |
4170 | 0 | } |
4171 | 0 | } |
4172 | |
|
4173 | 0 | for (h = lookup; h <= end - 5; h++) { |
4174 | 0 | if (memcmp(h, ";json", 5) == 0) { |
4175 | 0 | ctx->flags &= ~(STAT_F_FMT_MASK|STAT_F_JSON_SCHM); |
4176 | 0 | ctx->flags |= STAT_F_FMT_JSON; |
4177 | 0 | break; |
4178 | 0 | } |
4179 | 0 | } |
4180 | |
|
4181 | 0 | for (h = lookup; h <= end - 12; h++) { |
4182 | 0 | if (memcmp(h, ";json-schema", 12) == 0) { |
4183 | 0 | ctx->flags &= ~STAT_F_FMT_MASK; |
4184 | 0 | ctx->flags |= STAT_F_JSON_SCHM; |
4185 | 0 | break; |
4186 | 0 | } |
4187 | 0 | } |
4188 | |
|
4189 | 0 | for (h = lookup; h <= end - 8; h++) { |
4190 | 0 | if (memcmp(h, ";st=", 4) == 0) { |
4191 | 0 | int i; |
4192 | 0 | h += 4; |
4193 | 0 | ctx->st_code = STAT_STATUS_UNKN; |
4194 | 0 | for (i = STAT_STATUS_INIT + 1; i < STAT_STATUS_SIZE; i++) { |
4195 | 0 | if (strncmp(stat_status_codes[i], h, 4) == 0) { |
4196 | 0 | ctx->st_code = i; |
4197 | 0 | break; |
4198 | 0 | } |
4199 | 0 | } |
4200 | 0 | break; |
4201 | 0 | } |
4202 | 0 | } |
4203 | |
|
4204 | 0 | ctx->scope_str = 0; |
4205 | 0 | ctx->scope_len = 0; |
4206 | 0 | for (h = lookup; h <= end - 8; h++) { |
4207 | 0 | if (memcmp(h, STAT_SCOPE_INPUT_NAME "=", strlen(STAT_SCOPE_INPUT_NAME) + 1) == 0) { |
4208 | 0 | int itx = 0; |
4209 | 0 | const char *h2; |
4210 | 0 | char scope_txt[STAT_SCOPE_TXT_MAXLEN + 1]; |
4211 | 0 | const char *err; |
4212 | |
|
4213 | 0 | h += strlen(STAT_SCOPE_INPUT_NAME) + 1; |
4214 | 0 | h2 = h; |
4215 | 0 | ctx->scope_str = h2 - HTX_SL_REQ_UPTR(sl); |
4216 | 0 | while (h < end) { |
4217 | 0 | if (*h == ';' || *h == '&' || *h == ' ') |
4218 | 0 | break; |
4219 | 0 | itx++; |
4220 | 0 | h++; |
4221 | 0 | } |
4222 | |
|
4223 | 0 | if (itx > STAT_SCOPE_TXT_MAXLEN) |
4224 | 0 | itx = STAT_SCOPE_TXT_MAXLEN; |
4225 | 0 | ctx->scope_len = itx; |
4226 | | |
4227 | | /* scope_txt = search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */ |
4228 | 0 | memcpy(scope_txt, h2, itx); |
4229 | 0 | scope_txt[itx] = '\0'; |
4230 | 0 | err = invalid_char(scope_txt); |
4231 | 0 | if (err) { |
4232 | | /* bad char in search text => clear scope */ |
4233 | 0 | ctx->scope_str = 0; |
4234 | 0 | ctx->scope_len = 0; |
4235 | 0 | } |
4236 | 0 | break; |
4237 | 0 | } |
4238 | 0 | } |
4239 | | |
4240 | | /* now check whether we have some admin rules for this request */ |
4241 | 0 | list_for_each_entry(stats_admin_rule, &uri_auth->admin_rules, list) { |
4242 | 0 | if (!acl_match_cond(stats_admin_rule->cond, s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL)) |
4243 | 0 | continue; |
4244 | | |
4245 | | /* no rule, or the rule matches */ |
4246 | 0 | ctx->flags |= STAT_F_ADMIN; |
4247 | 0 | break; |
4248 | 0 | } |
4249 | |
|
4250 | 0 | if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD) |
4251 | 0 | appctx->st0 = STAT_HTTP_HEAD; |
4252 | 0 | else if (txn->meth == HTTP_METH_POST) { |
4253 | 0 | if (ctx->flags & STAT_F_ADMIN) { |
4254 | 0 | appctx->st0 = STAT_HTTP_POST; |
4255 | 0 | if (msg->msg_state < HTTP_MSG_DATA) |
4256 | 0 | req->analysers |= AN_REQ_HTTP_BODY; |
4257 | 0 | } |
4258 | 0 | else { |
4259 | | /* POST without admin level */ |
4260 | 0 | ctx->flags &= ~STAT_F_CHUNKED; |
4261 | 0 | ctx->st_code = STAT_STATUS_DENY; |
4262 | 0 | appctx->st0 = STAT_HTTP_LAST; |
4263 | 0 | } |
4264 | 0 | } |
4265 | 0 | else { |
4266 | | /* Unsupported method */ |
4267 | 0 | ctx->flags &= ~STAT_F_CHUNKED; |
4268 | 0 | ctx->st_code = STAT_STATUS_IVAL; |
4269 | 0 | appctx->st0 = STAT_HTTP_LAST; |
4270 | 0 | } |
4271 | |
|
4272 | 0 | s->task->nice = -32; /* small boost for HTTP statistics */ |
4273 | 0 | return 1; |
4274 | 0 | } |
4275 | | |
4276 | | /* This function waits for the message payload at most <time> milliseconds (may |
4277 | | * be set to TICK_ETERNITY). It stops to wait if at least <bytes> bytes of the |
4278 | | * payload are received (0 means no limit). It returns HTTP_RULE_* depending on |
4279 | | * the result: |
4280 | | * |
4281 | | * - HTTP_RULE_RES_CONT when conditions are met to stop waiting |
4282 | | * - HTTP_RULE_RES_YIELD to wait for more data |
4283 | | * - HTTP_RULE_RES_ABRT when a timeout occurred. |
4284 | | * - HTTP_RULE_RES_BADREQ if a parsing error is raised by lower level |
4285 | | * - HTTP_RULE_RES_ERROR if an internal error occurred |
4286 | | * |
4287 | | * If a timeout occurred, this function is responsible to emit the right response |
4288 | | * to the client, depending on the channel (408 on request side, 504 on response |
4289 | | * side). All other errors must be handled by the caller. |
4290 | | */ |
4291 | | enum rule_result http_wait_for_msg_body(struct stream *s, struct channel *chn, |
4292 | | unsigned int time, unsigned int bytes, unsigned int large_buffer) |
4293 | 0 | { |
4294 | 0 | struct session *sess = s->sess; |
4295 | 0 | struct http_txn *txn = s->txn.http; |
4296 | 0 | struct http_msg *msg = ((chn->flags & CF_ISRESP) ? &txn->rsp : &txn->req); |
4297 | 0 | struct htx *htx; |
4298 | 0 | enum rule_result ret = HTTP_RULE_RES_CONT; |
4299 | |
|
4300 | 0 | htx = htxbuf(&chn->buf); |
4301 | |
|
4302 | 0 | if (htx->flags & HTX_FL_PARSING_ERROR) { |
4303 | 0 | ret = HTTP_RULE_RES_BADREQ; |
4304 | 0 | goto end; |
4305 | 0 | } |
4306 | 0 | if (htx->flags & HTX_FL_PROCESSING_ERROR) { |
4307 | 0 | ret = HTTP_RULE_RES_ERROR; |
4308 | 0 | goto end; |
4309 | 0 | } |
4310 | | |
4311 | | /* Do nothing for bodyless and CONNECT requests */ |
4312 | 0 | if (txn->meth == HTTP_METH_CONNECT || (msg->flags & HTTP_MSGF_BODYLESS)) |
4313 | 0 | goto end; |
4314 | | |
4315 | 0 | if (!(chn->flags & CF_ISRESP)) { |
4316 | 0 | if (http_handle_expect_hdr(s, htx, msg) == -1) { |
4317 | 0 | ret = HTTP_RULE_RES_ERROR; |
4318 | 0 | goto end; |
4319 | 0 | } |
4320 | 0 | } |
4321 | | |
4322 | | /* Now we're are waiting for the payload. We just need to know if all |
4323 | | * data have been received or if the buffer is full. |
4324 | | */ |
4325 | 0 | if ((htx->flags & HTX_FL_EOM) || htx_get_tail_type(htx) > HTX_BLK_DATA) |
4326 | 0 | goto end; /* all data received */ |
4327 | | |
4328 | 0 | if (bytes) { |
4329 | 0 | struct htx_blk *blk; |
4330 | 0 | unsigned int len = 0; |
4331 | |
|
4332 | 0 | for (blk = htx_get_first_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) { |
4333 | 0 | if (htx_get_blk_type(blk) != HTX_BLK_DATA) |
4334 | 0 | continue; |
4335 | 0 | len += htx_get_blksz(blk); |
4336 | 0 | if (len >= bytes) |
4337 | 0 | goto end; |
4338 | 0 | } |
4339 | 0 | } |
4340 | | |
4341 | 0 | if (channel_htx_full(chn, htx, global.tune.maxrewrite) || sc_waiting_room(chn_prod(chn))) { |
4342 | 0 | struct buffer lbuf = BUF_NULL; |
4343 | |
|
4344 | 0 | if (large_buffer == 0 || b_is_large(&chn->buf) || !htx_move_to_large_buffer(&lbuf, &chn->buf)) |
4345 | 0 | goto end; /* don't use large buffer or already a large buffer */ |
4346 | 0 | b_free(&chn->buf); |
4347 | 0 | offer_buffers(s, 1); |
4348 | 0 | chn->buf = lbuf; |
4349 | 0 | htx = htxbuf(&chn->buf); |
4350 | 0 | } |
4351 | | |
4352 | 0 | if ((chn->flags & CF_READ_TIMEOUT) || tick_is_expired(chn->analyse_exp, now_ms)) { |
4353 | 0 | if (!(chn->flags & CF_ISRESP)) |
4354 | 0 | goto abort_req; |
4355 | 0 | goto abort_res; |
4356 | 0 | } |
4357 | | |
4358 | | /* we get here if we need to wait for more data */ |
4359 | | |
4360 | 0 | if ((s->scf->flags & SC_FL_ERROR) || |
4361 | 0 | ((s->scf->flags & SC_FL_EOS) && proxy_abrt_close_def(s->be, 1))) |
4362 | 0 | ret = HTTP_RULE_RES_CONT; |
4363 | 0 | else if (!(chn_prod(chn)->flags & (SC_FL_ERROR|SC_FL_EOS|SC_FL_ABRT_DONE))) { |
4364 | 0 | if (!tick_isset(chn->analyse_exp)) |
4365 | 0 | chn->analyse_exp = tick_add_ifset(now_ms, time); |
4366 | 0 | ret = HTTP_RULE_RES_YIELD; |
4367 | 0 | } |
4368 | |
|
4369 | 0 | end: |
4370 | 0 | if (ret != HTTP_RULE_RES_YIELD) |
4371 | 0 | chn->analyse_exp = TICK_ETERNITY; |
4372 | |
|
4373 | 0 | if (htx->flags & (HTX_FL_FRAGMENTED|HTX_FL_UNORDERED)) |
4374 | 0 | htx_defrag(htx, NULL, 0); |
4375 | 0 | return ret; |
4376 | | |
4377 | 0 | abort: |
4378 | 0 | http_set_term_flags(s); |
4379 | 0 | http_reply_and_close(s, txn->status, http_error_message(s)); |
4380 | 0 | ret = HTTP_RULE_RES_ABRT; |
4381 | 0 | goto end; |
4382 | | |
4383 | 0 | abort_req: |
4384 | 0 | txn->status = 408; |
4385 | 0 | if (!(s->flags & SF_ERR_MASK)) |
4386 | 0 | s->flags |= SF_ERR_CLITO; |
4387 | 0 | if (sess->fe_tgcounters) |
4388 | 0 | _HA_ATOMIC_INC(&sess->fe_tgcounters->failed_req); |
4389 | 0 | if (sess->li_tgcounters) |
4390 | 0 | _HA_ATOMIC_INC(&sess->li_tgcounters->failed_req); |
4391 | 0 | goto abort; |
4392 | | |
4393 | 0 | abort_res: |
4394 | 0 | txn->status = 504; |
4395 | 0 | if (!(s->flags & SF_ERR_MASK)) |
4396 | 0 | s->flags |= SF_ERR_SRVTO; |
4397 | 0 | stream_inc_http_fail_ctr(s); |
4398 | 0 | goto abort; |
4399 | 0 | } |
4400 | | |
4401 | | void http_perform_server_redirect(struct stream *s, struct stconn *sc) |
4402 | 0 | { |
4403 | 0 | struct channel *req = &s->req; |
4404 | 0 | struct channel *res = &s->res; |
4405 | 0 | struct server *srv; |
4406 | 0 | struct htx *htx; |
4407 | 0 | struct htx_sl *sl; |
4408 | 0 | struct ist path, location; |
4409 | 0 | unsigned int flags; |
4410 | 0 | struct http_uri_parser parser; |
4411 | | |
4412 | | /* |
4413 | | * Create the location |
4414 | | */ |
4415 | 0 | chunk_reset(&trash); |
4416 | | |
4417 | | /* 1: add the server's prefix */ |
4418 | | /* special prefix "/" means don't change URL */ |
4419 | 0 | srv = __objt_server(s->target); |
4420 | 0 | if (srv->rdr_len != 1 || *srv->rdr_pfx != '/') { |
4421 | 0 | if (!chunk_memcat(&trash, srv->rdr_pfx, srv->rdr_len)) |
4422 | 0 | return; |
4423 | 0 | } |
4424 | | |
4425 | | /* 2: add the request Path */ |
4426 | 0 | htx = htxbuf(&req->buf); |
4427 | 0 | sl = http_get_stline(htx); |
4428 | 0 | parser = http_uri_parser_init(htx_sl_req_uri(sl)); |
4429 | 0 | path = http_parse_path(&parser); |
4430 | 0 | if (!isttest(path)) |
4431 | 0 | return; |
4432 | | |
4433 | 0 | if (!chunk_memcat(&trash, path.ptr, path.len)) |
4434 | 0 | return; |
4435 | 0 | location = ist2(trash.area, trash.data); |
4436 | | |
4437 | | /* |
4438 | | * Create the 302 response |
4439 | | */ |
4440 | 0 | htx = htx_from_buf(&res->buf); |
4441 | 0 | flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN|HTX_SL_F_BODYLESS); |
4442 | 0 | sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, |
4443 | 0 | ist("HTTP/1.1"), ist("302"), ist("Found")); |
4444 | 0 | if (!sl) |
4445 | 0 | goto fail; |
4446 | 0 | sl->info.res.status = 302; |
4447 | 0 | s->txn.http->status = 302; |
4448 | |
|
4449 | 0 | if (!htx_add_header(htx, ist("Cache-Control"), ist("no-cache")) || |
4450 | 0 | !htx_add_header(htx, ist("Content-length"), ist("0")) || |
4451 | 0 | !htx_add_header(htx, ist("Location"), location)) |
4452 | 0 | goto fail; |
4453 | | |
4454 | 0 | if (!htx_add_endof(htx, HTX_BLK_EOH)) |
4455 | 0 | goto fail; |
4456 | | |
4457 | 0 | htx->flags |= HTX_FL_EOM; |
4458 | 0 | htx_to_buf(htx, &res->buf); |
4459 | |
|
4460 | 0 | if (!(s->flags & SF_ERR_MASK)) |
4461 | 0 | s->flags |= SF_ERR_LOCAL; |
4462 | 0 | if (!(s->flags & SF_FINST_MASK)) |
4463 | 0 | s->flags |= SF_FINST_C; |
4464 | |
|
4465 | 0 | if (!http_forward_proxy_resp(s, 1)) |
4466 | 0 | goto fail; |
4467 | | |
4468 | | /* return without error. */ |
4469 | 0 | sc_abort(sc); |
4470 | 0 | sc_shutdown(sc); |
4471 | 0 | s->conn_err_type = STRM_ET_NONE; |
4472 | 0 | sc->state = SC_ST_CLO; |
4473 | | |
4474 | | |
4475 | | /* FIXME: we should increase a counter of redirects per server and per backend. */ |
4476 | 0 | srv_inc_sess_ctr(srv); |
4477 | 0 | srv_set_sess_last(srv); |
4478 | 0 | return; |
4479 | | |
4480 | 0 | fail: |
4481 | | /* If an error occurred, remove the incomplete HTTP response from the |
4482 | | * buffer */ |
4483 | 0 | channel_htx_truncate(res, htx); |
4484 | 0 | } |
4485 | | |
4486 | | /* This function terminates the request because it was completely analyzed or |
4487 | | * because an error was triggered during the body forwarding. |
4488 | | */ |
4489 | | static void http_end_request(struct stream *s) |
4490 | 0 | { |
4491 | 0 | struct channel *chn = &s->req; |
4492 | 0 | struct http_txn *txn = s->txn.http; |
4493 | |
|
4494 | 0 | DBG_TRACE_ENTER(STRM_EV_HTTP_ANA, s, txn); |
4495 | |
|
4496 | 0 | if (unlikely(txn->req.msg_state < HTTP_MSG_DONE)) { |
4497 | 0 | DBG_TRACE_DEVEL("waiting end of the request", STRM_EV_HTTP_ANA, s, txn); |
4498 | 0 | return; |
4499 | 0 | } |
4500 | | |
4501 | 0 | if (txn->req.msg_state == HTTP_MSG_DONE) { |
4502 | | /* No need to read anymore, the request was completely parsed. |
4503 | | * We can shut the read side unless we want to abort_on_close, |
4504 | | * or we have a POST request. The issue with POST requests is |
4505 | | * that some browsers still send a CRLF after the request, and |
4506 | | * this CRLF must be read so that it does not remain in the kernel |
4507 | | * buffers, otherwise a close could cause an RST on some systems |
4508 | | * (eg: Linux). |
4509 | | */ |
4510 | 0 | if (!proxy_abrt_close_def(s->be, 1) && txn->meth != HTTP_METH_POST) |
4511 | 0 | channel_dont_read(chn); |
4512 | | |
4513 | | /* if the server closes the connection, we want to immediately react |
4514 | | * and close the socket to save packets and syscalls. |
4515 | | */ |
4516 | 0 | s->scb->flags |= SC_FL_NOHALF; |
4517 | | |
4518 | | /* In any case we've finished parsing the request so we must |
4519 | | * disable Nagle when sending data because 1) we're not going |
4520 | | * to shut this side, and 2) the server is waiting for us to |
4521 | | * send pending data. |
4522 | | */ |
4523 | 0 | s->scb->flags |= SC_FL_SND_NEVERWAIT; |
4524 | |
|
4525 | 0 | if (txn->rsp.msg_state < HTTP_MSG_BODY || |
4526 | 0 | (txn->rsp.msg_state < HTTP_MSG_DONE && s->scb->state != SC_ST_CLO)) { |
4527 | | /* The server has not finished to respond and the |
4528 | | * backend SC is not closed, so we don't want to move in |
4529 | | * order not to upset it. |
4530 | | */ |
4531 | 0 | DBG_TRACE_DEVEL("waiting end of the response", STRM_EV_HTTP_ANA, s, txn); |
4532 | 0 | return; |
4533 | 0 | } |
4534 | | |
4535 | | /* When we get here, it means that both the request and the |
4536 | | * response have finished receiving. Depending on the connection |
4537 | | * mode, we'll have to wait for the last bytes to leave in either |
4538 | | * direction, and sometimes for a close to be effective. |
4539 | | */ |
4540 | 0 | if (txn->flags & TX_CON_WANT_TUN) { |
4541 | | /* Tunnel mode will not have any analyser so it needs to |
4542 | | * poll for reads. |
4543 | | */ |
4544 | 0 | channel_auto_read(&s->req); |
4545 | 0 | txn->req.msg_state = HTTP_MSG_TUNNEL; |
4546 | 0 | s->scb->flags &= ~SC_FL_NOHALF; |
4547 | 0 | if (txn->rsp.msg_state != HTTP_MSG_TUNNEL) |
4548 | 0 | s->res.flags |= CF_WAKE_ONCE; |
4549 | 0 | } |
4550 | 0 | else { |
4551 | | /* we're not expecting any new data to come for this |
4552 | | * transaction, so we can close it. |
4553 | | * |
4554 | | * However, there is an exception if the response |
4555 | | * length is undefined. In this case, we need to wait |
4556 | | * the close from the server. The response will be |
4557 | | * switched in TUNNEL mode until the end. |
4558 | | */ |
4559 | 0 | if (!(txn->rsp.flags & HTTP_MSGF_XFER_LEN) && |
4560 | 0 | txn->rsp.msg_state != HTTP_MSG_CLOSED) |
4561 | 0 | goto check_channel_flags; |
4562 | | |
4563 | 0 | if (!(s->scb->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED))) { |
4564 | 0 | sc_schedule_abort(s->scf); |
4565 | 0 | sc_schedule_shutdown(s->scb); |
4566 | 0 | } |
4567 | 0 | } |
4568 | 0 | goto check_channel_flags; |
4569 | 0 | } |
4570 | | |
4571 | 0 | if (txn->req.msg_state == HTTP_MSG_CLOSING) { |
4572 | 0 | http_msg_closing: |
4573 | | /* nothing else to forward, just waiting for the output buffer |
4574 | | * to be empty and for the shut_wanted to take effect. |
4575 | | */ |
4576 | 0 | if (!co_data(chn)) { |
4577 | 0 | txn->req.msg_state = HTTP_MSG_CLOSED; |
4578 | 0 | goto http_msg_closed; |
4579 | 0 | } |
4580 | 0 | DBG_TRACE_LEAVE(STRM_EV_HTTP_ANA, s, txn); |
4581 | 0 | return; |
4582 | 0 | } |
4583 | | |
4584 | 0 | if (txn->req.msg_state == HTTP_MSG_CLOSED) { |
4585 | 0 | http_msg_closed: |
4586 | | /* if we don't know whether the server will close, we need to hard close */ |
4587 | 0 | if (txn->rsp.flags & HTTP_MSGF_XFER_LEN) |
4588 | 0 | s->scb->flags |= SC_FL_NOLINGER; /* we want to close ASAP */ |
4589 | | /* see above in MSG_DONE why we only do this in these states */ |
4590 | 0 | if (!proxy_abrt_close_def(s->be, 1)) |
4591 | 0 | channel_dont_read(chn); |
4592 | 0 | goto end; |
4593 | 0 | } |
4594 | | |
4595 | 0 | check_channel_flags: |
4596 | | /* Here, we are in HTTP_MSG_DONE or HTTP_MSG_TUNNEL */ |
4597 | 0 | if (s->scb->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED)) { |
4598 | | /* if we've just closed an output, let's switch */ |
4599 | 0 | txn->req.msg_state = HTTP_MSG_CLOSING; |
4600 | 0 | goto http_msg_closing; |
4601 | 0 | } |
4602 | | |
4603 | 0 | end: |
4604 | 0 | chn->analysers &= AN_REQ_FLT_END; |
4605 | 0 | if (txn->req.msg_state == HTTP_MSG_TUNNEL) { |
4606 | 0 | s->scb->flags |= SC_FL_SND_NEVERWAIT; |
4607 | 0 | if (HAS_REQ_DATA_FILTERS(s)) |
4608 | 0 | chn->analysers |= AN_REQ_FLT_XFER_DATA; |
4609 | 0 | else |
4610 | 0 | c_adv(chn, htxbuf(&chn->buf)->data - co_data(chn)); |
4611 | 0 | } |
4612 | 0 | channel_auto_close(chn); |
4613 | 0 | channel_auto_read(chn); |
4614 | 0 | DBG_TRACE_LEAVE(STRM_EV_HTTP_ANA, s, txn); |
4615 | 0 | } |
4616 | | |
4617 | | |
4618 | | /* This function terminates the response because it was completely analyzed or |
4619 | | * because an error was triggered during the body forwarding. |
4620 | | */ |
4621 | | static void http_end_response(struct stream *s) |
4622 | 0 | { |
4623 | 0 | struct channel *chn = &s->res; |
4624 | 0 | struct http_txn *txn = s->txn.http; |
4625 | |
|
4626 | 0 | DBG_TRACE_ENTER(STRM_EV_HTTP_ANA, s, txn); |
4627 | |
|
4628 | 0 | if (unlikely(txn->rsp.msg_state < HTTP_MSG_DONE)) { |
4629 | 0 | DBG_TRACE_DEVEL("waiting end of the response", STRM_EV_HTTP_ANA, s, txn); |
4630 | 0 | return; |
4631 | 0 | } |
4632 | | |
4633 | 0 | if (txn->rsp.msg_state == HTTP_MSG_DONE) { |
4634 | | /* In theory, we don't need to read anymore, but we must |
4635 | | * still monitor the server connection for a possible close |
4636 | | * while the request is being uploaded, so we don't disable |
4637 | | * reading. |
4638 | | */ |
4639 | | /* channel_dont_read(chn); */ |
4640 | |
|
4641 | 0 | if (txn->req.msg_state < HTTP_MSG_DONE && s->scf->state != SC_ST_CLO) { |
4642 | | /* The client seems to still be sending data, probably |
4643 | | * because we got an error response during an upload. |
4644 | | * We have the choice of either breaking the connection |
4645 | | * or letting it pass through. Let's do the later. |
4646 | | */ |
4647 | 0 | DBG_TRACE_DEVEL("waiting end of the request", STRM_EV_HTTP_ANA, s, txn); |
4648 | 0 | return; |
4649 | 0 | } |
4650 | | |
4651 | | /* When we get here, it means that both the request and the |
4652 | | * response have finished receiving. Depending on the connection |
4653 | | * mode, we'll have to wait for the last bytes to leave in either |
4654 | | * direction, and sometimes for a close to be effective. |
4655 | | */ |
4656 | 0 | if (txn->flags & TX_CON_WANT_TUN) { |
4657 | 0 | channel_auto_read(&s->res); |
4658 | 0 | txn->rsp.msg_state = HTTP_MSG_TUNNEL; |
4659 | 0 | if (txn->req.msg_state != HTTP_MSG_TUNNEL) |
4660 | 0 | s->req.flags |= CF_WAKE_ONCE; |
4661 | 0 | } |
4662 | 0 | else { |
4663 | | /* we're not expecting any new data to come for this |
4664 | | * transaction, so we can close it. |
4665 | | */ |
4666 | 0 | if (!(s->scf->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED))) { |
4667 | 0 | sc_schedule_abort(s->scb); |
4668 | 0 | sc_schedule_shutdown(s->scf); |
4669 | 0 | } |
4670 | 0 | } |
4671 | 0 | goto check_channel_flags; |
4672 | 0 | } |
4673 | | |
4674 | 0 | if (txn->rsp.msg_state == HTTP_MSG_CLOSING) { |
4675 | 0 | http_msg_closing: |
4676 | | /* nothing else to forward, just waiting for the output buffer |
4677 | | * to be empty and for the shut_wanted to take effect. |
4678 | | */ |
4679 | 0 | if (!co_data(chn)) { |
4680 | 0 | txn->rsp.msg_state = HTTP_MSG_CLOSED; |
4681 | 0 | goto http_msg_closed; |
4682 | 0 | } |
4683 | 0 | DBG_TRACE_LEAVE(STRM_EV_HTTP_ANA, s, txn); |
4684 | 0 | return; |
4685 | 0 | } |
4686 | | |
4687 | 0 | if (txn->rsp.msg_state == HTTP_MSG_CLOSED) { |
4688 | 0 | http_msg_closed: |
4689 | | /* drop any pending data */ |
4690 | 0 | channel_htx_truncate(&s->req, htxbuf(&s->req.buf)); |
4691 | 0 | channel_abort(&s->req); |
4692 | 0 | goto end; |
4693 | 0 | } |
4694 | | |
4695 | 0 | check_channel_flags: |
4696 | | /* Here, we are in HTTP_MSG_DONE or HTTP_MSG_TUNNEL */ |
4697 | 0 | if (s->scf->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED)) { |
4698 | | /* if we've just closed an output, let's switch */ |
4699 | 0 | txn->rsp.msg_state = HTTP_MSG_CLOSING; |
4700 | 0 | goto http_msg_closing; |
4701 | 0 | } |
4702 | | |
4703 | 0 | end: |
4704 | 0 | chn->analysers &= AN_RES_FLT_END; |
4705 | 0 | if (txn->rsp.msg_state == HTTP_MSG_TUNNEL) { |
4706 | 0 | s->scf->flags |= SC_FL_SND_NEVERWAIT; |
4707 | 0 | if (HAS_RSP_DATA_FILTERS(s)) |
4708 | 0 | chn->analysers |= AN_RES_FLT_XFER_DATA; |
4709 | 0 | else |
4710 | 0 | c_adv(chn, htxbuf(&chn->buf)->data - co_data(chn)); |
4711 | 0 | } |
4712 | 0 | channel_auto_close(chn); |
4713 | 0 | channel_auto_read(chn); |
4714 | 0 | DBG_TRACE_LEAVE(STRM_EV_HTTP_ANA, s, txn); |
4715 | 0 | } |
4716 | | |
4717 | | /* Forward a response generated by HAProxy (error/redirect/return). This |
4718 | | * function forwards all pending incoming data. If <final> is set to 0, nothing |
4719 | | * more is performed. It is used for 1xx informational messages. Otherwise, the |
4720 | | * transaction is terminated and the request is emptied. On success 1 is |
4721 | | * returned. If an error occurred, 0 is returned. If it fails, this function |
4722 | | * only exits. It is the caller responsibility to do the cleanup. |
4723 | | */ |
4724 | | int http_forward_proxy_resp(struct stream *s, int final) |
4725 | 0 | { |
4726 | 0 | struct channel *req = &s->req; |
4727 | 0 | struct channel *res = &s->res; |
4728 | 0 | struct htx *htx = htxbuf(&res->buf); |
4729 | 0 | size_t data; |
4730 | |
|
4731 | 0 | if (final) { |
4732 | 0 | if (s->txn.http->server_status == -1) |
4733 | 0 | s->txn.http->server_status = 0; |
4734 | |
|
4735 | 0 | if (!htx_is_empty(htx) && !http_eval_after_res_rules(s)) |
4736 | 0 | return 0; |
4737 | | |
4738 | 0 | if (s->txn.http->meth == HTTP_METH_HEAD) |
4739 | 0 | htx_skip_msg_payload(htx); |
4740 | | |
4741 | | /* Response from haproxy, override HTTP response version using the request one */ |
4742 | 0 | s->txn.http->rsp.vsn = s->txn.http->req.vsn; |
4743 | |
|
4744 | 0 | channel_auto_read(req); |
4745 | 0 | channel_abort(req); |
4746 | 0 | channel_htx_erase(req, htxbuf(&req->buf)); |
4747 | |
|
4748 | 0 | channel_auto_read(res); |
4749 | 0 | channel_auto_close(res); |
4750 | 0 | sc_schedule_abort(s->scb); |
4751 | 0 | s->scb->flags |= SC_FL_EOI; /* The response is terminated, add EOI */ |
4752 | 0 | htxbuf(&res->buf)->flags |= HTX_FL_EOM; /* no more data are expected */ |
4753 | 0 | } |
4754 | 0 | else { |
4755 | | /* Send ASAP informational messages. Rely on SC_FL_EOI for final |
4756 | | * response. |
4757 | | */ |
4758 | 0 | s->scf->flags |= SC_FL_SND_ASAP; |
4759 | 0 | } |
4760 | | |
4761 | 0 | data = htx->data - co_data(res); |
4762 | 0 | c_adv(res, data); |
4763 | 0 | htx->first = -1; |
4764 | 0 | s->scf->bytes_out += data; |
4765 | 0 | return 1; |
4766 | 0 | } |
4767 | | |
4768 | | void http_server_error(struct stream *s, struct stconn *sc, int err, |
4769 | | int finst, struct http_reply *msg) |
4770 | 0 | { |
4771 | 0 | if (!(s->flags & SF_ERR_MASK)) |
4772 | 0 | s->flags |= err; |
4773 | 0 | if (!(s->flags & SF_FINST_MASK)) |
4774 | 0 | s->flags |= finst; |
4775 | |
|
4776 | 0 | http_reply_and_close(s, s->txn.http->status, msg); |
4777 | 0 | } |
4778 | | |
4779 | | void http_reply_and_close(struct stream *s, short status, struct http_reply *msg) |
4780 | 0 | { |
4781 | 0 | if (!msg) { |
4782 | 0 | channel_htx_truncate(&s->res, htxbuf(&s->res.buf)); |
4783 | 0 | goto end; |
4784 | 0 | } |
4785 | | |
4786 | 0 | if (http_reply_message(s, msg) == -1) { |
4787 | | /* On error, return a 500 error message, but don't rewrite it if |
4788 | | * it is already an internal error. If it was already a "const" |
4789 | | * 500 error, just fail. |
4790 | | */ |
4791 | 0 | if (s->txn.http->status == 500) { |
4792 | 0 | if (s->txn.http->flags & TX_CONST_REPLY) |
4793 | 0 | goto end; |
4794 | 0 | s->txn.http->flags |= TX_CONST_REPLY; |
4795 | 0 | } |
4796 | 0 | s->txn.http->status = 500; |
4797 | 0 | s->txn.http->http_reply = NULL; |
4798 | 0 | return http_reply_and_close(s, s->txn.http->status, |
4799 | 0 | http_error_message(s)); |
4800 | 0 | } |
4801 | | |
4802 | 0 | end: |
4803 | | /* At this staged, HTTP analysis is finished */ |
4804 | 0 | s->req.analysers &= AN_REQ_FLT_END; |
4805 | 0 | s->req.analyse_exp = TICK_ETERNITY; |
4806 | |
|
4807 | 0 | s->res.analysers &= AN_RES_FLT_END; |
4808 | 0 | s->res.analyse_exp = TICK_ETERNITY; |
4809 | |
|
4810 | 0 | channel_auto_read(&s->req); |
4811 | 0 | channel_abort(&s->req); |
4812 | 0 | channel_htx_erase(&s->req, htxbuf(&s->req.buf)); |
4813 | 0 | channel_auto_read(&s->res); |
4814 | 0 | channel_auto_close(&s->res); |
4815 | 0 | sc_schedule_abort(s->scb); |
4816 | 0 | } |
4817 | | |
4818 | | struct http_reply *http_error_message(struct stream *s) |
4819 | 0 | { |
4820 | 0 | const int msgnum = http_get_status_idx(s->txn.http->status); |
4821 | |
|
4822 | 0 | if (s->txn.http->http_reply) |
4823 | 0 | return s->txn.http->http_reply; |
4824 | 0 | else if (s->be->replies[msgnum]) |
4825 | 0 | return s->be->replies[msgnum]; |
4826 | 0 | else if (strm_fe(s)->replies[msgnum]) |
4827 | 0 | return strm_fe(s)->replies[msgnum]; |
4828 | 0 | else |
4829 | 0 | return &http_err_replies[msgnum]; |
4830 | 0 | } |
4831 | | |
4832 | | /* Produces an HTX message from an http reply. Depending on the http reply type, |
4833 | | * an errorfile, a raw file or a log-format string is used. On success, it |
4834 | | * returns 0. If an error occurs -1 is returned. If it fails, this function only |
4835 | | * exits. It is the caller responsibility to do the cleanup. |
4836 | | */ |
4837 | | int http_reply_to_htx(struct stream *s, struct htx *htx, struct http_reply *reply) |
4838 | 0 | { |
4839 | 0 | struct buffer *errmsg; |
4840 | 0 | struct htx_sl *sl; |
4841 | 0 | struct buffer *body = NULL; |
4842 | 0 | const char *status, *reason, *clen, *ctype; |
4843 | 0 | unsigned int slflags; |
4844 | 0 | int ret = 0; |
4845 | | |
4846 | | /* |
4847 | | * - HTTP_REPLY_ERRFILES unexpected here. handled as no payload if so |
4848 | | * |
4849 | | * - HTTP_REPLY_INDIRECT: switch on another reply if defined or handled |
4850 | | * as no payload if NULL. the TXN status code is set with the status |
4851 | | * of the original reply. |
4852 | | */ |
4853 | |
|
4854 | 0 | if (reply->type == HTTP_REPLY_INDIRECT) { |
4855 | 0 | if (reply->body.reply) |
4856 | 0 | reply = reply->body.reply; |
4857 | 0 | } |
4858 | 0 | if (reply->type == HTTP_REPLY_ERRMSG && !reply->body.errmsg) { |
4859 | | /* get default error message */ |
4860 | 0 | if (reply == s->txn.http->http_reply) |
4861 | 0 | s->txn.http->http_reply = NULL; |
4862 | 0 | reply = http_error_message(s); |
4863 | 0 | if (reply->type == HTTP_REPLY_INDIRECT) { |
4864 | 0 | if (reply->body.reply) |
4865 | 0 | reply = reply->body.reply; |
4866 | 0 | } |
4867 | 0 | } |
4868 | |
|
4869 | 0 | if (reply->type == HTTP_REPLY_ERRMSG) { |
4870 | | /* implicit or explicit error message*/ |
4871 | 0 | errmsg = reply->body.errmsg; |
4872 | 0 | if (errmsg && !b_is_null(errmsg)) { |
4873 | 0 | if (!htx_copy_msg(htx, errmsg)) |
4874 | 0 | goto fail; |
4875 | 0 | } |
4876 | 0 | } |
4877 | 0 | else { |
4878 | | /* no payload, file or log-format string */ |
4879 | 0 | if (reply->type == HTTP_REPLY_RAW) { |
4880 | | /* file */ |
4881 | 0 | body = &reply->body.obj; |
4882 | 0 | } |
4883 | 0 | else if (reply->type == HTTP_REPLY_LOGFMT) { |
4884 | | /* log-format string */ |
4885 | 0 | body = alloc_trash_chunk(); |
4886 | 0 | if (!body) |
4887 | 0 | goto fail_alloc; |
4888 | 0 | body->data = build_logline(s, body->area, body->size, &reply->body.fmt); |
4889 | 0 | } |
4890 | | /* else no payload */ |
4891 | | |
4892 | 0 | status = ultoa(reply->status); |
4893 | 0 | reason = http_get_reason(reply->status); |
4894 | 0 | slflags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN); |
4895 | 0 | if (!body || !b_data(body)) |
4896 | 0 | slflags |= HTX_SL_F_BODYLESS; |
4897 | 0 | sl = htx_add_stline(htx, HTX_BLK_RES_SL, slflags, ist("HTTP/1.1"), ist(status), ist(reason)); |
4898 | 0 | if (!sl) |
4899 | 0 | goto fail; |
4900 | 0 | sl->info.res.status = reply->status; |
4901 | |
|
4902 | 0 | clen = (body ? ultoa(b_data(body)) : "0"); |
4903 | 0 | ctype = reply->ctype; |
4904 | |
|
4905 | 0 | if (!LIST_ISEMPTY(&reply->hdrs)) { |
4906 | 0 | struct http_reply_hdr *hdr; |
4907 | 0 | struct buffer *value = alloc_trash_chunk(); |
4908 | |
|
4909 | 0 | if (!value) |
4910 | 0 | goto fail; |
4911 | | |
4912 | 0 | list_for_each_entry(hdr, &reply->hdrs, list) { |
4913 | 0 | chunk_reset(value); |
4914 | 0 | value->data = build_logline(s, value->area, value->size, &hdr->value); |
4915 | 0 | if (b_data(value) && !htx_add_header(htx, hdr->name, ist2(b_head(value), b_data(value)))) { |
4916 | 0 | free_trash_chunk(value); |
4917 | 0 | goto fail; |
4918 | 0 | } |
4919 | 0 | chunk_reset(value); |
4920 | 0 | } |
4921 | 0 | free_trash_chunk(value); |
4922 | 0 | } |
4923 | | |
4924 | 0 | if (!htx_add_header(htx, ist("content-length"), ist(clen)) || |
4925 | 0 | (body && b_data(body) && ctype && !htx_add_header(htx, ist("content-type"), ist(ctype))) || |
4926 | 0 | !htx_add_endof(htx, HTX_BLK_EOH) || |
4927 | 0 | (body && b_data(body) && !htx_add_data_atonce(htx, ist2(b_head(body), b_data(body))))) |
4928 | 0 | goto fail; |
4929 | | |
4930 | 0 | htx->flags |= HTX_FL_EOM; |
4931 | 0 | } |
4932 | | |
4933 | 0 | leave: |
4934 | 0 | if (reply->type == HTTP_REPLY_LOGFMT) |
4935 | 0 | free_trash_chunk(body); |
4936 | 0 | return ret; |
4937 | | |
4938 | 0 | fail_alloc: |
4939 | 0 | if (!(s->flags & SF_ERR_MASK)) |
4940 | 0 | s->flags |= SF_ERR_RESOURCE; |
4941 | | /* fall through */ |
4942 | 0 | fail: |
4943 | 0 | ret = -1; |
4944 | 0 | goto leave; |
4945 | 0 | } |
4946 | | |
4947 | | /* Send an http reply to the client. On success, it returns 0. If an error |
4948 | | * occurs -1 is returned and the response channel is truncated, removing this |
4949 | | * way the faulty reply. This function may fail when the reply is formatted |
4950 | | * (http_reply_to_htx) or when the reply is forwarded |
4951 | | * (http_forward_proxy_resp). On the last case, it is because a |
4952 | | * http-after-response rule fails. |
4953 | | */ |
4954 | | int http_reply_message(struct stream *s, struct http_reply *reply) |
4955 | 0 | { |
4956 | 0 | struct channel *res = &s->res; |
4957 | 0 | struct htx *htx = htx_from_buf(&res->buf); |
4958 | |
|
4959 | 0 | if (s->txn.http->status == -1) |
4960 | 0 | s->txn.http->status = reply->status; |
4961 | 0 | channel_htx_truncate(res, htx); |
4962 | |
|
4963 | 0 | if (http_reply_to_htx(s, htx, reply) == -1) |
4964 | 0 | goto fail; |
4965 | | |
4966 | 0 | htx_to_buf(htx, &s->res.buf); |
4967 | 0 | if (!http_forward_proxy_resp(s, 1)) |
4968 | 0 | goto fail; |
4969 | 0 | return 0; |
4970 | | |
4971 | 0 | fail: |
4972 | 0 | channel_htx_truncate(res, htx); |
4973 | 0 | if (!(s->flags & SF_ERR_MASK)) |
4974 | 0 | s->flags |= SF_ERR_PRXCOND; |
4975 | 0 | return -1; |
4976 | 0 | } |
4977 | | |
4978 | | /* Return the error message corresponding to s->conn_err_type. It is assumed |
4979 | | * that the server side is closed. Note that err_type is actually a |
4980 | | * bitmask, where almost only aborts may be cumulated with other |
4981 | | * values. We consider that aborted operations are more important |
4982 | | * than timeouts or errors due to the fact that nobody else in the |
4983 | | * logs might explain incomplete retries. All others should avoid |
4984 | | * being cumulated. It should normally not be possible to have multiple |
4985 | | * aborts at once, but just in case, the first one in sequence is reported. |
4986 | | * Note that connection errors appearing on the second request of a keep-alive |
4987 | | * connection are not reported since this allows the client to retry. |
4988 | | */ |
4989 | | void http_return_srv_error(struct stream *s, struct stconn *sc) |
4990 | 0 | { |
4991 | 0 | int err_type = s->conn_err_type; |
4992 | | |
4993 | | /* set s->txn.http->status for http_error_message(s) */ |
4994 | 0 | if (err_type & STRM_ET_QUEUE_ABRT) { |
4995 | 0 | s->txn.http->status = -1; |
4996 | 0 | http_server_error(s, sc, SF_ERR_CLICL, SF_FINST_Q, NULL); |
4997 | 0 | } |
4998 | 0 | else if (err_type & STRM_ET_CONN_ABRT) { |
4999 | 0 | s->txn.http->status = -1; |
5000 | 0 | http_server_error(s, sc, SF_ERR_CLICL, SF_FINST_C, NULL); |
5001 | 0 | } |
5002 | 0 | else if (err_type & STRM_ET_QUEUE_TO) { |
5003 | 0 | s->txn.http->status = 503; |
5004 | 0 | http_server_error(s, sc, SF_ERR_SRVTO, SF_FINST_Q, |
5005 | 0 | http_error_message(s)); |
5006 | 0 | } |
5007 | 0 | else if (err_type & STRM_ET_QUEUE_ERR) { |
5008 | 0 | s->txn.http->status = 503; |
5009 | 0 | http_server_error(s, sc, SF_ERR_SRVCL, SF_FINST_Q, |
5010 | 0 | http_error_message(s)); |
5011 | 0 | } |
5012 | 0 | else if (err_type & STRM_ET_CONN_TO) { |
5013 | 0 | s->txn.http->status = 503; |
5014 | 0 | http_server_error(s, sc, SF_ERR_SRVTO, SF_FINST_C, |
5015 | 0 | (s->txn.http->flags & TX_NOT_FIRST) ? NULL : |
5016 | 0 | http_error_message(s)); |
5017 | 0 | } |
5018 | 0 | else if (err_type & STRM_ET_CONN_ERR) { |
5019 | 0 | s->txn.http->status = 503; |
5020 | 0 | http_server_error(s, sc, SF_ERR_SRVCL, SF_FINST_C, |
5021 | 0 | (s->flags & SF_SRV_REUSED) ? NULL : |
5022 | 0 | http_error_message(s)); |
5023 | 0 | } |
5024 | 0 | else if (err_type & STRM_ET_CONN_RES) { |
5025 | 0 | s->txn.http->status = 503; |
5026 | 0 | http_server_error(s, sc, SF_ERR_RESOURCE, SF_FINST_C, |
5027 | 0 | (s->txn.http->flags & TX_NOT_FIRST) ? NULL : |
5028 | 0 | http_error_message(s)); |
5029 | 0 | } |
5030 | 0 | else { /* STRM_ET_CONN_OTHER and others */ |
5031 | 0 | s->txn.http->status = 500; |
5032 | 0 | http_server_error(s, sc, SF_ERR_INTERNAL, SF_FINST_C, |
5033 | 0 | http_error_message(s)); |
5034 | 0 | } |
5035 | 0 | } |
5036 | | |
5037 | | |
5038 | | /* Handle Expect: 100-continue for HTTP/1.1 messages if necessary. It returns 0 |
5039 | | * on success and -1 on error. |
5040 | | */ |
5041 | | static int http_handle_expect_hdr(struct stream *s, struct htx *htx, struct http_msg *msg) |
5042 | 0 | { |
5043 | | /* If we have HTTP/1.1 message with a body and Expect: 100-continue, |
5044 | | * then we must send an HTTP/1.1 100 Continue intermediate response. |
5045 | | */ |
5046 | 0 | if (!(msg->flags & HTTP_MSGF_EXPECT_CHECKED) && |
5047 | 0 | (msg->flags & HTTP_MSGF_VER_11) && |
5048 | 0 | (msg->flags & (HTTP_MSGF_CNT_LEN|HTTP_MSGF_TE_CHNK))) { |
5049 | 0 | struct ist hdr = { .ptr = "Expect", .len = 6 }; |
5050 | 0 | struct http_hdr_ctx ctx; |
5051 | |
|
5052 | 0 | ctx.blk = NULL; |
5053 | | /* Expect is allowed in 1.1, look for it */ |
5054 | 0 | if (http_find_header(htx, hdr, &ctx, 0) && |
5055 | 0 | unlikely(isteqi(ctx.value, ist2("100-continue", 12)))) { |
5056 | 0 | if (http_reply_100_continue(s) == -1) |
5057 | 0 | return -1; |
5058 | 0 | http_remove_header(htx, &ctx); |
5059 | 0 | } |
5060 | 0 | } |
5061 | 0 | msg->flags |= HTTP_MSGF_EXPECT_CHECKED; |
5062 | 0 | return 0; |
5063 | 0 | } |
5064 | | |
5065 | | /* Send a 100-Continue response to the client. It returns 0 on success and -1 |
5066 | | * on error. The response channel is updated accordingly. |
5067 | | */ |
5068 | | static int http_reply_100_continue(struct stream *s) |
5069 | 0 | { |
5070 | 0 | struct channel *res = &s->res; |
5071 | 0 | struct htx *htx = htx_from_buf(&res->buf); |
5072 | 0 | struct htx_sl *sl; |
5073 | 0 | unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11| |
5074 | 0 | HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS); |
5075 | |
|
5076 | 0 | sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, |
5077 | 0 | ist("HTTP/1.1"), ist("100"), ist("Continue")); |
5078 | 0 | if (!sl) |
5079 | 0 | goto fail; |
5080 | 0 | sl->info.res.status = 100; |
5081 | |
|
5082 | 0 | if (!htx_add_endof(htx, HTX_BLK_EOH)) |
5083 | 0 | goto fail; |
5084 | | |
5085 | 0 | if (!http_forward_proxy_resp(s, 0)) |
5086 | 0 | goto fail; |
5087 | 0 | return 0; |
5088 | | |
5089 | 0 | fail: |
5090 | | /* If an error occurred, remove the incomplete HTTP response from the |
5091 | | * buffer */ |
5092 | 0 | channel_htx_truncate(res, htx); |
5093 | 0 | return -1; |
5094 | 0 | } |
5095 | | |
5096 | | |
5097 | | /* |
5098 | | * Capture headers from message <htx> according to header list <cap_hdr>, and |
5099 | | * fill the <cap> pointers appropriately. |
5100 | | */ |
5101 | | static void http_capture_headers(struct htx *htx, char **cap, struct cap_hdr *cap_hdr) |
5102 | 0 | { |
5103 | 0 | struct cap_hdr *h; |
5104 | 0 | int32_t pos; |
5105 | |
|
5106 | 0 | for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) { |
5107 | 0 | struct htx_blk *blk = htx_get_blk(htx, pos); |
5108 | 0 | enum htx_blk_type type = htx_get_blk_type(blk); |
5109 | 0 | struct ist n, v; |
5110 | |
|
5111 | 0 | if (type == HTX_BLK_EOH) |
5112 | 0 | break; |
5113 | 0 | if (type != HTX_BLK_HDR) |
5114 | 0 | continue; |
5115 | | |
5116 | 0 | n = htx_get_blk_name(htx, blk); |
5117 | |
|
5118 | 0 | for (h = cap_hdr; h; h = h->next) { |
5119 | 0 | if (h->namelen && (h->namelen == n.len) && |
5120 | 0 | (strncasecmp(n.ptr, h->name, h->namelen) == 0)) { |
5121 | 0 | if (cap[h->index] == NULL) |
5122 | 0 | cap[h->index] = |
5123 | 0 | pool_alloc(h->pool); |
5124 | |
|
5125 | 0 | if (cap[h->index] == NULL) { |
5126 | 0 | ha_alert("HTTP capture : out of memory.\n"); |
5127 | 0 | break; |
5128 | 0 | } |
5129 | | |
5130 | 0 | v = htx_get_blk_value(htx, blk); |
5131 | 0 | v = isttrim(v, h->len); |
5132 | |
|
5133 | 0 | memcpy(cap[h->index], v.ptr, v.len); |
5134 | 0 | cap[h->index][v.len]=0; |
5135 | 0 | } |
5136 | 0 | } |
5137 | 0 | } |
5138 | 0 | } |
5139 | | |
5140 | | /* Delete a value in a header between delimiters <from> and <next>. The header |
5141 | | * itself is delimited by <start> and <end> pointers. The number of characters |
5142 | | * displaced is returned, and the pointer to the first delimiter is updated if |
5143 | | * required. The function tries as much as possible to respect the following |
5144 | | * principles : |
5145 | | * - replace <from> delimiter by the <next> one unless <from> points to <start>, |
5146 | | * in which case <next> is simply removed |
5147 | | * - set exactly one space character after the new first delimiter, unless there |
5148 | | * are not enough characters in the block being moved to do so. |
5149 | | * - remove unneeded spaces before the previous delimiter and after the new |
5150 | | * one. |
5151 | | * |
5152 | | * It is the caller's responsibility to ensure that : |
5153 | | * - <from> points to a valid delimiter or <start> ; |
5154 | | * - <next> points to a valid delimiter or <end> ; |
5155 | | * - there are non-space chars before <from>. |
5156 | | */ |
5157 | | static int http_del_hdr_value(char *start, char *end, char **from, char *next) |
5158 | 0 | { |
5159 | 0 | char *prev = *from; |
5160 | |
|
5161 | 0 | if (prev == start) { |
5162 | | /* We're removing the first value. eat the semicolon, if <next> |
5163 | | * is lower than <end> */ |
5164 | 0 | if (next < end) |
5165 | 0 | next++; |
5166 | |
|
5167 | 0 | while (next < end && HTTP_IS_SPHT(*next)) |
5168 | 0 | next++; |
5169 | 0 | } |
5170 | 0 | else { |
5171 | | /* Remove useless spaces before the old delimiter. */ |
5172 | 0 | while (HTTP_IS_SPHT(*(prev-1))) |
5173 | 0 | prev--; |
5174 | 0 | *from = prev; |
5175 | | |
5176 | | /* copy the delimiter and if possible a space if we're |
5177 | | * not at the end of the line. |
5178 | | */ |
5179 | 0 | if (next < end) { |
5180 | 0 | *prev++ = *next++; |
5181 | 0 | if (prev + 1 < next) |
5182 | 0 | *prev++ = ' '; |
5183 | 0 | while (next < end && HTTP_IS_SPHT(*next)) |
5184 | 0 | next++; |
5185 | 0 | } |
5186 | 0 | } |
5187 | 0 | memmove(prev, next, end - next); |
5188 | 0 | return (prev - next); |
5189 | 0 | } |
5190 | | |
5191 | | |
5192 | | /* Formats the start line of the request (without CRLF) and puts it in <str> and |
5193 | | * return the written length. The line can be truncated if it exceeds <len>. |
5194 | | */ |
5195 | | static size_t http_fmt_req_line(const struct htx_sl *sl, char *str, size_t len) |
5196 | 0 | { |
5197 | 0 | struct ist dst = ist2(str, 0); |
5198 | |
|
5199 | 0 | if (istcat(&dst, htx_sl_req_meth(sl), len) == -1) |
5200 | 0 | goto end; |
5201 | 0 | if (dst.len + 1 > len) |
5202 | 0 | goto end; |
5203 | 0 | dst.ptr[dst.len++] = ' '; |
5204 | |
|
5205 | 0 | if (istcat(&dst, htx_sl_req_uri(sl), len) == -1) |
5206 | 0 | goto end; |
5207 | 0 | if (dst.len + 1 > len) |
5208 | 0 | goto end; |
5209 | 0 | dst.ptr[dst.len++] = ' '; |
5210 | |
|
5211 | 0 | istcat(&dst, htx_sl_req_vsn(sl), len); |
5212 | 0 | end: |
5213 | 0 | return dst.len; |
5214 | 0 | } |
5215 | | |
5216 | | /* |
5217 | | * Print a debug line with a start line. |
5218 | | */ |
5219 | | static void http_debug_stline(const char *dir, struct stream *s, const struct htx_sl *sl) |
5220 | 0 | { |
5221 | 0 | struct session *sess = strm_sess(s); |
5222 | 0 | int max; |
5223 | |
|
5224 | 0 | chunk_printf(&trash, "%08x:%s.%s[%04x:%04x]: ", s->uniq_id, s->be->id, |
5225 | 0 | dir, |
5226 | 0 | objt_conn(sess->origin) ? (unsigned short)__objt_conn(sess->origin)->handle.fd : -1, |
5227 | 0 | sc_conn(s->scb) ? (unsigned short)(__sc_conn(s->scb))->handle.fd : -1); |
5228 | |
|
5229 | 0 | max = HTX_SL_P1_LEN(sl); |
5230 | 0 | UBOUND(max, trash.size - trash.data - 3); |
5231 | 0 | chunk_memcat(&trash, HTX_SL_P1_PTR(sl), max); |
5232 | 0 | trash.area[trash.data++] = ' '; |
5233 | |
|
5234 | 0 | max = HTX_SL_P2_LEN(sl); |
5235 | 0 | UBOUND(max, trash.size - trash.data - 2); |
5236 | 0 | chunk_memcat(&trash, HTX_SL_P2_PTR(sl), max); |
5237 | 0 | trash.area[trash.data++] = ' '; |
5238 | |
|
5239 | 0 | max = HTX_SL_P3_LEN(sl); |
5240 | 0 | UBOUND(max, trash.size - trash.data - 1); |
5241 | 0 | chunk_memcat(&trash, HTX_SL_P3_PTR(sl), max); |
5242 | 0 | trash.area[trash.data++] = '\n'; |
5243 | |
|
5244 | 0 | DISGUISE(write(1, trash.area, trash.data)); |
5245 | 0 | } |
5246 | | |
5247 | | /* |
5248 | | * Print a debug line with a header. |
5249 | | */ |
5250 | | static void http_debug_hdr(const char *dir, struct stream *s, const struct ist n, const struct ist v) |
5251 | 0 | { |
5252 | 0 | struct session *sess = strm_sess(s); |
5253 | 0 | int max; |
5254 | |
|
5255 | 0 | chunk_printf(&trash, "%08x:%s.%s[%04x:%04x]: ", s->uniq_id, s->be->id, |
5256 | 0 | dir, |
5257 | 0 | objt_conn(sess->origin) ? (unsigned short)__objt_conn(sess->origin)->handle.fd : -1, |
5258 | 0 | sc_conn(s->scb) ? (unsigned short)(__sc_conn(s->scb))->handle.fd : -1); |
5259 | |
|
5260 | 0 | max = n.len; |
5261 | 0 | UBOUND(max, trash.size - trash.data - 3); |
5262 | 0 | chunk_memcat(&trash, n.ptr, max); |
5263 | 0 | trash.area[trash.data++] = ':'; |
5264 | 0 | trash.area[trash.data++] = ' '; |
5265 | |
|
5266 | 0 | max = v.len; |
5267 | 0 | UBOUND(max, trash.size - trash.data - 1); |
5268 | 0 | chunk_memcat(&trash, v.ptr, max); |
5269 | 0 | trash.area[trash.data++] = '\n'; |
5270 | |
|
5271 | 0 | DISGUISE(write(1, trash.area, trash.data)); |
5272 | 0 | } |
5273 | | |
5274 | | void http_txn_reset_req(struct http_txn *txn) |
5275 | 0 | { |
5276 | 0 | txn->req.flags = 0; |
5277 | 0 | txn->req.msg_state = HTTP_MSG_RQBEFORE; /* at the very beginning of the request */ |
5278 | 0 | } |
5279 | | |
5280 | | void http_txn_reset_res(struct http_txn *txn) |
5281 | 0 | { |
5282 | 0 | txn->rsp.flags = 0; |
5283 | 0 | txn->rsp.msg_state = HTTP_MSG_RPBEFORE; /* at the very beginning of the response */ |
5284 | 0 | } |
5285 | | |
5286 | | /* |
5287 | | * Create and initialize a new HTTP transaction for stream <s>. This should be |
5288 | | * used before processing any new request. It returns the transaction or NLULL |
5289 | | * on error. |
5290 | | */ |
5291 | | struct http_txn *http_create_txn(struct stream *s) |
5292 | 0 | { |
5293 | 0 | struct http_txn *txn; |
5294 | 0 | struct stconn *sc = s->scf; |
5295 | |
|
5296 | 0 | if ((s->flags & SF_TXN_MASK) != SF_TXN_NONE) |
5297 | 0 | return NULL; |
5298 | | |
5299 | 0 | txn = pool_alloc(pool_head_http_txn); |
5300 | 0 | if (!txn) |
5301 | 0 | return NULL; |
5302 | 0 | s->txn.http = txn; |
5303 | |
|
5304 | 0 | txn->meth = HTTP_METH_OTHER; |
5305 | 0 | txn->flags = ((sc && sc_ep_test(sc, SE_FL_NOT_FIRST)) ? TX_NOT_FIRST : 0); |
5306 | 0 | txn->status = -1; |
5307 | 0 | txn->server_status = -1; |
5308 | 0 | txn->http_reply = NULL; |
5309 | 0 | txn->l7_buffer = BUF_NULL; |
5310 | 0 | write_u32(txn->cache_hash, 0); |
5311 | |
|
5312 | 0 | txn->cookie_first_date = 0; |
5313 | 0 | txn->cookie_last_date = 0; |
5314 | |
|
5315 | 0 | txn->srv_cookie = NULL; |
5316 | 0 | txn->cli_cookie = NULL; |
5317 | 0 | txn->uri = NULL; |
5318 | |
|
5319 | 0 | http_txn_reset_req(txn); |
5320 | 0 | http_txn_reset_res(txn); |
5321 | |
|
5322 | 0 | txn->req.chn = &s->req; |
5323 | 0 | txn->rsp.chn = &s->res; |
5324 | |
|
5325 | 0 | txn->auth.method = HTTP_AUTH_UNKNOWN; |
5326 | |
|
5327 | 0 | s->flags |= SF_TXN_HTTP; |
5328 | | |
5329 | | /* here we don't want to re-initialize s->vars_txn and s->vars_reqres |
5330 | | * variable lists, because they were already initialized upon stream |
5331 | | * creation in stream_new(), and thus may already contain some variables |
5332 | | */ |
5333 | |
|
5334 | 0 | return txn; |
5335 | 0 | } |
5336 | | |
5337 | | /* to be used at the end of a transaction */ |
5338 | | void http_destroy_txn(struct stream *s) |
5339 | 0 | { |
5340 | 0 | struct http_txn *txn = s->txn.http; |
5341 | | |
5342 | | /* these ones will have been dynamically allocated */ |
5343 | 0 | pool_free(pool_head_requri, txn->uri); |
5344 | 0 | pool_free(pool_head_capture, txn->cli_cookie); |
5345 | 0 | pool_free(pool_head_capture, txn->srv_cookie); |
5346 | 0 | pool_free(pool_head_uniqueid, s->unique_id.ptr); |
5347 | |
|
5348 | 0 | s->unique_id = IST_NULL; |
5349 | 0 | txn->uri = NULL; |
5350 | 0 | txn->srv_cookie = NULL; |
5351 | 0 | txn->cli_cookie = NULL; |
5352 | |
|
5353 | 0 | vars_prune(&s->vars_txn, s->sess, s); |
5354 | 0 | vars_prune(&s->vars_reqres, s->sess, s); |
5355 | |
|
5356 | 0 | b_free(&txn->l7_buffer); |
5357 | |
|
5358 | 0 | pool_free(pool_head_http_txn, txn); |
5359 | 0 | s->txn.http = NULL; |
5360 | 0 | s->flags &= ~SF_TXN_MASK; |
5361 | 0 | } |
5362 | | |
5363 | | |
5364 | | void http_set_term_flags(struct stream *s) |
5365 | 0 | { |
5366 | 0 | if (!(s->flags & SF_ERR_MASK)) |
5367 | 0 | s->flags |= SF_ERR_PRXCOND; |
5368 | |
|
5369 | 0 | if (!(s->flags & SF_FINST_MASK)) { |
5370 | 0 | if (s->scb->state == SC_ST_INI) { |
5371 | | /* Before any connection attempt on the server side, we |
5372 | | * are still in the request analysis. Just take case to |
5373 | | * detect tarpit error |
5374 | | */ |
5375 | 0 | if (s->req.analysers & AN_REQ_HTTP_TARPIT) |
5376 | 0 | s->flags |= SF_FINST_T; |
5377 | 0 | else |
5378 | 0 | s->flags |= SF_FINST_R; |
5379 | 0 | } |
5380 | 0 | else if (s->scb->state == SC_ST_QUE) |
5381 | 0 | s->flags |= SF_FINST_Q; |
5382 | 0 | else if (sc_state_in(s->scb->state, SC_SB_REQ|SC_SB_TAR|SC_SB_ASS|SC_SB_CON|SC_SB_CER|SC_SB_RDY)) { |
5383 | 0 | if (unlikely(objt_applet(s->target))) { |
5384 | 0 | s->flags |= SF_FINST_R; |
5385 | 0 | } |
5386 | 0 | else |
5387 | 0 | s->flags |= SF_FINST_C; |
5388 | 0 | } |
5389 | 0 | else { |
5390 | 0 | if (s->txn.http->rsp.msg_state < HTTP_MSG_DATA) { |
5391 | | /* We are still processing the response headers */ |
5392 | 0 | s->flags |= SF_FINST_H; |
5393 | 0 | } |
5394 | | // (res == (done|closing|closed)) & (res->flags & shutw) |
5395 | 0 | else if (s->txn.http->rsp.msg_state >= HTTP_MSG_DONE && s->txn.http->rsp.msg_state < HTTP_MSG_TUNNEL && |
5396 | 0 | (s->flags & (SF_ERR_CLITO|SF_ERR_CLICL))) { |
5397 | | /* A client error was reported and we are |
5398 | | * transmitting the last block of data |
5399 | | */ |
5400 | 0 | s->flags |= SF_FINST_L; |
5401 | 0 | } |
5402 | 0 | else { |
5403 | | /* Otherwise we are in DATA phase on both sides */ |
5404 | 0 | s->flags |= SF_FINST_D; |
5405 | 0 | } |
5406 | 0 | } |
5407 | 0 | } |
5408 | 0 | } |
5409 | | |
5410 | | |
5411 | | DECLARE_TYPED_POOL(pool_head_http_txn, "http_txn", struct http_txn); |
5412 | | |
5413 | | /* |
5414 | | * Local variables: |
5415 | | * c-indent-level: 8 |
5416 | | * c-basic-offset: 8 |
5417 | | * End: |
5418 | | */ |