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)
ts->keepon = KEEPON_CONNECT;
ts->cl = 0;
ts->close_connection = FALSE;
+ ts->maybe_folded = FALSE;
+ ts->leading_unfold = FALSE;
return CURLE_OK;
}
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
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;
/* 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 {
- 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;
}
-
- 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)
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);
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)