Newer
Older
if(result == CURLE_OK)
Daniel Stenberg
committed
data->state.expect100header = TRUE;
}
return result;
}
static CURLcode add_custom_headers(struct connectdata *conn,
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 */
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))
;
CURLcode result = add_bufferf(req_buffer, "%s\r\n", headers->data);
if(result)
return result;
}
}
}
headers = headers->next;
}
/*
* 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;
char *buf = data->state.buffer; /* this is a short cut to the buffer */
CURLcode result=CURLE_OK;
const char *ppath = data->state.path;
bool paste_ftp_userpwd = FALSE;
Daniel Stenberg
committed
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;
send_buffer *req_buffer;
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(sizeof(struct HTTP), 1);
if(!http)
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
data->state.proto.http = http;
Daniel Stenberg
committed
http = data->state.proto.http;
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
if(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);
if(data->change.referer && !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] && !checkheaders(data, "Cookie:"))
addcookies = data->set.str[STRING_COOKIE];
if(!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;
}
ptr = 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 (use_http_1_1(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;
}
if(data->req.upload_chunky)
Daniel Stenberg
committed
te = "Transfer-Encoding: chunked\r\n";
}
Daniel Stenberg
committed
Curl_safefree(conn->allocptr.host);
ptr = 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->allocptr.host = aprintf("Host: %s%s%s:%d\r\n",
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 *p = strstr(ppath, ";type=");
if(p && p[6] && p[7] == 0) {
switch (Curl_raw_toupper(p[6])) {
case 'A':
case 'D':
case 'I':
break;
default:
p = NULL;
}
Daniel Stenberg
committed
}
if(!p)
snprintf(ftp_typecode, sizeof(ftp_typecode), ";type=%c",
data->set.prefer_ascii ? 'a' : 'i');
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,
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 = 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
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
/* 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)) &&
!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) &&
!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 */
req_buffer = 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 */
result = add_bufferf(req_buffer, "%s ", request);
if (result)
return result;
/* url */
if (paste_ftp_userpwd)
result = add_bufferf(req_buffer, "ftp://%s:%s@%s",
conn->user, conn->passwd, ppath + sizeof("ftp://") - 1);
else
result = add_buffer(req_buffer, ppath, strlen(ppath));
if (result)
return result;
result = 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 &&
!checkheaders(data, "Proxy-Connection:"))?
"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) {
result = add_bufferf(req_buffer, "Cookie: ");
Daniel Stenberg
committed
break;
Daniel Stenberg
committed
result = add_bufferf(req_buffer,
"%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)
result = add_bufferf(req_buffer, "Cookie: ");
if(CURLE_OK == result) {
result = add_bufferf(req_buffer, "%s%s",
count?"; ":"",
addcookies);
count++;
}
}
if(count && (CURLE_OK == result))
result = add_buffer(req_buffer, "\r\n", 2);
if(result)
return result;
}
#endif
Daniel Stenberg
committed
if(data->set.timecondition) {
struct tm *tm;
Daniel Stenberg
committed
/* 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).
*/
Daniel Stenberg
committed
/* thread-safe version */
struct tm keeptime;
tm = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime);
Daniel Stenberg
committed
tm = gmtime(&data->set.timevalue);
Daniel Stenberg
committed
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
/* 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 = add_bufferf(req_buffer,
"If-Modified-Since: %s\r\n", buf);
break;
case CURL_TIMECOND_IFUNMODSINCE:
result = add_bufferf(req_buffer,
"If-Unmodified-Since: %s\r\n", buf);
break;
case CURL_TIMECOND_LASTMOD:
result = add_bufferf(req_buffer,
"Last-Modified: %s\r\n", buf);
break;
if(result)
return result;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
result = add_custom_headers(conn, req_buffer);
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! */
result = add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
if(result)
return result;
Daniel Stenberg
committed
Daniel Stenberg
committed
result = add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
if(result)
failf(data, "Failed sending POST request");
else
/* setup variables for the upcoming transfer */
result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount,
-1, NULL);
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 */
result = 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 = add_buffer(req_buffer, contentType, linelength);
if(result)
return result;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
/* make the request end in a true CRLF */
result = add_buffer(req_buffer, "\r\n", 2);
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 */
result = add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
if(result)
failf(data, "Failed sending POST request");
else
/* setup variables for the upcoming transfer */
result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount,
FIRSTSOCKET,
&http->writebytecount);
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 */
result = 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 = add_buffer(req_buffer, "\r\n", 2); /* end of headers */
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 */
result = add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, FIRSTSOCKET);
if(result)
failf(data, "Failed sending PUT request");
else
/* prepare for transfer */
result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount,
postsize?FIRSTSOCKET:-1,
postsize?&http->writebytecount:NULL);
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:
(data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);
}
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 || !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 = add_bufferf(req_buffer,
"Content-Length: %" FORMAT_OFF_T"\r\n",
postsize);
if(result)
return result;
}
}
Daniel Stenberg
committed
if(!checkheaders(data, "Content-Type:")) {
result = 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. */
if(postsize > TINY_INITIAL_POST_SIZE) {
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. */
result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
if(result)
return result;
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
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 */
result = add_buffer(req_buffer, data->set.postfields,
(size_t)postsize);
included_body = postsize;
}
else {
/* Append the POST data chunky-style */
result = add_bufferf(req_buffer, "%x\r\n", (int)postsize);
if(CURLE_OK == result)
result = add_buffer(req_buffer, data->set.postfields,
(size_t)postsize);
if(CURLE_OK == result)
result = 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);
result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
if(result)
return result;
}
}
else {
result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
if(result)
return result;
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 */
result = add_buffer_send(req_buffer, conn, &data->info.request_size,
(size_t)included_body, FIRSTSOCKET);
if(result)
failf(data, "Failed sending HTTP POST request");
else
result =
Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
&http->readbytecount,
http->postdata?FIRSTSOCKET:-1,
http->postdata?&http->writebytecount:NULL);
break;
Daniel Stenberg
committed
default:
result = add_buffer(req_buffer, "\r\n", 2);
if(result)
return result;
Daniel Stenberg
committed
/* issue the request */
result = 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: */
result = 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;