diff options
Diffstat (limited to 'libs/libcurl/src/http.c')
-rw-r--r-- | libs/libcurl/src/http.c | 1741 |
1 files changed, 981 insertions, 760 deletions
diff --git a/libs/libcurl/src/http.c b/libs/libcurl/src/http.c index 6d61a9ec34..f3e4121d3a 100644 --- a/libs/libcurl/src/http.c +++ b/libs/libcurl/src/http.c @@ -47,10 +47,6 @@ #include <sys/param.h>
#endif
-#ifdef USE_HYPER
-#include <hyper.h>
-#endif
-
#include "urldata.h"
#include <curl/curl.h>
#include "transfer.h"
@@ -58,7 +54,7 @@ #include "formdata.h"
#include "mime.h"
#include "progress.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
#include "cookie.h"
#include "vauth/vauth.h"
#include "vtls/vtls.h"
@@ -68,6 +64,7 @@ #include "http_negotiate.h"
#include "http_aws_sigv4.h"
#include "url.h"
+#include "urlapi-int.h"
#include "share.h"
#include "hostip.h"
#include "dynhds.h"
@@ -75,12 +72,11 @@ #include "headers.h"
#include "select.h"
#include "parsedate.h" /* for the week day and month names */
-#include "strtoofft.h"
#include "multiif.h"
#include "strcase.h"
#include "content_encoding.h"
#include "http_proxy.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "http2.h"
#include "cfilters.h"
#include "connect.h"
@@ -88,8 +84,8 @@ #include "altsvc.h"
#include "hsts.h"
#include "ws.h"
-#include "c-hyper.h"
#include "curl_ctype.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -104,6 +100,30 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode); static bool http_exp100_is_waiting(struct Curl_easy *data);
static CURLcode http_exp100_add_reader(struct Curl_easy *data);
static void http_exp100_send_anyway(struct Curl_easy *data);
+static bool http_exp100_is_selected(struct Curl_easy *data);
+static void http_exp100_got100(struct Curl_easy *data);
+static CURLcode http_firstwrite(struct Curl_easy *data);
+static CURLcode http_header(struct Curl_easy *data,
+ const char *hd, size_t hdlen);
+static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn);
+static CURLcode http_range(struct Curl_easy *data,
+ Curl_HttpReq httpreq);
+static CURLcode http_req_complete(struct Curl_easy *data,
+ struct dynbuf *r, int httpversion,
+ Curl_HttpReq httpreq);
+static CURLcode http_req_set_reader(struct Curl_easy *data,
+ Curl_HttpReq httpreq, int httpversion,
+ const char **tep);
+static CURLcode http_size(struct Curl_easy *data);
+static CURLcode http_statusline(struct Curl_easy *data,
+ struct connectdata *conn);
+static CURLcode http_target(struct Curl_easy *data, struct connectdata *conn,
+ struct dynbuf *req);
+static CURLcode http_useragent(struct Curl_easy *data);
+#ifdef HAVE_LIBZ
+static CURLcode http_transferencode(struct Curl_easy *data);
+#endif
+
/*
* HTTP handler interface.
@@ -126,6 +146,7 @@ const struct Curl_handler Curl_handler_http = { Curl_http_write_resp_hd, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ Curl_http_follow, /* follow */
PORT_HTTP, /* defport */
CURLPROTO_HTTP, /* protocol */
CURLPROTO_HTTP, /* family */
@@ -155,6 +176,7 @@ const struct Curl_handler Curl_handler_https = { Curl_http_write_resp_hd, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ Curl_http_follow, /* follow */
PORT_HTTPS, /* defport */
CURLPROTO_HTTPS, /* protocol */
CURLPROTO_HTTP, /* family */
@@ -164,19 +186,56 @@ const struct Curl_handler Curl_handler_https = { #endif
+void Curl_http_neg_init(struct Curl_easy *data, struct http_negotiation *neg)
+{
+ memset(neg, 0, sizeof(*neg));
+ neg->accept_09 = data->set.http09_allowed;
+ switch(data->set.httpwant) {
+ case CURL_HTTP_VERSION_1_0:
+ neg->wanted = neg->allowed = (CURL_HTTP_V1x);
+ neg->only_10 = TRUE;
+ break;
+ case CURL_HTTP_VERSION_1_1:
+ neg->wanted = neg->allowed = (CURL_HTTP_V1x);
+ break;
+ case CURL_HTTP_VERSION_2_0:
+ neg->wanted = neg->allowed = (CURL_HTTP_V1x | CURL_HTTP_V2x);
+ neg->h2_upgrade = TRUE;
+ break;
+ case CURL_HTTP_VERSION_2TLS:
+ neg->wanted = neg->allowed = (CURL_HTTP_V1x | CURL_HTTP_V2x);
+ break;
+ case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
+ neg->wanted = neg->allowed = (CURL_HTTP_V2x);
+ data->state.http_neg.h2_prior_knowledge = TRUE;
+ break;
+ case CURL_HTTP_VERSION_3:
+ neg->wanted = (CURL_HTTP_V1x | CURL_HTTP_V2x | CURL_HTTP_V3x);
+ neg->allowed = neg->wanted;
+ break;
+ case CURL_HTTP_VERSION_3ONLY:
+ neg->wanted = neg->allowed = (CURL_HTTP_V3x);
+ break;
+ case CURL_HTTP_VERSION_NONE:
+ default:
+ neg->wanted = (CURL_HTTP_V1x | CURL_HTTP_V2x);
+ neg->allowed = (CURL_HTTP_V1x | CURL_HTTP_V2x | CURL_HTTP_V3x);
+ break;
+ }
+}
+
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 */
connkeep(conn, "HTTP default");
-
- if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) {
+ if(data->state.http_neg.wanted == CURL_HTTP_V3x) {
+ /* only HTTP/3, needs to work */
CURLcode result = Curl_conn_may_http3(data, conn);
if(result)
return result;
}
-
return CURLE_OK;
}
@@ -213,46 +272,28 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, #endif
/*
- * Strip off leading and trailing whitespace from the value in the
- * given HTTP header line and return a strdupped copy. Returns NULL in
- * case of allocation failure. Returns an empty string if the header value
- * consists entirely of whitespace.
+ * Strip off leading and trailing whitespace from the value in the given HTTP
+ * header line and return a strdup()ed copy. Returns NULL in case of
+ * allocation failure or bad input. Returns an empty string if the header
+ * value consists entirely of whitespace.
+ *
+ * If the header is provided as "name;", ending with a semicolon, it must
+ * return a blank string.
*/
char *Curl_copy_header_value(const char *header)
{
- const char *start;
- const char *end;
- size_t len;
+ struct Curl_str out;
- /* Find the end of the header name */
- while(*header && (*header != ':'))
- ++header;
+ /* find the end of the header name */
+ if(!curlx_str_cspn(&header, &out, ";:") &&
+ (!curlx_str_single(&header, ':') || !curlx_str_single(&header, ';'))) {
+ curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE);
+ curlx_str_trimblanks(&out);
- if(*header)
- /* Skip over colon */
- ++header;
-
- /* Find the first non-space letter */
- start = header;
- while(*start && ISSPACE(*start))
- start++;
-
- end = strchr(start, '\r');
- if(!end)
- end = strchr(start, '\n');
- if(!end)
- end = strchr(start, '\0');
- if(!end)
- return NULL;
-
- /* skip all trailing space letters */
- while((end > start) && ISSPACE(*end))
- end--;
-
- /* get length of the type */
- len = end - start + 1;
-
- return Curl_memdup0(start, len);
+ return Curl_memdup0(curlx_str(&out), curlx_strlen(&out));
+ }
+ /* bad input */
+ return NULL;
}
#ifndef CURL_DISABLE_HTTP_AUTH
@@ -295,7 +336,7 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) if(!out)
return CURLE_OUT_OF_MEMORY;
- result = Curl_base64_encode(out, strlen(out), &authorization, &size);
+ result = curlx_base64_encode(out, strlen(out), &authorization, &size);
if(result)
goto fail;
@@ -436,7 +477,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, #if defined(USE_NTLM)
if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
(data->state.authhost.picked == CURLAUTH_NTLM)) {
- ongoing_auth = "NTML";
+ ongoing_auth = "NTLM";
if((conn->http_ntlm_state != NTLMSTATE_NONE) ||
(conn->proxy_ntlm_state != NTLMSTATE_NONE)) {
/* The NTLM-negotiation has started, keep on sending.
@@ -473,7 +514,6 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, ongoing_auth ? " send, " : "");
/* We decided to abort the ongoing transfer */
streamclose(conn, "Mid-auth HTTP and much data left to send");
- /* FIXME: questionable manipulation here, can we do this differently? */
data->req.size = 0; /* do not download any more than 0 bytes */
}
return CURLE_OK;
@@ -510,11 +550,14 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) pickhost = pickoneauth(&data->state.authhost, authmask);
if(!pickhost)
data->state.authproblem = TRUE;
+ else
+ data->info.httpauthpicked = data->state.authhost.picked;
if(data->state.authhost.picked == CURLAUTH_NTLM &&
- conn->httpversion > 11) {
+ (data->req.httpversion_sent > 11)) {
infof(data, "Forcing HTTP/1.1 for NTLM");
connclose(conn, "Force HTTP/1.1 connection");
- data->state.httpwant = CURL_HTTP_VERSION_1_1;
+ data->state.http_neg.wanted = CURL_HTTP_V1x;
+ data->state.http_neg.allowed = CURL_HTTP_V1x;
}
}
#ifndef CURL_DISABLE_PROXY
@@ -525,6 +568,9 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) authmask & ~CURLAUTH_BEARER);
if(!pickproxy)
data->state.authproblem = TRUE;
+ else
+ data->info.proxyauthpicked = data->state.authproxy.picked;
+
}
#endif
@@ -536,7 +582,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) /* In case this is GSS auth, the newurl field is already allocated so
we must make sure to free it before allocating a new one. As figured
out in bug #2284386 */
- Curl_safefree(data->req.newurl);
+ free(data->req.newurl);
data->req.newurl = strdup(data->state.url); /* clone URL */
if(!data->req.newurl)
return CURLE_OUT_OF_MEMORY;
@@ -587,9 +633,10 @@ output_auth_headers(struct Curl_easy *data, (void)path;
#endif
#ifndef CURL_DISABLE_AWS
- if(authstatus->picked == CURLAUTH_AWS_SIGV4) {
+ if((authstatus->picked == CURLAUTH_AWS_SIGV4) && !proxy) {
+ /* this method is never for proxy */
auth = "AWS_SIGV4";
- result = Curl_output_aws_sigv4(data, proxy);
+ result = Curl_output_aws_sigv4(data);
if(result)
return result;
}
@@ -721,12 +768,12 @@ Curl_http_output_auth(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY
(conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
#endif
- data->state.aptr.user ||
+ data->state.aptr.user ||
#ifdef USE_SPNEGO
- authhost->want & CURLAUTH_NEGOTIATE ||
- authproxy->want & CURLAUTH_NEGOTIATE ||
+ authhost->want & CURLAUTH_NEGOTIATE ||
+ authproxy->want & CURLAUTH_NEGOTIATE ||
#endif
- data->set.str[STRING_BEARER])
+ data->set.str[STRING_BEARER])
/* continue please */;
else {
authhost->done = TRUE;
@@ -811,9 +858,137 @@ Curl_http_output_auth(struct Curl_easy *data, !defined(CURL_DISABLE_DIGEST_AUTH) || \
!defined(CURL_DISABLE_BASIC_AUTH) || \
!defined(CURL_DISABLE_BEARER_AUTH)
-static int is_valid_auth_separator(char ch)
+static bool authcmp(const char *auth, const char *line)
{
- return ch == '\0' || ch == ',' || ISSPACE(ch);
+ /* the auth string must not have an alnum following */
+ size_t n = strlen(auth);
+ return strncasecompare(auth, line, n) && !ISALNUM(line[n]);
+}
+#endif
+
+#ifdef USE_SPNEGO
+static CURLcode auth_spnego(struct Curl_easy *data,
+ bool proxy,
+ const char *auth,
+ struct auth *authp,
+ unsigned long *availp)
+{
+ if((authp->avail & CURLAUTH_NEGOTIATE) || Curl_auth_is_spnego_supported()) {
+ *availp |= CURLAUTH_NEGOTIATE;
+ authp->avail |= CURLAUTH_NEGOTIATE;
+
+ if(authp->picked == CURLAUTH_NEGOTIATE) {
+ struct connectdata *conn = data->conn;
+ CURLcode result = Curl_input_negotiate(data, conn, proxy, auth);
+ curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state :
+ &conn->http_negotiate_state;
+ if(!result) {
+ free(data->req.newurl);
+ data->req.newurl = strdup(data->state.url);
+ if(!data->req.newurl)
+ return CURLE_OUT_OF_MEMORY;
+ data->state.authproblem = FALSE;
+ /* we received a GSS auth token and we dealt with it fine */
+ *negstate = GSS_AUTHRECV;
+ }
+ else
+ data->state.authproblem = TRUE;
+ }
+ }
+ return CURLE_OK;
+}
+#endif
+
+#ifdef USE_NTLM
+static CURLcode auth_ntlm(struct Curl_easy *data,
+ bool proxy,
+ const char *auth,
+ struct auth *authp,
+ unsigned long *availp)
+{
+ /* NTLM support requires the SSL crypto libs */
+ if((authp->avail & CURLAUTH_NTLM) || Curl_auth_is_ntlm_supported()) {
+ *availp |= CURLAUTH_NTLM;
+ authp->avail |= CURLAUTH_NTLM;
+
+ if(authp->picked == CURLAUTH_NTLM) {
+ /* NTLM authentication is picked and activated */
+ CURLcode result = Curl_input_ntlm(data, proxy, auth);
+ if(!result)
+ data->state.authproblem = FALSE;
+ else {
+ infof(data, "NTLM authentication problem, ignoring.");
+ data->state.authproblem = TRUE;
+ }
+ }
+ }
+ return CURLE_OK;
+}
+#endif
+
+#ifndef CURL_DISABLE_DIGEST_AUTH
+static CURLcode auth_digest(struct Curl_easy *data,
+ bool proxy,
+ const char *auth,
+ struct auth *authp,
+ unsigned long *availp)
+{
+ if(authp->avail & CURLAUTH_DIGEST)
+ infof(data, "Ignoring duplicate digest auth header.");
+ else if(Curl_auth_is_digest_supported()) {
+ CURLcode result;
+
+ *availp |= CURLAUTH_DIGEST;
+ authp->avail |= CURLAUTH_DIGEST;
+
+ /* We call this function on input Digest headers even if Digest
+ * authentication is not activated yet, as we need to store the
+ * incoming data from this header in case we are going to use
+ * Digest */
+ result = Curl_input_digest(data, proxy, auth);
+ if(result) {
+ infof(data, "Digest authentication problem, ignoring.");
+ data->state.authproblem = TRUE;
+ }
+ }
+ return CURLE_OK;
+}
+#endif
+
+#ifndef CURL_DISABLE_BASIC_AUTH
+static CURLcode auth_basic(struct Curl_easy *data,
+ struct auth *authp,
+ unsigned long *availp)
+{
+ *availp |= CURLAUTH_BASIC;
+ authp->avail |= CURLAUTH_BASIC;
+ if(authp->picked == CURLAUTH_BASIC) {
+ /* We asked for Basic authentication but got a 40X back
+ anyway, which basically means our name+password is not
+ valid. */
+ authp->avail = CURLAUTH_NONE;
+ infof(data, "Basic authentication problem, ignoring.");
+ data->state.authproblem = TRUE;
+ }
+ return CURLE_OK;
+}
+#endif
+
+#ifndef CURL_DISABLE_BEARER_AUTH
+static CURLcode auth_bearer(struct Curl_easy *data,
+ struct auth *authp,
+ unsigned long *availp)
+{
+ *availp |= CURLAUTH_BEARER;
+ authp->avail |= CURLAUTH_BEARER;
+ if(authp->picked == CURLAUTH_BEARER) {
+ /* We asked for Bearer authentication but got a 40X back
+ anyway, which basically means our token is not valid. */
+ authp->avail = CURLAUTH_NONE;
+ infof(data, "Bearer authentication problem, ignoring.");
+ data->state.authproblem = TRUE;
+ }
+ return CURLE_OK;
}
#endif
@@ -821,6 +996,8 @@ static int is_valid_auth_separator(char ch) * 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.
+ *
+ * The 'auth' line ends with a null byte without CR or LF present.
*/
CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
const char *auth) /* the first non-space */
@@ -828,19 +1005,17 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, /*
* This resource requires authentication
*/
- struct connectdata *conn = data->conn;
-#ifdef USE_SPNEGO
- 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) || \
+#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;
+ CURLcode result = CURLE_OK;
+ DEBUGASSERT(auth);
+ DEBUGASSERT(data);
if(proxy) {
availp = &data->info.proxyauthavail;
@@ -850,11 +1025,6 @@ 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
@@ -874,126 +1044,43 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, while(*auth) {
#ifdef USE_SPNEGO
- if(checkprefix("Negotiate", auth) && is_valid_auth_separator(auth[9])) {
- if((authp->avail & CURLAUTH_NEGOTIATE) ||
- Curl_auth_is_spnego_supported()) {
- *availp |= CURLAUTH_NEGOTIATE;
- authp->avail |= CURLAUTH_NEGOTIATE;
-
- if(authp->picked == CURLAUTH_NEGOTIATE) {
- CURLcode result = Curl_input_negotiate(data, conn, proxy, auth);
- if(!result) {
- free(data->req.newurl);
- data->req.newurl = strdup(data->state.url);
- if(!data->req.newurl)
- return CURLE_OUT_OF_MEMORY;
- data->state.authproblem = FALSE;
- /* we received a GSS auth token and we dealt with it fine */
- *negstate = GSS_AUTHRECV;
- }
- else
- data->state.authproblem = TRUE;
- }
- }
- }
- else
+ if(authcmp("Negotiate", auth))
+ result = auth_spnego(data, proxy, auth, authp, availp);
#endif
#ifdef USE_NTLM
- /* NTLM support requires the SSL crypto libs */
- if(checkprefix("NTLM", auth) && is_valid_auth_separator(auth[4])) {
- if((authp->avail & CURLAUTH_NTLM) ||
- Curl_auth_is_ntlm_supported()) {
- *availp |= CURLAUTH_NTLM;
- authp->avail |= CURLAUTH_NTLM;
-
- if(authp->picked == CURLAUTH_NTLM) {
- /* NTLM authentication is picked and activated */
- CURLcode result = Curl_input_ntlm(data, proxy, auth);
- if(!result) {
- data->state.authproblem = FALSE;
- }
- else {
- infof(data, "Authentication problem. Ignoring this.");
- data->state.authproblem = TRUE;
- }
- }
- }
- }
- else
+ if(!result && authcmp("NTLM", auth))
+ result = auth_ntlm(data, proxy, auth, authp, availp);
#endif
#ifndef CURL_DISABLE_DIGEST_AUTH
- if(checkprefix("Digest", auth) && is_valid_auth_separator(auth[6])) {
- if((authp->avail & CURLAUTH_DIGEST) != 0)
- infof(data, "Ignoring duplicate digest auth header.");
- else if(Curl_auth_is_digest_supported()) {
- CURLcode result;
-
- *availp |= CURLAUTH_DIGEST;
- authp->avail |= CURLAUTH_DIGEST;
-
- /* We call this function on input Digest headers even if Digest
- * authentication is not activated yet, as we need to store the
- * incoming data from this header in case we are going to use
- * Digest */
- result = Curl_input_digest(data, proxy, auth);
- if(result) {
- infof(data, "Authentication problem. Ignoring this.");
- data->state.authproblem = TRUE;
- }
- }
- }
- else
+ if(!result && authcmp("Digest", auth))
+ result = auth_digest(data, proxy, auth, authp, availp);
#endif
#ifndef CURL_DISABLE_BASIC_AUTH
- if(checkprefix("Basic", auth) &&
- is_valid_auth_separator(auth[5])) {
- *availp |= CURLAUTH_BASIC;
- authp->avail |= CURLAUTH_BASIC;
- if(authp->picked == CURLAUTH_BASIC) {
- /* We asked for Basic authentication but got a 40X back
- anyway, which basically means our name+password is not
- valid. */
- authp->avail = CURLAUTH_NONE;
- infof(data, "Authentication problem. Ignoring this.");
- data->state.authproblem = TRUE;
- }
- }
- else
+ if(!result && authcmp("Basic", auth))
+ result = auth_basic(data, authp, availp);
#endif
#ifndef CURL_DISABLE_BEARER_AUTH
- if(checkprefix("Bearer", auth) &&
- is_valid_auth_separator(auth[6])) {
- *availp |= CURLAUTH_BEARER;
- authp->avail |= CURLAUTH_BEARER;
- if(authp->picked == CURLAUTH_BEARER) {
- /* We asked for Bearer authentication but got a 40X back
- anyway, which basically means our token is not valid. */
- authp->avail = CURLAUTH_NONE;
- infof(data, "Authentication problem. Ignoring this.");
- data->state.authproblem = TRUE;
- }
- }
-#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.
- */
- }
+ if(authcmp("Bearer", auth))
+ result = auth_bearer(data, authp, availp);
#endif
+ if(result)
+ break;
+
/* there may be multiple methods on one line, so keep reading */
- while(*auth && *auth != ',') /* read up to the next comma */
- auth++;
- if(*auth == ',') /* if we are on a comma, skip it */
- auth++;
- while(*auth && ISSPACE(*auth))
+ auth = strchr(auth, ',');
+ if(auth) /* if we are on a comma, skip it */
auth++;
+ else
+ break;
+ curlx_str_passblanks(&auth);
}
-
+ return result;
+#else
+ (void) proxy;
+ /* nothing to do when disabled */
return CURLE_OK;
+#endif
}
/**
@@ -1067,6 +1154,309 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode) return data->state.authproblem;
}
+static void http_switch_to_get(struct Curl_easy *data, int code)
+{
+ const char *req = data->set.str[STRING_CUSTOMREQUEST];
+ if((req || data->state.httpreq != HTTPREQ_GET) &&
+ (data->set.http_follow_mode == CURLFOLLOW_OBEYCODE)) {
+ infof(data, "Switch to GET because of %d response", code);
+ data->state.http_ignorecustom = TRUE;
+ }
+ else if(req && (data->set.http_follow_mode != CURLFOLLOW_FIRSTONLY))
+ infof(data, "Stick to %s instead of GET", req);
+
+ data->state.httpreq = HTTPREQ_GET;
+ Curl_creader_set_rewind(data, FALSE);
+}
+
+CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
+ followtype type)
+{
+ bool disallowport = FALSE;
+ bool reachedmax = FALSE;
+ char *follow_url = NULL;
+ CURLUcode uc;
+ CURLcode rewind_result;
+ bool switch_to_get = FALSE;
+
+ DEBUGASSERT(type != FOLLOW_NONE);
+
+ if(type != FOLLOW_FAKE)
+ data->state.requests++; /* count all real follows */
+ if(type == FOLLOW_REDIR) {
+ if((data->set.maxredirs != -1) &&
+ (data->state.followlocation >= data->set.maxredirs)) {
+ reachedmax = TRUE;
+ type = FOLLOW_FAKE; /* switch to fake to store the would-be-redirected
+ to URL */
+ }
+ else {
+ data->state.followlocation++; /* count redirect-followings, including
+ auth reloads */
+
+ if(data->set.http_auto_referer) {
+ CURLU *u;
+ char *referer = NULL;
+
+ /* We are asked to automatically set the previous URL as the referer
+ when we get the next URL. We pick the ->url field, which may or may
+ not be 100% correct */
+
+ if(data->state.referer_alloc) {
+ Curl_safefree(data->state.referer);
+ data->state.referer_alloc = FALSE;
+ }
+
+ /* Make a copy of the URL without credentials and fragment */
+ u = curl_url();
+ if(!u)
+ return CURLE_OUT_OF_MEMORY;
+
+ uc = curl_url_set(u, CURLUPART_URL, data->state.url, 0);
+ if(!uc)
+ uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0);
+ if(!uc)
+ uc = curl_url_set(u, CURLUPART_USER, NULL, 0);
+ if(!uc)
+ uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0);
+ if(!uc)
+ uc = curl_url_get(u, CURLUPART_URL, &referer, 0);
+
+ curl_url_cleanup(u);
+
+ if(uc || !referer)
+ return CURLE_OUT_OF_MEMORY;
+
+ data->state.referer = referer;
+ data->state.referer_alloc = TRUE; /* yes, free this later */
+ }
+ }
+ }
+
+ if((type != FOLLOW_RETRY) &&
+ (data->req.httpcode != 401) && (data->req.httpcode != 407) &&
+ Curl_is_absolute_url(newurl, NULL, 0, FALSE)) {
+ /* If this is not redirect due to a 401 or 407 response and an absolute
+ URL: do not allow a custom port number */
+ disallowport = TRUE;
+ }
+
+ DEBUGASSERT(data->state.uh);
+ uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, (unsigned int)
+ ((type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME :
+ ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) |
+ CURLU_ALLOW_SPACE |
+ (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)));
+ if(uc) {
+ if(type != FOLLOW_FAKE) {
+ failf(data, "The redirect target URL could not be parsed: %s",
+ curl_url_strerror(uc));
+ return Curl_uc_to_curlcode(uc);
+ }
+
+ /* the URL could not be parsed for some reason, but since this is FAKE
+ mode, just duplicate the field as-is */
+ follow_url = strdup(newurl);
+ if(!follow_url)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ uc = curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 0);
+ if(uc)
+ return Curl_uc_to_curlcode(uc);
+
+ /* Clear auth if this redirects to a different port number or protocol,
+ unless permitted */
+ if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) {
+ char *portnum;
+ int port;
+ bool clear = FALSE;
+
+ if(data->set.use_port && data->state.allow_port)
+ /* a custom port is used */
+ port = (int)data->set.use_port;
+ else {
+ uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum,
+ CURLU_DEFAULT_PORT);
+ if(uc) {
+ free(follow_url);
+ return Curl_uc_to_curlcode(uc);
+ }
+ port = atoi(portnum);
+ free(portnum);
+ }
+ if(port != data->info.conn_remote_port) {
+ infof(data, "Clear auth, redirects to port from %u to %u",
+ data->info.conn_remote_port, port);
+ clear = TRUE;
+ }
+ else {
+ char *scheme;
+ const struct Curl_handler *p;
+ uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0);
+ if(uc) {
+ free(follow_url);
+ return Curl_uc_to_curlcode(uc);
+ }
+
+ p = Curl_get_scheme_handler(scheme);
+ if(p && (p->protocol != data->info.conn_protocol)) {
+ infof(data, "Clear auth, redirects scheme from %s to %s",
+ data->info.conn_scheme, scheme);
+ clear = TRUE;
+ }
+ free(scheme);
+ }
+ if(clear) {
+ Curl_safefree(data->state.aptr.user);
+ Curl_safefree(data->state.aptr.passwd);
+ }
+ }
+ }
+ DEBUGASSERT(follow_url);
+
+ if(type == FOLLOW_FAKE) {
+ /* we are only figuring out the new URL if we would have followed locations
+ but now we are done so we can get out! */
+ data->info.wouldredirect = follow_url;
+
+ if(reachedmax) {
+ failf(data, "Maximum (%ld) redirects followed", data->set.maxredirs);
+ return CURLE_TOO_MANY_REDIRECTS;
+ }
+ return CURLE_OK;
+ }
+
+ if(disallowport)
+ data->state.allow_port = FALSE;
+
+ if(data->state.url_alloc)
+ Curl_safefree(data->state.url);
+
+ data->state.url = follow_url;
+ data->state.url_alloc = TRUE;
+ rewind_result = Curl_req_soft_reset(&data->req, data);
+ infof(data, "Issue another request to this URL: '%s'", data->state.url);
+ if((data->set.http_follow_mode == CURLFOLLOW_FIRSTONLY) &&
+ data->set.str[STRING_CUSTOMREQUEST] &&
+ !data->state.http_ignorecustom) {
+ data->state.http_ignorecustom = TRUE;
+ infof(data, "Drop custom request method for next request");
+ }
+
+ /*
+ * We get here when the HTTP code is 300-399 (and 401). We need to perform
+ * differently based on exactly what return code there was.
+ *
+ * News from 7.10.6: we can also get here on a 401 or 407, in case we act on
+ * an HTTP (proxy-) authentication scheme other than Basic.
+ */
+ switch(data->info.httpcode) {
+ /* 401 - Act on a WWW-Authenticate, we keep on moving and do the
+ Authorization: XXXX header in the HTTP request code snippet */
+ /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the
+ Proxy-Authorization: XXXX header in the HTTP request code snippet */
+ /* 300 - Multiple Choices */
+ /* 306 - Not used */
+ /* 307 - Temporary Redirect */
+ default: /* for all above (and the unknown ones) */
+ /* Some codes are explicitly mentioned since I have checked RFC2616 and
+ * they seem to be OK to POST to.
+ */
+ break;
+ case 301: /* Moved Permanently */
+ /* (quote from RFC7231, section 6.4.2)
+ *
+ * Note: For historical reasons, a user agent MAY change the request
+ * method from POST to GET for the subsequent request. If this
+ * behavior is undesired, the 307 (Temporary Redirect) status code
+ * can be used instead.
+ *
+ * ----
+ *
+ * Many webservers expect this, so these servers often answers to a POST
+ * request with an error page. To be sure that libcurl gets the page that
+ * most user agents would get, libcurl has to force GET.
+ *
+ * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
+ * can be overridden with CURLOPT_POSTREDIR.
+ */
+ if((data->state.httpreq == HTTPREQ_POST
+ || data->state.httpreq == HTTPREQ_POST_FORM
+ || data->state.httpreq == HTTPREQ_POST_MIME)
+ && !(data->set.keep_post & CURL_REDIR_POST_301)) {
+ http_switch_to_get(data, 301);
+ switch_to_get = TRUE;
+ }
+ break;
+ case 302: /* Found */
+ /* (quote from RFC7231, section 6.4.3)
+ *
+ * Note: For historical reasons, a user agent MAY change the request
+ * method from POST to GET for the subsequent request. If this
+ * behavior is undesired, the 307 (Temporary Redirect) status code
+ * can be used instead.
+ *
+ * ----
+ *
+ * Many webservers expect this, so these servers often answers to a POST
+ * request with an error page. To be sure that libcurl gets the page that
+ * most user agents would get, libcurl has to force GET.
+ *
+ * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
+ * can be overridden with CURLOPT_POSTREDIR.
+ */
+ if((data->state.httpreq == HTTPREQ_POST
+ || data->state.httpreq == HTTPREQ_POST_FORM
+ || data->state.httpreq == HTTPREQ_POST_MIME)
+ && !(data->set.keep_post & CURL_REDIR_POST_302)) {
+ http_switch_to_get(data, 302);
+ switch_to_get = TRUE;
+ }
+ break;
+
+ case 303: /* See Other */
+ /* 'See Other' location is not the resource but a substitute for the
+ * resource. In this case we switch the method to GET/HEAD, unless the
+ * method is POST and the user specified to keep it as POST.
+ * https://github.com/curl/curl/issues/5237#issuecomment-614641049
+ */
+ if(data->state.httpreq != HTTPREQ_GET &&
+ ((data->state.httpreq != HTTPREQ_POST &&
+ data->state.httpreq != HTTPREQ_POST_FORM &&
+ data->state.httpreq != HTTPREQ_POST_MIME) ||
+ !(data->set.keep_post & CURL_REDIR_POST_303))) {
+ http_switch_to_get(data, 303);
+ switch_to_get = TRUE;
+ }
+ break;
+ case 304: /* Not Modified */
+ /* 304 means we did a conditional request and it was "Not modified".
+ * We should not get any Location: header in this response!
+ */
+ break;
+ case 305: /* Use Proxy */
+ /* (quote from RFC2616, section 10.3.6):
+ * "The requested resource MUST be accessed through the proxy given
+ * by the Location field. The Location field gives the URI of the
+ * proxy. The recipient is expected to repeat this single request
+ * via the proxy. 305 responses MUST only be generated by origin
+ * servers."
+ */
+ break;
+ }
+
+ /* When rewind of upload data failed and we are not switching to GET,
+ * we need to fail the follow, as we cannot send the data again. */
+ if(rewind_result && !switch_to_get)
+ return rewind_result;
+
+ Curl_pgrsTime(data, TIMER_REDIRECT);
+ Curl_pgrsResetTransferSizes(data);
+
+ return CURLE_OK;
+}
+
/*
* Curl_compareheader()
*
@@ -1085,9 +1475,8 @@ Curl_compareheader(const char *headerline, /* line to check */ * The field value MAY be preceded by any amount of LWS, though a single SP
* is preferred." */
- size_t len;
- const char *start;
- const char *end;
+ const char *p;
+ struct Curl_str val;
DEBUGASSERT(hlen);
DEBUGASSERT(clen);
DEBUGASSERT(header);
@@ -1097,31 +1486,21 @@ Curl_compareheader(const char *headerline, /* line to check */ return FALSE; /* does not start with header */
/* pass the header */
- start = &headerline[hlen];
-
- /* pass all whitespace */
- while(*start && ISSPACE(*start))
- start++;
-
- /* find the end of the header line */
- end = strchr(start, '\r'); /* lines end with CRLF */
- if(!end) {
- /* in case there is a non-standard compliant line here */
- end = strchr(start, '\n');
-
- if(!end)
- /* hm, there is no line ending here, use the zero byte! */
- end = strchr(start, '\0');
- }
+ p = &headerline[hlen];
- len = end-start; /* length of the content part of the input line */
+ if(curlx_str_untilnl(&p, &val, MAX_HTTP_RESP_HEADER_SIZE))
+ return FALSE;
+ curlx_str_trimblanks(&val);
/* find the content string in the rest of the line */
- for(; len >= clen; len--, start++) {
- if(strncasecompare(start, content, clen))
- return TRUE; /* match! */
+ if(curlx_strlen(&val) >= clen) {
+ size_t len;
+ p = curlx_str(&val);
+ for(len = curlx_strlen(&val); len >= curlx_strlen(&val); len--, p++) {
+ if(strncasecompare(p, content, clen))
+ return TRUE; /* match! */
+ }
}
-
return FALSE; /* no match */
}
@@ -1168,8 +1547,7 @@ CURLcode Curl_http_done(struct Curl_easy *data, data->state.authhost.multipass = FALSE;
data->state.authproxy.multipass = FALSE;
- Curl_dyn_reset(&data->state.headerb);
- Curl_hyper_done(data);
+ curlx_dyn_reset(&data->state.headerb);
if(status)
return status;
@@ -1193,75 +1571,66 @@ CURLcode Curl_http_done(struct Curl_easy *data, return CURLE_OK;
}
-/*
- * Determine if we should use HTTP 1.1 (OR BETTER) for this request. Reasons
- * to avoid it include:
- *
- * - if the user specifically requested HTTP 1.0
- * - if the server we are connected to only supports 1.0
- * - if any server previously contacted to handle this request only supports
- * 1.0.
- */
-bool Curl_use_http_1_1plus(const struct Curl_easy *data,
- const struct connectdata *conn)
+/* Determine if we may use HTTP 1.1 for this request. */
+static bool http_may_use_1_1(const struct Curl_easy *data)
{
- if((data->state.httpversion == 10) || (conn->httpversion == 10))
+ const struct connectdata *conn = data->conn;
+ /* We have seen a previous response for *this* transfer with 1.0,
+ * on another connection or the same one. */
+ if(data->state.http_neg.rcvd_min == 10)
return FALSE;
- if((data->state.httpwant == CURL_HTTP_VERSION_1_0) &&
- (conn->httpversion <= 10))
+ /* We have seen a previous response on *this* connection with 1.0. */
+ if(conn && conn->httpversion_seen == 10)
return FALSE;
- return ((data->state.httpwant == CURL_HTTP_VERSION_NONE) ||
- (data->state.httpwant >= CURL_HTTP_VERSION_1_1));
+ /* We want 1.0 and have seen no previous response on *this* connection
+ with a higher version (maybe no response at all yet). */
+ if((data->state.http_neg.only_10) &&
+ (!conn || conn->httpversion_seen <= 10))
+ return FALSE;
+ /* We are not restricted to use 1.0 only. */
+ return !data->state.http_neg.only_10;
}
-#ifndef USE_HYPER
-static const char *get_http_string(const struct Curl_easy *data,
- const struct connectdata *conn)
+static unsigned char http_request_version(struct Curl_easy *data)
{
- if(Curl_conn_is_http3(data, conn, FIRSTSOCKET))
- return "3";
- if(Curl_conn_is_http2(data, conn, FIRSTSOCKET))
- return "2";
- if(Curl_use_http_1_1plus(data, conn))
- return "1.1";
-
- return "1.0";
+ unsigned char v = Curl_conn_http_version(data, data->conn);
+ if(!v) {
+ /* No specific HTTP connection filter installed. */
+ v = http_may_use_1_1(data) ? 11 : 10;
+ }
+ return v;
}
-#endif
-
-enum proxy_use {
- HEADER_SERVER, /* direct to server */
- HEADER_PROXY, /* regular request to proxy */
- HEADER_CONNECT /* sending CONNECT to a proxy */
-};
-static bool hd_name_eq(const char *n1, size_t n1len,
- const char *n2, size_t n2len)
+static const char *get_http_string(int httpversion)
{
- if(n1len == n2len) {
- return strncasecompare(n1, n2, n1len);
+ switch(httpversion) {
+ case 30:
+ return "3";
+ case 20:
+ return "2";
+ case 11:
+ return "1.1";
+ default:
+ return "1.0";
}
- return FALSE;
}
-CURLcode Curl_dynhds_add_custom(struct Curl_easy *data,
- bool is_connect,
- struct dynhds *hds)
+CURLcode Curl_add_custom_headers(struct Curl_easy *data,
+ bool is_connect, int httpversion,
+ struct dynbuf *req)
{
- struct connectdata *conn = data->conn;
- char *ptr;
struct curl_slist *h[2];
struct curl_slist *headers;
int numlists = 1; /* by default */
int i;
#ifndef CURL_DISABLE_PROXY
- enum proxy_use proxy;
+ enum Curl_proxy_use proxy;
if(is_connect)
proxy = HEADER_CONNECT;
else
- proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy ?
+ proxy = data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy ?
HEADER_PROXY : HEADER_SERVER;
switch(proxy) {
@@ -1290,250 +1659,80 @@ CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, /* loop through one or two lists */
for(i = 0; i < numlists; i++) {
for(headers = h[i]; headers; headers = headers->next) {
- const char *name, *value;
- size_t namelen, valuelen;
-
- /* There are 2 quirks in place for custom headers:
- * 1. setting only 'name:' to suppress a header from being sent
- * 2. setting only 'name;' to send an empty (illegal) header
- */
- ptr = strchr(headers->data, ':');
- if(ptr) {
- name = headers->data;
- namelen = ptr - headers->data;
- ptr++; /* pass the colon */
- while(*ptr && ISSPACE(*ptr))
- ptr++;
- if(*ptr) {
- value = ptr;
- valuelen = strlen(value);
- }
- else {
- /* quirk #1, suppress this header */
- continue;
- }
- }
+ CURLcode result = CURLE_OK;
+ bool blankheader = FALSE;
+ struct Curl_str name;
+ const char *p = headers->data;
+ const char *origp = p;
+
+ /* explicitly asked to send header without content is done by a header
+ that ends with a semicolon, but there must be no colon present in the
+ name */
+ if(!curlx_str_until(&p, &name, MAX_HTTP_RESP_HEADER_SIZE, ';') &&
+ !curlx_str_single(&p, ';') &&
+ !curlx_str_single(&p, '\0') &&
+ !memchr(curlx_str(&name), ':', curlx_strlen(&name)))
+ blankheader = TRUE;
else {
- ptr = strchr(headers->data, ';');
-
- if(!ptr) {
- /* neither : nor ; in provided header value. We seem
- * to ignore this silently */
- continue;
- }
-
- name = headers->data;
- namelen = ptr - headers->data;
- ptr++; /* pass the semicolon */
- while(*ptr && ISSPACE(*ptr))
- ptr++;
- if(!*ptr) {
- /* quirk #2, send an empty header */
- value = "";
- valuelen = 0;
+ p = origp;
+ if(!curlx_str_until(&p, &name, MAX_HTTP_RESP_HEADER_SIZE, ':') &&
+ !curlx_str_single(&p, ':')) {
+ struct Curl_str val;
+ curlx_str_untilnl(&p, &val, MAX_HTTP_RESP_HEADER_SIZE);
+ curlx_str_trimblanks(&val);
+ if(!curlx_strlen(&val))
+ /* no content, don't send this */
+ continue;
}
- else {
- /* this may be used for something else in the future,
- * ignore this for now */
+ else
+ /* no colon */
continue;
- }
}
- DEBUGASSERT(name && value);
+ /* only send this if the contents was non-blank or done special */
+
if(data->state.aptr.host &&
- /* a Host: header was sent already, do not pass on any custom Host:
- header as that will produce *two* in the same request! */
- hd_name_eq(name, namelen, STRCONST("Host:")))
+ /* a Host: header was sent already, do not pass on any custom
+ Host: header as that will produce *two* in the same
+ request! */
+ curlx_str_casecompare(&name, "Host"))
;
else if(data->state.httpreq == HTTPREQ_POST_FORM &&
/* this header (extended by formdata.c) is sent later */
- hd_name_eq(name, namelen, STRCONST("Content-Type:")))
+ curlx_str_casecompare(&name, "Content-Type"))
;
else if(data->state.httpreq == HTTPREQ_POST_MIME &&
/* this header is sent later */
- hd_name_eq(name, namelen, STRCONST("Content-Type:")))
+ curlx_str_casecompare(&name, "Content-Type"))
;
else if(data->req.authneg &&
/* while doing auth neg, do not allow the custom length since
we will force length zero then */
- hd_name_eq(name, namelen, STRCONST("Content-Length:")))
+ curlx_str_casecompare(&name, "Content-Length"))
;
else if(data->state.aptr.te &&
/* when asking for Transfer-Encoding, do not pass on a custom
Connection: */
- hd_name_eq(name, namelen, STRCONST("Connection:")))
+ curlx_str_casecompare(&name, "Connection"))
;
- else if((conn->httpversion >= 20) &&
- hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:")))
+ else if((httpversion >= 20) &&
+ curlx_str_casecompare(&name, "Transfer-Encoding"))
/* HTTP/2 does not support chunked requests */
;
- else if((hd_name_eq(name, namelen, STRCONST("Authorization:")) ||
- hd_name_eq(name, namelen, STRCONST("Cookie:"))) &&
+ else if((curlx_str_casecompare(&name, "Authorization") ||
+ curlx_str_casecompare(&name, "Cookie")) &&
/* be careful of sending this potentially sensitive header to
other hosts */
!Curl_auth_allowed_to_host(data))
;
- else {
- CURLcode result;
-
- result = Curl_dynhds_add(hds, name, namelen, value, valuelen);
- if(result)
- return result;
- }
- }
- }
-
- return CURLE_OK;
-}
-
-CURLcode Curl_add_custom_headers(struct Curl_easy *data,
- bool is_connect,
-#ifndef USE_HYPER
- struct dynbuf *req
-#else
- void *req
-#endif
- )
-{
- struct connectdata *conn = data->conn;
- char *ptr;
- struct curl_slist *h[2];
- struct curl_slist *headers;
- int numlists = 1; /* by default */
- int i;
-
-#ifndef CURL_DISABLE_PROXY
- enum proxy_use proxy;
-
- if(is_connect)
- proxy = HEADER_CONNECT;
- else
- proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy ?
- HEADER_PROXY : HEADER_SERVER;
-
- switch(proxy) {
- case HEADER_SERVER:
- h[0] = data->set.headers;
- break;
- case HEADER_PROXY:
- h[0] = data->set.headers;
- if(data->set.sep_headers) {
- h[1] = data->set.proxyheaders;
- numlists++;
- }
- break;
- case HEADER_CONNECT:
- if(data->set.sep_headers)
- h[0] = data->set.proxyheaders;
- else
- h[0] = data->set.headers;
- break;
- }
-#else
- (void)is_connect;
- h[0] = data->set.headers;
-#endif
+ else if(blankheader)
+ result = curlx_dyn_addf(req, "%.*s:\r\n", (int)curlx_strlen(&name),
+ curlx_str(&name));
+ else
+ result = curlx_dyn_addf(req, "%s\r\n", origp);
- /* loop through one or two lists */
- for(i = 0; i < numlists; i++) {
- headers = h[i];
-
- while(headers) {
- char *semicolonp = NULL;
- ptr = strchr(headers->data, ':');
- if(!ptr) {
- char *optr;
- /* no colon, semicolon? */
- ptr = strchr(headers->data, ';');
- if(ptr) {
- optr = ptr;
- ptr++; /* pass the semicolon */
- while(*ptr && ISSPACE(*ptr))
- ptr++;
-
- if(*ptr) {
- /* this may be used for something else in the future */
- optr = NULL;
- }
- else {
- if(*(--ptr) == ';') {
- /* copy the source */
- semicolonp = strdup(headers->data);
- if(!semicolonp) {
-#ifndef USE_HYPER
- Curl_dyn_free(req);
-#endif
- return CURLE_OUT_OF_MEMORY;
- }
- /* put a colon where the semicolon is */
- semicolonp[ptr - headers->data] = ':';
- /* point at the colon */
- optr = &semicolonp [ptr - headers->data];
- }
- }
- ptr = optr;
- }
- }
- if(ptr && (ptr != headers->data)) {
- /* we require a colon for this to be a true header */
-
- ptr++; /* pass the colon */
- while(*ptr && ISSPACE(*ptr))
- ptr++;
-
- if(*ptr || semicolonp) {
- /* only send this if the contents was non-blank or done special */
- CURLcode result = CURLE_OK;
- char *compare = semicolonp ? semicolonp : headers->data;
-
- if(data->state.aptr.host &&
- /* a Host: header was sent already, do not pass on any custom
- Host: header as that will produce *two* in the same
- request! */
- checkprefix("Host:", compare))
- ;
- else if(data->state.httpreq == HTTPREQ_POST_FORM &&
- /* this header (extended by formdata.c) is sent later */
- checkprefix("Content-Type:", compare))
- ;
- else if(data->state.httpreq == HTTPREQ_POST_MIME &&
- /* this header is sent later */
- checkprefix("Content-Type:", compare))
- ;
- else if(data->req.authneg &&
- /* while doing auth neg, do not allow the custom length since
- we will force length zero then */
- checkprefix("Content-Length:", compare))
- ;
- else if(data->state.aptr.te &&
- /* when asking for Transfer-Encoding, do not pass on a custom
- Connection: */
- checkprefix("Connection:", compare))
- ;
- else if((conn->httpversion >= 20) &&
- checkprefix("Transfer-Encoding:", compare))
- /* HTTP/2 does not support chunked requests */
- ;
- else if((checkprefix("Authorization:", compare) ||
- checkprefix("Cookie:", compare)) &&
- /* be careful of sending this potentially sensitive header to
- other hosts */
- !Curl_auth_allowed_to_host(data))
- ;
- else {
-#ifdef USE_HYPER
- result = Curl_hyper_header(data, req, compare);
-#else
- result = Curl_dyn_addf(req, "%s\r\n", compare);
-#endif
- }
- if(semicolonp)
- free(semicolonp);
- if(result)
- return result;
- }
- }
- headers = headers->next;
+ if(result)
+ return result;
}
}
@@ -1542,12 +1741,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, #ifndef CURL_DISABLE_PARSEDATE
CURLcode Curl_add_timecondition(struct Curl_easy *data,
-#ifndef USE_HYPER
- struct dynbuf *req
-#else
- void *req
-#endif
- )
+ struct dynbuf *req)
{
const struct tm *tm;
struct tm keeptime;
@@ -1610,12 +1804,7 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, tm->tm_min,
tm->tm_sec);
-#ifndef USE_HYPER
- result = Curl_dyn_add(req, datestr);
-#else
- result = Curl_hyper_header(data, req, datestr);
-#endif
-
+ result = curlx_dyn_add(req, datestr);
return result;
}
#else
@@ -1639,8 +1828,10 @@ void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, httpreq = HTTPREQ_PUT;
/* Now set the 'request' pointer to the proper request string */
- if(data->set.str[STRING_CUSTOMREQUEST])
+ if(data->set.str[STRING_CUSTOMREQUEST] &&
+ !data->state.http_ignorecustom) {
request = data->set.str[STRING_CUSTOMREQUEST];
+ }
else {
if(data->req.no_body)
request = "HEAD";
@@ -1669,7 +1860,7 @@ void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, *reqp = httpreq;
}
-CURLcode Curl_http_useragent(struct Curl_easy *data)
+static CURLcode http_useragent(struct Curl_easy *data)
{
/* The User-Agent string might have been allocated in url.c already, because
it might have been used in the proxy connect, but if we have got a header
@@ -1683,7 +1874,7 @@ CURLcode Curl_http_useragent(struct Curl_easy *data) }
-CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn)
+static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn)
{
const char *ptr;
struct dynamically_allocated_data *aptr = &data->state.aptr;
@@ -1733,7 +1924,7 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) if(colon)
*colon = 0; /* The host must not include an embedded port number */
}
- Curl_safefree(aptr->cookiehost);
+ free(aptr->cookiehost);
aptr->cookiehost = cookiehost;
}
#endif
@@ -1773,9 +1964,9 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) /*
* Append the request-target to the HTTP request
*/
-CURLcode Curl_http_target(struct Curl_easy *data,
- struct connectdata *conn,
- struct dynbuf *r)
+static CURLcode http_target(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct dynbuf *r)
{
CURLcode result = CURLE_OK;
const char *path = data->state.up.path;
@@ -1837,11 +2028,11 @@ CURLcode Curl_http_target(struct Curl_easy *data, curl_url_cleanup(h);
/* target or URL */
- result = Curl_dyn_add(r, data->set.str[STRING_TARGET] ?
+ result = curlx_dyn_add(r, data->set.str[STRING_TARGET] ?
data->set.str[STRING_TARGET] : url);
free(url);
if(result)
- return (result);
+ return result;
if(strcasecompare("ftp", data->state.up.scheme)) {
if(data->set.proxy_transfer_mode) {
@@ -1858,8 +2049,8 @@ CURLcode Curl_http_target(struct Curl_easy *data, }
}
if(!type) {
- result = Curl_dyn_addf(r, ";type=%c",
- data->state.prefer_ascii ? 'a' : 'i');
+ result = curlx_dyn_addf(r, ";type=%c",
+ data->state.prefer_ascii ? 'a' : 'i');
if(result)
return result;
}
@@ -1872,11 +2063,11 @@ CURLcode Curl_http_target(struct Curl_easy *data, (void)conn; /* not used in disabled-proxy builds */
#endif
{
- result = Curl_dyn_add(r, path);
+ result = curlx_dyn_add(r, path);
if(result)
return result;
if(query)
- result = Curl_dyn_addf(r, "?%s", query);
+ result = curlx_dyn_addf(r, "?%s", query);
}
return result;
@@ -2055,9 +2246,9 @@ static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq) return CURLE_OK;
}
-CURLcode Curl_http_req_set_reader(struct Curl_easy *data,
- Curl_HttpReq httpreq,
- const char **tep)
+static CURLcode http_req_set_reader(struct Curl_easy *data,
+ Curl_HttpReq httpreq, int httpversion,
+ const char **tep)
{
CURLcode result = CURLE_OK;
const char *ptr;
@@ -2076,12 +2267,10 @@ CURLcode Curl_http_req_set_reader(struct Curl_easy *data, data->req.upload_chunky =
Curl_compareheader(ptr,
STRCONST("Transfer-Encoding:"), STRCONST("chunked"));
- if(data->req.upload_chunky &&
- Curl_use_http_1_1plus(data, data->conn) &&
- (data->conn->httpversion >= 20)) {
- infof(data, "suppressing chunked transfer encoding on connection "
- "using HTTP version 2 or higher");
- data->req.upload_chunky = FALSE;
+ if(data->req.upload_chunky && (httpversion >= 20)) {
+ infof(data, "suppressing chunked transfer encoding on connection "
+ "using HTTP version 2 or higher");
+ data->req.upload_chunky = FALSE;
}
}
else {
@@ -2089,10 +2278,10 @@ CURLcode Curl_http_req_set_reader(struct Curl_easy *data, if(req_clen < 0) {
/* indeterminate request content length */
- if(Curl_use_http_1_1plus(data, data->conn)) {
+ if(httpversion > 10) {
/* On HTTP/1.1, enable chunked, on HTTP/2 and later we do not
* need it */
- data->req.upload_chunky = (data->conn->httpversion < 20);
+ data->req.upload_chunky = (httpversion < 20);
}
else {
failf(data, "Chunky upload is not supported by HTTP 1.0");
@@ -2111,7 +2300,7 @@ CURLcode Curl_http_req_set_reader(struct Curl_easy *data, }
static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r,
- bool *announced_exp100)
+ int httpversion, bool *announced_exp100)
{
CURLcode result;
char *ptr;
@@ -2130,15 +2319,13 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, *announced_exp100 =
Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
}
- else if(!data->state.disableexpect &&
- Curl_use_http_1_1plus(data, data->conn) &&
- (data->conn->httpversion < 20)) {
+ else if(!data->state.disableexpect && (httpversion == 11)) {
/* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an
Expect: 100-continue to the headers which actually speeds up post
operations (as there is one packet coming back from the web server) */
curl_off_t client_len = Curl_creader_client_length(data);
if(client_len > EXPECT_100_THRESHOLD || client_len < 0) {
- result = Curl_dyn_addn(r, STRCONST("Expect: 100-continue\r\n"));
+ result = curlx_dyn_addn(r, STRCONST("Expect: 100-continue\r\n"));
if(result)
return result;
*announced_exp100 = TRUE;
@@ -2147,21 +2334,20 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, return CURLE_OK;
}
-CURLcode Curl_http_req_complete(struct Curl_easy *data,
- struct dynbuf *r, Curl_HttpReq httpreq)
+static CURLcode http_req_complete(struct Curl_easy *data,
+ struct dynbuf *r, int httpversion,
+ Curl_HttpReq httpreq)
{
CURLcode result = CURLE_OK;
curl_off_t req_clen;
bool announced_exp100 = FALSE;
DEBUGASSERT(data->conn);
-#ifndef USE_HYPER
if(data->req.upload_chunky) {
result = Curl_httpchunk_add_reader(data);
if(result)
return result;
}
-#endif
/* Get the request body length that has been set up */
req_clen = Curl_creader_total_length(data);
@@ -2183,8 +2369,8 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data, !Curl_checkheaders(data, STRCONST("Content-Length")))) {
/* we allow replacing this header if not during auth negotiation,
although it is not very wise to actually set your own */
- result = Curl_dyn_addf(r, "Content-Length: %" FMT_OFF_T "\r\n",
- req_clen);
+ result = curlx_dyn_addf(r, "Content-Length: %" FMT_OFF_T "\r\n",
+ req_clen);
}
if(result)
goto out;
@@ -2196,7 +2382,7 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data, struct curl_slist *hdr;
for(hdr = data->state.mimepost->curlheaders; hdr; hdr = hdr->next) {
- result = Curl_dyn_addf(r, "%s\r\n", hdr->data);
+ result = curlx_dyn_addf(r, "%s\r\n", hdr->data);
if(result)
goto out;
}
@@ -2204,13 +2390,13 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data, #endif
if(httpreq == HTTPREQ_POST) {
if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
- result = Curl_dyn_addn(r, STRCONST("Content-Type: application/"
- "x-www-form-urlencoded\r\n"));
+ result = curlx_dyn_addn(r, STRCONST("Content-Type: application/"
+ "x-www-form-urlencoded\r\n"));
if(result)
goto out;
}
}
- result = addexpect(data, r, &announced_exp100);
+ result = addexpect(data, r, httpversion, &announced_exp100);
if(result)
goto out;
break;
@@ -2219,7 +2405,7 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data, }
/* end of headers */
- result = Curl_dyn_addn(r, STRCONST("\r\n"));
+ result = curlx_dyn_addn(r, STRCONST("\r\n"));
if(!result) {
Curl_pgrsSetUploadSize(data, req_clen);
if(announced_exp100)
@@ -2236,9 +2422,9 @@ out: #if !defined(CURL_DISABLE_COOKIES)
-CURLcode Curl_http_cookies(struct Curl_easy *data,
- struct connectdata *conn,
- struct dynbuf *r)
+static CURLcode http_cookies(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct dynbuf *r)
{
CURLcode result = CURLE_OK;
char *addcookies = NULL;
@@ -2275,7 +2461,7 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, if(co->value) {
size_t add;
if(!count) {
- result = Curl_dyn_addn(r, STRCONST("Cookie: "));
+ result = curlx_dyn_addn(r, STRCONST("Cookie: "));
if(result)
break;
}
@@ -2286,8 +2472,8 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, linecap = TRUE;
break;
}
- result = Curl_dyn_addf(r, "%s%s=%s", count ? "; " : "",
- co->name, co->value);
+ result = curlx_dyn_addf(r, "%s%s=%s", count ? "; " : "",
+ co->name, co->value);
if(result)
break;
clen += add + (count ? 2 : 0);
@@ -2298,24 +2484,26 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, }
if(addcookies && !result && !linecap) {
if(!count)
- result = Curl_dyn_addn(r, STRCONST("Cookie: "));
+ result = curlx_dyn_addn(r, STRCONST("Cookie: "));
if(!result) {
- result = Curl_dyn_addf(r, "%s%s", count ? "; " : "", addcookies);
+ result = curlx_dyn_addf(r, "%s%s", count ? "; " : "", addcookies);
count++;
}
}
if(count && !result)
- result = Curl_dyn_addn(r, STRCONST("\r\n"));
+ result = curlx_dyn_addn(r, STRCONST("\r\n"));
if(result)
return result;
}
return result;
}
+#else
+#define http_cookies(a,b,c) CURLE_OK
#endif
-CURLcode Curl_http_range(struct Curl_easy *data,
- Curl_HttpReq httpreq)
+static CURLcode http_range(struct Curl_easy *data,
+ Curl_HttpReq httpreq)
{
if(data->state.use_range) {
/*
@@ -2347,7 +2535,7 @@ CURLcode Curl_http_range(struct Curl_easy *data, }
else if(data->state.resume_from) {
/* This is because "resume" was selected */
- /* TODO: not sure if we want to send this header during authentication
+ /* Not sure if we want to send this header during authentication
* negotiation, but test1084 checks for it. In which case we have a
* "null" client reader installed that gives an unexpected length. */
curl_off_t total_len = data->req.authneg ?
@@ -2371,7 +2559,7 @@ CURLcode Curl_http_range(struct Curl_easy *data, return CURLE_OK;
}
-CURLcode Curl_http_firstwrite(struct Curl_easy *data)
+static CURLcode http_firstwrite(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
struct SingleRequest *k = &data->req;
@@ -2434,7 +2622,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data) }
#ifdef HAVE_LIBZ
-CURLcode Curl_transferencode(struct Curl_easy *data)
+static CURLcode http_transferencode(struct Curl_easy *data)
{
if(!Curl_checkheaders(data, STRCONST("TE")) &&
data->set.http_transfer_encoding) {
@@ -2466,7 +2654,6 @@ CURLcode Curl_transferencode(struct Curl_easy *data) }
#endif
-#ifndef USE_HYPER
/*
* Curl_http() gets called from the generic multi_do() function when an HTTP
* request is to be performed. This creates and sends a properly constructed
@@ -2483,6 +2670,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) struct dynbuf req;
char *altused = NULL;
const char *p_accept; /* Accept: string */
+ unsigned char httpversion;
/* Always consider the DO phase done after this function call, even if there
may be parts of the request that are not yet sent, since we can deal with
@@ -2491,29 +2679,29 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) switch(conn->alpn) {
case CURL_HTTP_VERSION_3:
- DEBUGASSERT(Curl_conn_is_http3(data, conn, FIRSTSOCKET));
+ DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
break;
case CURL_HTTP_VERSION_2:
#ifndef CURL_DISABLE_PROXY
- if(!Curl_conn_is_http2(data, conn, FIRSTSOCKET) &&
+ if((Curl_conn_http_version(data, conn) != 20) &&
conn->bits.proxy && !conn->bits.tunnel_proxy
) {
- result = Curl_http2_switch(data, conn, FIRSTSOCKET);
+ result = Curl_http2_switch(data);
if(result)
goto fail;
}
else
#endif
- DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET));
+ DEBUGASSERT(Curl_conn_http_version(data, conn) == 20);
break;
case CURL_HTTP_VERSION_1_1:
/* continue with HTTP/1.x when explicitly requested */
break;
default:
/* Check if user wants to use HTTP/2 with clear TCP */
- if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) {
+ if(Curl_http2_may_switch(data)) {
DEBUGF(infof(data, "HTTP/2 over clean TCP"));
- result = Curl_http2_switch(data, conn, FIRSTSOCKET);
+ result = Curl_http2_switch(data);
if(result)
goto fail;
}
@@ -2527,11 +2715,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(result)
goto fail;
- result = Curl_http_host(data, conn);
+ result = http_host(data, conn);
if(result)
goto fail;
- result = Curl_http_useragent(data);
+ result = http_useragent(data);
if(result)
goto fail;
@@ -2561,7 +2749,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
data->set.str[STRING_ENCODING]) {
- Curl_safefree(data->state.aptr.accept_encoding);
+ free(data->state.aptr.accept_encoding);
data->state.aptr.accept_encoding =
aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
if(!data->state.aptr.accept_encoding)
@@ -2572,38 +2760,39 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) #ifdef HAVE_LIBZ
/* we only consider transfer-encoding magic if libz support is built-in */
- result = Curl_transferencode(data);
+ result = http_transferencode(data);
if(result)
goto fail;
#endif
- result = Curl_http_req_set_reader(data, httpreq, &te);
+ httpversion = http_request_version(data);
+ httpstring = get_http_string(httpversion);
+
+ result = http_req_set_reader(data, httpreq, httpversion, &te);
if(result)
goto fail;
p_accept = Curl_checkheaders(data,
STRCONST("Accept")) ? NULL : "Accept: */*\r\n";
- result = Curl_http_range(data, httpreq);
+ result = http_range(data, httpreq);
if(result)
goto fail;
- httpstring = get_http_string(data, conn);
-
/* initialize a dynamic send-buffer */
- Curl_dyn_init(&req, DYN_HTTP_REQUEST);
+ curlx_dyn_init(&req, DYN_HTTP_REQUEST);
/* make sure the header buffer is reset - if there are leftovers from a
previous transfer */
- Curl_dyn_reset(&data->state.headerb);
+ curlx_dyn_reset(&data->state.headerb);
/* add the main request stuff */
/* GET/HEAD/POST/PUT */
- result = Curl_dyn_addf(&req, "%s ", request);
+ result = curlx_dyn_addf(&req, "%s ", request);
if(!result)
- result = Curl_http_target(data, conn, &req);
+ result = http_target(data, conn, &req);
if(result) {
- Curl_dyn_free(&req);
+ curlx_dyn_free(&req);
goto fail;
}
@@ -2612,62 +2801,62 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) altused = aprintf("Alt-Used: %s:%d\r\n",
conn->conn_to_host.name, conn->conn_to_port);
if(!altused) {
- Curl_dyn_free(&req);
+ curlx_dyn_free(&req);
return CURLE_OUT_OF_MEMORY;
}
}
#endif
result =
- Curl_dyn_addf(&req,
- " HTTP/%s\r\n" /* HTTP version */
- "%s" /* host */
- "%s" /* proxyuserpwd */
- "%s" /* userpwd */
- "%s" /* range */
- "%s" /* user agent */
- "%s" /* accept */
- "%s" /* TE: */
- "%s" /* accept-encoding */
- "%s" /* referer */
- "%s" /* Proxy-Connection */
- "%s" /* transfer-encoding */
- "%s",/* Alt-Used */
-
- httpstring,
- (data->state.aptr.host ? data->state.aptr.host : ""),
+ curlx_dyn_addf(&req,
+ " HTTP/%s\r\n" /* HTTP version */
+ "%s" /* host */
+ "%s" /* proxyuserpwd */
+ "%s" /* userpwd */
+ "%s" /* range */
+ "%s" /* user agent */
+ "%s" /* accept */
+ "%s" /* TE: */
+ "%s" /* accept-encoding */
+ "%s" /* referer */
+ "%s" /* Proxy-Connection */
+ "%s" /* transfer-encoding */
+ "%s",/* Alt-Used */
+
+ httpstring,
+ (data->state.aptr.host ? data->state.aptr.host : ""),
#ifndef CURL_DISABLE_PROXY
- data->state.aptr.proxyuserpwd ?
- data->state.aptr.proxyuserpwd : "",
+ data->state.aptr.proxyuserpwd ?
+ data->state.aptr.proxyuserpwd : "",
#else
- "",
+ "",
#endif
- data->state.aptr.userpwd ? data->state.aptr.userpwd : "",
- (data->state.use_range && data->state.aptr.rangeline) ?
- data->state.aptr.rangeline : "",
- (data->set.str[STRING_USERAGENT] &&
- *data->set.str[STRING_USERAGENT] &&
- data->state.aptr.uagent) ?
- data->state.aptr.uagent : "",
- p_accept ? p_accept : "",
- data->state.aptr.te ? data->state.aptr.te : "",
- (data->set.str[STRING_ENCODING] &&
- *data->set.str[STRING_ENCODING] &&
- data->state.aptr.accept_encoding) ?
- data->state.aptr.accept_encoding : "",
- (data->state.referer && data->state.aptr.ref) ?
- data->state.aptr.ref : "" /* Referer: <data> */,
+ data->state.aptr.userpwd ? data->state.aptr.userpwd : "",
+ (data->state.use_range && data->state.aptr.rangeline) ?
+ data->state.aptr.rangeline : "",
+ (data->set.str[STRING_USERAGENT] &&
+ *data->set.str[STRING_USERAGENT] &&
+ data->state.aptr.uagent) ?
+ data->state.aptr.uagent : "",
+ p_accept ? p_accept : "",
+ data->state.aptr.te ? data->state.aptr.te : "",
+ (data->set.str[STRING_ENCODING] &&
+ *data->set.str[STRING_ENCODING] &&
+ data->state.aptr.accept_encoding) ?
+ data->state.aptr.accept_encoding : "",
+ (data->state.referer && data->state.aptr.ref) ?
+ data->state.aptr.ref : "" /* Referer: <data> */,
#ifndef CURL_DISABLE_PROXY
- (conn->bits.httpproxy &&
- !conn->bits.tunnel_proxy &&
- !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
- !Curl_checkProxyheaders(data, conn,
- STRCONST("Proxy-Connection"))) ?
- "Proxy-Connection: Keep-Alive\r\n":"",
+ (conn->bits.httpproxy &&
+ !conn->bits.tunnel_proxy &&
+ !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
+ !Curl_checkProxyheaders(data, conn,
+ STRCONST("Proxy-Connection"))) ?
+ "Proxy-Connection: Keep-Alive\r\n":"",
#else
- "",
+ "",
#endif
- te,
- altused ? altused : ""
+ te,
+ altused ? altused : ""
);
/* clear userpwd and proxyuserpwd to avoid reusing old credentials
@@ -2679,23 +2868,23 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) free(altused);
if(result) {
- Curl_dyn_free(&req);
+ curlx_dyn_free(&req);
goto fail;
}
- if(!(conn->handler->flags&PROTOPT_SSL) &&
- conn->httpversion < 20 &&
- (data->state.httpwant == CURL_HTTP_VERSION_2)) {
+ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET) && (httpversion < 20) &&
+ (data->state.http_neg.wanted & CURL_HTTP_V2x) &&
+ data->state.http_neg.h2_upgrade) {
/* append HTTP2 upgrade magic stuff to the HTTP request if it is not done
over SSL */
result = Curl_http2_request_upgrade(&req, data);
if(result) {
- Curl_dyn_free(&req);
+ curlx_dyn_free(&req);
return result;
}
}
- result = Curl_http_cookies(data, conn, &req);
+ result = http_cookies(data, conn, &req);
#ifndef CURL_DISABLE_WEBSOCKETS
if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
result = Curl_ws_request(data, &req);
@@ -2703,19 +2892,19 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(!result)
result = Curl_add_timecondition(data, &req);
if(!result)
- result = Curl_add_custom_headers(data, FALSE, &req);
+ result = Curl_add_custom_headers(data, FALSE, httpversion, &req);
if(!result) {
/* req_send takes ownership of the 'req' memory on success */
- result = Curl_http_req_complete(data, &req, httpreq);
+ result = http_req_complete(data, &req, httpversion, httpreq);
if(!result)
- result = Curl_req_send(data, &req);
+ result = Curl_req_send(data, &req, httpversion);
}
- Curl_dyn_free(&req);
+ curlx_dyn_free(&req);
if(result)
goto fail;
- if((conn->httpversion >= 20) && data->req.upload_chunky)
+ if((httpversion >= 20) && data->req.upload_chunky)
/* upload_chunky was set above to set up the request in a chunky fashion,
but is disabled here again to avoid that the chunked encoded version is
actually used when sending the request body over h2 */
@@ -2726,8 +2915,6 @@ fail: return result;
}
-#endif /* USE_HYPER */
-
typedef enum {
STATUS_UNKNOWN, /* not enough data to tell yet */
STATUS_DONE, /* a status line was read */
@@ -2814,10 +3001,10 @@ checkprotoprefix(struct Curl_easy *data, struct connectdata *conn, Curl_compareheader(hd, STRCONST(n), STRCONST(v)))
/*
- * Curl_http_header() parses a single response header.
+ * http_header() parses a single response header.
*/
-CURLcode Curl_http_header(struct Curl_easy *data,
- const char *hd, size_t hdlen)
+static CURLcode http_header(struct Curl_easy *data,
+ const char *hd, size_t hdlen)
{
struct connectdata *conn = data->conn;
CURLcode result;
@@ -2829,7 +3016,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, case 'A':
#ifndef CURL_DISABLE_ALTSVC
v = (data->asi &&
- ((data->conn->handler->flags & PROTOPT_SSL) ||
+ (Curl_conn_is_ssl(data->conn, FIRSTSOCKET) ||
#ifdef DEBUGBUILD
/* allow debug builds to circumvent the HTTPS restriction */
getenv("CURL_ALTSVC_HTTP")
@@ -2839,8 +3026,8 @@ CURLcode Curl_http_header(struct Curl_easy *data, )) ? HD_VAL(hd, hdlen, "Alt-Svc:") : NULL;
if(v) {
/* the ALPN of the current request */
- enum alpnid id = (conn->httpversion == 30) ? ALPN_h3 :
- (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
+ enum alpnid id = (k->httpversion == 30) ? ALPN_h3 :
+ (k->httpversion == 20) ? ALPN_h2 : ALPN_h1;
return Curl_altsvc_parse(data, data->asi, v, id, conn->host.name,
curlx_uitous((unsigned int)conn->remote_port));
}
@@ -2853,13 +3040,13 @@ CURLcode Curl_http_header(struct Curl_easy *data, HD_VAL(hd, hdlen, "Content-Length:") : NULL;
if(v) {
curl_off_t contentlength;
- CURLofft offt = curlx_strtoofft(v, NULL, 10, &contentlength);
+ int offt = curlx_str_numblanks(&v, &contentlength);
- if(offt == CURL_OFFT_OK) {
+ if(offt == STRE_OK) {
k->size = contentlength;
k->maxdownload = k->size;
}
- else if(offt == CURL_OFFT_FLOW) {
+ else if(offt == STRE_OVERFLOW) {
/* out of range */
if(data->set.max_filesize) {
failf(data, "Maximum file size exceeded");
@@ -2897,7 +3084,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, /* ignore empty data */
free(contenttype);
else {
- Curl_safefree(data->info.contenttype);
+ free(data->info.contenttype);
data->info.contenttype = contenttype;
}
return CURLE_OK;
@@ -2912,7 +3099,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, streamclose(conn, "Connection: close used");
return CURLE_OK;
}
- if((conn->httpversion == 10) &&
+ if((k->httpversion == 10) &&
HD_IS_AND_SAYS(hd, hdlen, "Connection:", "keep-alive")) {
/*
* An HTTP/1.0 reply with the 'Connection: keep-alive' line
@@ -2945,11 +3132,10 @@ CURLcode Curl_http_header(struct Curl_easy *data, /* if it truly stopped on a digit */
if(ISDIGIT(*ptr)) {
- if(!curlx_strtoofft(ptr, NULL, 10, &k->offset)) {
- if(data->state.resume_from == k->offset)
- /* we asked for a resume and we got it */
- k->content_range = TRUE;
- }
+ if(!curlx_str_number(&ptr, &k->offset, CURL_OFF_T_MAX) &&
+ (data->state.resume_from == k->offset))
+ /* we asked for a resume and we got it */
+ k->content_range = TRUE;
}
else if(k->httpcode < 300)
data->state.resume_from = 0; /* get everything */
@@ -2979,7 +3165,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, else {
data->req.location = location;
- if(data->set.http_follow_location) {
+ if(data->set.http_follow_mode) {
DEBUGASSERT(!data->req.newurl);
data->req.newurl = strdup(data->req.location); /* clone */
if(!data->req.newurl)
@@ -3002,7 +3188,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY
v = HD_VAL(hd, hdlen, "Proxy-Connection:");
if(v) {
- if((conn->httpversion == 10) && conn->bits.httpproxy &&
+ if((k->httpversion == 10) && conn->bits.httpproxy &&
HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) {
/*
* When an HTTP/1.0 reply comes when using a proxy, the
@@ -3013,7 +3199,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */
infof(data, "HTTP/1.0 proxy connection set to keep alive");
}
- else if((conn->httpversion == 11) && conn->bits.httpproxy &&
+ else if((k->httpversion == 11) && conn->bits.httpproxy &&
HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) {
/*
* We get an HTTP/1.1 response from a proxy and it says it will
@@ -3056,15 +3242,27 @@ CURLcode Curl_http_header(struct Curl_easy *data, if(v) {
/* Retry-After = HTTP-date / delay-seconds */
curl_off_t retry_after = 0; /* zero for unknown or "now" */
- /* Try it as a decimal number, if it works it is not a date */
- (void)curlx_strtoofft(v, NULL, 10, &retry_after);
- if(!retry_after) {
- time_t date = Curl_getdate_capped(v);
- if(-1 != date)
+ time_t date;
+ curlx_str_passblanks(&v);
+
+ /* try it as a date first, because a date can otherwise start with and
+ get treated as a number */
+ date = Curl_getdate_capped(v);
+
+ if((time_t)-1 != date) {
+ time_t current = time(NULL);
+ if(date >= current)
/* convert date to number of seconds into the future */
- retry_after = date - time(NULL);
+ retry_after = date - current;
}
- data->info.retry_after = retry_after; /* store it */
+ else
+ /* Try it as a decimal number */
+ curlx_str_number(&v, &retry_after, CURL_OFF_T_MAX);
+ /* limit to 6 hours max. this is not documented so that it can be changed
+ in the future if necessary. */
+ if(retry_after > 21600)
+ retry_after = 21600;
+ data->info.retry_after = retry_after;
return CURLE_OK;
}
break;
@@ -3095,7 +3293,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, #ifndef CURL_DISABLE_HSTS
/* If enabled, the header is incoming and this is over HTTPS */
v = (data->hsts &&
- ((conn->handler->flags & PROTOPT_SSL) ||
+ (Curl_conn_is_ssl(conn, FIRSTSOCKET) ||
#ifdef DEBUGBUILD
/* allow debug builds to circumvent the HTTPS restriction */
getenv("CURL_HSTS_HTTP")
@@ -3179,8 +3377,8 @@ CURLcode Curl_http_header(struct Curl_easy *data, * Called after the first HTTP response line (the status line) has been
* received and parsed.
*/
-CURLcode Curl_http_statusline(struct Curl_easy *data,
- struct connectdata *conn)
+static CURLcode http_statusline(struct Curl_easy *data,
+ struct connectdata *conn)
{
struct SingleRequest *k = &data->req;
@@ -3194,11 +3392,11 @@ CURLcode Curl_http_statusline(struct Curl_easy *data, case 30:
#endif
/* no major version switch mid-connection */
- if(conn->httpversion &&
- (k->httpversion/10 != conn->httpversion/10)) {
+ if(k->httpversion_sent &&
+ (k->httpversion/10 != k->httpversion_sent/10)) {
failf(data, "Version mismatch (from HTTP/%u to HTTP/%u)",
- conn->httpversion/10, k->httpversion/10);
- return CURLE_UNSUPPORTED_PROTOCOL;
+ k->httpversion_sent/10, k->httpversion/10);
+ return CURLE_WEIRD_SERVER_REPLY;
}
break;
default:
@@ -3209,11 +3407,12 @@ CURLcode Curl_http_statusline(struct Curl_easy *data, data->info.httpcode = k->httpcode;
data->info.httpversion = k->httpversion;
- conn->httpversion = (unsigned char)k->httpversion;
+ conn->httpversion_seen = (unsigned char)k->httpversion;
- if(!data->state.httpversion || data->state.httpversion > k->httpversion)
+ if(!data->state.http_neg.rcvd_min ||
+ data->state.http_neg.rcvd_min > k->httpversion)
/* store the lowest server version we encounter */
- data->state.httpversion = (unsigned char)k->httpversion;
+ data->state.http_neg.rcvd_min = (unsigned char)k->httpversion;
/*
* This code executes as part of processing the header. As a
@@ -3273,7 +3472,7 @@ CURLcode Curl_http_statusline(struct Curl_easy *data, figured out here after all headers have been received but before the final
call to the user's header callback, so that a valid content length can be
retrieved by the user in the final call. */
-CURLcode Curl_http_size(struct Curl_easy *data)
+static CURLcode http_size(struct Curl_easy *data)
{
struct SingleRequest *k = &data->req;
if(data->req.ignore_cl || k->chunk) {
@@ -3356,7 +3555,7 @@ static CURLcode http_write_header(struct Curl_easy *data, /* now, only output this if the header AND body are requested:
*/
- Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen);
+ Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
writetype = CLIENTWRITE_HEADER |
((data->req.httpcode/100 == 1) ? CLIENTWRITE_1XX : 0);
@@ -3389,7 +3588,7 @@ static CURLcode http_on_response(struct Curl_easy *data, if(k->upgr101 == UPGR101_RECEIVED) {
/* supposedly upgraded to http2 now */
- if(conn->httpversion != 20)
+ if(data->req.httpversion != 20)
infof(data, "Lying server, not serving HTTP/2");
}
@@ -3421,12 +3620,11 @@ static CURLcode http_on_response(struct Curl_easy *data, * that tells us that the server is OK with this and ready
* to receive the data.
*/
- Curl_http_exp100_got100(data);
+ http_exp100_got100(data);
break;
case 101:
/* Switching Protocols only allowed from HTTP/1.1 */
-
- if(conn->httpversion != 11) {
+ if(k->httpversion_sent != 11) {
/* invalid for other HTTP versions */
failf(data, "unexpected 101 response code");
result = CURLE_WEIRD_SERVER_REPLY;
@@ -3440,6 +3638,7 @@ static CURLcode http_on_response(struct Curl_easy *data, /* We expect more response from HTTP/2 later */
k->header = TRUE;
k->headerline = 0; /* restart the header line counter */
+ k->httpversion_sent = 20; /* It's an HTTP/2 request now */
/* Any remaining `buf` bytes are already HTTP/2 and passed to
* be processed. */
result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
@@ -3461,10 +3660,9 @@ static CURLcode http_on_response(struct Curl_easy *data, }
#endif
else {
- /* We silently accept this as the final response.
- * TODO: this looks, uhm, wrong. What are we switching to if we
- * did not ask for an Upgrade? Maybe the application provided an
- * `Upgrade: xxx` header? */
+ /* We silently accept this as the final response. What are we
+ * switching to if we did not ask for an Upgrade? Maybe the
+ * application provided an `Upgrade: xxx` header? */
k->header = FALSE;
}
break;
@@ -3488,7 +3686,7 @@ static CURLcode http_on_response(struct Curl_easy *data, }
if((k->size == -1) && !k->chunk && !conn->bits.close &&
- (conn->httpversion == 11) &&
+ (k->httpversion == 11) &&
!(conn->handler->protocol & CURLPROTO_RTSP) &&
data->state.httpreq != HTTPREQ_HEAD) {
/* On HTTP 1.1, when connection is not to get closed, but no
@@ -3580,7 +3778,7 @@ static CURLcode http_on_response(struct Curl_easy *data, * connection for closure after we have read the entire response.
*/
if(!Curl_req_done_sending(data)) {
- if((k->httpcode == 417) && Curl_http_exp100_is_selected(data)) {
+ if((k->httpcode == 417) && http_exp100_is_selected(data)) {
/* 417 Expectation Failed - try again without the Expect
header */
if(!k->writebytecount && http_exp100_is_waiting(data)) {
@@ -3637,19 +3835,17 @@ static CURLcode http_on_response(struct Curl_easy *data, like to call http2_handle_stream_close to properly close a
stream. In order to do this, we keep reading until we
close the stream. */
- if(0 == k->maxdownload
- && !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
- && !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
+ if((0 == k->maxdownload) && (k->httpversion_sent < 20))
k->download_done = TRUE;
/* final response without error, prepare to receive the body */
- result = Curl_http_firstwrite(data);
+ result = http_firstwrite(data);
if(!result)
/* This is the last response that we get for the current request.
* Check on the body size and determine if the response is complete.
*/
- result = Curl_http_size(data);
+ result = http_size(data);
out:
if(last_hd) {
@@ -3676,8 +3872,8 @@ static CURLcode http_rw_hd(struct Curl_easy *data, struct dynbuf last_header;
size_t consumed;
- Curl_dyn_init(&last_header, hdlen + 1);
- result = Curl_dyn_addn(&last_header, hd, hdlen);
+ curlx_dyn_init(&last_header, hdlen + 1);
+ result = curlx_dyn_addn(&last_header, hd, hdlen);
if(result)
return result;
@@ -3685,12 +3881,12 @@ static CURLcode http_rw_hd(struct Curl_easy *data, /* Caveat: we clear anything in the header brigade, because a
* response might switch HTTP version which may call use recursively.
* Not nice, but that is currently the way of things. */
- Curl_dyn_reset(&data->state.headerb);
- result = http_on_response(data, Curl_dyn_ptr(&last_header),
- Curl_dyn_len(&last_header),
+ curlx_dyn_reset(&data->state.headerb);
+ result = http_on_response(data, curlx_dyn_ptr(&last_header),
+ curlx_dyn_len(&last_header),
buf_remain, blen, &consumed);
*pconsumed += consumed;
- Curl_dyn_free(&last_header);
+ curlx_dyn_free(&last_header);
return result;
}
@@ -3715,8 +3911,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, */
const char *p = hd;
- while(*p && ISBLANK(*p))
- p++;
+ curlx_str_passblanks(&p);
if(!strncmp(p, "HTTP/", 5)) {
p += 5;
switch(*p) {
@@ -3724,14 +3919,14 @@ static CURLcode http_rw_hd(struct Curl_easy *data, p++;
if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) {
if(ISBLANK(p[2])) {
- k->httpversion = 10 + (p[1] - '0');
+ k->httpversion = (unsigned char)(10 + (p[1] - '0'));
p += 3;
if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
(p[2] - '0');
- p += 3;
- if(ISSPACE(*p))
- fine_statusline = TRUE;
+ /* RFC 9112 requires a single space following the status code,
+ but the browsers don't so let's not insist */
+ fine_statusline = TRUE;
}
}
}
@@ -3744,13 +3939,13 @@ static CURLcode http_rw_hd(struct Curl_easy *data, case '3':
if(!ISBLANK(p[1]))
break;
- k->httpversion = (*p - '0') * 10;
+ k->httpversion = (unsigned char)((*p - '0') * 10);
p += 2;
if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
(p[2] - '0');
p += 3;
- if(!ISSPACE(*p))
+ if(!ISBLANK(*p))
break;
fine_statusline = TRUE;
}
@@ -3775,34 +3970,26 @@ static CURLcode http_rw_hd(struct Curl_easy *data, }
else if(data->conn->handler->protocol & CURLPROTO_RTSP) {
const char *p = hd;
- while(*p && ISBLANK(*p))
- p++;
- if(!strncmp(p, "RTSP/", 5)) {
- p += 5;
- if(ISDIGIT(*p)) {
- p++;
- if((p[0] == '.') && ISDIGIT(p[1])) {
- if(ISBLANK(p[2])) {
- p += 3;
- if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
- k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
- (p[2] - '0');
- p += 3;
- if(ISSPACE(*p)) {
- fine_statusline = TRUE;
- k->httpversion = 11; /* RTSP acts like HTTP 1.1 */
- }
- }
- }
- }
+ struct Curl_str ver;
+ curl_off_t status;
+ /* we set the max string a little excessive to forgive some leading
+ spaces */
+ if(!curlx_str_until(&p, &ver, 32, ' ') &&
+ !curlx_str_single(&p, ' ') &&
+ !curlx_str_number(&p, &status, 999)) {
+ curlx_str_trimblanks(&ver);
+ if(curlx_str_cmp(&ver, "RTSP/1.0")) {
+ k->httpcode = (int)status;
+ fine_statusline = TRUE;
+ k->httpversion = 11; /* RTSP acts like HTTP 1.1 */
}
- if(!fine_statusline)
- return CURLE_WEIRD_SERVER_REPLY;
}
+ if(!fine_statusline)
+ return CURLE_WEIRD_SERVER_REPLY;
}
if(fine_statusline) {
- result = Curl_http_statusline(data, data->conn);
+ result = http_statusline(data, data->conn);
if(result)
return result;
writetype |= CLIENTWRITE_STATUS;
@@ -3817,14 +4004,14 @@ static CURLcode http_rw_hd(struct Curl_easy *data, if(result)
return result;
- result = Curl_http_header(data, hd, hdlen);
+ result = http_header(data, hd, hdlen);
if(result)
return result;
/*
* Taken in one (more) header. Write it to the client.
*/
- Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen);
+ Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
if(k->httpcode/100 == 1)
writetype |= CLIENTWRITE_1XX;
@@ -3861,7 +4048,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data, 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, buf, blen);
+ result = curlx_dyn_addn(&data->state.headerb, buf, blen);
if(result)
return result;
*pconsumed += blen;
@@ -3870,18 +4057,19 @@ static CURLcode http_parse_headers(struct Curl_easy *data, /* check if this looks like a protocol header */
statusline st =
checkprotoprefix(data, conn,
- Curl_dyn_ptr(&data->state.headerb),
- Curl_dyn_len(&data->state.headerb));
+ curlx_dyn_ptr(&data->state.headerb),
+ curlx_dyn_len(&data->state.headerb));
if(st == STATUS_BAD) {
- /* this is not the beginning of a protocol first header line */
+ /* this is not the beginning of a protocol first header line.
+ * Cannot be 0.9 if version was detected or connection was reused. */
k->header = FALSE;
streamclose(conn, "bad HTTP: No end-of-message indicator");
- if(conn->httpversion >= 10) {
+ if((k->httpversion >= 10) || conn->bits.reuse) {
failf(data, "Invalid status line");
return CURLE_WEIRD_SERVER_REPLY;
}
- if(!data->set.http09_allowed) {
+ if(!data->state.http_neg.accept_09) {
failf(data, "Received HTTP/0.9 when not allowed");
return CURLE_UNSUPPORTED_PROTOCOL;
}
@@ -3894,7 +4082,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data, /* decrease the size of the remaining (supposed) header line */
consumed = (end_ptr - buf) + 1;
- result = Curl_dyn_addn(&data->state.headerb, buf, consumed);
+ result = curlx_dyn_addn(&data->state.headerb, buf, consumed);
if(result)
return result;
blen -= consumed;
@@ -3908,16 +4096,17 @@ static CURLcode http_parse_headers(struct Curl_easy *data, if(!k->headerline) {
/* the first read header */
statusline st = checkprotoprefix(data, conn,
- Curl_dyn_ptr(&data->state.headerb),
- Curl_dyn_len(&data->state.headerb));
+ curlx_dyn_ptr(&data->state.headerb),
+ curlx_dyn_len(&data->state.headerb));
if(st == STATUS_BAD) {
streamclose(conn, "bad HTTP: No end-of-message indicator");
- /* this is not the beginning of a protocol first header line */
- if(conn->httpversion >= 10) {
+ /* this is not the beginning of a protocol first header line.
+ * Cannot be 0.9 if version was detected or connection was reused. */
+ if((k->httpversion >= 10) || conn->bits.reuse) {
failf(data, "Invalid status line");
return CURLE_WEIRD_SERVER_REPLY;
}
- if(!data->set.http09_allowed) {
+ if(!data->state.http_neg.accept_09) {
failf(data, "Received HTTP/0.9 when not allowed");
return CURLE_UNSUPPORTED_PROTOCOL;
}
@@ -3927,13 +4116,13 @@ static CURLcode http_parse_headers(struct Curl_easy *data, }
}
- result = http_rw_hd(data, Curl_dyn_ptr(&data->state.headerb),
- Curl_dyn_len(&data->state.headerb),
+ result = http_rw_hd(data, curlx_dyn_ptr(&data->state.headerb),
+ curlx_dyn_len(&data->state.headerb),
buf, blen, &consumed);
/* We are done with this line. We reset because response
* processing might switch to HTTP/2 and that might call us
* directly again. */
- Curl_dyn_reset(&data->state.headerb);
+ curlx_dyn_reset(&data->state.headerb);
if(consumed) {
blen -= consumed;
buf += consumed;
@@ -3948,7 +4137,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data, buffer. */
out:
if(!k->header && !leftover_body) {
- Curl_dyn_free(&data->state.headerb);
+ curlx_dyn_free(&data->state.headerb);
}
return CURLE_OK;
}
@@ -3986,15 +4175,15 @@ CURLcode Curl_http_write_resp_hds(struct Curl_easy *data, result = http_parse_headers(data, buf, blen, pconsumed);
if(!result && !data->req.header) {
- if(!data->req.no_body && Curl_dyn_len(&data->state.headerb)) {
+ if(!data->req.no_body && curlx_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));
+ curlx_dyn_ptr(&data->state.headerb),
+ curlx_dyn_len(&data->state.headerb));
}
- Curl_dyn_free(&data->state.headerb);
+ curlx_dyn_free(&data->state.headerb);
}
return result;
}
@@ -4023,7 +4212,7 @@ CURLcode Curl_http_write_resp(struct Curl_easy *data, flags = CLIENTWRITE_BODY;
if(is_eos)
flags |= CLIENTWRITE_EOS;
- result = Curl_client_write(data, flags, (char *)buf, blen);
+ result = Curl_client_write(data, flags, buf, blen);
}
out:
return result;
@@ -4063,11 +4252,9 @@ CURLcode Curl_http_req_make(struct httpreq **preq, struct httpreq *req;
CURLcode result = CURLE_OUT_OF_MEMORY;
- DEBUGASSERT(method);
- if(m_len + 1 > sizeof(req->method))
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ DEBUGASSERT(method && m_len);
- req = calloc(1, sizeof(*req));
+ req = calloc(1, sizeof(*req) + m_len);
if(!req)
goto out;
memcpy(req->method, method, m_len);
@@ -4105,7 +4292,7 @@ static CURLcode req_assign_url_authority(struct httpreq *req, CURLU *url) CURLcode result = CURLE_URL_MALFORMAT;
user = pass = host = port = NULL;
- Curl_dyn_init(&buf, DYN_HTTP_REQUEST);
+ curlx_dyn_init(&buf, DYN_HTTP_REQUEST);
uc = curl_url_get(url, CURLUPART_HOST, &host, 0);
if(uc && uc != CURLUE_NO_HOST)
@@ -4129,27 +4316,27 @@ static CURLcode req_assign_url_authority(struct httpreq *req, CURLU *url) }
if(user) {
- result = Curl_dyn_add(&buf, user);
+ result = curlx_dyn_add(&buf, user);
if(result)
goto out;
if(pass) {
- result = Curl_dyn_addf(&buf, ":%s", pass);
+ result = curlx_dyn_addf(&buf, ":%s", pass);
if(result)
goto out;
}
- result = Curl_dyn_add(&buf, "@");
+ result = curlx_dyn_add(&buf, "@");
if(result)
goto out;
}
- result = Curl_dyn_add(&buf, host);
+ result = curlx_dyn_add(&buf, host);
if(result)
goto out;
if(port) {
- result = Curl_dyn_addf(&buf, ":%s", port);
+ result = curlx_dyn_addf(&buf, ":%s", port);
if(result)
goto out;
}
- req->authority = strdup(Curl_dyn_ptr(&buf));
+ req->authority = strdup(curlx_dyn_ptr(&buf));
if(!req->authority)
goto out;
result = CURLE_OK;
@@ -4159,7 +4346,7 @@ out: free(pass);
free(host);
free(port);
- Curl_dyn_free(&buf);
+ curlx_dyn_free(&buf);
return result;
}
@@ -4171,7 +4358,7 @@ static CURLcode req_assign_url_path(struct httpreq *req, CURLU *url) CURLcode result = CURLE_URL_MALFORMAT;
path = query = NULL;
- Curl_dyn_init(&buf, DYN_HTTP_REQUEST);
+ curlx_dyn_init(&buf, DYN_HTTP_REQUEST);
uc = curl_url_get(url, CURLUPART_PATH, &path, CURLU_PATH_AS_IS);
if(uc)
@@ -4189,16 +4376,16 @@ static CURLcode req_assign_url_path(struct httpreq *req, CURLU *url) }
else {
if(path) {
- result = Curl_dyn_add(&buf, path);
+ result = curlx_dyn_add(&buf, path);
if(result)
goto out;
}
if(query) {
- result = Curl_dyn_addf(&buf, "?%s", query);
+ result = curlx_dyn_addf(&buf, "?%s", query);
if(result)
goto out;
}
- req->path = strdup(Curl_dyn_ptr(&buf));
+ req->path = strdup(curlx_dyn_ptr(&buf));
if(!req->path)
goto out;
}
@@ -4207,7 +4394,7 @@ static CURLcode req_assign_url_path(struct httpreq *req, CURLU *url) out:
free(path);
free(query);
- Curl_dyn_free(&buf);
+ curlx_dyn_free(&buf);
return result;
}
@@ -4219,11 +4406,9 @@ CURLcode Curl_http_req_make2(struct httpreq **preq, CURLcode result = CURLE_OUT_OF_MEMORY;
CURLUcode uc;
- DEBUGASSERT(method);
- if(m_len + 1 > sizeof(req->method))
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ DEBUGASSERT(method && m_len);
- req = calloc(1, sizeof(*req));
+ req = calloc(1, sizeof(*req) + m_len);
if(!req)
goto out;
memcpy(req->method, method, m_len);
@@ -4272,6 +4457,7 @@ struct name_const { size_t namelen;
};
+/* keep them sorted by length! */
static struct name_const H2_NON_FIELD[] = {
{ STRCONST("Host") },
{ STRCONST("Upgrade") },
@@ -4281,15 +4467,44 @@ static struct name_const H2_NON_FIELD[] = { { STRCONST("Transfer-Encoding") },
};
-static bool h2_non_field(const char *name, size_t namelen)
+static bool h2_permissible_field(struct dynhds_entry *e)
{
size_t i;
- for(i = 0; i < sizeof(H2_NON_FIELD)/sizeof(H2_NON_FIELD[0]); ++i) {
- if(namelen < H2_NON_FIELD[i].namelen)
+ for(i = 0; i < CURL_ARRAYSIZE(H2_NON_FIELD); ++i) {
+ if(e->namelen < H2_NON_FIELD[i].namelen)
+ return TRUE;
+ if(e->namelen == H2_NON_FIELD[i].namelen &&
+ strcasecompare(H2_NON_FIELD[i].name, e->name))
return FALSE;
- if(namelen == H2_NON_FIELD[i].namelen &&
- strcasecompare(H2_NON_FIELD[i].name, name))
+ }
+ return TRUE;
+}
+
+static bool http_TE_has_token(const char *fvalue, const char *token)
+{
+ while(*fvalue) {
+ struct Curl_str name;
+
+ /* skip to first token */
+ while(ISBLANK(*fvalue) || *fvalue == ',')
+ fvalue++;
+ if(curlx_str_cspn(&fvalue, &name, " \t\r;,"))
+ return FALSE;
+ if(curlx_str_casecompare(&name, token))
return TRUE;
+
+ /* skip any remainder after token, e.g. parameters with quoted strings */
+ while(*fvalue && *fvalue != ',') {
+ if(*fvalue == '"') {
+ struct Curl_str qw;
+ /* if we do not cleanly find a quoted word here, the header value
+ * does not follow HTTP syntax and we reject */
+ if(curlx_str_quotedword(&fvalue, &qw, CURL_MAX_HTTP_HEADER))
+ return FALSE;
+ }
+ else
+ fvalue++;
+ }
}
return FALSE;
}
@@ -4312,12 +4527,11 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, scheme = Curl_checkheaders(data, STRCONST(HTTP_PSEUDO_SCHEME));
if(scheme) {
scheme += sizeof(HTTP_PSEUDO_SCHEME);
- while(*scheme && ISBLANK(*scheme))
- scheme++;
+ curlx_str_passblanks(&scheme);
infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme);
}
else {
- scheme = (data->conn && data->conn->handler->flags & PROTOPT_SSL) ?
+ scheme = Curl_conn_is_ssl(data->conn, FIRSTSOCKET) ?
"https" : "http";
}
}
@@ -4349,7 +4563,14 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, }
for(i = 0; !result && i < Curl_dynhds_count(&req->headers); ++i) {
e = Curl_dynhds_getn(&req->headers, i);
- if(!h2_non_field(e->name, e->namelen)) {
+ /* "TE" is special in that it is only permissible when it
+ * has only value "trailers". RFC 9113 ch. 8.2.2 */
+ if(e->namelen == 2 && strcasecompare("TE", e->name)) {
+ if(http_TE_has_token(e->value, "trailers"))
+ result = Curl_dynhds_add(h2_headers, e->name, e->namelen,
+ "trailers", sizeof("trailers") - 1);
+ }
+ else if(h2_permissible_field(e)) {
result = Curl_dynhds_add(h2_headers, e->name, e->namelen,
e->value, e->valuelen);
}
@@ -4441,7 +4662,7 @@ static CURLcode cr_exp100_read(struct Curl_easy *data, DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, "
"timeout %ldms", data->set.expect_100_timeout));
ctx->state = EXP100_AWAITING_CONTINUE;
- ctx->start = Curl_now();
+ ctx->start = curlx_now();
Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
data->req.keepon &= ~KEEP_SEND;
data->req.keepon |= KEEP_SEND_TIMED;
@@ -4454,7 +4675,7 @@ static CURLcode cr_exp100_read(struct Curl_easy *data, *eos = FALSE;
return CURLE_READ_ERROR;
case EXP100_AWAITING_CONTINUE:
- ms = Curl_timediff(Curl_now(), ctx->start);
+ ms = curlx_timediff(curlx_now(), ctx->start);
if(ms < data->set.expect_100_timeout) {
DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired"));
data->req.keepon &= ~KEEP_SEND;
@@ -4516,7 +4737,7 @@ static CURLcode http_exp100_add_reader(struct Curl_easy *data) return result;
}
-void Curl_http_exp100_got100(struct Curl_easy *data)
+static void http_exp100_got100(struct Curl_easy *data)
{
struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
if(r)
@@ -4528,7 +4749,7 @@ static bool http_exp100_is_waiting(struct Curl_easy *data) struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
if(r) {
struct cr_exp100_ctx *ctx = r->ctx;
- return (ctx->state == EXP100_AWAITING_CONTINUE);
+ return ctx->state == EXP100_AWAITING_CONTINUE;
}
return FALSE;
}
@@ -4540,7 +4761,7 @@ static void http_exp100_send_anyway(struct Curl_easy *data) http_exp100_continue(data, r);
}
-bool Curl_http_exp100_is_selected(struct Curl_easy *data)
+static bool http_exp100_is_selected(struct Curl_easy *data)
{
struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
return !!r;
|