mirror of
https://github.com/curl/curl.git
synced 2026-01-18 17:21:26 +01:00
cf-h1-proxy: support folded headers in CONNECT responses
Update test 1941 to verify this Remove unused code from dynhds for handling folded headers, and the associated unit tests of those functions in test 2602 and 2603. Closes #20080
This commit is contained in:
@@ -69,6 +69,8 @@ struct h1_tunnel_state {
|
||||
h1_tunnel_state tunnel_state;
|
||||
BIT(chunked_encoding);
|
||||
BIT(close_connection);
|
||||
BIT(maybe_folded);
|
||||
BIT(leading_unfold);
|
||||
};
|
||||
|
||||
static bool tunnel_is_established(struct h1_tunnel_state *ts)
|
||||
@@ -94,6 +96,8 @@ static CURLcode tunnel_reinit(struct Curl_cfilter *cf,
|
||||
ts->keepon = KEEPON_CONNECT;
|
||||
ts->cl = 0;
|
||||
ts->close_connection = FALSE;
|
||||
ts->maybe_folded = FALSE;
|
||||
ts->leading_unfold = FALSE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
@@ -345,16 +349,82 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf,
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode single_header(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct h1_tunnel_state *ts)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
char *linep = curlx_dyn_ptr(&ts->rcvbuf);
|
||||
size_t line_len = curlx_dyn_len(&ts->rcvbuf); /* bytes in this line */
|
||||
struct SingleRequest *k = &data->req;
|
||||
int writetype;
|
||||
ts->headerlines++;
|
||||
|
||||
/* output debug if that is requested */
|
||||
Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len);
|
||||
|
||||
/* send the header to the callback */
|
||||
writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
|
||||
(ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
|
||||
result = Curl_client_write(data, writetype, linep, line_len);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_bump_headersize(data, line_len, TRUE);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
/* Newlines are CRLF, so the CR is ignored as the line is not
|
||||
really terminated until the LF comes. Treat a following CR
|
||||
as end-of-headers as well.*/
|
||||
|
||||
if(ISNEWLINE(linep[0])) {
|
||||
/* end of response-headers from the proxy */
|
||||
|
||||
if((407 == k->httpcode) && !data->state.authproblem) {
|
||||
/* If we get a 407 response code with content length
|
||||
when we have no auth problem, we must ignore the
|
||||
whole response-body */
|
||||
ts->keepon = KEEPON_IGNORE;
|
||||
|
||||
if(ts->cl) {
|
||||
infof(data, "Ignore %" FMT_OFF_T " bytes of response-body", ts->cl);
|
||||
}
|
||||
else if(ts->chunked_encoding) {
|
||||
infof(data, "Ignore chunked response-body");
|
||||
}
|
||||
else {
|
||||
/* without content-length or chunked encoding, we
|
||||
cannot keep the connection alive since the close is
|
||||
the end signal so we bail out at once instead */
|
||||
CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked");
|
||||
ts->keepon = KEEPON_DONE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ts->keepon = KEEPON_DONE;
|
||||
}
|
||||
|
||||
DEBUGASSERT(ts->keepon == KEEPON_IGNORE ||
|
||||
ts->keepon == KEEPON_DONE);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = on_resp_header(cf, data, ts, linep);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
curlx_dyn_reset(&ts->rcvbuf);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct h1_tunnel_state *ts,
|
||||
bool *done)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct SingleRequest *k = &data->req;
|
||||
char *linep;
|
||||
size_t line_len;
|
||||
int error, writetype;
|
||||
int error;
|
||||
|
||||
#define SELECT_OK 0
|
||||
#define SELECT_ERROR 1
|
||||
@@ -428,6 +498,31 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ts->maybe_folded) {
|
||||
if(ISBLANK(byte)) {
|
||||
Curl_http_to_fold(&ts->rcvbuf);
|
||||
ts->leading_unfold = TRUE;
|
||||
}
|
||||
else {
|
||||
result = single_header(cf, data, ts);
|
||||
if(result)
|
||||
return result;
|
||||
/* now handle the new byte */
|
||||
}
|
||||
ts->maybe_folded = FALSE;
|
||||
}
|
||||
|
||||
if(ts->leading_unfold) {
|
||||
if(ISBLANK(byte))
|
||||
/* skip a bit brother */
|
||||
continue;
|
||||
/* non-blank, insert a space then continue the unfolding */
|
||||
if(curlx_dyn_addn(&ts->rcvbuf, " ", 1)) {
|
||||
failf(data, "CONNECT response too large");
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
ts->leading_unfold = FALSE;
|
||||
}
|
||||
if(curlx_dyn_addn(&ts->rcvbuf, &byte, 1)) {
|
||||
failf(data, "CONNECT response too large");
|
||||
return CURLE_RECV_ERROR;
|
||||
@@ -436,67 +531,19 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
|
||||
/* if this is not the end of a header line then continue */
|
||||
if(byte != 0x0a)
|
||||
continue;
|
||||
|
||||
ts->headerlines++;
|
||||
linep = curlx_dyn_ptr(&ts->rcvbuf);
|
||||
line_len = curlx_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
|
||||
|
||||
/* output debug if that is requested */
|
||||
Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len);
|
||||
|
||||
/* send the header to the callback */
|
||||
writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
|
||||
(ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
|
||||
result = Curl_client_write(data, writetype, linep, line_len);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_bump_headersize(data, line_len, TRUE);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
/* Newlines are CRLF, so the CR is ignored as the line is not
|
||||
really terminated until the LF comes. Treat a following CR
|
||||
as end-of-headers as well.*/
|
||||
|
||||
if(('\r' == linep[0]) ||
|
||||
('\n' == linep[0])) {
|
||||
/* end of response-headers from the proxy */
|
||||
|
||||
if((407 == k->httpcode) && !data->state.authproblem) {
|
||||
/* If we get a 407 response code with content length
|
||||
when we have no auth problem, we must ignore the
|
||||
whole response-body */
|
||||
ts->keepon = KEEPON_IGNORE;
|
||||
|
||||
if(ts->cl) {
|
||||
infof(data, "Ignore %" FMT_OFF_T " bytes of response-body", ts->cl);
|
||||
}
|
||||
else if(ts->chunked_encoding) {
|
||||
infof(data, "Ignore chunked response-body");
|
||||
}
|
||||
else {
|
||||
/* without content-length or chunked encoding, we
|
||||
cannot keep the connection alive since the close is
|
||||
the end signal so we bail out at once instead */
|
||||
CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked");
|
||||
ts->keepon = KEEPON_DONE;
|
||||
}
|
||||
else {
|
||||
char *linep = curlx_dyn_ptr(&ts->rcvbuf);
|
||||
size_t hlen = curlx_dyn_len(&ts->rcvbuf);
|
||||
if(hlen && ISNEWLINE(linep[0])) {
|
||||
/* end of headers */
|
||||
result = single_header(cf, data, ts);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
ts->keepon = KEEPON_DONE;
|
||||
}
|
||||
|
||||
DEBUGASSERT(ts->keepon == KEEPON_IGNORE ||
|
||||
ts->keepon == KEEPON_DONE);
|
||||
continue;
|
||||
else
|
||||
ts->maybe_folded = TRUE;
|
||||
}
|
||||
|
||||
result = on_resp_header(cf, data, ts, linep);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
curlx_dyn_reset(&ts->rcvbuf);
|
||||
} /* while there is buffer left and loop is requested */
|
||||
|
||||
if(error)
|
||||
|
||||
79
lib/dynhds.c
79
lib/dynhds.c
@@ -56,29 +56,6 @@ entry_new(const char *name, size_t namelen,
|
||||
return e;
|
||||
}
|
||||
|
||||
static struct dynhds_entry *entry_append(struct dynhds_entry *e,
|
||||
const char *value, size_t valuelen)
|
||||
{
|
||||
struct dynhds_entry *e2;
|
||||
size_t valuelen2 = e->valuelen + 1 + valuelen;
|
||||
char *p;
|
||||
|
||||
DEBUGASSERT(value);
|
||||
e2 = curlx_calloc(1, sizeof(*e) + e->namelen + valuelen2 + 2);
|
||||
if(!e2)
|
||||
return NULL;
|
||||
e2->name = p = ((char *)e2) + sizeof(*e2);
|
||||
memcpy(p, e->name, e->namelen);
|
||||
e2->namelen = e->namelen;
|
||||
e2->value = p += e->namelen + 1; /* leave a \0 at the end of name */
|
||||
memcpy(p, e->value, e->valuelen);
|
||||
p += e->valuelen;
|
||||
p[0] = ' ';
|
||||
memcpy(p + 1, value, valuelen);
|
||||
e2->valuelen = valuelen2;
|
||||
return e2;
|
||||
}
|
||||
|
||||
static void entry_free(struct dynhds_entry *e)
|
||||
{
|
||||
curlx_free(e);
|
||||
@@ -222,48 +199,26 @@ CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds,
|
||||
if(!line || !line_len)
|
||||
return CURLE_OK;
|
||||
|
||||
if((line[0] == ' ') || (line[0] == '\t')) {
|
||||
struct dynhds_entry *e, *e2;
|
||||
/* header continuation, yikes! */
|
||||
if(!dynhds->hds_len)
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
|
||||
while(line_len && ISBLANK(line[0])) {
|
||||
++line;
|
||||
--line_len;
|
||||
}
|
||||
if(!line_len)
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
e = dynhds->hds[dynhds->hds_len - 1];
|
||||
e2 = entry_append(e, line, line_len);
|
||||
if(!e2)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
dynhds->hds[dynhds->hds_len - 1] = e2;
|
||||
entry_free(e);
|
||||
return CURLE_OK;
|
||||
p = memchr(line, ':', line_len);
|
||||
if(!p)
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
name = line;
|
||||
namelen = p - line;
|
||||
p++; /* move past the colon */
|
||||
for(i = namelen + 1; i < line_len; ++i, ++p) {
|
||||
if(!ISBLANK(*p))
|
||||
break;
|
||||
}
|
||||
else {
|
||||
p = memchr(line, ':', line_len);
|
||||
if(!p)
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
name = line;
|
||||
namelen = p - line;
|
||||
p++; /* move past the colon */
|
||||
for(i = namelen + 1; i < line_len; ++i, ++p) {
|
||||
if(!ISBLANK(*p))
|
||||
break;
|
||||
}
|
||||
value = p;
|
||||
valuelen = line_len - i;
|
||||
value = p;
|
||||
valuelen = line_len - i;
|
||||
|
||||
p = memchr(value, '\r', valuelen);
|
||||
if(!p)
|
||||
p = memchr(value, '\n', valuelen);
|
||||
if(p)
|
||||
valuelen = (size_t)(p - value);
|
||||
p = memchr(value, '\r', valuelen);
|
||||
if(!p)
|
||||
p = memchr(value, '\n', valuelen);
|
||||
if(p)
|
||||
valuelen = (size_t)(p - value);
|
||||
|
||||
return Curl_dynhds_add(dynhds, name, namelen, value, valuelen);
|
||||
}
|
||||
return Curl_dynhds_add(dynhds, name, namelen, value, valuelen);
|
||||
}
|
||||
|
||||
CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line)
|
||||
|
||||
15
lib/http.c
15
lib/http.c
@@ -4288,18 +4288,23 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* cut off the newline characters */
|
||||
static void unfold_header(struct Curl_easy *data)
|
||||
/* remove trailing CRLF then all trailing whitespace */
|
||||
void Curl_http_to_fold(struct dynbuf *bf)
|
||||
{
|
||||
size_t len = curlx_dyn_len(&data->state.headerb);
|
||||
char *hd = curlx_dyn_ptr(&data->state.headerb);
|
||||
size_t len = curlx_dyn_len(bf);
|
||||
char *hd = curlx_dyn_ptr(bf);
|
||||
if(len && (hd[len - 1] == '\n'))
|
||||
len--;
|
||||
if(len && (hd[len - 1] == '\r'))
|
||||
len--;
|
||||
while(len && (ISBLANK(hd[len - 1]))) /* strip off trailing whitespace */
|
||||
len--;
|
||||
curlx_dyn_setlen(&data->state.headerb, len);
|
||||
curlx_dyn_setlen(bf, len);
|
||||
}
|
||||
|
||||
static void unfold_header(struct Curl_easy *data)
|
||||
{
|
||||
Curl_http_to_fold(&data->state.headerb);
|
||||
data->state.leading_unfold = TRUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,8 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect,
|
||||
CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect,
|
||||
struct dynhds *hds);
|
||||
|
||||
void Curl_http_to_fold(struct dynbuf *bf);
|
||||
|
||||
void Curl_http_method(struct Curl_easy *data,
|
||||
const char **method, Curl_HttpReq *);
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ CONNECT
|
||||
HTTP/1.1 200 OK
|
||||
Date: Thu, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test with trailing space%repeat[5 x ]%
|
||||
Content-Type: text/html
|
||||
Content-Type:
|
||||
%SPtext/html
|
||||
Content-Length: 0
|
||||
Set-Cookie: onecookie=data;
|
||||
Set-Cookie: secondcookie=2data;
|
||||
@@ -23,7 +24,9 @@ Location: /%TESTNUMBER0002
|
||||
</data>
|
||||
<connect crlf="headers">
|
||||
HTTP/1.1 200 Sure go ahead
|
||||
Server: from the connect
|
||||
Server: from%SP
|
||||
%SPthe%TAB
|
||||
%TAB%TABconnect
|
||||
Silly-thing: yes yes
|
||||
|
||||
</connect>
|
||||
|
||||
@@ -111,18 +111,6 @@ static CURLcode test_unit2602(const char *arg)
|
||||
}
|
||||
|
||||
Curl_dynhds_free(&hds);
|
||||
Curl_dynhds_init(&hds, 128, 4 * 1024);
|
||||
/* continuation without previous header fails */
|
||||
res = Curl_dynhds_h1_cadd_line(&hds, " indented value");
|
||||
fail_unless(res, "add should have failed");
|
||||
|
||||
/* continuation with previous header must succeed */
|
||||
fail_if(Curl_dynhds_h1_cadd_line(&hds, "ti1: val1"), "add");
|
||||
fail_if(Curl_dynhds_h1_cadd_line(&hds, " val2"), "add indent");
|
||||
fail_if(Curl_dynhds_h1_cadd_line(&hds, "ti2: val1"), "add");
|
||||
fail_if(Curl_dynhds_h1_cadd_line(&hds, "\tval2"), "add indent");
|
||||
fail_if(Curl_dynhds_h1_cadd_line(&hds, "ti3: val1"), "add");
|
||||
fail_if(Curl_dynhds_h1_cadd_line(&hds, " val2"), "add indent");
|
||||
|
||||
curlx_dyn_init(&dbuf, 32 * 1024);
|
||||
fail_if(Curl_dynhds_h1_dprint(&hds, &dbuf), "h1 print failed");
|
||||
|
||||
@@ -159,16 +159,6 @@ static CURLcode test_unit2603(const char *arg)
|
||||
T4_INPUT, NULL, NULL, "CONNECT", NULL, "ftp.curl.se:123", NULL, 3, 2
|
||||
};
|
||||
|
||||
static const char *T5_INPUT[] = {
|
||||
"OPTIONS * HTTP/1.1\r\nContent-Length: 0\r\nBlabla: xxx.yyy\r",
|
||||
"\n\tzzzzzz\r\n\r\n",
|
||||
"123",
|
||||
NULL,
|
||||
};
|
||||
static const struct tcase TEST5a = {
|
||||
T5_INPUT, NULL, NULL, "OPTIONS", NULL, NULL, "*", 2, 3
|
||||
};
|
||||
|
||||
static const char *T6_INPUT[] = {
|
||||
"PUT /path HTTP/1.1\nHost: test.curl.se\n\n123",
|
||||
NULL,
|
||||
@@ -194,7 +184,6 @@ static CURLcode test_unit2603(const char *arg)
|
||||
parse_success(&TEST2);
|
||||
parse_success(&TEST3a);
|
||||
parse_success(&TEST4a);
|
||||
parse_success(&TEST5a);
|
||||
parse_success(&TEST6a);
|
||||
parse_success(&TEST7a);
|
||||
parse_success(&TEST7b);
|
||||
|
||||
Reference in New Issue
Block a user