* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2015, 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 "share.h"
#include "hostip.h"
#include "http.h"
+#include "curl_memory.h"
#include "select.h"
#include "parsedate.h" /* for the week day and month names */
#include "strtoofft.h"
#include "http_proxy.h"
#include "warnless.h"
#include "non-ascii.h"
-#include "conncache.h"
+#include "bundles.h"
#include "pipeline.h"
#include "http2.h"
#include "connect.h"
-#include "curl_printf.h"
-/* The last #include files should be: */
-#include "curl_memory.h"
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
#include "memdebug.h"
/*
{
/* allocate the HTTP-specific struct for the SessionHandle, only to survive
during this request */
- struct HTTP *http;
DEBUGASSERT(conn->data->req.protop == NULL);
- http = calloc(1, sizeof(struct HTTP));
- if(!http)
+ conn->data->req.protop = calloc(1, sizeof(struct HTTP));
+ if(!conn->data->req.protop)
return CURLE_OUT_OF_MEMORY;
- conn->data->req.protop = http;
-
- Curl_http2_setup_conn(conn);
- Curl_http2_setup_req(conn->data);
-
return CURLE_OK;
}
if(!authorization)
return CURLE_REMOTE_ACCESS_DENIED;
- free(*userp);
+ Curl_safefree(*userp);
*userp = aprintf("%sAuthorization: Basic %s\r\n",
proxy?"Proxy-":"",
authorization);
return picked;
}
+/* whether to complete request (for authentication) in current connection */
+static bool complete_request(struct connectdata *conn,
+ curl_off_t remaining_bytes)
+{
+#if defined(USE_NTLM) || defined(USE_SPNEGO)
+ struct SessionHandle *data = conn->data;
+ bool have_ntlm_or_negotiate = FALSE;
+ bool auth_started = FALSE;
+
+ /* don't reset connection when we're in NTLM or Negotiate authentication;
+ * those authenticate the connection - creating a new connection breaks the
+ * authentication.
+ */
+
+#if defined(USE_NTLM)
+ /* proxy NTLM authentication */
+ if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
+ (data->state.authproxy.picked == CURLAUTH_NTLM_WB)) {
+ have_ntlm_or_negotiate = TRUE;
+ auth_started = auth_started
+ || (conn->proxyntlm.state != NTLMSTATE_NONE);
+ }
+
+ /* normal NTLM authentication */
+ if((data->state.authhost.picked == CURLAUTH_NTLM) ||
+ (data->state.authhost.picked == CURLAUTH_NTLM_WB)) {
+ have_ntlm_or_negotiate = TRUE;
+ auth_started = auth_started
+ || (conn->ntlm.state != NTLMSTATE_NONE);
+ }
+#endif
+
+#if defined(USE_SPNEGO)
+ /* proxy Negotiate authentication */
+ if(data->state.authproxy.picked == CURLAUTH_NEGOTIATE) {
+ have_ntlm_or_negotiate = TRUE;
+ auth_started = auth_started
+ || (data->state.proxyneg.state != GSS_AUTHNONE);
+ }
+
+ /* normal Negotiate authentication */
+ if(data->state.authhost.picked == CURLAUTH_NEGOTIATE) {
+ have_ntlm_or_negotiate = TRUE;
+ auth_started = auth_started
+ || (data->state.negotiate.state != GSS_AUTHNONE);
+ }
+#endif
+
+ if(have_ntlm_or_negotiate) {
+ if(remaining_bytes < 2000 || auth_started) {
+ /* NTLM/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->bits.rewindaftersend = TRUE;
+ infof(data, "Rewind stream after send\n");
+ }
+
+ return TRUE;
+ }
+
+ infof(data, "NTLM/Negotiate send, close instead of sending %"
+ CURL_FORMAT_CURL_OFF_T " bytes\n",
+ remaining_bytes);
+ }
+#else
+ /* unused parameters: */
+ (void)conn;
+ (void)remaining_bytes;
+#endif
+
+ return FALSE;
+}
+
/*
* Curl_http_perhapsrewind()
*
/* figure out how much data we are expected to send */
switch(data->set.httpreq) {
case HTTPREQ_POST:
- if(data->state.infilesize != -1)
- expectsend = data->state.infilesize;
+ if(data->set.postfieldsize != -1)
+ expectsend = data->set.postfieldsize;
else if(data->set.postfields)
expectsend = (curl_off_t)strlen(data->set.postfields);
break;
conn->bits.rewindaftersend = FALSE; /* default */
if((expectsend == -1) || (expectsend > bytessent)) {
-#if defined(USE_NTLM)
- /* There is still data left to send */
- if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
- (data->state.authhost.picked == CURLAUTH_NTLM) ||
- (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)) {
- /* The NTLM-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->bits.rewindaftersend = TRUE;
- infof(data, "Rewind stream after send\n");
- }
+ if(conn->bits.close)
+ /* this is already marked to get closed */
+ return CURLE_OK;
- return CURLE_OK;
- }
-
- if(conn->bits.close)
- /* this is already marked to get closed */
- return CURLE_OK;
-
- infof(data, "NTLM send, close instead of sending %"
- CURL_FORMAT_CURL_OFF_T " bytes\n",
- (curl_off_t)(expectsend - bytessent));
- }
-#endif
+ if(complete_request(conn, (curl_off_t)(expectsend - bytessent)))
+ return CURLE_OK;
/* This is not NTLM or many bytes left to send: close */
connclose(conn, "Mid-auth HTTP and much data left to send");
}
if(bytessent)
- /* we rewind now at once since if we already sent something */
+ /* we rewind now at once since we already sent something */
return Curl_readrewind(conn);
return CURLE_OK;
if((conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
conn->bits.user_passwd)
- /* continue please */;
+ /* continue please */ ;
else {
authhost->done = TRUE;
authproxy->done = TRUE;
while(*auth) {
#ifdef USE_SPNEGO
if(checkprefix("Negotiate", auth)) {
+ int neg;
*availp |= CURLAUTH_NEGOTIATE;
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) {
+ neg = Curl_input_negotiate(conn, proxy, auth);
+ if(neg == 0) {
DEBUGASSERT(!data->req.newurl);
data->req.newurl = strdup(data->change.url);
if(!data->req.newurl)
/* move backup data into focus and continue on that */
http->postdata = http->backup.postdata;
http->postsize = http->backup.postsize;
- conn->data->set.fread_func = http->backup.fread_func;
- conn->data->set.in = http->backup.fread_in;
+ conn->fread_func = http->backup.fread_func;
+ conn->fread_in = http->backup.fread_in;
http->sending++; /* move one step up */
}
/*
- * Curl_add_buffer_free() frees all associated resources.
- */
-void Curl_add_buffer_free(Curl_send_buffer *buff)
-{
- if(buff) /* deal with NULL input */
- free(buff->buffer);
- free(buff);
-}
-
-/*
* Curl_add_buffer_send() sends a header buffer and frees all associated
* memory. Body data may be appended to the header data if desired.
*
/* Curl_convert_to_network calls failf if unsuccessful */
if(result) {
/* conversion failed, free memory and return to the caller */
- Curl_add_buffer_free(in);
+ if(in->buffer)
+ free(in->buffer);
+ free(in);
return result;
}
ptr = in->buffer + amount;
/* backup the currently set pointers */
- http->backup.fread_func = conn->data->set.fread_func;
- http->backup.fread_in = conn->data->set.in;
+ http->backup.fread_func = conn->fread_func;
+ http->backup.fread_in = conn->fread_in;
http->backup.postdata = http->postdata;
http->backup.postsize = http->postsize;
/* set the new pointers for the request-sending */
- conn->data->set.fread_func = (curl_read_callback)readmoredata;
- conn->data->set.in = (void *)conn;
+ conn->fread_func = (curl_read_callback)readmoredata;
+ conn->fread_in = (void *)conn;
http->postdata = ptr;
http->postsize = (curl_off_t)size;
*/
return CURLE_SEND_ERROR;
else
- Curl_pipeline_leave_write(conn);
+ conn->writechannel_inuse = FALSE;
}
}
- Curl_add_buffer_free(in);
+ if(in->buffer)
+ free(in->buffer);
+ free(in);
return result;
}
return result;
}
/* If we failed, we cleanup the whole buffer and return error */
- free(in->buffer);
+ if(in->buffer)
+ free(in->buffer);
free(in);
return CURLE_OUT_OF_MEMORY;
}
}
#endif
-#if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \
+#if defined(USE_SSLEAY) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \
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. */
return GETSOCK_BLANK;
}
#endif /* USE_SSL */
-#endif /* USE_OPENSSL || USE_GNUTLS || USE_SCHANNEL */
+#endif /* USE_SSLEAY || USE_GNUTLS || USE_SCHANNEL */
/*
* Curl_http_done() gets called from Curl_done() after a single HTTP request
CURLcode status, bool premature)
{
struct SessionHandle *data = conn->data;
- struct HTTP *http = data->req.protop;
-#ifdef USE_NGHTTP2
- struct http_conn *httpc = &conn->proto.httpc;
-#endif
+ struct HTTP *http =data->req.protop;
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 */
- if((data->req.httpcode != 401) && (data->req.httpcode != 407))
- connclose(conn, "Negotiate transfer completed");
+ data->state.negotiate.state == GSS_AUTHSENT)
Curl_cleanup_negotiate(data);
- }
#endif
/* set the proper values (possibly modified on POST) */
+ conn->fread_func = data->set.fread_func; /* restore */
+ conn->fread_in = data->set.in; /* restore */
conn->seek_func = data->set.seek_func; /* restore */
conn->seek_client = data->set.seek_client; /* restore */
return CURLE_OK;
if(http->send_buffer) {
- Curl_add_buffer_free(http->send_buffer);
- http->send_buffer = NULL; /* clear the pointer */
- }
+ Curl_send_buffer *buff = http->send_buffer;
-#ifdef USE_NGHTTP2
- if(http->header_recvbuf) {
- DEBUGF(infof(data, "free header_recvbuf!!\n"));
- Curl_add_buffer_free(http->header_recvbuf);
- http->header_recvbuf = NULL; /* clear the pointer */
- for(; http->push_headers_used > 0; --http->push_headers_used) {
- free(http->push_headers[http->push_headers_used - 1]);
- }
- free(http->push_headers);
- http->push_headers = NULL;
- }
- if(http->stream_id) {
- nghttp2_session_set_stream_user_data(httpc->h2, http->stream_id, 0);
- http->stream_id = 0;
+ free(buff->buffer);
+ free(buff);
+ http->send_buffer = NULL; /* clear the pointer */
}
-#endif
if(HTTPREQ_POST_FORM == data->set.httpreq) {
data->req.bytecount = http->readbytecount + http->writebytecount;
const char *ptr;
data->state.expect100header = FALSE; /* default to false unless it is set
to TRUE below */
- if(use_http_1_1plus(data, conn) &&
- (conn->httpversion != 20)) {
- /* 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) */
+ 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(conn, "Expect:");
if(ptr) {
data->state.expect100header =
if(conn->httpversion < 20) { /* unless the connection is re-used and already
http2 */
switch(conn->negnpn) {
- case CURL_HTTP_VERSION_2_0:
- conn->httpversion = 20; /* we know we're on HTTP/2 now */
+ case NPN_HTTP2:
result = Curl_http2_init(conn);
if(result)
return result;
if(result)
return result;
break;
- case CURL_HTTP_VERSION_1_1:
+ case NPN_HTTP1_1:
/* continue with HTTP/1.1 when explicitly requested */
break;
default:
http = data->req.protop;
if(!data->state.this_is_a_follow) {
- /* Free to avoid leaking memory on multiple requests*/
- free(data->state.first_host);
+ /* this is not a followed location, get the original host name */
+ if(data->state.first_host)
+ /* Free to avoid leaking memory on multiple requests*/
+ free(data->state.first_host);
data->state.first_host = strdup(conn->host.name);
if(!data->state.first_host)
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(conn, "User-Agent:")) {
+ if(Curl_checkheaders(conn, "User-Agent:") && conn->allocptr.uagent) {
free(conn->allocptr.uagent);
conn->allocptr.uagent=NULL;
}
}
#endif
- if(strcmp("Host:", ptr)) {
- conn->allocptr.host = aprintf("%s\r\n", ptr);
- if(!conn->allocptr.host)
- return CURLE_OUT_OF_MEMORY;
- }
- else
- /* when clearing the header */
- conn->allocptr.host = NULL;
+ conn->allocptr.host = NULL;
}
else {
/* When building Host: headers, we must put the host name within
if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
!Curl_checkheaders(conn, "Range:")) {
/* if a line like this was already allocated, free the previous one */
- free(conn->allocptr.rangeline);
+ if(conn->allocptr.rangeline)
+ free(conn->allocptr.rangeline);
conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n",
data->state.range);
}
!Curl_checkheaders(conn, "Content-Range:")) {
/* if a line like this was already allocated, free the previous one */
- free(conn->allocptr.rangeline);
+ if(conn->allocptr.rangeline)
+ free(conn->allocptr.rangeline);
if(data->set.set_resume_from < 0) {
/* Upload resume was asked for, but we don't know the size of the
Curl_add_bufferf(req_buffer,
"%s" /* ftp typecode (;type=x) */
" HTTP/%s\r\n" /* HTTP version */
- "%s" /* host */
"%s" /* proxyuserpwd */
"%s" /* userpwd */
"%s" /* range */
"%s" /* user agent */
+ "%s" /* host */
"%s" /* accept */
"%s" /* TE: */
"%s" /* accept-encoding */
ftp_typecode,
httpstring,
- (conn->allocptr.host?conn->allocptr.host:""),
conn->allocptr.proxyuserpwd?
conn->allocptr.proxyuserpwd:"",
conn->allocptr.userpwd?conn->allocptr.userpwd:"",
*data->set.str[STRING_USERAGENT] &&
conn->allocptr.uagent)?
conn->allocptr.uagent:"",
+ (conn->allocptr.host?conn->allocptr.host:""),
http->p_accept?http->p_accept:"",
conn->allocptr.te?conn->allocptr.te:"",
(data->set.str[STRING_ENCODING] &&
te
);
- /* clear userpwd to avoid re-using credentials from re-used connections */
- Curl_safefree(conn->allocptr.userpwd);
+ /*
+ * Free userpwd for Negotiate/NTLM. Cannot reuse as it is associated with
+ * the connection and shouldn't be repeated over it either.
+ */
+ switch (data->state.authhost.picked) {
+ case CURLAUTH_NEGOTIATE:
+ case CURLAUTH_NTLM:
+ case CURLAUTH_NTLM_WB:
+ Curl_safefree(conn->allocptr.userpwd);
+ break;
+ }
/*
- * Free proxyuserpwd for Negotiate/NTLM. Cannot reuse as it is associated
- * with the connection and shouldn't be repeated over it either.
+ * Same for proxyuserpwd
*/
switch (data->state.authproxy.picked) {
case CURLAUTH_NEGOTIATE:
/* Get the currently set callback function pointer and store that in the
form struct since we might want the actual user-provided callback later
- on. The data->set.fread_func pointer itself will be changed for the
+ on. The conn->fread_func pointer itself will be changed for the
multipart case to the function that returns a multipart formatted
stream. */
- http->form.fread_func = data->set.fread_func;
+ http->form.fread_func = conn->fread_func;
/* Set the read function to read from the generated form data */
- data->set.fread_func = (curl_read_callback)Curl_FormReader;
- data->set.in = &http->form;
+ conn->fread_func = (curl_read_callback)Curl_FormReader;
+ conn->fread_in = &http->form;
http->sending = HTTPSEND_BODY;
postsize = 0;
else {
/* figure out the size of the postfields */
- postsize = (data->state.infilesize != -1)?
- data->state.infilesize:
+ postsize = (data->set.postfieldsize != -1)?
+ data->set.postfieldsize:
(data->set.postfields? (curl_off_t)strlen(data->set.postfields):-1);
}
http->sending = HTTPSEND_BODY;
- data->set.fread_func = (curl_read_callback)readmoredata;
- data->set.in = (void *)conn;
+ conn->fread_func = (curl_read_callback)readmoredata;
+ conn->fread_in = (void *)conn;
/* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, http->postsize);
return result;
}
- else if(data->state.infilesize) {
+ else if(data->set.postfieldsize) {
/* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, postsize?postsize:-1);
}
}
- /* At this point we have some idea about the fate of the connection.
- If we are closing the connection it may result auth failure. */
-#if defined(USE_NTLM)
- if(conn->bits.close &&
- (((data->req.httpcode == 401) &&
- (conn->ntlm.state == NTLMSTATE_TYPE2)) ||
- ((data->req.httpcode == 407) &&
- (conn->proxyntlm.state == NTLMSTATE_TYPE2)))) {
- infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n");
- data->state.authproblem = TRUE;
- }
-#endif
-
/*
* When all the headers have been parsed, see if we should give
* up and return an error.
}
else if(conn->httpversion == 20 ||
(k->upgr101 == UPGR101_REQUESTED && k->httpcode == 101)) {
- DEBUGF(infof(data, "HTTP/2 found, allow multiplexing\n"));
-
- /* HTTP/2 cannot blacklist multiplexing since it is a core
- functionality of the protocol */
- conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ /* Don't enable pipelining for HTTP/2 or upgraded connection. For
+ HTTP/2, we do not support multiplexing. In general, requests
+ cannot be pipelined in upgraded connection, since it is now
+ different protocol. */
+ DEBUGF(infof(data,
+ "HTTP 2 or upgraded connection do not support "
+ "pipelining for now\n"));
}
else if(conn->httpversion >= 11 &&
!conn->bits.close) {
+ struct connectbundle *cb_ptr;
+
/* If HTTP version is >= 1.1 and connection is persistent
server supports pipelining. */
DEBUGF(infof(data,
"HTTP 1.1 or later with persistent connection, "
"pipelining supported\n"));
/* Activate pipelining if needed */
- if(conn->bundle) {
+ cb_ptr = conn->bundle;
+ if(cb_ptr) {
if(!Curl_pipeline_site_blacklisted(data, conn))
- conn->bundle->multiuse = BUNDLE_PIPELINING;
+ cb_ptr->server_supports_pipelining = TRUE;
}
}
}
}
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);
+ 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) {
+ if(Curl_pipeline_server_blacklisted(data, server_name))
+ conn->bundle->server_supports_pipelining = FALSE;
}
+ Curl_safefree(server_name);
}
else if((conn->httpversion == 10) &&
conn->bits.httpproxy &&
k->auto_decoding = GZIP;
start += 6;
}
+ else if(checkprefix("compress", start)) {
+ k->auto_decoding = COMPRESS;
+ start += 8;
+ }
+ else if(checkprefix("x-compress", start)) {
+ k->auto_decoding = COMPRESS;
+ start += 10;
+ }
else
/* unknown! */
break;
else if(checkprefix("gzip", start)
|| checkprefix("x-gzip", start))
k->auto_decoding = GZIP;
+ else if(checkprefix("compress", start)
+ || checkprefix("x-compress", start))
+ k->auto_decoding = COMPRESS;
}
else if(checkprefix("Content-Range:", k->p)) {
/* Content-Range: bytes [num]-
result = Curl_http_input_auth(conn, proxy, auth);
- free(auth);
+ Curl_safefree(auth);
if(result)
return result;