|
|
@@ -5,7 +5,7 @@
|
|
|
* | (__| |_| | _ <| |___
|
|
|
* \___|\___/|_| \_\_____|
|
|
|
*
|
|
|
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <[email protected]>, et al.
|
|
|
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <[email protected]>, et al.
|
|
|
*
|
|
|
* This software is licensed as described in the file COPYING, which
|
|
|
* you should have received as part of this distribution. The terms
|
|
|
@@ -73,10 +73,10 @@
|
|
|
#include "http_proxy.h"
|
|
|
#include "warnless.h"
|
|
|
#include "non-ascii.h"
|
|
|
-#include "pipeline.h"
|
|
|
#include "http2.h"
|
|
|
#include "connect.h"
|
|
|
#include "strdup.h"
|
|
|
+#include "altsvc.h"
|
|
|
|
|
|
/* The last 3 #include files should be in this order */
|
|
|
#include "curl_printf.h"
|
|
|
@@ -92,7 +92,9 @@ static int http_getsock_do(struct connectdata *conn,
|
|
|
int numsocks);
|
|
|
static int http_should_fail(struct connectdata *conn);
|
|
|
|
|
|
+#ifndef CURL_DISABLE_PROXY
|
|
|
static CURLcode add_haproxy_protocol_header(struct connectdata *conn);
|
|
|
+#endif
|
|
|
|
|
|
#ifdef USE_SSL
|
|
|
static CURLcode https_connecting(struct connectdata *conn, bool *done);
|
|
|
@@ -102,13 +104,14 @@ static int https_getsock(struct connectdata *conn,
|
|
|
#else
|
|
|
#define https_connecting(x,y) CURLE_COULDNT_CONNECT
|
|
|
#endif
|
|
|
+static CURLcode http_setup_conn(struct connectdata *conn);
|
|
|
|
|
|
/*
|
|
|
* HTTP handler interface.
|
|
|
*/
|
|
|
const struct Curl_handler Curl_handler_http = {
|
|
|
"HTTP", /* scheme */
|
|
|
- Curl_http_setup_conn, /* setup_connection */
|
|
|
+ http_setup_conn, /* setup_connection */
|
|
|
Curl_http, /* do_it */
|
|
|
Curl_http_done, /* done */
|
|
|
ZERO_NULL, /* do_more */
|
|
|
@@ -133,7 +136,7 @@ const struct Curl_handler Curl_handler_http = {
|
|
|
*/
|
|
|
const struct Curl_handler Curl_handler_https = {
|
|
|
"HTTPS", /* scheme */
|
|
|
- Curl_http_setup_conn, /* setup_connection */
|
|
|
+ http_setup_conn, /* setup_connection */
|
|
|
Curl_http, /* do_it */
|
|
|
Curl_http_done, /* done */
|
|
|
ZERO_NULL, /* do_more */
|
|
|
@@ -153,7 +156,7 @@ const struct Curl_handler Curl_handler_https = {
|
|
|
};
|
|
|
#endif
|
|
|
|
|
|
-CURLcode Curl_http_setup_conn(struct connectdata *conn)
|
|
|
+static CURLcode http_setup_conn(struct connectdata *conn)
|
|
|
{
|
|
|
/* allocate the HTTP-specific struct for the Curl_easy, only to survive
|
|
|
during this request */
|
|
|
@@ -175,7 +178,7 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn)
|
|
|
return CURLE_OK;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+#ifndef CURL_DISABLE_PROXY
|
|
|
/*
|
|
|
* checkProxyHeaders() checks the linked list of custom proxy headers
|
|
|
* if proxy headers are not available, then it will lookup into http header
|
|
|
@@ -202,6 +205,10 @@ char *Curl_checkProxyheaders(const struct connectdata *conn,
|
|
|
|
|
|
return NULL;
|
|
|
}
|
|
|
+#else
|
|
|
+/* disabled */
|
|
|
+#define Curl_checkProxyheaders(x,y) NULL
|
|
|
+#endif
|
|
|
|
|
|
/*
|
|
|
* Strip off leading and trailing whitespace from the value in the
|
|
|
@@ -256,6 +263,7 @@ char *Curl_copy_header_value(const char *header)
|
|
|
return value;
|
|
|
}
|
|
|
|
|
|
+#ifndef CURL_DISABLE_HTTP_AUTH
|
|
|
/*
|
|
|
* http_output_basic() sets up an Authorization: header (or the proxy version)
|
|
|
* for HTTP Basic authentication.
|
|
|
@@ -337,6 +345,8 @@ static CURLcode http_output_bearer(struct connectdata *conn)
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+#endif
|
|
|
+
|
|
|
/* pickoneauth() selects the most favourable authentication method from the
|
|
|
* ones available and the ones we want.
|
|
|
*
|
|
|
@@ -415,7 +425,7 @@ static CURLcode http_perhapsrewind(struct connectdata *conn)
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- bytessent = http->writebytecount;
|
|
|
+ bytessent = data->req.writebytecount;
|
|
|
|
|
|
if(conn->bits.authneg) {
|
|
|
/* This is a state where we are known to be negotiating and we don't send
|
|
|
@@ -456,8 +466,8 @@ static CURLcode http_perhapsrewind(struct connectdata *conn)
|
|
|
(data->state.authproxy.picked == CURLAUTH_NTLM_WB) ||
|
|
|
(data->state.authhost.picked == CURLAUTH_NTLM_WB)) {
|
|
|
if(((expectsend - bytessent) < 2000) ||
|
|
|
- (conn->ntlm.state != NTLMSTATE_NONE) ||
|
|
|
- (conn->proxyntlm.state != NTLMSTATE_NONE)) {
|
|
|
+ (conn->http_ntlm_state != NTLMSTATE_NONE) ||
|
|
|
+ (conn->proxy_ntlm_state != NTLMSTATE_NONE)) {
|
|
|
/* The NTLM-negotiation has started *OR* there is just a little (<2K)
|
|
|
data left to send, keep on sending. */
|
|
|
|
|
|
@@ -479,8 +489,36 @@ static CURLcode http_perhapsrewind(struct connectdata *conn)
|
|
|
(curl_off_t)(expectsend - bytessent));
|
|
|
}
|
|
|
#endif
|
|
|
+#if defined(USE_SPNEGO)
|
|
|
+ /* There is still data left to send */
|
|
|
+ if((data->state.authproxy.picked == CURLAUTH_NEGOTIATE) ||
|
|
|
+ (data->state.authhost.picked == CURLAUTH_NEGOTIATE)) {
|
|
|
+ if(((expectsend - bytessent) < 2000) ||
|
|
|
+ (conn->http_negotiate_state != GSS_AUTHNONE) ||
|
|
|
+ (conn->proxy_negotiate_state != GSS_AUTHNONE)) {
|
|
|
+ /* The NEGOTIATE-negotiation has started *OR*
|
|
|
+ there is just a little (<2K) data left to send, keep on sending. */
|
|
|
+
|
|
|
+ /* rewind data when completely done sending! */
|
|
|
+ if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) {
|
|
|
+ conn->bits.rewindaftersend = TRUE;
|
|
|
+ infof(data, "Rewind stream after send\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ return CURLE_OK;
|
|
|
+ }
|
|
|
|
|
|
- /* This is not NTLM or many bytes left to send: close */
|
|
|
+ if(conn->bits.close)
|
|
|
+ /* this is already marked to get closed */
|
|
|
+ return CURLE_OK;
|
|
|
+
|
|
|
+ infof(data, "NEGOTIATE send, close instead of sending %"
|
|
|
+ CURL_FORMAT_CURL_OFF_T " bytes\n",
|
|
|
+ (curl_off_t)(expectsend - bytessent));
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ /* This is not NEGOTIATE/NTLM or many bytes left to send: close */
|
|
|
streamclose(conn, "Mid-auth HTTP and much data left to send");
|
|
|
data->req.size = 0; /* don't download any more than 0 bytes */
|
|
|
|
|
|
@@ -526,6 +564,12 @@ CURLcode Curl_http_auth_act(struct connectdata *conn)
|
|
|
pickhost = pickoneauth(&data->state.authhost, authmask);
|
|
|
if(!pickhost)
|
|
|
data->state.authproblem = TRUE;
|
|
|
+ if(data->state.authhost.picked == CURLAUTH_NTLM &&
|
|
|
+ conn->httpversion > 11) {
|
|
|
+ infof(data, "Forcing HTTP/1.1 for NTLM");
|
|
|
+ connclose(conn, "Force HTTP/1.1 connection");
|
|
|
+ conn->data->set.httpversion = CURL_HTTP_VERSION_1_1;
|
|
|
+ }
|
|
|
}
|
|
|
if(conn->bits.proxy_user_passwd &&
|
|
|
((data->req.httpcode == 407) ||
|
|
|
@@ -576,6 +620,7 @@ CURLcode Curl_http_auth_act(struct connectdata *conn)
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+#ifndef CURL_DISABLE_HTTP_AUTH
|
|
|
/*
|
|
|
* Output the correct authentication header depending on the auth type
|
|
|
* and whether or not it is to a proxy.
|
|
|
@@ -592,10 +637,6 @@ output_auth_headers(struct connectdata *conn,
|
|
|
#if !defined(CURL_DISABLE_VERBOSE_STRINGS) || defined(USE_SPNEGO)
|
|
|
struct Curl_easy *data = conn->data;
|
|
|
#endif
|
|
|
-#ifdef USE_SPNEGO
|
|
|
- struct negotiatedata *negdata = proxy ?
|
|
|
- &data->state.proxyneg : &data->state.negotiate;
|
|
|
-#endif
|
|
|
|
|
|
#ifdef CURL_DISABLE_CRYPTO_AUTH
|
|
|
(void)request;
|
|
|
@@ -603,15 +644,11 @@ output_auth_headers(struct connectdata *conn,
|
|
|
#endif
|
|
|
|
|
|
#ifdef USE_SPNEGO
|
|
|
- negdata->state = GSS_AUTHNONE;
|
|
|
- if((authstatus->picked == CURLAUTH_NEGOTIATE) &&
|
|
|
- negdata->context && !GSS_ERROR(negdata->status)) {
|
|
|
+ if((authstatus->picked == CURLAUTH_NEGOTIATE)) {
|
|
|
auth = "Negotiate";
|
|
|
result = Curl_output_negotiate(conn, proxy);
|
|
|
if(result)
|
|
|
return result;
|
|
|
- authstatus->done = TRUE;
|
|
|
- negdata->state = GSS_AUTHSENT;
|
|
|
}
|
|
|
else
|
|
|
#endif
|
|
|
@@ -697,7 +734,7 @@ output_auth_headers(struct connectdata *conn,
|
|
|
*
|
|
|
* @param conn all information about the current connection
|
|
|
* @param request pointer to the request keyword
|
|
|
- * @param path pointer to the requested path
|
|
|
+ * @param path pointer to the requested path; should include query part
|
|
|
* @param proxytunnel boolean if this is the request setting up a "proxy
|
|
|
* tunnel"
|
|
|
*
|
|
|
@@ -744,7 +781,7 @@ Curl_http_output_auth(struct connectdata *conn,
|
|
|
#ifndef CURL_DISABLE_PROXY
|
|
|
/* Send proxy authentication header if needed */
|
|
|
if(conn->bits.httpproxy &&
|
|
|
- (conn->bits.tunnel_proxy == proxytunnel)) {
|
|
|
+ (conn->bits.tunnel_proxy == (bit)proxytunnel)) {
|
|
|
result = output_auth_headers(conn, authproxy, request, path, TRUE);
|
|
|
if(result)
|
|
|
return result;
|
|
|
@@ -772,6 +809,22 @@ Curl_http_output_auth(struct connectdata *conn,
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+#else
|
|
|
+/* when disabled */
|
|
|
+CURLcode
|
|
|
+Curl_http_output_auth(struct connectdata *conn,
|
|
|
+ const char *request,
|
|
|
+ const char *path,
|
|
|
+ bool proxytunnel)
|
|
|
+{
|
|
|
+ (void)conn;
|
|
|
+ (void)request;
|
|
|
+ (void)path;
|
|
|
+ (void)proxytunnel;
|
|
|
+ return CURLE_OK;
|
|
|
+}
|
|
|
+#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
|
|
|
@@ -787,8 +840,8 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy,
|
|
|
struct Curl_easy *data = conn->data;
|
|
|
|
|
|
#ifdef USE_SPNEGO
|
|
|
- struct negotiatedata *negdata = proxy?
|
|
|
- &data->state.proxyneg:&data->state.negotiate;
|
|
|
+ curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state :
|
|
|
+ &conn->http_negotiate_state;
|
|
|
#endif
|
|
|
unsigned long *availp;
|
|
|
struct auth *authp;
|
|
|
@@ -827,21 +880,18 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy,
|
|
|
authp->avail |= CURLAUTH_NEGOTIATE;
|
|
|
|
|
|
if(authp->picked == CURLAUTH_NEGOTIATE) {
|
|
|
- if(negdata->state == GSS_AUTHSENT ||
|
|
|
- negdata->state == GSS_AUTHNONE) {
|
|
|
- CURLcode result = Curl_input_negotiate(conn, proxy, auth);
|
|
|
- if(!result) {
|
|
|
- DEBUGASSERT(!data->req.newurl);
|
|
|
- data->req.newurl = strdup(data->change.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 */
|
|
|
- negdata->state = GSS_AUTHRECV;
|
|
|
- }
|
|
|
- else
|
|
|
- data->state.authproblem = TRUE;
|
|
|
+ CURLcode result = Curl_input_negotiate(conn, proxy, auth);
|
|
|
+ if(!result) {
|
|
|
+ DEBUGASSERT(!data->req.newurl);
|
|
|
+ data->req.newurl = strdup(data->change.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;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -869,19 +919,10 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy,
|
|
|
*availp |= CURLAUTH_NTLM_WB;
|
|
|
authp->avail |= CURLAUTH_NTLM_WB;
|
|
|
|
|
|
- /* Get the challenge-message which will be passed to
|
|
|
- * ntlm_auth for generating the type 3 message later */
|
|
|
- while(*auth && ISSPACE(*auth))
|
|
|
- auth++;
|
|
|
- if(checkprefix("NTLM", auth)) {
|
|
|
- auth += strlen("NTLM");
|
|
|
- while(*auth && ISSPACE(*auth))
|
|
|
- auth++;
|
|
|
- if(*auth) {
|
|
|
- conn->challenge_header = strdup(auth);
|
|
|
- if(!conn->challenge_header)
|
|
|
- return CURLE_OUT_OF_MEMORY;
|
|
|
- }
|
|
|
+ result = Curl_input_ntlm_wb(conn, proxy, auth);
|
|
|
+ if(result) {
|
|
|
+ infof(data, "Authentication problem. Ignoring this.\n");
|
|
|
+ data->state.authproblem = TRUE;
|
|
|
}
|
|
|
}
|
|
|
#endif
|
|
|
@@ -1111,14 +1152,13 @@ void Curl_add_buffer_free(Curl_send_buffer **inp)
|
|
|
CURLcode Curl_add_buffer_send(Curl_send_buffer **inp,
|
|
|
struct connectdata *conn,
|
|
|
|
|
|
- /* add the number of sent bytes to this
|
|
|
- counter */
|
|
|
- long *bytes_written,
|
|
|
+ /* add the number of sent bytes to this
|
|
|
+ counter */
|
|
|
+ curl_off_t *bytes_written,
|
|
|
|
|
|
- /* how much of the buffer contains body data */
|
|
|
+ /* how much of the buffer contains body data */
|
|
|
size_t included_body_bytes,
|
|
|
int socketindex)
|
|
|
-
|
|
|
{
|
|
|
ssize_t amount;
|
|
|
CURLcode result;
|
|
|
@@ -1214,7 +1254,8 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp,
|
|
|
if(http) {
|
|
|
/* if we sent a piece of the body here, up the byte counter for it
|
|
|
accordingly */
|
|
|
- http->writebytecount += bodylen;
|
|
|
+ data->req.writebytecount += bodylen;
|
|
|
+ Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
|
|
|
|
|
|
if((size_t)amount != size) {
|
|
|
/* The whole request could not be sent in one system call. We must
|
|
|
@@ -1255,7 +1296,6 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp,
|
|
|
This needs FIXing.
|
|
|
*/
|
|
|
return CURLE_SEND_ERROR;
|
|
|
- Curl_pipeline_leave_write(conn);
|
|
|
}
|
|
|
}
|
|
|
Curl_add_buffer_free(&in);
|
|
|
@@ -1432,12 +1472,14 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
|
|
|
/* nothing else to do except wait right now - we're not done here. */
|
|
|
return CURLE_OK;
|
|
|
|
|
|
+#ifndef CURL_DISABLE_PROXY
|
|
|
if(conn->data->set.haproxyprotocol) {
|
|
|
/* add HAProxy PROXY protocol header */
|
|
|
result = add_haproxy_protocol_header(conn);
|
|
|
if(result)
|
|
|
return result;
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
if(conn->given->protocol & CURLPROTO_HTTPS) {
|
|
|
/* perform SSL initialization */
|
|
|
@@ -1464,6 +1506,7 @@ static int http_getsock_do(struct connectdata *conn,
|
|
|
return GETSOCK_WRITESOCK(0);
|
|
|
}
|
|
|
|
|
|
+#ifndef CURL_DISABLE_PROXY
|
|
|
static CURLcode add_haproxy_protocol_header(struct connectdata *conn)
|
|
|
{
|
|
|
char proxy_header[128];
|
|
|
@@ -1479,14 +1522,14 @@ static CURLcode add_haproxy_protocol_header(struct connectdata *conn)
|
|
|
strcpy(tcp_version, "TCP4");
|
|
|
}
|
|
|
|
|
|
- snprintf(proxy_header,
|
|
|
- sizeof(proxy_header),
|
|
|
- "PROXY %s %s %s %li %li\r\n",
|
|
|
- tcp_version,
|
|
|
- conn->data->info.conn_local_ip,
|
|
|
- conn->data->info.conn_primary_ip,
|
|
|
- conn->data->info.conn_local_port,
|
|
|
- conn->data->info.conn_primary_port);
|
|
|
+ msnprintf(proxy_header,
|
|
|
+ sizeof(proxy_header),
|
|
|
+ "PROXY %s %s %s %li %li\r\n",
|
|
|
+ tcp_version,
|
|
|
+ conn->data->info.conn_local_ip,
|
|
|
+ conn->data->info.conn_primary_ip,
|
|
|
+ conn->data->info.conn_local_port,
|
|
|
+ conn->data->info.conn_primary_port);
|
|
|
|
|
|
req_buffer = Curl_add_buffer_init();
|
|
|
if(!req_buffer)
|
|
|
@@ -1504,6 +1547,7 @@ static CURLcode add_haproxy_protocol_header(struct connectdata *conn)
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
#ifdef USE_SSL
|
|
|
static CURLcode https_connecting(struct connectdata *conn, bool *done)
|
|
|
@@ -1547,20 +1591,6 @@ CURLcode Curl_http_done(struct connectdata *conn,
|
|
|
|
|
|
Curl_unencode_cleanup(conn);
|
|
|
|
|
|
-#ifdef USE_SPNEGO
|
|
|
- if(data->state.proxyneg.state == GSS_AUTHSENT ||
|
|
|
- data->state.negotiate.state == GSS_AUTHSENT) {
|
|
|
- /* add forbid re-use if http-code != 401/407 as a WA only needed for
|
|
|
- * 401/407 that signal auth failure (empty) otherwise state will be RECV
|
|
|
- * with current code.
|
|
|
- * Do not close CONNECT_ONLY connections. */
|
|
|
- if((data->req.httpcode != 401) && (data->req.httpcode != 407) &&
|
|
|
- !data->set.connect_only)
|
|
|
- streamclose(conn, "Negotiate transfer completed");
|
|
|
- Curl_cleanup_negotiate(data);
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
/* set the proper values (possibly modified on POST) */
|
|
|
conn->seek_func = data->set.seek_func; /* restore */
|
|
|
conn->seek_client = data->set.seek_client; /* restore */
|
|
|
@@ -1576,16 +1606,6 @@ CURLcode Curl_http_done(struct connectdata *conn,
|
|
|
|
|
|
Curl_mime_cleanpart(&http->form);
|
|
|
|
|
|
- switch(data->set.httpreq) {
|
|
|
- case HTTPREQ_PUT:
|
|
|
- case HTTPREQ_POST_FORM:
|
|
|
- case HTTPREQ_POST_MIME:
|
|
|
- data->req.bytecount = http->readbytecount + http->writebytecount;
|
|
|
- break;
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
if(status)
|
|
|
return status;
|
|
|
|
|
|
@@ -1593,7 +1613,7 @@ CURLcode Curl_http_done(struct connectdata *conn,
|
|
|
entire operation is complete */
|
|
|
!conn->bits.retry &&
|
|
|
!data->set.connect_only &&
|
|
|
- (http->readbytecount +
|
|
|
+ (data->req.bytecount +
|
|
|
data->req.headerbytecount -
|
|
|
data->req.deductheadercount) <= 0) {
|
|
|
/* If this connection isn't simply closed to be retried, AND nothing was
|
|
|
@@ -1676,6 +1696,50 @@ enum proxy_use {
|
|
|
HEADER_CONNECT /* sending CONNECT to a proxy */
|
|
|
};
|
|
|
|
|
|
+/* used to compile the provided trailers into one buffer
|
|
|
+ will return an error code if one of the headers is
|
|
|
+ not formatted correctly */
|
|
|
+CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
|
|
|
+ Curl_send_buffer *buffer,
|
|
|
+ struct Curl_easy *handle)
|
|
|
+{
|
|
|
+ char *ptr = NULL;
|
|
|
+ CURLcode result = CURLE_OK;
|
|
|
+ const char *endofline_native = NULL;
|
|
|
+ const char *endofline_network = NULL;
|
|
|
+
|
|
|
+ if(
|
|
|
+#ifdef CURL_DO_LINEEND_CONV
|
|
|
+ (handle->set.prefer_ascii) ||
|
|
|
+#endif
|
|
|
+ (handle->set.crlf)) {
|
|
|
+ /* \n will become \r\n later on */
|
|
|
+ endofline_native = "\n";
|
|
|
+ endofline_network = "\x0a";
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ endofline_native = "\r\n";
|
|
|
+ endofline_network = "\x0d\x0a";
|
|
|
+ }
|
|
|
+
|
|
|
+ while(trailers) {
|
|
|
+ /* only add correctly formatted trailers */
|
|
|
+ ptr = strchr(trailers->data, ':');
|
|
|
+ if(ptr && *(ptr + 1) == ' ') {
|
|
|
+ result = Curl_add_bufferf(&buffer, "%s%s", trailers->data,
|
|
|
+ endofline_native);
|
|
|
+ if(result)
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ infof(handle, "Malformatted trailing header ! Skipping trailer.");
|
|
|
+ trailers = trailers->next;
|
|
|
+ }
|
|
|
+ result = Curl_add_buffer(&buffer, endofline_network,
|
|
|
+ strlen(endofline_network));
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
CURLcode Curl_add_custom_headers(struct connectdata *conn,
|
|
|
bool is_connect,
|
|
|
Curl_send_buffer *req_buffer)
|
|
|
@@ -1737,9 +1801,16 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
|
|
|
}
|
|
|
else {
|
|
|
if(*(--ptr) == ';') {
|
|
|
- /* send no-value custom header if terminated by semicolon */
|
|
|
- *ptr = ':';
|
|
|
- semicolonp = ptr;
|
|
|
+ /* copy the source */
|
|
|
+ semicolonp = strdup(headers->data);
|
|
|
+ if(!semicolonp) {
|
|
|
+ Curl_add_buffer_free(&req_buffer);
|
|
|
+ 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;
|
|
|
@@ -1755,35 +1826,37 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
|
|
|
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(conn->allocptr.host &&
|
|
|
/* a Host: header was sent already, don't pass on any custom Host:
|
|
|
header as that will produce *two* in the same request! */
|
|
|
- checkprefix("Host:", headers->data))
|
|
|
+ checkprefix("Host:", compare))
|
|
|
;
|
|
|
else if(data->set.httpreq == HTTPREQ_POST_FORM &&
|
|
|
/* this header (extended by formdata.c) is sent later */
|
|
|
- checkprefix("Content-Type:", headers->data))
|
|
|
+ checkprefix("Content-Type:", compare))
|
|
|
;
|
|
|
else if(data->set.httpreq == HTTPREQ_POST_MIME &&
|
|
|
/* this header is sent later */
|
|
|
- checkprefix("Content-Type:", headers->data))
|
|
|
+ checkprefix("Content-Type:", compare))
|
|
|
;
|
|
|
else if(conn->bits.authneg &&
|
|
|
/* while doing auth neg, don't allow the custom length since
|
|
|
we will force length zero then */
|
|
|
- checkprefix("Content-Length:", headers->data))
|
|
|
+ checkprefix("Content-Length:", compare))
|
|
|
;
|
|
|
else if(conn->allocptr.te &&
|
|
|
/* when asking for Transfer-Encoding, don't pass on a custom
|
|
|
Connection: */
|
|
|
- checkprefix("Connection:", headers->data))
|
|
|
+ checkprefix("Connection:", compare))
|
|
|
;
|
|
|
else if((conn->httpversion == 20) &&
|
|
|
- checkprefix("Transfer-Encoding:", headers->data))
|
|
|
+ checkprefix("Transfer-Encoding:", compare))
|
|
|
/* HTTP/2 doesn't support chunked requests */
|
|
|
;
|
|
|
- else if(checkprefix("Authorization:", headers->data) &&
|
|
|
+ else if((checkprefix("Authorization:", compare) ||
|
|
|
+ checkprefix("Cookie:", compare)) &&
|
|
|
/* be careful of sending this potentially sensitive header to
|
|
|
other hosts */
|
|
|
(data->state.this_is_a_follow &&
|
|
|
@@ -1792,10 +1865,10 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
|
|
|
!strcasecompare(data->state.first_host, conn->host.name)))
|
|
|
;
|
|
|
else {
|
|
|
- result = Curl_add_bufferf(&req_buffer, "%s\r\n", headers->data);
|
|
|
+ result = Curl_add_bufferf(&req_buffer, "%s\r\n", compare);
|
|
|
}
|
|
|
if(semicolonp)
|
|
|
- *semicolonp = ';'; /* put back the semicolon */
|
|
|
+ free(semicolonp);
|
|
|
if(result)
|
|
|
return result;
|
|
|
}
|
|
|
@@ -1807,6 +1880,7 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
|
|
|
return CURLE_OK;
|
|
|
}
|
|
|
|
|
|
+#ifndef CURL_DISABLE_PARSEDATE
|
|
|
CURLcode Curl_add_timecondition(struct Curl_easy *data,
|
|
|
Curl_send_buffer *req_buffer)
|
|
|
{
|
|
|
@@ -1850,21 +1924,31 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data,
|
|
|
*/
|
|
|
|
|
|
/* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
|
|
|
- snprintf(datestr, sizeof(datestr),
|
|
|
- "%s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
|
|
|
- condp,
|
|
|
- Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
|
|
|
- tm->tm_mday,
|
|
|
- Curl_month[tm->tm_mon],
|
|
|
- tm->tm_year + 1900,
|
|
|
- tm->tm_hour,
|
|
|
- tm->tm_min,
|
|
|
- tm->tm_sec);
|
|
|
+ msnprintf(datestr, sizeof(datestr),
|
|
|
+ "%s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
|
|
|
+ condp,
|
|
|
+ Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
|
|
|
+ tm->tm_mday,
|
|
|
+ Curl_month[tm->tm_mon],
|
|
|
+ tm->tm_year + 1900,
|
|
|
+ tm->tm_hour,
|
|
|
+ tm->tm_min,
|
|
|
+ tm->tm_sec);
|
|
|
|
|
|
result = Curl_add_buffer(&req_buffer, datestr, strlen(datestr));
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
+#else
|
|
|
+/* disabled */
|
|
|
+CURLcode Curl_add_timecondition(struct Curl_easy *data,
|
|
|
+ Curl_send_buffer *req_buffer)
|
|
|
+{
|
|
|
+ (void)data;
|
|
|
+ (void)req_buffer;
|
|
|
+ return CURLE_OK;
|
|
|
+}
|
|
|
+#endif
|
|
|
|
|
|
/*
|
|
|
* Curl_http() gets called from the generic multi_do() function when a HTTP
|
|
|
@@ -1916,6 +2000,13 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|
|
#ifdef USE_NGHTTP2
|
|
|
if(conn->data->set.httpversion ==
|
|
|
CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
|
|
|
+ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
|
|
|
+ /* We don't support HTTP/2 proxies yet. Also it's debatable whether
|
|
|
+ or not this setting should apply to HTTP/2 proxies. */
|
|
|
+ infof(data, "Ignoring HTTP/2 prior knowledge due to proxy\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
DEBUGF(infof(data, "HTTP/2 over clean TCP\n"));
|
|
|
conn->httpversion = 20;
|
|
|
|
|
|
@@ -1947,7 +2038,6 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|
|
|
|
|
data->state.first_remote_port = conn->remote_port;
|
|
|
}
|
|
|
- http->writebytecount = http->readbytecount = 0;
|
|
|
|
|
|
if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) &&
|
|
|
data->set.upload) {
|
|
|
@@ -1995,11 +2085,21 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|
|
}
|
|
|
|
|
|
/* setup the authentication headers */
|
|
|
- result = Curl_http_output_auth(conn, request, path, FALSE);
|
|
|
- if(result)
|
|
|
- return result;
|
|
|
+ {
|
|
|
+ char *pq = NULL;
|
|
|
+ if(query && *query) {
|
|
|
+ pq = aprintf("%s?%s", path, query);
|
|
|
+ if(!pq)
|
|
|
+ return CURLE_OUT_OF_MEMORY;
|
|
|
+ }
|
|
|
+ result = Curl_http_output_auth(conn, request, (pq ? pq : path), FALSE);
|
|
|
+ free(pq);
|
|
|
+ if(result)
|
|
|
+ return result;
|
|
|
+ }
|
|
|
|
|
|
- if((data->state.authhost.multipass || data->state.authproxy.multipass) &&
|
|
|
+ if(((data->state.authhost.multipass && !data->state.authhost.done)
|
|
|
+ || (data->state.authproxy.multipass && !data->state.authproxy.done)) &&
|
|
|
(httpreq != HTTPREQ_GET) &&
|
|
|
(httpreq != HTTPREQ_HEAD)) {
|
|
|
/* Auth is required and we are not authenticated yet. Make a PUT or POST
|
|
|
@@ -2084,6 +2184,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|
|
http->sendit = NULL;
|
|
|
}
|
|
|
|
|
|
+#ifndef CURL_DISABLE_MIME
|
|
|
if(http->sendit) {
|
|
|
const char *cthdr = Curl_checkheaders(conn, "Content-Type");
|
|
|
|
|
|
@@ -2108,6 +2209,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|
|
return result;
|
|
|
http->postsize = Curl_mime_size(http->sendit);
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
ptr = Curl_checkheaders(conn, "Transfer-Encoding");
|
|
|
if(ptr) {
|
|
|
@@ -2293,8 +2395,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|
|
if(!*data->state.up.path && path[strlen(path) - 1] != '/') {
|
|
|
*p++ = '/';
|
|
|
}
|
|
|
- snprintf(p, sizeof(ftp_typecode) - 1, ";type=%c",
|
|
|
- data->set.prefer_ascii ? 'a' : 'i');
|
|
|
+ msnprintf(p, sizeof(ftp_typecode) - 1, ";type=%c",
|
|
|
+ data->set.prefer_ascii ? 'a' : 'i');
|
|
|
}
|
|
|
}
|
|
|
if(conn->bits.user_passwd && !conn->bits.userpwd_in_url)
|
|
|
@@ -2635,9 +2737,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|
|
failf(data, "Failed sending PUT request");
|
|
|
else
|
|
|
/* prepare for transfer */
|
|
|
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
|
|
|
- &http->readbytecount, postsize?FIRSTSOCKET:-1,
|
|
|
- postsize?&http->writebytecount:NULL);
|
|
|
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE,
|
|
|
+ postsize?FIRSTSOCKET:-1);
|
|
|
if(result)
|
|
|
return result;
|
|
|
break;
|
|
|
@@ -2657,12 +2758,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|
|
failf(data, "Failed sending POST request");
|
|
|
else
|
|
|
/* setup variables for the upcoming transfer */
|
|
|
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
|
|
|
- -1, NULL);
|
|
|
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- postsize = http->postsize;
|
|
|
+ data->state.infilesize = postsize = http->postsize;
|
|
|
|
|
|
/* We only set Content-Length and allow a custom Content-Length if
|
|
|
we don't upload data chunked, as RFC2616 forbids us to set both
|
|
|
@@ -2678,6 +2778,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+#ifndef CURL_DISABLE_MIME
|
|
|
/* Output mime-generated headers. */
|
|
|
{
|
|
|
struct curl_slist *hdr;
|
|
|
@@ -2688,6 +2789,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|
|
return result;
|
|
|
}
|
|
|
}
|
|
|
+#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
|
|
|
@@ -2726,9 +2828,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|
|
failf(data, "Failed sending POST request");
|
|
|
else
|
|
|
/* prepare for transfer */
|
|
|
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
|
|
|
- &http->readbytecount, postsize?FIRSTSOCKET:-1,
|
|
|
- postsize?&http->writebytecount:NULL);
|
|
|
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE,
|
|
|
+ postsize?FIRSTSOCKET:-1);
|
|
|
if(result)
|
|
|
return result;
|
|
|
|
|
|
@@ -2882,9 +2983,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|
|
if(result)
|
|
|
failf(data, "Failed sending HTTP POST request");
|
|
|
else
|
|
|
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
|
|
|
- &http->readbytecount, http->postdata?FIRSTSOCKET:-1,
|
|
|
- http->postdata?&http->writebytecount:NULL);
|
|
|
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE,
|
|
|
+ http->postdata?FIRSTSOCKET:-1);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
@@ -2900,33 +3000,30 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
|
|
|
failf(data, "Failed sending HTTP request");
|
|
|
else
|
|
|
/* HTTP GET/HEAD download: */
|
|
|
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
|
|
|
- http->postdata?FIRSTSOCKET:-1,
|
|
|
- http->postdata?&http->writebytecount:NULL);
|
|
|
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE,
|
|
|
+ http->postdata?FIRSTSOCKET:-1);
|
|
|
}
|
|
|
if(result)
|
|
|
return result;
|
|
|
|
|
|
- if(http->writebytecount) {
|
|
|
+ if(data->req.writebytecount) {
|
|
|
/* if a request-body has been sent off, we make sure this progress is noted
|
|
|
properly */
|
|
|
- Curl_pgrsSetUploadCounter(data, http->writebytecount);
|
|
|
+ Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
|
|
|
if(Curl_pgrsUpdate(conn))
|
|
|
result = CURLE_ABORTED_BY_CALLBACK;
|
|
|
|
|
|
- if(http->writebytecount >= postsize) {
|
|
|
+ if(data->req.writebytecount >= postsize) {
|
|
|
/* already sent the entire request body, mark the "upload" as
|
|
|
complete */
|
|
|
infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
|
|
|
" out of %" CURL_FORMAT_CURL_OFF_T " bytes\n",
|
|
|
- http->writebytecount, postsize);
|
|
|
+ data->req.writebytecount, postsize);
|
|
|
data->req.upload_done = TRUE;
|
|
|
data->req.keepon &= ~KEEP_SEND; /* we're done writing */
|
|
|
data->req.exp100 = EXP100_SEND_DATA; /* already sent */
|
|
|
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
|
|
|
}
|
|
|
- else
|
|
|
- data->req.writebytecount = http->writebytecount;
|
|
|
}
|
|
|
|
|
|
if((conn->httpversion == 20) && data->req.upload_chunky)
|
|
|
@@ -3161,6 +3258,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|
|
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\n");
|
|
|
+ return CURLE_UNSUPPORTED_PROTOCOL;
|
|
|
+ }
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
@@ -3194,6 +3295,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|
|
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(!data->set.http09_allowed) {
|
|
|
+ failf(data, "Received HTTP/0.9 when not allowed\n");
|
|
|
+ return CURLE_UNSUPPORTED_PROTOCOL;
|
|
|
+ }
|
|
|
k->header = FALSE;
|
|
|
if(*nread)
|
|
|
/* since there's more, this is a partial bad header */
|
|
|
@@ -3306,14 +3411,31 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|
|
#if defined(USE_NTLM)
|
|
|
if(conn->bits.close &&
|
|
|
(((data->req.httpcode == 401) &&
|
|
|
- (conn->ntlm.state == NTLMSTATE_TYPE2)) ||
|
|
|
+ (conn->http_ntlm_state == NTLMSTATE_TYPE2)) ||
|
|
|
((data->req.httpcode == 407) &&
|
|
|
- (conn->proxyntlm.state == NTLMSTATE_TYPE2)))) {
|
|
|
+ (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) {
|
|
|
infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n");
|
|
|
data->state.authproblem = TRUE;
|
|
|
}
|
|
|
#endif
|
|
|
-
|
|
|
+#if defined(USE_SPNEGO)
|
|
|
+ if(conn->bits.close &&
|
|
|
+ (((data->req.httpcode == 401) &&
|
|
|
+ (conn->http_negotiate_state == GSS_AUTHRECV)) ||
|
|
|
+ ((data->req.httpcode == 407) &&
|
|
|
+ (conn->proxy_negotiate_state == GSS_AUTHRECV)))) {
|
|
|
+ infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n");
|
|
|
+ data->state.authproblem = TRUE;
|
|
|
+ }
|
|
|
+ if((conn->http_negotiate_state == GSS_AUTHDONE) &&
|
|
|
+ (data->req.httpcode != 401)) {
|
|
|
+ conn->http_negotiate_state = GSS_AUTHSUCC;
|
|
|
+ }
|
|
|
+ if((conn->proxy_negotiate_state == GSS_AUTHDONE) &&
|
|
|
+ (data->req.httpcode != 407)) {
|
|
|
+ conn->proxy_negotiate_state = GSS_AUTHSUCC;
|
|
|
+ }
|
|
|
+#endif
|
|
|
/*
|
|
|
* When all the headers have been parsed, see if we should give
|
|
|
* up and return an error.
|
|
|
@@ -3549,6 +3671,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|
|
if(conn->httpversion != 20)
|
|
|
infof(data, "Lying server, not serving HTTP/2\n");
|
|
|
}
|
|
|
+ if(conn->httpversion < 20) {
|
|
|
+ conn->bundle->multiuse = BUNDLE_NO_MULTIUSE;
|
|
|
+ infof(data, "Mark bundle as not supporting multiuse\n");
|
|
|
+ }
|
|
|
}
|
|
|
else if(!nc) {
|
|
|
/* this is the real world, not a Nirvana
|
|
|
@@ -3586,7 +3712,6 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|
|
conn->httpversion = 11; /* For us, RTSP acts like HTTP 1.1 */
|
|
|
}
|
|
|
else {
|
|
|
- /* TODO: do we care about the other cases here? */
|
|
|
nc = 0;
|
|
|
}
|
|
|
}
|
|
|
@@ -3639,26 +3764,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|
|
}
|
|
|
else if(conn->httpversion >= 11 &&
|
|
|
!conn->bits.close) {
|
|
|
- /* If HTTP version is >= 1.1 and connection is persistent
|
|
|
- server supports pipelining. */
|
|
|
+ /* If HTTP version is >= 1.1 and connection is persistent */
|
|
|
DEBUGF(infof(data,
|
|
|
- "HTTP 1.1 or later with persistent connection, "
|
|
|
- "pipelining supported\n"));
|
|
|
- /* Activate pipelining if needed */
|
|
|
- if(conn->bundle) {
|
|
|
- if(!Curl_pipeline_site_blacklisted(data, conn))
|
|
|
- conn->bundle->multiuse = BUNDLE_PIPELINING;
|
|
|
- }
|
|
|
+ "HTTP 1.1 or later with persistent connection\n"));
|
|
|
}
|
|
|
|
|
|
switch(k->httpcode) {
|
|
|
- case 204:
|
|
|
- /* (quote from RFC2616, section 10.2.5): The server has
|
|
|
- * fulfilled the request but does not need to return an
|
|
|
- * entity-body ... The 204 response MUST NOT include a
|
|
|
- * message-body, and thus is always terminated by the first
|
|
|
- * empty line after the header fields. */
|
|
|
- /* FALLTHROUGH */
|
|
|
case 304:
|
|
|
/* (quote from RFC2616, section 10.3.5): The 304 response
|
|
|
* MUST NOT contain a message-body, and thus is always
|
|
|
@@ -3666,6 +3777,13 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|
|
* fields. */
|
|
|
if(data->set.timecondition)
|
|
|
data->info.timecond = TRUE;
|
|
|
+ /* FALLTHROUGH */
|
|
|
+ case 204:
|
|
|
+ /* (quote from RFC2616, section 10.2.5): The server has
|
|
|
+ * fulfilled the request but does not need to return an
|
|
|
+ * entity-body ... The 204 response MUST NOT include a
|
|
|
+ * message-body, and thus is always terminated by the first
|
|
|
+ * empty line after the header fields. */
|
|
|
k->size = 0;
|
|
|
k->maxdownload = 0;
|
|
|
k->ignorecl = TRUE; /* ignore Content-Length headers */
|
|
|
@@ -3733,19 +3851,6 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|
|
data->info.contenttype = contenttype;
|
|
|
}
|
|
|
}
|
|
|
- else if(checkprefix("Server:", k->p)) {
|
|
|
- if(conn->httpversion < 20) {
|
|
|
- /* only do this for non-h2 servers */
|
|
|
- char *server_name = Curl_copy_header_value(k->p);
|
|
|
-
|
|
|
- /* Turn off pipelining if the server version is blacklisted */
|
|
|
- if(conn->bundle && (conn->bundle->multiuse == BUNDLE_PIPELINING)) {
|
|
|
- if(Curl_pipeline_server_blacklisted(data, server_name))
|
|
|
- conn->bundle->multiuse = BUNDLE_NO_MULTIUSE;
|
|
|
- }
|
|
|
- free(server_name);
|
|
|
- }
|
|
|
- }
|
|
|
else if((conn->httpversion == 10) &&
|
|
|
conn->bits.httpproxy &&
|
|
|
Curl_compareheader(k->p,
|
|
|
@@ -3859,7 +3964,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|
|
here, or else use real peer host name. */
|
|
|
conn->allocptr.cookiehost?
|
|
|
conn->allocptr.cookiehost:conn->host.name,
|
|
|
- data->state.up.path);
|
|
|
+ data->state.up.path,
|
|
|
+ (conn->handler->protocol&CURLPROTO_HTTPS)?
|
|
|
+ TRUE:FALSE);
|
|
|
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
|
|
|
}
|
|
|
#endif
|
|
|
@@ -3888,6 +3995,22 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|
|
if(result)
|
|
|
return result;
|
|
|
}
|
|
|
+ #ifdef USE_SPNEGO
|
|
|
+ else if(checkprefix("Persistent-Auth", k->p)) {
|
|
|
+ struct negotiatedata *negdata = &conn->negotiate;
|
|
|
+ struct auth *authp = &data->state.authhost;
|
|
|
+ if(authp->picked == CURLAUTH_NEGOTIATE) {
|
|
|
+ char *persistentauth = Curl_copy_header_value(k->p);
|
|
|
+ if(!persistentauth)
|
|
|
+ return CURLE_OUT_OF_MEMORY;
|
|
|
+ negdata->noauthpersist = checkprefix("false", persistentauth);
|
|
|
+ negdata->havenoauthpersist = TRUE;
|
|
|
+ infof(data, "Negotiate: noauthpersist -> %d, header part: %s",
|
|
|
+ negdata->noauthpersist, persistentauth);
|
|
|
+ free(persistentauth);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ #endif
|
|
|
else if((k->httpcode >= 300 && k->httpcode < 400) &&
|
|
|
checkprefix("Location:", k->p) &&
|
|
|
!data->req.location) {
|
|
|
@@ -3915,6 +4038,27 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+#ifdef USE_ALTSVC
|
|
|
+ /* If enabled, the header is incoming and this is over HTTPS */
|
|
|
+ else if(data->asi && checkprefix("Alt-Svc:", k->p) &&
|
|
|
+ ((conn->handler->flags & PROTOPT_SSL) ||
|
|
|
+#ifdef CURLDEBUG
|
|
|
+ /* allow debug builds to circumvent the HTTPS restriction */
|
|
|
+ getenv("CURL_ALTSVC_HTTP")
|
|
|
+#else
|
|
|
+ 0
|
|
|
+#endif
|
|
|
+ )) {
|
|
|
+ /* the ALPN of the current request */
|
|
|
+ enum alpnid id = (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
|
|
|
+ result = Curl_altsvc_parse(data, data->asi,
|
|
|
+ &k->p[ strlen("Alt-Svc:") ],
|
|
|
+ id, conn->host.name,
|
|
|
+ curlx_uitous(conn->remote_port));
|
|
|
+ if(result)
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+#endif
|
|
|
else if(conn->handler->protocol & CURLPROTO_RTSP) {
|
|
|
result = Curl_rtsp_parseheader(conn, k->p);
|
|
|
if(result)
|