Newer
Older
ptr = strchr(headers->data, ':');
if(ptr) {
/* we require a colon for this to be a true header */
ptr++; /* pass the colon */
Daniel Stenberg
committed
while(*ptr && ISSPACE(*ptr))
ptr++;
if(*ptr) {
/* 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(conn->data->set.httpreq == HTTPREQ_POST_FORM &&
/* this header (extended by formdata.c) is sent later */
checkprefix("Content-Type:", headers->data))
;
Daniel Stenberg
committed
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))
;
Daniel Stenberg
committed
CURLcode result = Curl_add_bufferf(req_buffer, "%s\r\n",
headers->data);
if(result)
return result;
}
}
}
headers = headers->next;
}
Daniel Stenberg
committed
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
CURLcode Curl_add_timecondition(struct SessionHandle *data,
Curl_send_buffer *req_buffer)
{
struct tm *tm;
char *buf = data->state.buffer;
CURLcode result = CURLE_OK;
/* 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).
*/
#ifdef HAVE_GMTIME_R
/* thread-safe version */
struct tm keeptime;
tm = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime);
#else
tm = gmtime(&data->set.timevalue);
#endif
/* 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;
Daniel Stenberg
committed
curl_off_t postsize; /* off_t type to be able to hold a large file size */
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 there already is a protocol-specific struct allocated for this
sessionhandle, deal with it */
Curl_reset_reqproto(conn);
Daniel Stenberg
committed
if(!data->state.proto.http) {
/* Only allocate this struct if we don't already have it! */
http = calloc(1, sizeof(struct HTTP));
if(!http)
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
data->state.proto.http = http;
Daniel Stenberg
committed
http = data->state.proto.http;
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;
}
Daniel Stenberg
committed
if( (conn->protocol&(PROT_HTTP|PROT_FTP)) &&
Daniel Stenberg
committed
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;
}
}
}
Daniel Stenberg
committed
/* 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
Daniel Stenberg
committed
if(Curl_checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
free(conn->allocptr.uagent);
conn->allocptr.uagent=NULL;
/* setup the authentication headers */
result = 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);
Daniel Stenberg
committed
if(data->change.referer && !Curl_checkheaders(data, "Referer:"))
conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
Daniel Stenberg
committed
else
conn->allocptr.ref = NULL;
Daniel Stenberg
committed
if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(data, "Cookie:"))
Daniel Stenberg
committed
addcookies = data->set.str[STRING_COOKIE];
Daniel Stenberg
committed
if(!Curl_checkheaders(data, "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;
}
Daniel Stenberg
committed
ptr = Curl_checkheaders(data, "Transfer-Encoding:");
if(ptr) {
/* Some kind of TE is requested, check if 'chunked' is chosen */
Daniel Stenberg
committed
data->req.upload_chunky =
Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
}
else {
Daniel Stenberg
committed
if((conn->protocol&PROT_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;
Daniel Stenberg
committed
}
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;
}
if(data->req.upload_chunky)
Daniel Stenberg
committed
te = "Transfer-Encoding: chunked\r\n";
}
Daniel Stenberg
committed
Curl_safefree(conn->allocptr.host);
Daniel Stenberg
committed
ptr = Curl_checkheaders(data, "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);
if (!cookiehost)
return CURLE_OUT_OF_MEMORY;
if (!*cookiehost)
/* ignore empty data */
free(cookiehost);
else {
char *colon = strchr(cookiehost, ':');
if (colon)
*colon = 0; /* The host must not include an embedded port number */
Daniel Stenberg
committed
Curl_safefree(conn->allocptr.cookiehost);
conn->allocptr.cookiehost = cookiehost;
#endif
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->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTPS)) ||
(!(conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTP)) )
Daniel Stenberg
committed
/* if(HTTPS on port 443) OR (non-HTTPS 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 */
Daniel Stenberg
committed
/* The path sent to the proxy is in fact the entire URL. But if the remote
host is a IDN-name, we must make sure that the request we produce only
uses the encoded host name! */
if(conn->host.dispname != conn->host.name) {
char *url = data->change.url;
ptr = strstr(url, conn->host.dispname);
Daniel Stenberg
committed
if(ptr) {
/* This is where the display name starts in the URL, now replace this
part with the encoded name. TODO: This method of replacing the host
name is rather crude as I believe there's a slight risk that the
user has entered a user name or password that contain the host name
string. */
size_t currlen = strlen(conn->host.dispname);
size_t newlen = strlen(conn->host.name);
size_t urllen = strlen(url);
Daniel Stenberg
committed
char *newurl;
Daniel Stenberg
committed
newurl = malloc(urllen + newlen - currlen + 1);
if(newurl) {
/* copy the part before the host name */
memcpy(newurl, url, ptr - url);
/* append the new host name instead of the old */
memcpy(newurl + (ptr - url), conn->host.name, newlen);
/* append the piece after the host name */
memcpy(newurl + newlen + (ptr - url),
ptr + currlen, /* copy the trailing zero byte too */
urllen - (ptr-url) - currlen + 1);
if(data->change.url_alloc)
free(data->change.url);
data->change.url = newurl;
data->change.url_alloc = TRUE;
}
else
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
}
}
ppath = data->change.url;
if(checkprefix("ftp://", ppath)) {
if (data->set.proxy_transfer_mode) {
/* when doing ftp, append ;type=<a|i> if not present */
char *type = strstr(ppath, ";type=");
if(type && type[6] && type[7] == 0) {
switch (Curl_raw_toupper(type[6])) {
case 'A':
case 'D':
case 'I':
break;
default:
type = NULL;
Daniel Stenberg
committed
}
if(!type) {
char *p = ftp_typecode;
/* avoid sending invalid URLs like ftp://example.com;type=i if the
* user specified ftp://example.com without the slash */
if (!*data->state.path && ppath[strlen(ppath) - 1] != '/') {
*p++ = '/';
}
snprintf(p, sizeof(ftp_typecode) - 1, ";type=%c",
Daniel Stenberg
committed
}
if (conn->bits.user_passwd && !conn->bits.userpwd_in_url)
paste_ftp_userpwd = TRUE;
Daniel Stenberg
committed
}
}
#endif /* CURL_DISABLE_PROXY */
Daniel Stenberg
committed
if(HTTPREQ_POST_FORM == httpreq) {
/* we must build the whole darned post sequence first, so that we have
a size of the whole shebang before we start to send it */
result = Curl_getFormData(&http->sendit, data->set.httppost,
Daniel Stenberg
committed
Curl_checkheaders(data, "Content-Type:"),
&http->postsize);
if(CURLE_OK != result) {
/* Curl_getFormData() doesn't use failf() */
failf(data, "failed creating formpost data");
return result;
}
}
Daniel Stenberg
committed
http->p_accept = Curl_checkheaders(data, "Accept:")?NULL:"Accept: */*\r\n";
Daniel Stenberg
committed
if(( (HTTPREQ_POST == httpreq) ||
(HTTPREQ_POST_FORM == httpreq) ||
(HTTPREQ_PUT == httpreq) ) &&
Daniel Stenberg
committed
data->state.resume_from) {
/**********************************************************************
* Resuming upload in HTTP means that we PUT or POST and that we have
* got a resume_from value set. The resume value has already created
* a Range: header that will be passed along. We need to "fast forward"
* the file the given number of bytes and decrease the assume upload
* file size before we continue this venture in the dark lands of HTTP.
*********************************************************************/
Daniel Stenberg
committed
if(data->state.resume_from < 0 ) {
/*
* This is meant to get the size of the present remote-file by itself.
* We don't support this now. Bail out!
*/
Daniel Stenberg
committed
data->state.resume_from = 0;
Daniel Stenberg
committed
if(data->state.resume_from && !data->state.this_is_a_follow) {
/* do we still game? */
/* Now, let's read off the proper amount of bytes from the
Daniel Stenberg
committed
input. */
if(conn->seek_func) {
Daniel Stenberg
committed
seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
SEEK_SET);
}
Daniel Stenberg
committed
Daniel Stenberg
committed
if(seekerr != CURL_SEEKFUNC_OK) {
if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
Daniel Stenberg
committed
failf(data, "Could not seek stream");
return CURLE_READ_ERROR;
}
Daniel Stenberg
committed
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
/* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
else {
curl_off_t passed=0;
do {
size_t readthisamountnow = (size_t)(data->state.resume_from -
passed);
size_t actuallyread;
if(readthisamountnow > BUFSIZE)
readthisamountnow = BUFSIZE;
actuallyread = data->set.fread_func(data->state.buffer, 1,
(size_t)readthisamountnow,
data->set.in);
passed += actuallyread;
if(actuallyread != readthisamountnow) {
failf(data, "Could only read %" FORMAT_OFF_T
" bytes from the input",
passed);
return CURLE_READ_ERROR;
}
} while(passed != data->state.resume_from); /* loop until done */
}
Daniel Stenberg
committed
}
/* now, decrease the size of the read */
Daniel Stenberg
committed
if(data->set.infilesize>0) {
Daniel Stenberg
committed
data->set.infilesize -= data->state.resume_from;
Daniel Stenberg
committed
if(data->set.infilesize <= 0) {
failf(data, "File already completely uploaded");
return CURLE_PARTIAL_FILE;
}
}
/* we've passed, proceed as normal */
}
}
Daniel Stenberg
committed
if(data->state.use_range) {
/*
* A range is selected. We use different headers whether we're downloading
* or uploading and we always let customized headers override our internal
* ones if any such are specified.
*/
if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
Daniel Stenberg
committed
!Curl_checkheaders(data, "Range:")) {
/* if a line like this was already allocated, free the previous one */
if(conn->allocptr.rangeline)
free(conn->allocptr.rangeline);
Daniel Stenberg
committed
conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n",
Daniel Stenberg
committed
data->state.range);
}
Daniel Stenberg
committed
else if((httpreq != HTTPREQ_GET) &&
Daniel Stenberg
committed
!Curl_checkheaders(data, "Content-Range:")) {
Daniel Stenberg
committed
/* if a line like this was already allocated, free the previous one */
if(conn->allocptr.rangeline)
free(conn->allocptr.rangeline);
Daniel Stenberg
committed
if(data->set.set_resume_from < 0) {
/* Upload resume was asked for, but we don't know the size of the
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",
Daniel Stenberg
committed
data->set.infilesize - 1, data->set.infilesize);
}
else if(data->state.resume_from) {
/* This is because "resume" was selected */
Daniel Stenberg
committed
data->state.resume_from + data->set.infilesize;
aprintf("Content-Range: bytes %s%" FORMAT_OFF_T
"/%" FORMAT_OFF_T "\r\n",
Daniel Stenberg
committed
data->state.range, total_expected_size-1,
total_expected_size);
}
else {
/* Range was selected and then we just pass the incoming range and
append total size */
aprintf("Content-Range: bytes %s/%" FORMAT_OFF_T "\r\n",
Daniel Stenberg
committed
data->state.range, data->set.infilesize);
}
Daniel Stenberg
committed
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";
Daniel Stenberg
committed
/* initialize a dynamic send-buffer */
Daniel Stenberg
committed
req_buffer = Curl_add_buffer_init();
Daniel Stenberg
committed
Daniel Stenberg
committed
if(!req_buffer)
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
/* add the main request stuff */
/* GET/HEAD/POST/PUT */
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer, "%s ", request);
if (result)
return result;
/* url */
if (paste_ftp_userpwd)
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer, "ftp://%s:%s@%s",
conn->user, conn->passwd,
ppath + sizeof("ftp://") - 1);
else
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer, ppath, strlen(ppath));
if (result)
return result;
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer,
"%s" /* ftp typecode (;type=x) */
" HTTP/%s\r\n" /* HTTP version */
Daniel Stenberg
committed
"%s" /* proxyuserpwd */
"%s" /* userpwd */
"%s" /* range */
"%s" /* user agent */
"%s" /* host */
"%s" /* accept */
"%s" /* accept-encoding */
"%s" /* referer */
"%s" /* Proxy-Connection */
"%s",/* transfer-encoding */
Daniel Stenberg
committed
Daniel Stenberg
committed
ftp_typecode,
conn->allocptr.proxyuserpwd?
Daniel Stenberg
committed
conn->allocptr.proxyuserpwd:"",
conn->allocptr.userpwd?conn->allocptr.userpwd:"",
Daniel Stenberg
committed
(data->state.use_range && conn->allocptr.rangeline)?
Daniel Stenberg
committed
conn->allocptr.rangeline:"",
Daniel Stenberg
committed
(data->set.str[STRING_USERAGENT] &&
*data->set.str[STRING_USERAGENT] && conn->allocptr.uagent)?
Daniel Stenberg
committed
conn->allocptr.uagent:"",
(conn->allocptr.host?conn->allocptr.host:""), /* Host: host */
Daniel Stenberg
committed
http->p_accept?http->p_accept:"",
Daniel Stenberg
committed
(data->set.str[STRING_ENCODING] &&
*data->set.str[STRING_ENCODING] &&
conn->allocptr.accept_encoding)?
Daniel Stenberg
committed
conn->allocptr.accept_encoding:"",
(data->change.referer && conn->allocptr.ref)?
conn->allocptr.ref:"" /* Referer: <data> */,
(conn->bits.httpproxy &&
!conn->bits.tunnel_proxy &&
Daniel Stenberg
committed
!Curl_checkheaders(data, "Proxy-Connection:"))?
Daniel Stenberg
committed
"Proxy-Connection: Keep-Alive\r\n":"",
te
);
Daniel Stenberg
committed
/*
* Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
* with basic and digest, it will be freed anyway by the next request
*/
Daniel Stenberg
committed
Daniel Stenberg
committed
Curl_safefree (conn->allocptr.userpwd);
conn->allocptr.userpwd = NULL;
Daniel Stenberg
committed
Daniel Stenberg
committed
if(result)
return result;
#if !defined(CURL_DISABLE_COOKIES)
Daniel Stenberg
committed
if(data->cookies || addcookies) {
struct Cookie *co=NULL; /* no cookies from start */
int count=0;
if(data->cookies) {
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
co = Curl_cookie_getlist(data->cookies,
conn->allocptr.cookiehost?
conn->allocptr.cookiehost:host,
data->state.path,
(bool)(conn->protocol&PROT_HTTPS?TRUE:FALSE));
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
if(co) {
struct Cookie *store=co;
/* now loop through all cookies that matched */
while(co) {
if(co->value) {
if(0 == count) {
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer, "Cookie: ");
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer,
Daniel Stenberg
committed
"%s%s=%s", count?"; ":"",
co->name, co->value);
if(result)
break;
count++;
}
Daniel Stenberg
committed
co = co->next; /* next cookie please */
}
Daniel Stenberg
committed
Curl_cookie_freelist(store, FALSE); /* free the cookie list */
Daniel Stenberg
committed
if(addcookies && (CURLE_OK == result)) {
if(!count)
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer, "Cookie: ");
Daniel Stenberg
committed
if(CURLE_OK == result) {
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer, "%s%s",
Daniel Stenberg
committed
count?"; ":"",
addcookies);
count++;
}
}
if(count && (CURLE_OK == result))
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer, "\r\n", 2);
Daniel Stenberg
committed
if(result)
return result;
}
#endif
Daniel Stenberg
committed
if(data->set.timecondition) {
Daniel Stenberg
committed
result = Curl_add_timecondition(data, req_buffer);
if(result)
return result;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
result = Curl_add_custom_headers(conn, req_buffer);
Daniel Stenberg
committed
if(result)
return result;
Daniel Stenberg
committed
http->postdata = NULL; /* nothing to post at this point */
Curl_pgrsSetUploadSize(data, 0); /* upload size is 0 atm */
Daniel Stenberg
committed
/* If 'authdone' is FALSE, we must not set the write socket index to the
Curl_transfer() call below, as we're not ready to actually upload any
data yet. */
Daniel Stenberg
committed
switch(httpreq) {
Daniel Stenberg
committed
Daniel Stenberg
committed
case HTTPREQ_POST_FORM:
if(!http->sendit || conn->bits.authneg) {
/* nothing to post! */
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
Daniel Stenberg
committed
if(result)
return result;
Daniel Stenberg
committed
Daniel Stenberg
committed
result = Curl_add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
Daniel Stenberg
committed
if(result)
failf(data, "Failed sending POST request");
else
/* setup variables for the upcoming transfer */
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
-1, NULL);
Daniel Stenberg
committed
break;
}
Daniel Stenberg
committed
if(Curl_FormInit(&http->form, http->sendit)) {
failf(data, "Internal HTTP POST error!");
return CURLE_HTTP_POST_ERROR;
}
/* 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 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 = conn->fread_func;
/* Set the read function to read from the generated form data */
Daniel Stenberg
committed
conn->fread_func = (curl_read_callback)Curl_FormReader;
conn->fread_in = &http->form;
Daniel Stenberg
committed
Daniel Stenberg
committed
http->sending = HTTPSEND_BODY;
Daniel Stenberg
committed
if(!data->req.upload_chunky) {
/* only add Content-Length if not uploading chunked */
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer,
"Content-Length: %" FORMAT_OFF_T "\r\n",
Daniel Stenberg
committed
http->postsize);
if(result)
return result;
Daniel Stenberg
committed
}
result = expect100(data, conn, req_buffer);
Daniel Stenberg
committed
if(result)
return result;
Daniel Stenberg
committed
{
Daniel Stenberg
committed
/* Get Content-Type: line from Curl_formpostheader.
*/
char *contentType;
size_t linelength=0;
contentType = Curl_formpostheader((void *)&http->form,
&linelength);
if(!contentType) {
failf(data, "Could not get Content-Type header line!");
return CURLE_HTTP_POST_ERROR;
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer, contentType, linelength);
if(result)
return result;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
/* make the request end in a true CRLF */
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer, "\r\n", 2);
Daniel Stenberg
committed
if(result)
return result;
Daniel Stenberg
committed
/* set upload size to the progress meter */
Curl_pgrsSetUploadSize(data, http->postsize);
Daniel Stenberg
committed
/* fire away the whole request to the server */
Daniel Stenberg
committed
result = Curl_add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
Daniel Stenberg
committed
if(result)
failf(data, "Failed sending POST request");
else
/* setup variables for the upcoming transfer */
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount, FIRSTSOCKET,
&http->writebytecount);
Daniel Stenberg
committed
if(result) {
Curl_formclean(&http->sendit); /* free that whole lot */
return result;
}
#ifdef CURL_DOES_CONVERSIONS
/* time to convert the form data... */
Daniel Stenberg
committed
result = Curl_formconvert(data, http->sendit);
if(result) {
Curl_formclean(&http->sendit); /* free that whole lot */
return result;
}
#endif /* CURL_DOES_CONVERSIONS */
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
case HTTPREQ_PUT: /* Let's PUT the data to the server! */
Daniel Stenberg
committed
if(conn->bits.authneg)
postsize = 0;
else
postsize = data->set.infilesize;
Daniel Stenberg
committed
if((postsize != -1) && !data->req.upload_chunky) {
/* only add Content-Length if not uploading chunked */
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer,
"Content-Length: %" FORMAT_OFF_T "\r\n",
Daniel Stenberg
committed
postsize );
if(result)
return result;
Daniel Stenberg
committed
}
result = expect100(data, conn, req_buffer);
Daniel Stenberg
committed
if(result)
return result;
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers */
Daniel Stenberg
committed
if(result)
return result;
Daniel Stenberg
committed
/* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, postsize);
Daniel Stenberg
committed
/* this sends the buffer and frees all the buffer resources */
Daniel Stenberg
committed
result = Curl_add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
Daniel Stenberg
committed
if(result)
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);
Daniel Stenberg
committed
if(result)
return result;
break;
Daniel Stenberg
committed
case HTTPREQ_POST:
/* this is the simple POST, using x-www-form-urlencoded style */
if(conn->bits.authneg)
postsize = 0;
else {
Daniel Stenberg
committed
/* figure out the size of the postfields */
postsize = (data->set.postfieldsize != -1)?
data->set.postfieldsize:
Daniel Stenberg
committed
(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) */
Daniel Stenberg
committed
if(conn->bits.authneg || !Curl_checkheaders(data, "Content-Length:")) {
Daniel Stenberg
committed
/* we allow replacing this header if not during auth negotiation,
although it isn't very wise to actually set your own */
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer,
"Content-Length: %" FORMAT_OFF_T"\r\n",
postsize);
if(result)
return result;
}
}
Daniel Stenberg
committed
Daniel Stenberg
committed
if(!Curl_checkheaders(data, "Content-Type:")) {
result = Curl_add_bufferf(req_buffer,
"Content-Type: application/"
"x-www-form-urlencoded\r\n");
if(result)
return result;
}
/* 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
sure that the expect100header is always set to the preferred value
here. */
Daniel Stenberg
committed
ptr = Curl_checkheaders(data, "Expect:");
Daniel Stenberg
committed
if(ptr) {
data->state.expect100header =
Curl_compareheader(ptr, "Expect:", "100-continue");
}
Daniel Stenberg
committed
else if(postsize > TINY_INITIAL_POST_SIZE || postsize < 0) {
result = expect100(data, conn, req_buffer);
if(result)
return result;
}
else
data->state.expect100header = FALSE;
if(data->set.postfields) {
Daniel Stenberg
committed
if(!data->state.expect100header &&
(postsize < MAX_INITIAL_POST_SIZE)) {
/* if we don't use expect: 100 AND
postsize is less than MAX_INITIAL_POST_SIZE
then append the post data to the HTTP request header. This limit
is no magic limit but only set to prevent really huge POSTs to
get the data duplicated with malloc() and family. */
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
if(result)
return result;
if(!data->req.upload_chunky) {
/* We're not sending it 'chunked', append it to the request
already now to reduce the number if send() calls */
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer, data->set.postfields,
(size_t)postsize);
included_body = postsize;
}
else {
/* Append the POST data chunky-style */
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer, "%x\r\n", (int)postsize);
if(CURLE_OK == result)
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer, data->set.postfields,
(size_t)postsize);
if(CURLE_OK == result)
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer,
"\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7);
/* CR LF 0 CR LF CR LF */
included_body = postsize + 7;
}
if(result)
return result;
}
else {
/* A huge POST coming up, do data separate from the request */
http->postsize = postsize;
http->postdata = data->set.postfields;
Daniel Stenberg
committed
http->sending = HTTPSEND_BODY;
Daniel Stenberg
committed
conn->fread_func = (curl_read_callback)readmoredata;
conn->fread_in = (void *)conn;
Daniel Stenberg
committed
/* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, http->postsize);
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
if(result)
return result;
}
}
else {
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
if(result)
return result;
Daniel Stenberg
committed
if(data->req.upload_chunky && conn->bits.authneg) {
/* Chunky upload is selected and we're negotiating auth still, send
end-of-data only */
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer,
Daniel Stenberg
committed
"\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7);
/* CR LF 0 CR LF CR LF */
if(result)
return result;
}
else if(data->set.postfieldsize) {
/* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, postsize?postsize:-1);
/* set the pointer to mark that we will send the post body using the
read callback, but only if we're not in authenticate
negotiation */
if(!conn->bits.authneg) {
http->postdata = (char *)&http->postdata;
http->postsize = postsize;
Daniel Stenberg
committed
}
}
}
/* issue the request */
Daniel Stenberg
committed
result = Curl_add_buffer_send(req_buffer, conn, &data->info.request_size,
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);
break;
Daniel Stenberg
committed
default:
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer, "\r\n", 2);
Daniel Stenberg
committed
if(result)
return result;
Daniel Stenberg
committed
/* issue the request */
Daniel Stenberg
committed
result = Curl_add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
Daniel Stenberg
committed
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);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if(result)
return result;
Daniel Stenberg
committed
if(http->writebytecount) {
/* if a request-body has been sent off, we make sure this progress is noted
properly */
Curl_pgrsSetUploadCounter(data, http->writebytecount);
if(Curl_pgrsUpdate(conn))
result = CURLE_ABORTED_BY_CALLBACK;
}
return result;
/*
* checkhttpprefix()
*
* Returns TRUE if member of the list matches prefix of string
*/
static bool
checkhttpprefix(struct SessionHandle *data,
const char *s)
{
struct curl_slist *head = data->set.http200aliases;
bool rc = FALSE;
#ifdef CURL_DOES_CONVERSIONS
/* convert from the network encoding using a scratch area */
Daniel Stenberg
committed
char *scratch = strdup(s);