Newer
Older
Daniel Stenberg
committed
http->postsize = http->backup.postsize;
conn->fread_func = http->backup.fread_func;
Daniel Stenberg
committed
conn->fread_in = http->backup.fread_in;
http->sending++; /* move one step up */
http->backup.postsize=0;
}
else
http->postsize = 0;
return fullsize;
}
memcpy(buffer, http->postdata, fullsize);
http->postdata += fullsize;
http->postsize -= fullsize;
return fullsize;
}
/* ------------------------------------------------------------------------- */
Daniel Stenberg
committed
/* add_buffer functions */
/*
Daniel Stenberg
committed
* Curl_add_buffer_init() sets up and returns a fine buffer struct
Daniel Stenberg
committed
Curl_send_buffer *Curl_add_buffer_init(void)
Daniel Stenberg
committed
return calloc(1, sizeof(Curl_send_buffer));
}
/*
* Curl_add_buffer_send() sends a header buffer and frees all associated
* memory. Body data may be appended to the header data if desired.
Daniel Stenberg
committed
CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
struct connectdata *conn,
/* add the number of sent bytes to this
counter */
long *bytes_written,
/* how much of the buffer contains body data */
size_t included_body_bytes,
int socketindex)
ssize_t amount;
CURLcode res;
char *ptr;
struct HTTP *http = conn->data->req.protop;
Daniel Stenberg
committed
curl_socket_t sockfd;
size_t headersize;
Daniel Stenberg
committed
DEBUGASSERT(socketindex <= SECONDARYSOCKET);
Daniel Stenberg
committed
sockfd = conn->sock[socketindex];
Daniel Stenberg
committed
/* The looping below is required since we use non-blocking sockets, but due
to the circumstances we will just loop and try again and again etc */
ptr = in->buffer;
size = in->size_used;
headersize = size - included_body_bytes; /* the initial part that isn't body
is header */
DEBUGASSERT(size > included_body_bytes);
Daniel Stenberg
committed
res = Curl_convert_to_network(conn->data, ptr, headersize);
Daniel Stenberg
committed
/* Curl_convert_to_network calls failf if unsuccessful */
Daniel Stenberg
committed
/* conversion failed, free memory and return to the caller */
if(in->buffer)
free(in->buffer);
free(in);
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
needs to fit into the normal read-callback buffer later on and that
buffer is using this size.
*/
sendsize= (size > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:size;
/* OpenSSL is very picky and we must send the SAME buffer pointer to the
library when we attempt to re-send this buffer. Sending the same data
is not enough, we must use the exact same address. For this reason, we
must copy the data to the uploadbuffer first, since that is the buffer
we will be using if this send is retried later.
*/
memcpy(conn->data->state.uploadbuffer, ptr, sendsize);
ptr = conn->data->state.uploadbuffer;
}
else
sendsize = size;
res = Curl_write(conn, sockfd, ptr, sendsize, &amount);
Daniel Stenberg
committed
if(CURLE_OK == res) {
/*
* Note that we may not send the entire chunk at once, and we have a set
* number of data bytes at the end of the big buffer (out of which we may
* only send away a part).
*/
/* how much of the header that was sent */
size_t headlen = (size_t)amount>headersize?headersize:(size_t)amount;
size_t bodylen = amount - headlen;
if(conn->data->set.verbose) {
/* this data _may_ contain binary stuff */
Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, headlen, conn);
if(bodylen) {
/* there was body data sent beyond the initial header part, pass that
on to the debug callback too */
Daniel Stenberg
committed
Curl_debug(conn->data, CURLINFO_DATA_OUT,
ptr+headlen, bodylen, conn);
}
if(bodylen)
Daniel Stenberg
committed
/* since we sent a piece of the body here, up the byte counter for it
accordingly */
http->writebytecount += bodylen;
/* 'amount' can never be a very large value here so typecasting it so a
signed 31 bit value should not cause problems even if ssize_t is
64bit */
*bytes_written += (long)amount;
if(http) {
if((size_t)amount != size) {
/* The whole request could not be sent in one system call. We must
queue it up and send it later when we get the chance. We must not
loop here and wait until it might work again. */
Daniel Stenberg
committed
ptr = in->buffer + amount;
/* backup the currently set pointers */
http->backup.fread_func = conn->fread_func;
http->backup.fread_in = conn->fread_in;
http->backup.postdata = http->postdata;
http->backup.postsize = http->postsize;
Daniel Stenberg
committed
/* set the new pointers for the request-sending */
conn->fread_func = (curl_read_callback)readmoredata;
conn->fread_in = (void *)conn;
http->postdata = ptr;
http->postsize = (curl_off_t)size;
Daniel Stenberg
committed
http->send_buffer = in;
http->sending = HTTPSEND_REQUEST;
return CURLE_OK;
}
http->sending = HTTPSEND_BODY;
/* the full buffer was sent, clean up and return */
}
else {
if((size_t)amount != size)
/* We have no continue-send mechanism now, fail. This can only happen
when this function is used from the CONNECT sending function. We
currently (stupidly) assume that the whole request is always sent
away in the first single chunk.
This needs FIXing.
*/
return CURLE_SEND_ERROR;
else
conn->writechannel_inuse = FALSE;
}
Daniel Stenberg
committed
}
if(in->buffer)
free(in->buffer);
free(in);
return res;
}
/*
* add_bufferf() add the formatted input to the buffer.
Daniel Stenberg
committed
CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...)
{
char *s;
va_list ap;
va_start(ap, fmt);
s = vaprintf(fmt, ap); /* this allocs a new string to append */
va_end(ap);
if(s) {
Daniel Stenberg
committed
CURLcode result = Curl_add_buffer(in, s, strlen(s));
free(s);
/* If we failed, we cleanup the whole buffer and return error */
if(in->buffer)
free(in->buffer);
free(in);
return CURLE_OUT_OF_MEMORY;
}
/*
* add_buffer() appends a memory chunk to the existing buffer
Daniel Stenberg
committed
CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size)
{
char *new_rb;
if(~size < in->size_used) {
/* If resulting used size of send buffer would wrap size_t, cleanup
the whole buffer and return error. Otherwise the required buffer
size will fit into a single allocatable memory chunk */
Curl_safefree(in->buffer);
free(in);
return CURLE_OUT_OF_MEMORY;
}
if(!in->buffer ||
((in->size_used + size) > (in->size_max - 1))) {
/* If current buffer size isn't enough to hold the result, use a
buffer size that doubles the required size. If this new size
would wrap size_t, then just use the largest possible one */
if((size > (size_t)-1/2) || (in->size_used > (size_t)-1/2) ||
(~(size*2) < (in->size_used*2)))
new_size = (size_t)-1;
else
new_size = (in->size_used+size)*2;
if(in->buffer)
/* we have a buffer, enlarge the existing one */
new_rb = realloc(in->buffer, new_size);
else
/* create a new buffer */
if(!new_rb) {
/* If we failed, we cleanup the whole buffer and return error */
Curl_safefree(in->buffer);
free(in);
return CURLE_OUT_OF_MEMORY;
in->buffer = new_rb;
in->size_max = new_size;
memcpy(&in->buffer[in->size_used], inptr, size);
in->size_used += size;
return CURLE_OK;
}
/* end of the add_buffer functions */
/* ------------------------------------------------------------------------- */
Daniel Stenberg
committed
/*
* Curl_compareheader()
*
* Returns TRUE if 'headerline' contains the 'header' with given 'content'.
* Pass headers WITH the colon.
*/
bool
Curl_compareheader(const char *headerline, /* line to check */
const char *header, /* header keyword _with_ colon */
const char *content) /* content string to find */
{
/* RFC2616, section 4.2 says: "Each header field consists of a name followed
* by a colon (":") and the field value. Field names are case-insensitive.
* The field value MAY be preceded by any amount of LWS, though a single SP
* is preferred." */
size_t hlen = strlen(header);
size_t clen;
size_t len;
const char *start;
const char *end;
if(!Curl_raw_nequal(headerline, header, hlen))
return FALSE; /* doesn't start with header */
/* pass the header */
start = &headerline[hlen];
/* pass all white spaces */
Daniel Stenberg
committed
while(*start && ISSPACE(*start))
start++;
/* find the end of the header line */
end = strchr(start, '\r'); /* lines end with CRLF */
if(!end) {
/* in case there's a non-standard compliant line here */
end = strchr(start, '\n');
if(!end)
/* hm, there's no line ending here, use the zero byte! */
end = strchr(start, '\0');
}
len = end-start; /* length of the content part of the input line */
clen = strlen(content); /* length of the word to find */
/* find the content string in the rest of the line */
for(;len>=clen;len--, start++) {
if(Curl_raw_nequal(start, content, clen))
return TRUE; /* match! */
}
return FALSE; /* no match */
}
* Curl_http_connect() performs HTTP stuff to do at connect-time, called from
* the generic Curl_connect().
CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
Daniel Stenberg
committed
/* 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. */
connkeep(conn, "HTTP default");
Daniel Stenberg
committed
/* the CONNECT procedure might not have been completed */
result = Curl_proxy_connect(conn);
if(result)
return result;
if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
Daniel Stenberg
committed
/* nothing else to do except wait right now - we're not done here. */
return CURLE_OK;
if(conn->given->flags & PROTOPT_SSL) {
Daniel Stenberg
committed
/* perform SSL initialization */
result = https_connecting(conn, done);
if(result)
return result;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
*done = TRUE;
Daniel Stenberg
committed
Daniel Stenberg
committed
/* this returns the socket to wait for in the DO and DOING state for the multi
interface and then we're always _sending_ a request and thus we wait for
the single socket to become writable only */
static int http_getsock_do(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
/* write mode */
(void)numsocks; /* unused, we trust it to be at least 1 */
socks[0] = conn->sock[FIRSTSOCKET];
return GETSOCK_WRITESOCK(0);
}
static CURLcode https_connecting(struct connectdata *conn, bool *done)
Daniel Stenberg
committed
{
CURLcode result;
DEBUGASSERT((conn) && (conn->handler->flags & PROTOPT_SSL));
Daniel Stenberg
committed
/* perform SSL initialization for this socket */
result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
connclose(conn, "Failed HTTPS connection");
Daniel Stenberg
committed
}
Daniel Stenberg
committed
#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. */
static int https_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
Daniel Stenberg
committed
{
if(conn->handler->flags & PROTOPT_SSL) {
Daniel Stenberg
committed
struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
Daniel Stenberg
committed
if(!numsocks)
return GETSOCK_BLANK;
Daniel Stenberg
committed
Daniel Stenberg
committed
if(connssl->connecting_state == ssl_connect_2_writing) {
Daniel Stenberg
committed
/* write mode */
Daniel Stenberg
committed
socks[0] = conn->sock[FIRSTSOCKET];
return GETSOCK_WRITESOCK(0);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
else if(connssl->connecting_state == ssl_connect_2_reading) {
Daniel Stenberg
committed
/* read mode */
Daniel Stenberg
committed
socks[0] = conn->sock[FIRSTSOCKET];
return GETSOCK_READSOCK(0);
Daniel Stenberg
committed
}
}
return CURLE_OK;
}
Daniel Stenberg
committed
#else
#ifdef USE_SSL
static int https_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
(void)conn;
(void)socks;
(void)numsocks;
return GETSOCK_BLANK;
}
#endif /* USE_SSL */
#endif /* USE_SSLEAY || USE_GNUTLS || USE_SCHANNEL */
Daniel Stenberg
committed
/*
* Curl_http_done() gets called from Curl_done() after a single HTTP request
* has been performed.
*/
CURLcode Curl_http_done(struct connectdata *conn,
Daniel Stenberg
committed
CURLcode status, bool premature)
struct SessionHandle *data = conn->data;
struct HTTP *http =data->req.protop;
Daniel Stenberg
committed
Curl_unencode_cleanup(conn);
#ifdef USE_HTTP_NEGOTIATE
if(data->state.proxyneg.state == GSS_AUTHSENT ||
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 */
Daniel Stenberg
committed
conn->seek_func = data->set.seek_func; /* restore */
conn->seek_client = data->set.seek_client; /* restore */
Daniel Stenberg
committed
if(http == NULL)
return CURLE_OK;
Daniel Stenberg
committed
if(http->send_buffer) {
Daniel Stenberg
committed
Curl_send_buffer *buff = http->send_buffer;
Daniel Stenberg
committed
free(buff->buffer);
free(buff);
http->send_buffer = NULL; /* clear the pointer */
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if(HTTPREQ_POST_FORM == data->set.httpreq) {
Daniel Stenberg
committed
data->req.bytecount = http->readbytecount + http->writebytecount;
Curl_formclean(&http->sendit); /* Now free that whole lot */
/* a file being uploaded was left opened, close it! */
fclose(http->form.fp);
http->form.fp = NULL;
}
else if(HTTPREQ_PUT == data->set.httpreq)
Daniel Stenberg
committed
data->req.bytecount = http->readbytecount + http->writebytecount;
Daniel Stenberg
committed
if(status != CURLE_OK)
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 +
Daniel Stenberg
committed
data->req.headerbytecount -
data->req.deductheadercount)) <= 0) {
/* If this connection isn't simply closed to be retried, AND nothing was
read from the HTTP server (that counts), this can't be right so we
return an error here */
failf(data, "Empty reply from server");
Daniel Stenberg
committed
return CURLE_GOT_NOTHING;
}
/*
* 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) ||
((data->set.httpversion != CURL_HTTP_VERSION_1_0) &&
((conn->httpversion == 11) ||
((conn->httpversion != 10) &&
(data->state.httpversion != 10))))) ? TRUE : FALSE;
}
/* check and possibly add an Expect: header */
static CURLcode expect100(struct SessionHandle *data,
struct connectdata *conn,
Daniel Stenberg
committed
Curl_send_buffer *req_buffer)
{
CURLcode result = CURLE_OK;
Daniel Stenberg
committed
const char *ptr;
Daniel Stenberg
committed
data->state.expect100header = FALSE; /* default to false unless it is set
to TRUE below */
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:
Daniel Stenberg
committed
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:");
Daniel Stenberg
committed
data->state.expect100header =
Curl_compareheader(ptr, "Expect:", "100-continue");
}
else {
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer,
"Expect: 100-continue\r\n");
Daniel Stenberg
committed
if(result == CURLE_OK)
data->state.expect100header = TRUE;
}
}
return result;
}
enum proxy_use {
HEADER_SERVER, /* direct to server */
HEADER_PROXY, /* regular request to proxy */
HEADER_CONNECT /* sending CONNECT to a proxy */
};
Daniel Stenberg
committed
CURLcode Curl_add_custom_headers(struct connectdata *conn,
bool is_connect,
Curl_send_buffer *req_buffer)
struct curl_slist *h[2];
struct curl_slist *headers;
int numlists=1; /* by default */
struct SessionHandle *data = conn->data;
int i;
enum proxy_use proxy;
if(is_connect)
proxy = HEADER_CONNECT;
else
proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy?
HEADER_PROXY:HEADER_SERVER;
switch(proxy) {
case HEADER_SERVER:
h[0] = data->set.headers;
break;
case HEADER_PROXY:
h[0] = data->set.headers;
if(data->set.sep_headers) {
h[1] = data->set.proxyheaders;
numlists++;
break;
case HEADER_CONNECT:
if(data->set.sep_headers)
h[0] = data->set.proxyheaders;
else
h[0] = data->set.headers;
break;
}
/* loop through one or two lists */
for(i=0; i < numlists; i++) {
headers = h[i];
while(headers) {
ptr = strchr(headers->data, ':');
/* we require a colon for this to be a true header */
ptr++; /* pass the colon */
while(*ptr && ISSPACE(*ptr))
ptr++;
if(*ptr) {
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
/* 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;
}
}
}
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
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;
Daniel Stenberg
committed
CURLcode Curl_add_timecondition(struct SessionHandle *data,
Curl_send_buffer *req_buffer)
{
Daniel Stenberg
committed
char *buf = data->state.buffer;
CURLcode result = CURLE_OK;
struct tm keeptime;
result = Curl_gmtime(data->set.timevalue, &keeptime);
if(result) {
return result;
}
tm = &keeptime;
Daniel Stenberg
committed
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
/* The If-Modified-Since header family should have their times set in
* GMT as RFC2616 defines: "All HTTP date/time stamps MUST be
* represented in Greenwich Mean Time (GMT), without exception. For the
* purposes of HTTP, GMT is exactly equal to UTC (Coordinated Universal
* Time)." (see page 20 of RFC2616).
*/
/* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
snprintf(buf, BUFSIZE-1,
"%s, %02d %s %4d %02d:%02d:%02d GMT",
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);
switch(data->set.timecondition) {
case CURL_TIMECOND_IFMODSINCE:
default:
result = Curl_add_bufferf(req_buffer,
"If-Modified-Since: %s\r\n", buf);
break;
case CURL_TIMECOND_IFUNMODSINCE:
result = Curl_add_bufferf(req_buffer,
"If-Unmodified-Since: %s\r\n", buf);
break;
case CURL_TIMECOND_LASTMOD:
result = Curl_add_bufferf(req_buffer,
"Last-Modified: %s\r\n", buf);
break;
}
return result;
}
/*
* Curl_http() gets called from the generic Curl_do() function when a HTTP
* request is to be performed. This creates and sends a properly constructed
CURLcode Curl_http(struct connectdata *conn, bool *done)
Daniel Stenberg
committed
struct SessionHandle *data=conn->data;
CURLcode result=CURLE_OK;
const char *ppath = data->state.path;
bool paste_ftp_userpwd = FALSE;
char ftp_typecode[sizeof("/;type=?")] = "";
const char *host = conn->host.name;
const char *request;
Daniel Stenberg
committed
Curl_HttpReq httpreq = data->set.httpreq;
char *addcookies = NULL;
Daniel Stenberg
committed
const char *httpstring;
Daniel Stenberg
committed
Curl_send_buffer *req_buffer;
curl_off_t postsize = 0; /* curl_off_t to handle large file sizes */
Daniel Stenberg
committed
int seekerr = CURL_SEEKFUNC_OK;
/* Always consider the DO phase done after this function call, even if there
may be parts of the request that is not yet sent, since we can deal with
the rest of the request in the PERFORM phase. */
*done = TRUE;
if(conn->httpversion < 20) { /* unless the connection is re-used and already
http2 */
switch (conn->negnpn) {
result = Curl_http2_init(conn);
if(result)
return result;
result = Curl_http2_setup(conn);
if(result)
return result;
/* 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;
/* prepare for a http2 request */
Curl_http2_setup(conn);
http = data->req.protop;
if(!data->state.this_is_a_follow) {
/* 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)
return CURLE_OUT_OF_MEMORY;
}
http->writebytecount = http->readbytecount = 0;
if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) &&
data->set.upload) {
Daniel Stenberg
committed
httpreq = HTTPREQ_PUT;
Daniel Stenberg
committed
/* Now set the 'request' pointer to the proper request string */
Daniel Stenberg
committed
if(data->set.str[STRING_CUSTOMREQUEST])
request = data->set.str[STRING_CUSTOMREQUEST];
Daniel Stenberg
committed
else {
Daniel Stenberg
committed
if(data->set.opt_no_body)
Daniel Stenberg
committed
else {
DEBUGASSERT((httpreq > HTTPREQ_NONE) && (httpreq < HTTPREQ_LAST));
Daniel Stenberg
committed
switch(httpreq) {
case HTTPREQ_POST:
case HTTPREQ_POST_FORM:
Daniel Stenberg
committed
break;
case HTTPREQ_PUT:
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
default: /* this should never happen */
Daniel Stenberg
committed
case HTTPREQ_GET:
Daniel Stenberg
committed
break;
case HTTPREQ_HEAD:
Daniel Stenberg
committed
break;
}
}
}
/* The User-Agent string might have been allocated in url.c already, because
it might have been used in the proxy connect, but if we have got a header
with the user-agent string specified, we erase the previously made string
here. */
if(Curl_checkheaders(conn, "User-Agent:") && conn->allocptr.uagent) {
free(conn->allocptr.uagent);
conn->allocptr.uagent=NULL;
/* setup the authentication headers */
result = Curl_http_output_auth(conn, request, ppath, FALSE);
if(result)
return result;
if((data->state.authhost.multi || data->state.authproxy.multi) &&
(httpreq != HTTPREQ_GET) &&
(httpreq != HTTPREQ_HEAD)) {
/* Auth is required and we are not authenticated yet. Make a PUT or POST
with content-length zero as a "probe". */
conn->bits.authneg = TRUE;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
else
conn->bits.authneg = FALSE;
Daniel Stenberg
committed
Daniel Stenberg
committed
Curl_safefree(conn->allocptr.ref);
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;
}
Daniel Stenberg
committed
else
conn->allocptr.ref = NULL;
if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(conn, "Cookie:"))
Daniel Stenberg
committed
addcookies = data->set.str[STRING_COOKIE];
if(!Curl_checkheaders(conn, "Accept-Encoding:") &&
Daniel Stenberg
committed
data->set.str[STRING_ENCODING]) {
Curl_safefree(conn->allocptr.accept_encoding);
conn->allocptr.accept_encoding =
Daniel Stenberg
committed
aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
if(!conn->allocptr.accept_encoding)
return CURLE_OUT_OF_MEMORY;
}
#ifdef HAVE_LIBZ
/* we only consider transfer-encoding magic if libz support is built-in */
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(conn, "Connection:");
#define TE_HEADER "TE: gzip\r\n"
Curl_safefree(conn->allocptr.te);
/* Create the (updated) Connection: header */
conn->allocptr.te = cptr? aprintf("%s, TE\r\n" TE_HEADER, cptr):
strdup("Connection: TE\r\n" TE_HEADER);
if(!conn->allocptr.te)
return CURLE_OUT_OF_MEMORY;
}
if(conn->httpversion == 20)
/* In HTTP2 forbids Transfer-Encoding: chunked */
ptr = NULL;
else {
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->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;
}
Daniel Stenberg
committed
}
else {
/* else, no chunky upload */
data->req.upload_chunky = FALSE;
}
Daniel Stenberg
committed
if(data->req.upload_chunky)
te = "Transfer-Encoding: chunked\r\n";
}
Daniel Stenberg
committed
}
Daniel Stenberg
committed
Curl_safefree(conn->allocptr.host);
ptr = Curl_checkheaders(conn, "Host:");
Daniel Stenberg
committed
if(ptr && (!data->state.this_is_a_follow ||
Daniel Stenberg
committed
Curl_raw_equal(data->state.first_host, conn->host.name))) {
#if !defined(CURL_DISABLE_COOKIES)
/* If we have a given custom Host: header, we extract the host name in
order to possibly use it for cookie reasons later on. We only allow the
custom Host: header if this is NOT a redirect, as setting Host: in the
Daniel Stenberg
committed
redirected request is being out on thin ice. Except if the host name
is the same as the first one! */
char *cookiehost = Curl_copy_header_value(ptr);
free(cookiehost);
/* If the host begins with '[', we start searching for the port after
the bracket has been closed */
int startsearch = 0;
if(*cookiehost == '[') {
char *closingbracket;
/* since the 'cookiehost' is an allocated memory area that will be
freed later we cannot simply increment the pointer */
memmove(cookiehost, cookiehost + 1, strlen(cookiehost) - 1);
closingbracket = strchr(cookiehost, ']');
if(closingbracket)
*closingbracket = 0;
}
else {
char *colon = strchr(cookiehost + startsearch, ':');
if(colon)
*colon = 0; /* The host must not include an embedded port number */
}
Curl_safefree(conn->allocptr.cookiehost);
conn->allocptr.cookiehost = cookiehost;
Daniel Stenberg
committed
conn->allocptr.host = NULL;
}
else {
/* When building Host: headers, we must put the host name within
[brackets] if the host name is a plain IPv6-address. RFC2732-style. */
if(((conn->given->protocol&CURLPROTO_HTTPS) &&
(conn->remote_port == PORT_HTTPS)) ||
((conn->given->protocol&CURLPROTO_HTTP) &&
(conn->remote_port == PORT_HTTP)) )
/* if(HTTPS on port 443) OR (HTTP on port 80) then don't include
the port number in the host string */
conn->allocptr.host = aprintf("Host: %s%s%s\r\n",
conn->bits.ipv6_ip?"[":"",
host,
conn->bits.ipv6_ip?"]":"");
conn->bits.ipv6_ip?"[":"",
host,
conn->bits.ipv6_ip?"]":"",
conn->remote_port);
if(!conn->allocptr.host)
/* without Host: we can't make a nice request */
return CURLE_OUT_OF_MEMORY;
#ifndef CURL_DISABLE_PROXY
Daniel Stenberg
committed
if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
/* Using a proxy but does not tunnel through it */