2 Content-length: 350
3 Content-Type: text/html
+As a special case, HTTP supports so called "Informational responses" as status
+codes 1xx. These messages are special in that they don't convey any part of the
+response, they're just used as sort of a signaling message to ask a client to
+continue to post its request for instance. The requested information will be
+carried by the next non-1xx response message following the informational one.
+This implies that multiple responses may be sent to a single request, and that
+this only works when keep-alive is enabled (1xx messages are HTTP/1.1 only).
+HAProxy handles these messages and is able to correctly forward and skip them,
+and only process the next non-1xx response. As such, these messages are neither
+logged nor transformed, unless explicitly state otherwise.
+
1.3.1. The Response line
------------------------
- a reason : OK
The status code is always 3-digit. The first digit indicates a general status :
+ - 1xx = informational message to be skipped (eg: 100, 101)
- 2xx = OK, content is following (eg: 200, 206)
- 3xx = OK, no content following (eg: 302, 304)
- 4xx = error caused by the client (eg: 401, 403, 404)
headers will be considered data only and not analyzed. Furthermore, HAProxy
never touches data contents, it stops analysis at the end of headers.
+There is an exception though. If HAProxy encounters an "Informational Response"
+(status code 1xx), it is able to process all rsp* rules which can allow, deny,
+rewrite or delete a header, but it will refuse to add a header to any such
+messages as this is not HTTP-compliant. The reason for still processing headers
+in such responses is to stop and/or fix any possible information leak which may
+happen, for instance because another downstream equipment would inconditionally
+add a header, or if a server name appears there. When such messages are seen,
+normal processing still occurs on the next non-informational messages.
+
This section covers common usage of the following keywords, described in detail
in section 4.2 :
http_msg_rpbefore:
case HTTP_MSG_RPBEFORE:
if (likely(HTTP_IS_TOKEN(*ptr))) {
- if (likely(ptr == buf->data)) {
- msg->sol = ptr;
- msg->som = 0;
- } else {
-#if PARSE_PRESERVE_EMPTY_LINES
- /* only skip empty leading lines, don't remove them */
- msg->sol = ptr;
- msg->som = ptr - buf->data;
-#else
+#if !defined(PARSE_PRESERVE_EMPTY_LINES)
+ if (likely(ptr != buf->data)) {
/* Remove empty leading lines, as recommended by
* RFC2616. This takes a lot of time because we
* must move all the buffer backwards, but this
* cleaner when we'll be able to start sending
* the request from any place in the buffer.
*/
- buf->lr = ptr;
- buffer_replace2(buf, buf->data, buf->lr, NULL, 0);
- msg->som = 0;
- msg->sol = buf->data;
- ptr = buf->data;
+ ptr += buffer_replace2(buf, buf->lr, ptr, NULL, 0);
end = buf->r;
-#endif
}
+#endif
+ msg->sol = ptr;
+ msg->som = ptr - buf->data;
hdr_idx_init(idx);
state = HTTP_MSG_RPVER;
goto http_msg_rpver;
struct buffer *req = t->req;
struct buffer *rep = t->rep;
+ next_response:
DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
now_ms, __FUNCTION__,
t,
t->flags |= SN_FINST_H;
return 0;
}
- buffer_write_dis(rep);
+
+ /* We disable sending only if we have nothing to send.
+ * Note that we should not need to do this since the
+ * buffer is protected by the fact that at least one
+ * analyser remains. But close events could still be
+ * forwarded if we don't disable the BF_WRITE_ENA flag.
+ */
+ if (!rep->send_max)
+ buffer_write_dis(rep);
return 0;
}
/* We might have to check for "Connection:" */
if (((t->fe->options | t->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO)) &&
- !(t->flags & SN_CONN_CLOSED)) {
+ !(t->flags & SN_CONN_CLOSED) &&
+ txn->status >= 200) {
char *cur_ptr, *cur_end, *cur_next;
int cur_idx, old_idx, delta, val;
struct hdr_idx_elem *cur_hdr;
/* add response headers from the rule sets in the same order */
for (cur_idx = 0; cur_idx < rule_set->nb_rspadd; cur_idx++) {
+ if (txn->status < 200)
+ break;
if (unlikely(http_header_add_tail(rep, &txn->rsp, &txn->hdr_idx,
rule_set->rsp_add[cur_idx])) < 0)
goto return_bad_resp;
/*
* 4: check for server cookie.
*/
- if (t->be->cookie_name || t->be->appsession_name || t->fe->capture_name
- || (t->be->options & PR_O_CHK_CACHE))
+ if ((t->be->cookie_name || t->be->appsession_name || t->fe->capture_name
+ || (t->be->options & PR_O_CHK_CACHE)) && txn->status >= 200)
manage_server_side_cookies(t, rep);
/*
* 5: check for cache-control or pragma headers if required.
*/
- if ((t->be->options & (PR_O_COOK_NOC | PR_O_CHK_CACHE)) != 0)
+ if ((t->be->options & (PR_O_COOK_NOC | PR_O_CHK_CACHE)) != 0 && txn->status >= 200)
check_response_for_cacheability(t, rep);
/*
* 6: add server cookie in the response if needed
*/
if ((t->srv) && !(t->flags & SN_DIRECT) && (t->be->options & PR_O_COOK_INS) &&
- (!(t->be->options & PR_O_COOK_POST) || (txn->meth == HTTP_METH_POST))) {
+ (!(t->be->options & PR_O_COOK_POST) || (txn->meth == HTTP_METH_POST)) &&
+ txn->status >= 200) {
int len;
/* the server is known, it's not the one the client requested, we have to
*/
if (((txn->flags & (TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_ANY)) ==
(TX_CACHEABLE | TX_CACHE_COOK | TX_SCK_ANY)) &&
- (t->be->options & PR_O_CHK_CACHE)) {
+ (t->be->options & PR_O_CHK_CACHE) &&
+ txn->status >= 200) {
/* we're in presence of a cacheable response containing
* a set-cookie header. We'll block it as requested by
* Note that we do not need to add it in case of HTTP/1.0.
*/
if (!(t->flags & SN_CONN_CLOSED) &&
- ((t->fe->options | t->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO))) {
+ ((t->fe->options | t->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO)) &&
+ txn->status >= 200) {
if ((unlikely(msg->sl.st.v_l != 8) ||
unlikely(req->data[msg->som + 7] != '0')) &&
unlikely(http_header_add_tail2(rep, &txn->rsp, &txn->hdr_idx,
t->flags |= SN_CONN_CLOSED;
}
+ /*
+ * 9: we may be facing a 1xx response (100 continue, 101 switching protocols),
+ * in which case this is not the right response, and we're waiting for the
+ * next one. Let's allow this response to go to the client and wait for the
+ * next one.
+ */
+ if (txn->status < 200) {
+ hdr_idx_init(&txn->hdr_idx);
+ buffer_forward(rep, rep->lr - (rep->data + msg->som));
+ msg->msg_state = HTTP_MSG_RPBEFORE;
+ txn->status = 0;
+ rep->analysers |= AN_RTR_HTTP_HDR;
+ goto next_response;
+ }
+
/*************************************************************
* OK, that's finished for the headers. We have done what we *
* could. Let's switch to the DATA state. *