aboutsummaryrefslogtreecommitdiff
path: root/lib/http.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/http.c')
-rw-r--r--lib/http.c539
1 files changed, 252 insertions, 287 deletions
diff --git a/lib/http.c b/lib/http.c
index 40ef70df5..679931e4b 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -100,24 +100,14 @@
* Forward declarations.
*/
-static int http_getsock_do(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t *socks);
static bool http_should_fail(struct Curl_easy *data);
-static CURLcode http_setup_conn(struct Curl_easy *data,
- struct connectdata *conn);
-#ifdef USE_WEBSOCKETS
-static CURLcode ws_setup_conn(struct Curl_easy *data,
- struct connectdata *conn);
-#endif
-
/*
* HTTP handler interface.
*/
const struct Curl_handler Curl_handler_http = {
"HTTP", /* scheme */
- http_setup_conn, /* setup_connection */
+ Curl_http_setup_conn, /* setup_connection */
Curl_http, /* do_it */
Curl_http_done, /* done */
ZERO_NULL, /* do_more */
@@ -125,11 +115,11 @@ const struct Curl_handler Curl_handler_http = {
ZERO_NULL, /* connecting */
ZERO_NULL, /* doing */
ZERO_NULL, /* proto_getsock */
- http_getsock_do, /* doing_getsock */
+ Curl_http_getsock_do, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
- ZERO_NULL, /* readwrite */
+ Curl_http_write_resp, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_HTTP, /* defport */
@@ -139,39 +129,13 @@ const struct Curl_handler Curl_handler_http = {
PROTOPT_USERPWDCTRL
};
-#ifdef USE_WEBSOCKETS
-const struct Curl_handler Curl_handler_ws = {
- "WS", /* scheme */
- ws_setup_conn, /* setup_connection */
- Curl_http, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- Curl_http_connect, /* connect_it */
- ZERO_NULL, /* connecting */
- ZERO_NULL, /* doing */
- ZERO_NULL, /* proto_getsock */
- http_getsock_do, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- ZERO_NULL, /* perform_getsock */
- Curl_ws_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- ZERO_NULL, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTP, /* defport */
- CURLPROTO_WS, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_CREDSPERREQUEST | /* flags */
- PROTOPT_USERPWDCTRL
-};
-#endif
-
#ifdef USE_SSL
/*
* HTTPS handler interface.
*/
const struct Curl_handler Curl_handler_https = {
"HTTPS", /* scheme */
- http_setup_conn, /* setup_connection */
+ Curl_http_setup_conn, /* setup_connection */
Curl_http, /* do_it */
Curl_http_done, /* done */
ZERO_NULL, /* do_more */
@@ -179,11 +143,11 @@ const struct Curl_handler Curl_handler_https = {
NULL, /* connecting */
ZERO_NULL, /* doing */
NULL, /* proto_getsock */
- http_getsock_do, /* doing_getsock */
+ Curl_http_getsock_do, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
- ZERO_NULL, /* readwrite */
+ Curl_http_write_resp, /* write_resp */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
PORT_HTTPS, /* defport */
@@ -193,36 +157,10 @@ const struct Curl_handler Curl_handler_https = {
PROTOPT_USERPWDCTRL
};
-#ifdef USE_WEBSOCKETS
-const struct Curl_handler Curl_handler_wss = {
- "WSS", /* scheme */
- ws_setup_conn, /* setup_connection */
- Curl_http, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- Curl_http_connect, /* connect_it */
- NULL, /* connecting */
- ZERO_NULL, /* doing */
- NULL, /* proto_getsock */
- http_getsock_do, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- ZERO_NULL, /* perform_getsock */
- Curl_ws_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- ZERO_NULL, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTPS, /* defport */
- CURLPROTO_WSS, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */
- PROTOPT_USERPWDCTRL
-};
-#endif
-
#endif
-static CURLcode http_setup_conn(struct Curl_easy *data,
- struct connectdata *conn)
+CURLcode Curl_http_setup_conn(struct Curl_easy *data,
+ struct connectdata *conn)
{
/* allocate the HTTP-specific struct for the Curl_easy, only to survive
during this request */
@@ -245,16 +183,6 @@ static CURLcode http_setup_conn(struct Curl_easy *data,
return CURLE_OK;
}
-#ifdef USE_WEBSOCKETS
-static CURLcode ws_setup_conn(struct Curl_easy *data,
- struct connectdata *conn)
-{
- /* websockets is 1.1 only (for now) */
- data->state.httpwant = CURL_HTTP_VERSION_1_1;
- return http_setup_conn(data, conn);
-}
-#endif
-
#ifndef CURL_DISABLE_PROXY
/*
* checkProxyHeaders() checks the linked list of custom proxy headers
@@ -297,7 +225,6 @@ char *Curl_copy_header_value(const char *header)
{
const char *start;
const char *end;
- char *value;
size_t len;
/* Find the end of the header name */
@@ -330,14 +257,7 @@ char *Curl_copy_header_value(const char *header)
/* get length of the type */
len = end - start + 1;
- value = malloc(len + 1);
- if(!value)
- return NULL;
-
- memcpy(value, start, len);
- value[len] = 0; /* null-terminate */
-
- return value;
+ return Curl_memdup0(start, len);
}
#ifndef CURL_DISABLE_HTTP_AUTH
@@ -836,6 +756,7 @@ output_auth_headers(struct Curl_easy *data,
(data->state.aptr.user ?
data->state.aptr.user : ""));
#else
+ (void)proxy;
infof(data, "Server auth using %s with user '%s'",
auth, data->state.aptr.user ?
data->state.aptr.user : "");
@@ -845,7 +766,7 @@ output_auth_headers(struct Curl_easy *data,
else
authstatus->multipass = FALSE;
- return CURLE_OK;
+ return result;
}
/**
@@ -970,17 +891,21 @@ Curl_http_output_auth(struct Curl_easy *data,
}
#endif
-/*
- * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
- * headers. They are dealt with both in the transfer.c main loop and in the
- * proxy CONNECT loop.
- */
-
+#if defined(USE_SPNEGO) || defined(USE_NTLM) || \
+ !defined(CURL_DISABLE_DIGEST_AUTH) || \
+ !defined(CURL_DISABLE_BASIC_AUTH) || \
+ !defined(CURL_DISABLE_BEARER_AUTH)
static int is_valid_auth_separator(char ch)
{
return ch == '\0' || ch == ',' || ISSPACE(ch);
}
+#endif
+/*
+ * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
+ * headers. They are dealt with both in the transfer.c main loop and in the
+ * proxy CONNECT loop.
+ */
CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
const char *auth) /* the first non-space */
{
@@ -992,11 +917,15 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state :
&conn->http_negotiate_state;
#endif
+#if defined(USE_SPNEGO) || \
+ defined(USE_NTLM) || \
+ !defined(CURL_DISABLE_DIGEST_AUTH) || \
+ !defined(CURL_DISABLE_BASIC_AUTH) || \
+ !defined(CURL_DISABLE_BEARER_AUTH)
+
unsigned long *availp;
struct auth *authp;
- (void) conn; /* In case conditionals make it unused. */
-
if(proxy) {
availp = &data->info.proxyauthavail;
authp = &data->state.authproxy;
@@ -1005,6 +934,11 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
availp = &data->info.httpauthavail;
authp = &data->state.authhost;
}
+#else
+ (void) proxy;
+#endif
+
+ (void) conn; /* In case conditionals make it unused. */
/*
* Here we check if we want the specific single authentication (using ==) and
@@ -1140,7 +1074,14 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
}
}
#else
- ;
+ {
+ /*
+ * Empty block to terminate the if-else chain correctly.
+ *
+ * A semicolon would yield the same result here, but can cause a
+ * compiler warning when -Wextra is enabled.
+ */
+ }
#endif
/* there may be multiple methods on one line, so keep reading */
@@ -1403,7 +1344,7 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
* and install our own `data->state.fread_func` that
* on subsequent calls reads `in` empty.
* - when the whisked away `in` is empty, the `fread_func`
- * is restored ot its original state.
+ * is restored to its original state.
* The problem is that `fread_func` can only return
* `upload_buffer_size` lengths. If the send we do here
* is larger and blocks, we do re-sending with smaller
@@ -1576,9 +1517,9 @@ CURLcode Curl_http_connect(struct Curl_easy *data, bool *done)
/* this returns the socket to wait for in the DO and DOING state for the multi
interface and then we're always _sending_ a request and thus we wait for
the single socket to become writable only */
-static int http_getsock_do(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t *socks)
+int Curl_http_getsock_do(struct Curl_easy *data,
+ struct connectdata *conn,
+ curl_socket_t *socks)
{
/* write mode */
(void)conn;
@@ -1678,8 +1619,6 @@ static CURLcode expect100(struct Curl_easy *data,
struct dynbuf *req)
{
CURLcode result = CURLE_OK;
- data->state.expect100header = FALSE; /* default to false unless it is set
- to TRUE below */
if(!data->state.disableexpect && Curl_use_http_1_1plus(data, conn) &&
(conn->httpversion < 20)) {
/* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an
@@ -2084,6 +2023,7 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data,
switch(data->set.timecondition) {
default:
+ DEBUGF(infof(data, "invalid time condition"));
return CURLE_BAD_FUNCTION_ARGUMENT;
case CURL_TIMECOND_IFMODSINCE:
@@ -2252,7 +2192,7 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn)
}
#endif
- if(strcmp("Host:", ptr)) {
+ if(!strcasecompare("Host:", ptr)) {
aptr->host = aprintf("Host:%s\r\n", &ptr[5]);
if(!aptr->host)
return CURLE_OUT_OF_MEMORY;
@@ -2340,9 +2280,7 @@ CURLcode Curl_http_target(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
}
}
- /* Extract the URL to use in the request. Store in STRING_TEMP_URL for
- clean-up reasons if the function returns before the free() further
- down. */
+ /* Extract the URL to use in the request. */
uc = curl_url_get(h, CURLUPART_URL, &url, CURLU_NO_DEFAULT_PORT);
if(uc) {
curl_url_cleanup(h);
@@ -2414,14 +2352,16 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
/* Convert the form structure into a mime structure, then keep
the conversion */
if(!data->state.formp) {
- data->state.formp = calloc(sizeof(curl_mimepart), 1);
+ data->state.formp = calloc(1, sizeof(curl_mimepart));
if(!data->state.formp)
return CURLE_OUT_OF_MEMORY;
Curl_mime_cleanpart(data->state.formp);
result = Curl_getformdata(data, data->state.formp, data->set.httppost,
data->state.fread_func);
- if(result)
+ if(result) {
+ Curl_safefree(data->state.formp);
return result;
+ }
data->state.mimepost = data->state.formp;
}
break;
@@ -2494,6 +2434,29 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
return result;
}
+static CURLcode addexpect(struct Curl_easy *data, struct connectdata *conn,
+ struct dynbuf *r)
+{
+ data->state.expect100header = FALSE;
+ /* Avoid Expect: 100-continue if Upgrade: is used */
+ if(data->req.upgr101 == UPGR101_INIT) {
+ struct HTTP *http = data->req.p.http;
+ /* For really small puts we don't use Expect: headers at all, and for
+ the somewhat bigger ones we allow the app to disable it. Just make
+ sure that the expect100header is always set to the preferred value
+ here. */
+ char *ptr = Curl_checkheaders(data, STRCONST("Expect"));
+ if(ptr) {
+ data->state.expect100header =
+ Curl_compareheader(ptr, STRCONST("Expect:"),
+ STRCONST("100-continue"));
+ }
+ else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0)
+ return expect100(data, conn, r);
+ }
+ return CURLE_OK;
+}
+
CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
struct dynbuf *r, Curl_HttpReq httpreq)
{
@@ -2506,14 +2469,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
#endif
CURLcode result = CURLE_OK;
struct HTTP *http = data->req.p.http;
- const char *ptr;
-
- /* If 'authdone' is FALSE, we must not set the write socket index to the
- Curl_transfer() call below, as we're not ready to actually upload any
- data yet. */
switch(httpreq) {
-
case HTTPREQ_PUT: /* Let's PUT the data to the server! */
if(conn->bits.authneg)
@@ -2531,20 +2488,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
return result;
}
- /* For really small puts we don't use Expect: headers at all, and for
- the somewhat bigger ones we allow the app to disable it. Just make
- sure that the expect100header is always set to the preferred value
- here. */
- ptr = Curl_checkheaders(data, STRCONST("Expect"));
- if(ptr) {
- data->state.expect100header =
- Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
- }
- else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
- result = expect100(data, conn, r);
- if(result)
- return result;
- }
+ result = addexpect(data, conn, r);
+ if(result)
+ return result;
/* end of headers */
result = Curl_dyn_addn(r, STRCONST("\r\n"));
@@ -2617,22 +2563,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
}
#endif
- /* For really small posts we don't use Expect: headers at all, and for
- the somewhat bigger ones we allow the app to disable it. Just make
- sure that the expect100header is always set to the preferred value
- here. */
- ptr = Curl_checkheaders(data, STRCONST("Expect"));
- if(ptr) {
- data->state.expect100header =
- Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
- }
- else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
- result = expect100(data, conn, r);
- if(result)
- return result;
- }
- else
- data->state.expect100header = FALSE;
+ result = addexpect(data, conn, r);
+ if(result)
+ return result;
/* make the request end in a true CRLF */
result = Curl_dyn_addn(r, STRCONST("\r\n"));
@@ -2692,22 +2625,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
return result;
}
- /* For really small posts we don't use Expect: headers at all, and for
- the somewhat bigger ones we allow the app to disable it. Just make
- sure that the expect100header is always set to the preferred value
- here. */
- ptr = Curl_checkheaders(data, STRCONST("Expect"));
- if(ptr) {
- data->state.expect100header =
- Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
- }
- else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
- result = expect100(data, conn, r);
- if(result)
- return result;
- }
- else
- data->state.expect100header = FALSE;
+ result = addexpect(data, conn, r);
+ if(result)
+ return result;
#ifndef USE_HYPER
/* With Hyper the body is always passed on separately */
@@ -3020,13 +2940,14 @@ CURLcode Curl_http_resume(struct Curl_easy *data,
}
/* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
do {
+ char scratch[4*1024];
size_t readthisamountnow =
- (data->state.resume_from - passed > data->set.buffer_size) ?
- (size_t)data->set.buffer_size :
+ (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ?
+ sizeof(scratch) :
curlx_sotouz(data->state.resume_from - passed);
size_t actuallyread =
- data->state.fread_func(data->state.buffer, 1, readthisamountnow,
+ data->state.fread_func(scratch, 1, readthisamountnow,
data->state.in);
passed += actuallyread;
@@ -3061,6 +2982,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data,
{
struct SingleRequest *k = &data->req;
+ *done = FALSE;
if(data->req.newurl) {
if(conn->bits.close) {
/* Abort after the headers if "follow Location" is set
@@ -3186,14 +3108,14 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
) {
result = Curl_http2_switch(data, conn, FIRSTSOCKET);
if(result)
- return result;
+ goto fail;
}
else
#endif
DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET));
break;
case CURL_HTTP_VERSION_1_1:
- /* continue with HTTP/1.1 when explicitly requested */
+ /* continue with HTTP/1.x when explicitly requested */
break;
default:
/* Check if user wants to use HTTP/2 with clear TCP */
@@ -3201,7 +3123,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
DEBUGF(infof(data, "HTTP/2 over clean TCP"));
result = Curl_http2_switch(data, conn, FIRSTSOCKET);
if(result)
- return result;
+ goto fail;
}
break;
}
@@ -3211,11 +3133,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
result = Curl_http_host(data, conn);
if(result)
- return result;
+ goto fail;
result = Curl_http_useragent(data);
if(result)
- return result;
+ goto fail;
Curl_http_method(data, conn, &request, &httpreq);
@@ -3231,7 +3153,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
(pq ? pq : data->state.up.path), FALSE);
free(pq);
if(result)
- return result;
+ goto fail;
}
Curl_safefree(data->state.aptr.ref);
@@ -3256,23 +3178,23 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
/* we only consider transfer-encoding magic if libz support is built-in */
result = Curl_transferencode(data);
if(result)
- return result;
+ goto fail;
#endif
result = Curl_http_body(data, conn, httpreq, &te);
if(result)
- return result;
+ goto fail;
p_accept = Curl_checkheaders(data,
STRCONST("Accept"))?NULL:"Accept: */*\r\n";
result = Curl_http_resume(data, conn, httpreq);
if(result)
- return result;
+ goto fail;
result = Curl_http_range(data, httpreq);
if(result)
- return result;
+ goto fail;
httpstring = get_http_string(data, conn);
@@ -3290,7 +3212,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
result = Curl_http_target(data, conn, &req);
if(result) {
Curl_dyn_free(&req);
- return result;
+ goto fail;
}
#ifndef CURL_DISABLE_ALTSVC
@@ -3361,7 +3283,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(result) {
Curl_dyn_free(&req);
- return result;
+ goto fail;
}
if(!(conn->handler->flags&PROTOPT_SSL) &&
@@ -3397,7 +3319,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
}
if(result) {
Curl_dyn_free(&req);
- return result;
+ goto fail;
}
if((http->postsize > -1) &&
@@ -3433,6 +3355,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
but is disabled here again to avoid that the chunked encoded version is
actually used when sending the request body over h2 */
data->req.upload_chunky = FALSE;
+fail:
+ if(CURLE_TOO_LARGE == result)
+ failf(data, "HTTP request too large");
return result;
}
@@ -3685,7 +3610,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
k->content_range = TRUE;
}
}
- else
+ else if(k->httpcode < 300)
data->state.resume_from = 0; /* get everything */
}
#if !defined(CURL_DISABLE_COOKIES)
@@ -3895,7 +3820,7 @@ CURLcode Curl_http_statusline(struct Curl_easy *data,
* fields. */
if(data->set.timecondition)
data->info.timecond = TRUE;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case 204:
/* (quote from RFC2616, section 10.2.5): The server has
* fulfilled the request but does not need to return an
@@ -3994,37 +3919,33 @@ CURLcode Curl_bump_headersize(struct Curl_easy *data,
/*
* Read any HTTP header lines from the server and pass them to the client app.
*/
-CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
- struct connectdata *conn,
- ssize_t *nread,
- bool *stop_reading)
+static CURLcode http_rw_headers(struct Curl_easy *data,
+ const char *buf, size_t blen,
+ size_t *pconsumed)
{
- CURLcode result;
+ struct connectdata *conn = data->conn;
+ CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
- ssize_t onread = *nread;
- char *ostr = k->str;
char *headp;
- char *str_start;
char *end_ptr;
+ bool leftover_body = FALSE;
/* header line within buffer loop */
+ *pconsumed = 0;
do {
- size_t rest_length;
- size_t full_length;
+ size_t line_length;
int writetype;
- /* str_start is start of line within buf */
- str_start = k->str;
-
/* data is in network encoding so use 0x0a instead of '\n' */
- end_ptr = memchr(str_start, 0x0a, *nread);
+ end_ptr = memchr(buf, 0x0a, blen);
if(!end_ptr) {
/* Not a complete header line within buffer, append the data to
the end of the headerbuff. */
- result = Curl_dyn_addn(&data->state.headerb, str_start, *nread);
+ result = Curl_dyn_addn(&data->state.headerb, buf, blen);
if(result)
return result;
+ *pconsumed += blen;
if(!k->headerline) {
/* check if this looks like a protocol header */
@@ -4036,31 +3957,28 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(st == STATUS_BAD) {
/* this is not the beginning of a protocol first header line */
k->header = FALSE;
- k->badheader = HEADER_ALLBAD;
streamclose(conn, "bad HTTP: No end-of-message indicator");
if(!data->set.http09_allowed) {
failf(data, "Received HTTP/0.9 when not allowed");
return CURLE_UNSUPPORTED_PROTOCOL;
}
- break;
+ leftover_body = TRUE;
+ goto out;
}
}
-
- break; /* read more and try again */
+ goto out; /* read more and try again */
}
/* decrease the size of the remaining (supposed) header line */
- rest_length = (end_ptr - k->str) + 1;
- *nread -= (ssize_t)rest_length;
-
- k->str = end_ptr + 1; /* move past new line */
-
- full_length = k->str - str_start;
-
- result = Curl_dyn_addn(&data->state.headerb, str_start, full_length);
+ line_length = (end_ptr - buf) + 1;
+ result = Curl_dyn_addn(&data->state.headerb, buf, line_length);
if(result)
return result;
+ blen -= line_length;
+ buf += line_length;
+ *pconsumed += line_length;
+
/****
* We now have a FULL header line in 'headerb'.
*****/
@@ -4078,17 +3996,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
return CURLE_UNSUPPORTED_PROTOCOL;
}
k->header = FALSE;
- if(*nread)
- /* since there's more, this is a partial bad header */
- k->badheader = HEADER_PARTHEADER;
- else {
- /* this was all we read so it's all a bad header */
- k->badheader = HEADER_ALLBAD;
- *nread = onread;
- k->str = ostr;
- return CURLE_OK;
- }
- break;
+ leftover_body = TRUE;
+ goto out;
}
}
@@ -4097,6 +4006,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
headp = Curl_dyn_ptr(&data->state.headerb);
if((0x0a == *headp) || (0x0d == *headp)) {
size_t headerlen;
+ bool switch_to_h2 = FALSE;
/* Zero-length header line means end of headers! */
if('\r' == *headp)
@@ -4126,41 +4036,40 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
}
break;
case 101:
- /* Switching Protocols */
- if(k->upgr101 == UPGR101_H2) {
- /* Switching to HTTP/2 */
- DEBUGASSERT(conn->httpversion < 20);
- infof(data, "Received 101, Switching to HTTP/2");
- k->upgr101 = UPGR101_RECEIVED;
-
- /* we'll get more headers (HTTP/2 response) */
- k->header = TRUE;
- k->headerline = 0; /* restart the header line counter */
-
- /* switch to http2 now. The bytes after response headers
- are also processed here, otherwise they are lost. */
- result = Curl_http2_upgrade(data, conn, FIRSTSOCKET,
- k->str, *nread);
- if(result)
- return result;
- *nread = 0;
- }
+ if(conn->httpversion == 11) {
+ /* Switching Protocols only allowed from HTTP/1.1 */
+ if(k->upgr101 == UPGR101_H2) {
+ /* Switching to HTTP/2 */
+ infof(data, "Received 101, Switching to HTTP/2");
+ k->upgr101 = UPGR101_RECEIVED;
+
+ /* we'll get more headers (HTTP/2 response) */
+ k->header = TRUE;
+ k->headerline = 0; /* restart the header line counter */
+ switch_to_h2 = TRUE;
+ }
#ifdef USE_WEBSOCKETS
- else if(k->upgr101 == UPGR101_WS) {
- /* verify the response */
- result = Curl_ws_accept(data, k->str, *nread);
- if(result)
- return result;
- k->header = FALSE; /* no more header to parse! */
- if(data->set.connect_only) {
- k->keepon &= ~KEEP_RECV; /* read no more content */
- *nread = 0;
+ else if(k->upgr101 == UPGR101_WS) {
+ /* verify the response */
+ result = Curl_ws_accept(data, buf, blen);
+ if(result)
+ return result;
+ k->header = FALSE; /* no more header to parse! */
+ *pconsumed += blen; /* ws accept handled the data */
+ blen = 0;
+ if(data->set.connect_only)
+ k->keepon &= ~KEEP_RECV; /* read no more content */
}
- }
#endif
+ else {
+ /* Not switching to another protocol */
+ k->header = FALSE; /* no more header to parse! */
+ }
+ }
else {
- /* Not switching to another protocol */
- k->header = FALSE; /* no more header to parse! */
+ /* invalid for other HTTP versions */
+ failf(data, "unexpected 101 response code");
+ return CURLE_WEIRD_SERVER_REPLY;
}
break;
default:
@@ -4366,17 +4275,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
* out and return home.
*/
if(data->req.no_body)
- *stop_reading = TRUE;
-#ifndef CURL_DISABLE_RTSP
- else if((conn->handler->protocol & CURLPROTO_RTSP) &&
- (data->set.rtspreq == RTSPREQ_DESCRIBE) &&
- (k->size <= -1))
- /* Respect section 4.4 of rfc2326: If the Content-Length header is
- absent, a length 0 must be assumed. It will prevent libcurl from
- hanging on DESCRIBE request that got refused for whatever
- reason */
- *stop_reading = TRUE;
-#endif
+ k->download_done = TRUE;
/* If max download size is *zero* (nothing) we already have
nothing and can safely return ok now! But for HTTP/2, we'd
@@ -4386,19 +4285,27 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(0 == k->maxdownload
&& !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
&& !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
- *stop_reading = TRUE;
+ k->download_done = TRUE;
- if(*stop_reading) {
- /* we make sure that this socket isn't read more now */
- k->keepon &= ~KEEP_RECV;
- }
-
- Curl_debug(data, CURLINFO_HEADER_IN, str_start, headerlen);
- break; /* exit header line loop */
+ Curl_debug(data, CURLINFO_HEADER_IN,
+ Curl_dyn_ptr(&data->state.headerb),
+ Curl_dyn_len(&data->state.headerb));
+ goto out; /* exit header line loop */
}
/* We continue reading headers, reset the line-based header */
Curl_dyn_reset(&data->state.headerb);
+ if(switch_to_h2) {
+ /* Having handled the headers, we can do the HTTP/2 switch.
+ * Any remaining `buf` bytes are already HTTP/2 and passed to
+ * be processed. */
+ result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
+ if(result)
+ return result;
+ *pconsumed += blen;
+ blen = 0;
+ }
+
continue;
}
@@ -4583,15 +4490,84 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
Curl_dyn_reset(&data->state.headerb);
}
- while(*k->str); /* header line within buffer */
+ while(blen);
/* We might have reached the end of the header part here, but
there might be a non-header part left in the end of the read
buffer. */
-
+out:
+ if(!k->header && !leftover_body) {
+ Curl_dyn_free(&data->state.headerb);
+ }
return CURLE_OK;
}
+/*
+ * HTTP protocol `write_resp` implementation. Will parse headers
+ * when not done yet and otherwise return without consuming data.
+ */
+CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
+ const char *buf, size_t blen,
+ size_t *pconsumed,
+ bool *done)
+{
+ *done = FALSE;
+ if(!data->req.header) {
+ *pconsumed = 0;
+ return CURLE_OK;
+ }
+ else {
+ CURLcode result;
+
+ result = http_rw_headers(data, buf, blen, pconsumed);
+ if(!result && !data->req.header) {
+ /* we have successfully finished parsing the HEADERs */
+ result = Curl_http_firstwrite(data, data->conn, done);
+
+ if(!data->req.no_body && Curl_dyn_len(&data->state.headerb)) {
+ /* leftover from parsing something that turned out not
+ * to be a header, only happens if we allow for
+ * HTTP/0.9 like responses */
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ Curl_dyn_ptr(&data->state.headerb),
+ Curl_dyn_len(&data->state.headerb));
+ }
+ Curl_dyn_free(&data->state.headerb);
+ }
+ return result;
+ }
+}
+
+CURLcode Curl_http_write_resp(struct Curl_easy *data,
+ const char *buf, size_t blen,
+ bool is_eos,
+ bool *done)
+{
+ CURLcode result;
+ size_t consumed;
+ int flags;
+
+ *done = FALSE;
+ result = Curl_http_write_resp_hds(data, buf, blen, &consumed, done);
+ if(result || *done)
+ goto out;
+
+ DEBUGASSERT(consumed <= blen);
+ blen -= consumed;
+ buf += consumed;
+ /* either all was consumed in header parsing, or we have data left
+ * and are done with heders, e.g. it is BODY data */
+ DEBUGASSERT(!blen || !data->req.header);
+ if(!data->req.header && (blen || is_eos)) {
+ /* BODY data after header been parsed, write and consume */
+ flags = CLIENTWRITE_BODY;
+ if(is_eos)
+ flags |= CLIENTWRITE_EOS;
+ result = Curl_client_write(data, flags, (char *)buf, blen);
+ }
+out:
+ return result;
+}
/* Decode HTTP status code string. */
CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len)
@@ -4618,17 +4594,6 @@ out:
return result;
}
-/* simple implementation of strndup(), which isn't portable */
-static char *my_strndup(const char *ptr, size_t len)
-{
- char *copy = malloc(len + 1);
- if(!copy)
- return NULL;
- memcpy(copy, ptr, len);
- copy[len] = '\0';
- return copy;
-}
-
CURLcode Curl_http_req_make(struct httpreq **preq,
const char *method, size_t m_len,
const char *scheme, size_t s_len,
@@ -4639,7 +4604,7 @@ CURLcode Curl_http_req_make(struct httpreq **preq,
CURLcode result = CURLE_OUT_OF_MEMORY;
DEBUGASSERT(method);
- if(m_len + 1 >= sizeof(req->method))
+ if(m_len + 1 > sizeof(req->method))
return CURLE_BAD_FUNCTION_ARGUMENT;
req = calloc(1, sizeof(*req));
@@ -4647,17 +4612,17 @@ CURLcode Curl_http_req_make(struct httpreq **preq,
goto out;
memcpy(req->method, method, m_len);
if(scheme) {
- req->scheme = my_strndup(scheme, s_len);
+ req->scheme = Curl_memdup0(scheme, s_len);
if(!req->scheme)
goto out;
}
if(authority) {
- req->authority = my_strndup(authority, a_len);
+ req->authority = Curl_memdup0(authority, a_len);
if(!req->authority)
goto out;
}
if(path) {
- req->path = my_strndup(path, p_len);
+ req->path = Curl_memdup0(path, p_len);
if(!req->path)
goto out;
}
@@ -4795,7 +4760,7 @@ CURLcode Curl_http_req_make2(struct httpreq **preq,
CURLUcode uc;
DEBUGASSERT(method);
- if(m_len + 1 >= sizeof(req->method))
+ if(m_len + 1 > sizeof(req->method))
return CURLE_BAD_FUNCTION_ARGUMENT;
req = calloc(1, sizeof(*req));