[MEDIUM] http: switch to tunnel mode after status 101 responses

A 101 response is accompanied with an Upgrade header indicating
a new protocol that is spoken on the connection after the exchange
completes. At least we should switch to tunnel mode after such a
response.
This commit is contained in:
Willy Tarreau 2010-02-01 15:13:32 +01:00
parent 95fa4698f1
commit 5843d1a894
2 changed files with 30 additions and 12 deletions

View File

@ -262,13 +262,17 @@ messages. Let's consider this HTTP response :
As a special case, HTTP supports so called "Informational responses" as status 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 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 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 continue to post its request for instance. In the case of a status 100 response
carried by the next non-1xx response message following the informational one. the requested information will be carried by the next non-100 response message
This implies that multiple responses may be sent to a single request, and that following the informational one. This implies that multiple responses may be
this only works when keep-alive is enabled (1xx messages are HTTP/1.1 only). sent to a single request, and that this only works when keep-alive is enabled
HAProxy handles these messages and is able to correctly forward and skip them, (1xx messages are HTTP/1.1 only). HAProxy handles these messages and is able to
and only process the next non-1xx response. As such, these messages are neither correctly forward and skip them, and only process the next non-100 response. As
logged nor transformed, unless explicitly state otherwise. such, these messages are neither logged nor transformed, unless explicitly
state otherwise. Status 101 messages indicate that the protocol is changing
over the same connection and that haproxy must switch to tunnel mode, just as
if a CONNECT had occurred. Then the Upgrade header would contain additional
information about the type of protocol the connection is switching to.
1.3.1. The Response line 1.3.1. The Response line

View File

@ -4669,7 +4669,18 @@ int http_process_res_common(struct session *t, struct buffer *rep, int an_bit, s
* See doc/internals/connection-header.txt for the complete matrix. * See doc/internals/connection-header.txt for the complete matrix.
*/ */
if ((txn->meth != HTTP_METH_CONNECT) && if (unlikely(txn->status == 101)) {
/* this is a "switching protocol" response, we're very unlikely
* to understand the next protocols. We have to switch to tunnel
* mode, so that we transfer the request and responses then let
* this protocol pass unmodified. When we later implement specific
* parsers for such protocols, we'll want to check the Upgrade
* header which contains information about that protocol (eg: see
* RFC2817 about TLS).
*/
txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_TUN;
}
else if ((txn->meth != HTTP_METH_CONNECT) &&
(txn->status >= 200) && !(txn->flags & TX_HDR_CONN_PRS) && (txn->status >= 200) && !(txn->flags & TX_HDR_CONN_PRS) &&
((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN || ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN ||
((t->fe->options|t->be->options) & PR_O_HTTP_CLOSE))) { ((t->fe->options|t->be->options) & PR_O_HTTP_CLOSE))) {
@ -4778,12 +4789,12 @@ int http_process_res_common(struct session *t, struct buffer *rep, int an_bit, s
} }
/* /*
* We may be facing a 1xx response (100 continue, 101 switching protocols), * We may be facing a 100-continue response, in which case this
* in which case this is not the right response, and we're waiting for the * is not the right response, and we're waiting for the next one.
* next one. Let's allow this response to go to the client and wait for the * Let's allow this response to go to the client and wait for the
* next one. * next one.
*/ */
if (txn->status < 200) { if (unlikely(txn->status == 100)) {
hdr_idx_init(&txn->hdr_idx); hdr_idx_init(&txn->hdr_idx);
buffer_forward(rep, rep->lr - msg->sol); buffer_forward(rep, rep->lr - msg->sol);
msg->msg_state = HTTP_MSG_RPBEFORE; msg->msg_state = HTTP_MSG_RPBEFORE;
@ -4791,6 +4802,8 @@ int http_process_res_common(struct session *t, struct buffer *rep, int an_bit, s
rep->analysers |= AN_RES_WAIT_HTTP | an_bit; rep->analysers |= AN_RES_WAIT_HTTP | an_bit;
return 1; return 1;
} }
else if (unlikely(txn->status < 200))
goto skip_header_mangling;
/* we don't have any 1xx status code now */ /* we don't have any 1xx status code now */
@ -4902,6 +4915,7 @@ int http_process_res_common(struct session *t, struct buffer *rep, int an_bit, s
http_change_connection_header(txn, msg, rep, want_flags); http_change_connection_header(txn, msg, rep, want_flags);
} }
skip_header_mangling:
if (txn->flags & TX_RES_XFER_LEN) if (txn->flags & TX_RES_XFER_LEN)
rep->analysers |= AN_RES_HTTP_XFER_BODY; rep->analysers |= AN_RES_HTTP_XFER_BODY;