* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
#include "curl_base64.h"
#include "cookie.h"
#include "strequal.h"
-#include "sslgen.h"
+#include "vtls/vtls.h"
#include "http_digest.h"
#include "curl_ntlm.h"
#include "curl_ntlm_wb.h"
#include "non-ascii.h"
#include "bundles.h"
#include "pipeline.h"
+#include "http2.h"
+#include "connect.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
*/
const struct Curl_handler Curl_handler_http = {
"HTTP", /* scheme */
- ZERO_NULL, /* setup_connection */
+ Curl_http_setup_conn, /* setup_connection */
Curl_http, /* do_it */
Curl_http_done, /* done */
ZERO_NULL, /* do_more */
ZERO_NULL, /* readwrite */
PORT_HTTP, /* defport */
CURLPROTO_HTTP, /* protocol */
- PROTOPT_NONE /* flags */
+ PROTOPT_CREDSPERREQUEST /* flags */
};
#ifdef USE_SSL
*/
const struct Curl_handler Curl_handler_https = {
"HTTPS", /* scheme */
- ZERO_NULL, /* setup_connection */
+ Curl_http_setup_conn, /* setup_connection */
Curl_http, /* do_it */
Curl_http_done, /* done */
ZERO_NULL, /* do_more */
ZERO_NULL, /* disconnect */
ZERO_NULL, /* readwrite */
PORT_HTTPS, /* defport */
- CURLPROTO_HTTP | CURLPROTO_HTTPS, /* protocol */
- PROTOPT_SSL /* flags */
+ CURLPROTO_HTTPS, /* protocol */
+ PROTOPT_SSL | PROTOPT_CREDSPERREQUEST /* flags */
};
#endif
+CURLcode Curl_http_setup_conn(struct connectdata *conn)
+{
+ /* allocate the HTTP-specific struct for the SessionHandle, only to survive
+ during this request */
+ DEBUGASSERT(conn->data->req.protop == NULL);
+
+ conn->data->req.protop = calloc(1, sizeof(struct HTTP));
+ if(!conn->data->req.protop)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+}
+
/*
* checkheaders() checks the linked list of custom HTTP headers for a
* particular header (prefix).
*
* Returns a pointer to the first matching header or NULL if none matched.
*/
-char *Curl_checkheaders(struct SessionHandle *data, const char *thisheader)
+char *Curl_checkheaders(const struct connectdata *conn,
+ const char *thisheader)
+{
+ struct curl_slist *head;
+ size_t thislen = strlen(thisheader);
+ struct SessionHandle *data = conn->data;
+
+ for(head = data->set.headers;head; head=head->next) {
+ if(Curl_raw_nequal(head->data, thisheader, thislen))
+ return head->data;
+ }
+ return NULL;
+}
+
+/*
+ * checkProxyHeaders() checks the linked list of custom proxy headers
+ * if proxy headers are not available, then it will lookup into http header
+ * link list
+ *
+ * It takes a connectdata struct as input instead of the SessionHandle simply
+ * to know if this is a proxy request or not, as it then might check a
+ * different header list.
+ *
+ */
+char *Curl_checkProxyheaders(const struct connectdata *conn,
+ const char *thisheader)
{
struct curl_slist *head;
size_t thislen = strlen(thisheader);
+ struct SessionHandle *data = conn->data;
- for(head = data->set.headers; head; head=head->next) {
+ for(head = (conn->bits.proxy && data->set.sep_headers)?
+ data->set.proxyheaders:data->set.headers;
+ head; head=head->next) {
if(Curl_raw_nequal(head->data, thisheader, thislen))
return head->data;
}
* case of allocation failure. Returns an empty string if the header value
* consists entirely of whitespace.
*/
-static char *copy_header_value(const char *h)
+char *Curl_copy_header_value(const char *header)
{
const char *start;
const char *end;
char *value;
size_t len;
- DEBUGASSERT(h);
+ DEBUGASSERT(header);
/* Find the end of the header name */
- while(*h && (*h != ':'))
- ++h;
+ while(*header && (*header != ':'))
+ ++header;
- if(*h)
+ if(*header)
/* Skip over colon */
- ++h;
+ ++header;
/* Find the first non-space letter */
- start = h;
+ start = header;
while(*start && ISSPACE(*start))
start++;
end--;
/* get length of the type */
- len = end-start+1;
+ len = end - start + 1;
value = malloc(len + 1);
if(!value)
static CURLcode http_perhapsrewind(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
- struct HTTP *http = data->state.proto.http;
+ struct HTTP *http = data->req.protop;
curl_off_t bytessent;
curl_off_t expectsend = -1; /* default is unknown */
expectsend = (curl_off_t)strlen(data->set.postfields);
break;
case HTTPREQ_PUT:
- if(data->set.infilesize != -1)
- expectsend = data->set.infilesize;
+ if(data->state.infilesize != -1)
+ expectsend = data->state.infilesize;
break;
case HTTPREQ_POST_FORM:
expectsend = http->postsize;
/* this is already marked to get closed */
return CURLE_OK;
- infof(data, "NTLM send, close instead of sending %" FORMAT_OFF_T
- " bytes\n", (curl_off_t)(expectsend - bytessent));
+ infof(data, "NTLM send, close instead of sending %"
+ CURL_FORMAT_CURL_OFF_T " bytes\n",
+ (curl_off_t)(expectsend - bytessent));
}
/* This is not NTLM or many bytes left to send: close
*/
- conn->bits.close = TRUE;
+ connclose(conn, "Mid-auth HTTP and much data left to send");
data->req.size = 0; /* don't download any more than 0 bytes */
/* There still is data left to send, but this connection is marked for
if(authstatus->picked == CURLAUTH_BASIC) {
/* Basic */
if((proxy && conn->bits.proxy_user_passwd &&
- !Curl_checkheaders(data, "Proxy-authorization:")) ||
+ !Curl_checkProxyheaders(conn, "Proxy-authorization:")) ||
(!proxy && conn->bits.user_passwd &&
- !Curl_checkheaders(data, "Authorization:"))) {
+ !Curl_checkheaders(conn, "Authorization:"))) {
auth="Basic";
result = http_output_basic(conn, proxy);
if(result)
* proxy CONNECT loop.
*/
-CURLcode Curl_http_input_auth(struct connectdata *conn,
- int httpcode,
- const char *header) /* the first non-space */
+CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy,
+ const char *auth) /* the first non-space */
{
/*
* This resource requires authentication
struct SessionHandle *data = conn->data;
unsigned long *availp;
- const char *start;
struct auth *authp;
- if(httpcode == 407) {
- start = header+strlen("Proxy-authenticate:");
+ if(proxy) {
availp = &data->info.proxyauthavail;
authp = &data->state.authproxy;
}
else {
- start = header+strlen("WWW-Authenticate:");
availp = &data->info.httpauthavail;
authp = &data->state.authhost;
}
- /* pass all white spaces */
- while(*start && ISSPACE(*start))
- start++;
-
/*
* Here we check if we want the specific single authentication (using ==) and
* if we do, we initiate usage of it.
*
*/
- while(*start) {
+ while(*auth) {
#ifdef USE_HTTP_NEGOTIATE
- if(checkprefix("GSS-Negotiate", start) ||
- checkprefix("Negotiate", start)) {
+ if(checkprefix("GSS-Negotiate", auth) ||
+ checkprefix("Negotiate", auth)) {
int neg;
*availp |= CURLAUTH_GSSNEGOTIATE;
authp->avail |= CURLAUTH_GSSNEGOTIATE;
infof(data, "Authentication problem. Ignoring this.\n");
data->state.authproblem = TRUE;
}
- else {
- neg = Curl_input_negotiate(conn, (bool)(httpcode == 407), start);
+ else if(data->state.negotiate.state == GSS_AUTHNONE) {
+ neg = Curl_input_negotiate(conn, proxy, auth);
if(neg == 0) {
DEBUGASSERT(!data->req.newurl);
data->req.newurl = strdup(data->change.url);
#endif
#ifdef USE_NTLM
/* NTLM support requires the SSL crypto libs */
- if(checkprefix("NTLM", start)) {
+ if(checkprefix("NTLM", auth)) {
*availp |= CURLAUTH_NTLM;
authp->avail |= CURLAUTH_NTLM;
if(authp->picked == CURLAUTH_NTLM ||
authp->picked == CURLAUTH_NTLM_WB) {
/* NTLM authentication is picked and activated */
CURLcode ntlm =
- Curl_input_ntlm(conn, (httpcode == 407)?TRUE:FALSE, start);
+ Curl_input_ntlm(conn, proxy, auth);
if(CURLE_OK == ntlm) {
data->state.authproblem = FALSE;
#ifdef NTLM_WB_ENABLED
/* Get the challenge-message which will be passed to
* ntlm_auth for generating the type 3 message later */
- while(*start && ISSPACE(*start))
- start++;
- if(checkprefix("NTLM", start)) {
- start += strlen("NTLM");
- while(*start && ISSPACE(*start))
- start++;
- if(*start)
- if((conn->challenge_header = strdup(start)) == NULL)
+ while(*auth && ISSPACE(*auth))
+ auth++;
+ if(checkprefix("NTLM", auth)) {
+ auth += strlen("NTLM");
+ while(*auth && ISSPACE(*auth))
+ auth++;
+ if(*auth)
+ if((conn->challenge_header = strdup(auth)) == NULL)
return CURLE_OUT_OF_MEMORY;
}
}
else
#endif
#ifndef CURL_DISABLE_CRYPTO_AUTH
- if(checkprefix("Digest", start)) {
+ if(checkprefix("Digest", auth)) {
if((authp->avail & CURLAUTH_DIGEST) != 0) {
infof(data, "Ignoring duplicate digest auth header.\n");
}
* authentication isn't activated yet, as we need to store the
* incoming data from this header in case we are gonna use
* Digest. */
- dig = Curl_input_digest(conn, (httpcode == 407)?TRUE:FALSE, start);
+ dig = Curl_input_digest(conn, proxy, auth);
if(CURLDIGEST_FINE != dig) {
infof(data, "Authentication problem. Ignoring this.\n");
}
else
#endif
- if(checkprefix("Basic", start)) {
+ if(checkprefix("Basic", auth)) {
*availp |= CURLAUTH_BASIC;
authp->avail |= CURLAUTH_BASIC;
if(authp->picked == CURLAUTH_BASIC) {
}
/* there may be multiple methods on one line, so keep reading */
- while(*start && *start != ',') /* read up to the next comma */
- start++;
- if(*start == ',') /* if we're on a comma, skip it */
- start++;
- while(*start && ISSPACE(*start))
- start++;
+ while(*auth && *auth != ',') /* read up to the next comma */
+ auth++;
+ if(*auth == ',') /* if we're on a comma, skip it */
+ auth++;
+ while(*auth && ISSPACE(*auth))
+ auth++;
}
return CURLE_OK;
}
void *userp)
{
struct connectdata *conn = (struct connectdata *)userp;
- struct HTTP *http = conn->data->state.proto.http;
+ struct HTTP *http = conn->data->req.protop;
size_t fullsize = size * nitems;
if(0 == http->postsize)
CURLcode res;
char *ptr;
size_t size;
- struct HTTP *http = conn->data->state.proto.http;
+ struct HTTP *http = conn->data->req.protop;
size_t sendsize;
curl_socket_t sockfd;
size_t headersize;
return res;
}
+
if(conn->handler->flags & PROTOPT_SSL) {
/* We never send more than CURL_MAX_WRITE_SIZE bytes in one single chunk
when we speak HTTPS, as if only a fraction of it is sent now, this data
/* We default to persistent connections. We set this already in this connect
function to make the re-use checks properly be able to check this bit. */
- conn->bits.close = FALSE;
+ connkeep(conn, "HTTP default");
/* the CONNECT procedure might not have been completed */
result = Curl_proxy_connect(conn);
/* perform SSL initialization for this socket */
result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
if(result)
- conn->bits.close = TRUE; /* a failed connection is marked for closure
- to prevent (bad) re-use or similar */
+ connclose(conn, "Failed HTTPS connection");
+
return result;
}
#endif
#if defined(USE_SSLEAY) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \
- defined(USE_DARWINSSL)
-/* This function is for OpenSSL, GnuTLS, darwinssl, and schannel only.
+ defined(USE_DARWINSSL) || defined(USE_POLARSSL) || defined(USE_NSS)
+/* This function is for OpenSSL, GnuTLS, darwinssl, schannel and polarssl only.
It should be made to query the generic SSL layer instead. */
static int https_getsock(struct connectdata *conn,
curl_socket_t *socks,
CURLcode status, bool premature)
{
struct SessionHandle *data = conn->data;
- struct HTTP *http =data->state.proto.http;
+ struct HTTP *http =data->req.protop;
Curl_unencode_cleanup(conn);
if(!premature && /* this check is pointless when DONE is called before the
entire operation is complete */
!conn->bits.retry &&
+ !data->set.connect_only &&
((http->readbytecount +
data->req.headerbytecount -
data->req.deductheadercount)) <= 0) {
}
-/* Determine if we should use HTTP 1.1 for this request. Reasons to avoid it
- are if the user specifically requested HTTP 1.0, if the server we are
- connected to only supports 1.0, or if any server previously contacted to
- handle this request only supports 1.0. */
-static bool use_http_1_1(const struct SessionHandle *data,
- const struct connectdata *conn)
+/*
+ * 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.
+ */
+static bool use_http_1_1plus(const struct SessionHandle *data,
+ const struct connectdata *conn)
{
- return ((data->set.httpversion == CURL_HTTP_VERSION_1_1) ||
+ return ((data->set.httpversion >= CURL_HTTP_VERSION_1_1) ||
((data->set.httpversion != CURL_HTTP_VERSION_1_0) &&
((conn->httpversion == 11) ||
((conn->httpversion != 10) &&
const char *ptr;
data->state.expect100header = FALSE; /* default to false unless it is set
to TRUE below */
- if(use_http_1_1(data, conn)) {
+ if(conn->httpversion == 20) {
+ /* We don't use Expect in HTTP2 */
+ return CURLE_OK;
+ }
+ if(use_http_1_1plus(data, conn)) {
/* if not doing HTTP 1.0 or disabled explicitly, we add a Expect:
100-continue to the headers which actually speeds up post operations
(as there is one packet coming back from the web server) */
- ptr = Curl_checkheaders(data, "Expect:");
+ ptr = Curl_checkheaders(conn, "Expect:");
if(ptr) {
data->state.expect100header =
Curl_compareheader(ptr, "Expect:", "100-continue");
return result;
}
+enum proxy_use {
+ HEADER_SERVER, /* direct to server */
+ HEADER_PROXY, /* regular request to proxy */
+ HEADER_CONNECT /* sending CONNECT to a proxy */
+};
+
CURLcode Curl_add_custom_headers(struct connectdata *conn,
- Curl_send_buffer *req_buffer)
+ bool is_connect,
+ Curl_send_buffer *req_buffer)
{
char *ptr;
- struct curl_slist *headers=conn->data->set.headers;
-
- while(headers) {
- ptr = strchr(headers->data, ':');
- if(ptr) {
- /* we require a colon for this to be a true header */
+ struct curl_slist *h[2];
+ struct curl_slist *headers;
+ int numlists=1; /* by default */
+ struct SessionHandle *data = conn->data;
+ int i;
- ptr++; /* pass the colon */
- while(*ptr && ISSPACE(*ptr))
- ptr++;
+ enum proxy_use proxy;
- if(*ptr) {
- /* only send this if the contents was non-blank */
+ if(is_connect)
+ proxy = HEADER_CONNECT;
+ else
+ proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy?
+ HEADER_PROXY:HEADER_SERVER;
- 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))
- ;
- else if(conn->data->set.httpreq == HTTPREQ_POST_FORM &&
- /* this header (extended by formdata.c) is sent later */
- checkprefix("Content-Type:", headers->data))
- ;
- 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))
- ;
- else if(conn->allocptr.te &&
- /* when asking for Transfer-Encoding, don't pass on a custom
- Connection: */
- checkprefix("Connection", headers->data))
- ;
- else {
- CURLcode result = Curl_add_bufferf(req_buffer, "%s\r\n",
- headers->data);
- if(result)
- return result;
- }
- }
+ 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++;
}
- else {
- ptr = strchr(headers->data, ';');
+ break;
+ case HEADER_CONNECT:
+ if(data->set.sep_headers)
+ h[0] = data->set.proxyheaders;
+ else
+ h[0] = data->set.headers;
+ break;
+ }
+
+ /* loop through one or two lists */
+ for(i=0; i < numlists; i++) {
+ headers = h[i];
+
+ while(headers) {
+ ptr = strchr(headers->data, ':');
if(ptr) {
+ /* we require a colon for this to be a true header */
- ptr++; /* pass the semicolon */
+ ptr++; /* pass the colon */
while(*ptr && ISSPACE(*ptr))
ptr++;
if(*ptr) {
- /* this may be used for something else in the future */
- }
- else {
- if(*(--ptr) == ';') {
- CURLcode result;
-
- /* send no-value custom header if terminated by semicolon */
- *ptr = ':';
- result = Curl_add_bufferf(req_buffer, "%s\r\n",
- headers->data);
+ /* only send this if the contents was non-blank */
+
+ 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))
+ ;
+ else if(data->set.httpreq == HTTPREQ_POST_FORM &&
+ /* this header (extended by formdata.c) is sent later */
+ checkprefix("Content-Type:", headers->data))
+ ;
+ 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))
+ ;
+ else if(conn->allocptr.te &&
+ /* when asking for Transfer-Encoding, don't pass on a custom
+ Connection: */
+ checkprefix("Connection", headers->data))
+ ;
+ else {
+ CURLcode result = Curl_add_bufferf(req_buffer, "%s\r\n",
+ headers->data);
if(result)
return result;
}
}
}
+ else {
+ ptr = strchr(headers->data, ';');
+ if(ptr) {
+
+ ptr++; /* pass the semicolon */
+ while(*ptr && ISSPACE(*ptr))
+ ptr++;
+
+ if(*ptr) {
+ /* this may be used for something else in the future */
+ }
+ else {
+ if(*(--ptr) == ';') {
+ CURLcode result;
+
+ /* send no-value custom header if terminated by semicolon */
+ *ptr = ':';
+ result = Curl_add_bufferf(req_buffer, "%s\r\n",
+ headers->data);
+ if(result)
+ return result;
+ }
+ }
+ }
+ }
+ headers = headers->next;
}
- headers = headers->next;
}
return CURLE_OK;
}
the rest of the request in the PERFORM phase. */
*done = TRUE;
- /* If there already is a protocol-specific struct allocated for this
- sessionhandle, deal with it */
- Curl_reset_reqproto(conn);
+ if(conn->httpversion < 20) { /* unless the connection is re-used and already
+ http2 */
+ switch (conn->negnpn) {
+ case NPN_HTTP2:
+ result = Curl_http2_init(conn);
+ if(result)
+ return result;
- if(!data->state.proto.http) {
- /* Only allocate this struct if we don't already have it! */
+ result = Curl_http2_setup(conn);
+ if(result)
+ return result;
- http = calloc(1, sizeof(struct HTTP));
- if(!http)
- return CURLE_OUT_OF_MEMORY;
- data->state.proto.http = http;
+ /* TODO: add error checking here */
+ Curl_http2_switched(conn);
+ break;
+ case NPN_HTTP1_1:
+ /* continue with HTTP/1.1 when explicitly requested */
+ break;
+ default:
+ /* and as fallback */
+ break;
+ }
+ }
+ else {
+ /* prepare for a http2 request */
+ Curl_http2_setup(conn);
}
- else
- http = data->state.proto.http;
+
+ http = data->req.protop;
if(!data->state.this_is_a_follow) {
/* this is not a followed location, get the original host name */
}
http->writebytecount = http->readbytecount = 0;
- if((conn->handler->protocol&(CURLPROTO_HTTP|CURLPROTO_FTP)) &&
+ if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) &&
data->set.upload) {
httpreq = HTTPREQ_PUT;
}
it might have been used in the proxy connect, but if we have got a header
with the user-agent string specified, we erase the previously made string
here. */
- if(Curl_checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
+ if(Curl_checkheaders(conn, "User-Agent:") && conn->allocptr.uagent) {
free(conn->allocptr.uagent);
conn->allocptr.uagent=NULL;
}
conn->bits.authneg = FALSE;
Curl_safefree(conn->allocptr.ref);
- if(data->change.referer && !Curl_checkheaders(data, "Referer:"))
+ if(data->change.referer && !Curl_checkheaders(conn, "Referer:")) {
conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
+ if(!conn->allocptr.ref)
+ return CURLE_OUT_OF_MEMORY;
+ }
else
conn->allocptr.ref = NULL;
- if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(data, "Cookie:"))
+ if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(conn, "Cookie:"))
addcookies = data->set.str[STRING_COOKIE];
- if(!Curl_checkheaders(data, "Accept-Encoding:") &&
+ if(!Curl_checkheaders(conn, "Accept-Encoding:") &&
data->set.str[STRING_ENCODING]) {
Curl_safefree(conn->allocptr.accept_encoding);
conn->allocptr.accept_encoding =
#ifdef HAVE_LIBZ
/* we only consider transfer-encoding magic if libz support is built-in */
- if(!Curl_checkheaders(data, "TE:") && data->set.http_transfer_encoding) {
+ if(!Curl_checkheaders(conn, "TE:") &&
+ data->set.http_transfer_encoding) {
/* When we are to insert a TE: header in the request, we must also insert
TE in a Connection: header, so we need to merge the custom provided
Connection: header and prevent the original to get sent. Note that if
the user has inserted his/hers own TE: header we don't do this magic
but then assume that the user will handle it all! */
- char *cptr = Curl_checkheaders(data, "Connection:");
+ char *cptr = Curl_checkheaders(conn, "Connection:");
#define TE_HEADER "TE: gzip\r\n"
Curl_safefree(conn->allocptr.te);
}
#endif
- ptr = Curl_checkheaders(data, "Transfer-Encoding:");
- if(ptr) {
- /* Some kind of TE is requested, check if 'chunked' is chosen */
- data->req.upload_chunky =
- Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
- }
+ if(conn->httpversion == 20)
+ /* In HTTP2 forbids Transfer-Encoding: chunked */
+ ptr = NULL;
else {
- if((conn->handler->protocol&CURLPROTO_HTTP) &&
- data->set.upload &&
- (data->set.infilesize == -1)) {
- if(conn->bits.authneg)
- /* don't enable chunked during auth neg */
- ;
- else if(use_http_1_1(data, conn)) {
- /* HTTP, upload, unknown file size and not HTTP 1.0 */
- data->req.upload_chunky = TRUE;
+ ptr = Curl_checkheaders(conn, "Transfer-Encoding:");
+ if(ptr) {
+ /* Some kind of TE is requested, check if 'chunked' is chosen */
+ data->req.upload_chunky =
+ Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
+ }
+ else {
+ if((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
+ data->set.upload &&
+ (data->state.infilesize == -1)) {
+ if(conn->bits.authneg)
+ /* don't enable chunked during auth neg */
+ ;
+ else if(use_http_1_1plus(data, conn)) {
+ /* HTTP, upload, unknown file size and not HTTP 1.0 */
+ data->req.upload_chunky = TRUE;
+ }
+ else {
+ failf(data, "Chunky upload is not supported by HTTP 1.0");
+ return CURLE_UPLOAD_FAILED;
+ }
}
else {
- failf(data, "Chunky upload is not supported by HTTP 1.0");
- return CURLE_UPLOAD_FAILED;
+ /* else, no chunky upload */
+ data->req.upload_chunky = FALSE;
}
- }
- else {
- /* else, no chunky upload */
- data->req.upload_chunky = FALSE;
- }
- if(data->req.upload_chunky)
- te = "Transfer-Encoding: chunked\r\n";
+ if(data->req.upload_chunky)
+ te = "Transfer-Encoding: chunked\r\n";
+ }
}
Curl_safefree(conn->allocptr.host);
- ptr = Curl_checkheaders(data, "Host:");
+ ptr = Curl_checkheaders(conn, "Host:");
if(ptr && (!data->state.this_is_a_follow ||
Curl_raw_equal(data->state.first_host, conn->host.name))) {
#if !defined(CURL_DISABLE_COOKIES)
custom Host: header if this is NOT a redirect, as setting Host: in the
redirected request is being out on thin ice. Except if the host name
is the same as the first one! */
- char *cookiehost = copy_header_value(ptr);
+ char *cookiehost = Curl_copy_header_value(ptr);
if(!cookiehost)
return CURLE_OUT_OF_MEMORY;
if(!*cookiehost)
/* we must build the whole post sequence first, so that we have a size of
the whole transfer before we start to send it */
result = Curl_getformdata(data, &http->sendit, data->set.httppost,
- Curl_checkheaders(data, "Content-Type:"),
+ Curl_checkheaders(conn, "Content-Type:"),
&http->postsize);
if(result)
return result;
}
- http->p_accept = Curl_checkheaders(data, "Accept:")?NULL:"Accept: */*\r\n";
+ http->p_accept = Curl_checkheaders(conn, "Accept:")?NULL:"Accept: */*\r\n";
if(( (HTTPREQ_POST == httpreq) ||
(HTTPREQ_POST_FORM == httpreq) ||
if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
/* this checks for greater-than only to make sure that the
CURL_READFUNC_ABORT return code still aborts */
- failf(data, "Could only read %" FORMAT_OFF_T
- " bytes from the input",
- passed);
+ failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T
+ " bytes from the input", passed);
return CURLE_READ_ERROR;
}
} while(passed < data->state.resume_from);
}
/* now, decrease the size of the read */
- if(data->set.infilesize>0) {
- data->set.infilesize -= data->state.resume_from;
+ if(data->state.infilesize>0) {
+ data->state.infilesize -= data->state.resume_from;
- if(data->set.infilesize <= 0) {
+ if(data->state.infilesize <= 0) {
failf(data, "File already completely uploaded");
return CURLE_PARTIAL_FILE;
}
* ones if any such are specified.
*/
if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
- !Curl_checkheaders(data, "Range:")) {
+ !Curl_checkheaders(conn, "Range:")) {
/* if a line like this was already allocated, free the previous one */
if(conn->allocptr.rangeline)
free(conn->allocptr.rangeline);
data->state.range);
}
else if((httpreq != HTTPREQ_GET) &&
- !Curl_checkheaders(data, "Content-Range:")) {
+ !Curl_checkheaders(conn, "Content-Range:")) {
/* if a line like this was already allocated, free the previous one */
if(conn->allocptr.rangeline)
remote part so we tell the server (and act accordingly) that we
upload the whole file (again) */
conn->allocptr.rangeline =
- aprintf("Content-Range: bytes 0-%" FORMAT_OFF_T
- "/%" FORMAT_OFF_T "\r\n",
- data->set.infilesize - 1, data->set.infilesize);
+ aprintf("Content-Range: bytes 0-%" CURL_FORMAT_CURL_OFF_T
+ "/%" CURL_FORMAT_CURL_OFF_T "\r\n",
+ data->state.infilesize - 1, data->state.infilesize);
}
else if(data->state.resume_from) {
/* This is because "resume" was selected */
curl_off_t total_expected_size=
- data->state.resume_from + data->set.infilesize;
+ data->state.resume_from + data->state.infilesize;
conn->allocptr.rangeline =
- aprintf("Content-Range: bytes %s%" FORMAT_OFF_T
- "/%" FORMAT_OFF_T "\r\n",
+ aprintf("Content-Range: bytes %s%" CURL_FORMAT_CURL_OFF_T
+ "/%" CURL_FORMAT_CURL_OFF_T "\r\n",
data->state.range, total_expected_size-1,
total_expected_size);
}
/* Range was selected and then we just pass the incoming range and
append total size */
conn->allocptr.rangeline =
- aprintf("Content-Range: bytes %s/%" FORMAT_OFF_T "\r\n",
- data->state.range, data->set.infilesize);
+ aprintf("Content-Range: bytes %s/%" CURL_FORMAT_CURL_OFF_T "\r\n",
+ data->state.range, data->state.infilesize);
}
if(!conn->allocptr.rangeline)
return CURLE_OUT_OF_MEMORY;
/* Use 1.1 unless the user specifically asked for 1.0 or the server only
supports 1.0 */
- httpstring= use_http_1_1(data, conn)?"1.1":"1.0";
+ httpstring= use_http_1_1plus(data, conn)?"1.1":"1.0";
/* initialize a dynamic send-buffer */
req_buffer = Curl_add_buffer_init();
conn->allocptr.ref:"" /* Referer: <data> */,
(conn->bits.httpproxy &&
!conn->bits.tunnel_proxy &&
- !Curl_checkheaders(data, "Proxy-Connection:"))?
+ !Curl_checkProxyheaders(conn, "Proxy-Connection:"))?
"Proxy-Connection: Keep-Alive\r\n":"",
te
);
if(result)
return result;
+ if(!(conn->handler->flags&PROTOPT_SSL) &&
+ (data->set.httpversion == CURL_HTTP_VERSION_2_0)) {
+ /* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done
+ over SSL */
+ result = Curl_http2_request_upgrade(req_buffer, conn);
+ if(result)
+ return result;
+ }
+
#if !defined(CURL_DISABLE_COOKIES)
if(data->cookies || addcookies) {
struct Cookie *co=NULL; /* no cookies from start */
return result;
}
- result = Curl_add_custom_headers(conn, req_buffer);
+ result = Curl_add_custom_headers(conn, FALSE, req_buffer);
if(result)
return result;
http->sending = HTTPSEND_BODY;
if(!data->req.upload_chunky &&
- !Curl_checkheaders(data, "Content-Length:")) {
+ !Curl_checkheaders(conn, "Content-Length:")) {
/* only add Content-Length if not uploading chunked */
result = Curl_add_bufferf(req_buffer,
- "Content-Length: %" FORMAT_OFF_T "\r\n",
- http->postsize);
+ "Content-Length: %" CURL_FORMAT_CURL_OFF_T
+ "\r\n", http->postsize);
if(result)
return result;
}
if(conn->bits.authneg)
postsize = 0;
else
- postsize = data->set.infilesize;
+ postsize = data->state.infilesize;
if((postsize != -1) && !data->req.upload_chunky &&
- !Curl_checkheaders(data, "Content-Length:")) {
+ !Curl_checkheaders(conn, "Content-Length:")) {
/* only add Content-Length if not uploading chunked */
result = Curl_add_bufferf(req_buffer,
- "Content-Length: %" FORMAT_OFF_T "\r\n",
- postsize );
+ "Content-Length: %" CURL_FORMAT_CURL_OFF_T
+ "\r\n", postsize);
if(result)
return result;
}
- result = expect100(data, conn, req_buffer);
- if(result)
- return result;
+ if(postsize != 0) {
+ result = expect100(data, conn, req_buffer);
+ if(result)
+ return result;
+ }
result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers */
if(result)
data->set.postfieldsize:
(data->set.postfields? (curl_off_t)strlen(data->set.postfields):-1);
}
- if(!data->req.upload_chunky) {
- /* 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
- kinds of headers (Transfer-Encoding: chunked and Content-Length) */
-
- if(conn->bits.authneg || !Curl_checkheaders(data, "Content-Length:")) {
- /* we allow replacing this header if not during auth negotiation,
- although it isn't very wise to actually set your own */
- result = Curl_add_bufferf(req_buffer,
- "Content-Length: %" FORMAT_OFF_T"\r\n",
- postsize);
- if(result)
- return result;
- }
+
+ /* 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
+ kinds of headers (Transfer-Encoding: chunked and Content-Length) */
+ if((postsize != -1) && !data->req.upload_chunky &&
+ !Curl_checkheaders(conn, "Content-Length:")) {
+ /* we allow replacing this header if not during auth negotiation,
+ although it isn't very wise to actually set your own */
+ result = Curl_add_bufferf(req_buffer,
+ "Content-Length: %" CURL_FORMAT_CURL_OFF_T
+ "\r\n", postsize);
+ if(result)
+ return result;
}
- if(!Curl_checkheaders(data, "Content-Type:")) {
+ if(!Curl_checkheaders(conn, "Content-Type:")) {
result = Curl_add_bufferf(req_buffer,
"Content-Type: application/"
"x-www-form-urlencoded\r\n");
the somewhat bigger ones we allow the app to disable it. Just make
sure that the expect100header is always set to the preferred value
here. */
- ptr = Curl_checkheaders(data, "Expect:");
+ ptr = Curl_checkheaders(conn, "Expect:");
if(ptr) {
data->state.expect100header =
Curl_compareheader(ptr, "Expect:", "100-continue");
if(data->set.postfields) {
- if(!data->state.expect100header &&
+ /* In HTTP2, we send request body in DATA frame regardless of
+ its size. */
+ if(conn->httpversion != 20 &&
+ !data->state.expect100header &&
(postsize < MAX_INITIAL_POST_SIZE)) {
/* if we don't use expect: 100 AND
postsize is less than MAX_INITIAL_POST_SIZE
if(http->writebytecount >= postsize) {
/* already sent the entire request body, mark the "upload" as
complete */
- infof(data, "upload completely sent off: %" FORMAT_OFF_T " out of "
- "%" FORMAT_OFF_T " bytes\n",
+ 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.upload_done = TRUE;
data->req.keepon &= ~KEEP_SEND; /* we're done writing */
k->header = TRUE;
k->headerline = 0; /* restart the header line counter */
- /* if we did wait for this do enable write now! */
- if(k->exp100) {
- k->exp100 = EXP100_SEND_DATA;
- k->keepon |= KEEP_SEND;
+ /* "A user agent MAY ignore unexpected 1xx status responses." */
+ switch(k->httpcode) {
+ case 100:
+ /* if we did wait for this do enable write now! */
+ if(k->exp100) {
+ k->exp100 = EXP100_SEND_DATA;
+ k->keepon |= KEEP_SEND;
+ }
+ break;
+ case 101:
+ /* Switching Protocols */
+ if(k->upgr101 == UPGR101_REQUESTED) {
+ infof(data, "Received 101\n");
+ k->upgr101 = UPGR101_RECEIVED;
+
+ /* switch to http2 now */
+ /* TODO: add error checking */
+ Curl_http2_switched(conn);
+ }
+ break;
+ default:
+ break;
}
}
else {
k->header = FALSE; /* no more header to parse! */
if((k->size == -1) && !k->chunk && !conn->bits.close &&
- (conn->httpversion >= 11) &&
+ (conn->httpversion == 11) &&
!(conn->handler->protocol & CURLPROTO_RTSP) &&
data->set.httpreq != HTTPREQ_HEAD) {
/* On HTTP 1.1, when connection is not to get closed, but no
signal the end of the document. */
infof(data, "no chunk, no close, no size. Assume close to "
"signal end\n");
- conn->bits.close = TRUE;
+ connclose(conn, "HTTP: No end-of-message indicator");
}
}
*/
if(!k->upload_done) {
infof(data, "HTTP error before end of send, stop sending\n");
- conn->bits.close = TRUE; /* close after this */
+ connclose(conn, "Stop sending data before everything sent");
k->upload_done = TRUE;
k->keepon &= ~KEEP_SEND; /* don't send */
if(data->state.expect100header)
#define HEADER1 k->p /* no conversion needed, just use k->p */
#endif /* CURL_DOES_CONVERSIONS */
- if(conn->handler->protocol & CURLPROTO_HTTP) {
+ if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
nc = sscanf(HEADER1,
" HTTP/%d.%d %3d",
&httpversion_major,
we get one of those fancy headers that tell us the
server keeps it open for us! */
infof(data, "HTTP 1.0, assume close after body\n");
- conn->bits.close = TRUE;
+ connclose(conn, "HTTP/1.0 close after body");
}
else if(conn->httpversion >= 11 &&
!conn->bits.close) {
/* Negative Content-Length is really odd, and we know it
happens for example when older Apache servers send large
files */
- conn->bits.close = TRUE;
- infof(data, "Negative content-length: %" FORMAT_OFF_T
+ connclose(conn, "negative content-length");
+ infof(data, "Negative content-length: %" CURL_FORMAT_CURL_OFF_T
", closing after transfer\n", contentlength);
}
}
/* check for Content-Type: header lines to get the MIME-type */
else if(checkprefix("Content-Type:", k->p)) {
- char *contenttype = copy_header_value(k->p);
+ char *contenttype = Curl_copy_header_value(k->p);
if(!contenttype)
return CURLE_OUT_OF_MEMORY;
if(!*contenttype)
}
}
else if(checkprefix("Server:", k->p)) {
- char *server_name = copy_header_value(k->p);
+ char *server_name = Curl_copy_header_value(k->p);
/* Turn off pipelining if the server version is blacklisted */
if(conn->bundle && conn->bundle->server_supports_pipelining) {
* connection will be kept alive for our pleasure.
* Default action for 1.0 is to close.
*/
- conn->bits.close = FALSE; /* don't close when done */
+ connkeep(conn, "Proxy-Connection keep-alive"); /* don't close */
infof(data, "HTTP/1.0 proxy connection set to keep alive!\n");
}
else if((conn->httpversion == 11) &&
* We get a HTTP/1.1 response from a proxy and it says it'll
* close down after this transfer.
*/
- conn->bits.close = TRUE; /* close when done */
+ connclose(conn, "Proxy-Connection: asked to close after done");
infof(data, "HTTP/1.1 proxy connection set close!\n");
}
else if((conn->httpversion == 10) &&
* pleasure. Default action for 1.0 is to close.
*
* [RFC2068, section 19.7.1] */
- conn->bits.close = FALSE; /* don't close when done */
+ connkeep(conn, "Connection keep-alive");
infof(data, "HTTP/1.0 connection set to keep alive!\n");
}
else if(Curl_compareheader(k->p, "Connection:", "close")) {
* the connection will close when this request has been
* served.
*/
- conn->bits.close = TRUE; /* close when done */
+ connclose(conn, "Connection: close used");
}
else if(checkprefix("Transfer-Encoding:", k->p)) {
/* One or more encodings. We check for chunked and/or a compression
}
else if(checkprefix("Content-Encoding:", k->p) &&
- data->set.str[STRING_ENCODING]) {
+ (data->set.str[STRING_ENCODING] ||
+ conn->httpversion == 20)) {
/*
* Process Content-Encoding. Look for the values: identity,
* gzip, deflate, compress, x-gzip and x-compress. x-gzip and
(401 == k->httpcode)) ||
(checkprefix("Proxy-authenticate:", k->p) &&
(407 == k->httpcode))) {
- result = Curl_http_input_auth(conn, k->httpcode, k->p);
+
+ bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
+ char *auth = Curl_copy_header_value(k->p);
+ if(!auth)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = Curl_http_input_auth(conn, proxy, auth);
+
+ Curl_safefree(auth);
+
if(result)
return result;
}
checkprefix("Location:", k->p) &&
!data->req.location) {
/* this is the URL that the server advises us to use instead */
- char *location = copy_header_value(k->p);
+ char *location = Curl_copy_header_value(k->p);
if(!location)
return CURLE_OUT_OF_MEMORY;
if(!*location)