Newer
Older
Daniel Stenberg
committed
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) {
Curl_safefree(data->change.url);
data->change.url_alloc = FALSE;
}
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 post sequence first, so that we have a size of
the whole transfer before we start to send it */
result = Curl_getformdata(data, &http->sendit, data->set.httppost,
Curl_checkheaders(conn, "Content-Type:"),
&http->postsize);
if(result)
return result;
}
http->p_accept = Curl_checkheaders(conn, "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!
*/
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
/* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
else {
curl_off_t passed=0;
do {
size_t readthisamountnow =
(data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
Daniel Stenberg
committed
size_t actuallyread =
data->set.fread_func(data->state.buffer, 1, readthisamountnow,
data->set.in);
Daniel Stenberg
committed
passed += actuallyread;
if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
/* this checks for greater-than only to make sure that the
CURL_READFUNC_ABORT return code still aborts */
failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T
" bytes from the input", passed);
Daniel Stenberg
committed
return CURLE_READ_ERROR;
}
} while(passed < data->state.resume_from);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
}
/* now, decrease the size of the read */
if(data->state.infilesize>0) {
data->state.infilesize -= data->state.resume_from;
if(data->state.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)) &&
!Curl_checkheaders(conn, "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) &&
!Curl_checkheaders(conn, "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-%" CURL_FORMAT_CURL_OFF_T
"/%" CURL_FORMAT_CURL_OFF_T "\r\n",
data->state.infilesize - 1, data->state.infilesize);
Daniel Stenberg
committed
}
else if(data->state.resume_from) {
/* This is because "resume" was selected */
data->state.resume_from + data->state.infilesize;
aprintf("Content-Range: bytes %s%" CURL_FORMAT_CURL_OFF_T
"/%" CURL_FORMAT_CURL_OFF_T "\r\n",
data->state.range, total_expected_size-1,
total_expected_size);
}
else {
/* Range was selected and then we just pass the incoming range and
append total size */
aprintf("Content-Range: bytes %s/%" CURL_FORMAT_CURL_OFF_T "\r\n",
data->state.range, data->state.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_1plus(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);
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));
return result;
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
result =
Curl_add_bufferf(req_buffer,
"%s" /* ftp typecode (;type=x) */
" HTTP/%s\r\n" /* HTTP version */
"%s" /* proxyuserpwd */
"%s" /* userpwd */
"%s" /* range */
"%s" /* user agent */
"%s" /* host */
"%s" /* accept */
"%s" /* TE: */
"%s" /* accept-encoding */
"%s" /* referer */
"%s" /* Proxy-Connection */
"%s",/* transfer-encoding */
ftp_typecode,
httpstring,
conn->allocptr.proxyuserpwd?
conn->allocptr.proxyuserpwd:"",
conn->allocptr.userpwd?conn->allocptr.userpwd:"",
(data->state.use_range && conn->allocptr.rangeline)?
conn->allocptr.rangeline:"",
(data->set.str[STRING_USERAGENT] &&
*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] &&
*data->set.str[STRING_ENCODING] &&
conn->allocptr.accept_encoding)?
conn->allocptr.accept_encoding:"",
(data->change.referer && conn->allocptr.ref)?
conn->allocptr.ref:"" /* Referer: <data> */,
(conn->bits.httpproxy &&
!conn->bits.tunnel_proxy &&
!Curl_checkProxyheaders(conn, "Proxy-Connection:"))?
"Proxy-Connection: Keep-Alive\r\n":"",
te
Daniel Stenberg
committed
);
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(!(conn->handler->flags&PROTOPT_SSL) &&
(data->set.httpversion == CURL_HTTP_VERSION_2_0)) {
/* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done
result = Curl_http2_request_upgrade(req_buffer, conn);
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,
Daniel Stenberg
committed
data->state.path,
(conn->handler->protocol&CURLPROTO_HTTPS)?
TRUE:FALSE);
Daniel Stenberg
committed
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,
"%s%s=%s", count?"; ":"",
co->name, co->value);
Daniel Stenberg
committed
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",
count?"; ":"",
addcookies);
Daniel Stenberg
committed
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) {
result = Curl_add_timecondition(data, req_buffer);
if(result)
return result;
Daniel Stenberg
committed
}
result = Curl_add_custom_headers(conn, FALSE, 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;
if(!data->req.upload_chunky &&
!Curl_checkheaders(conn, "Content-Length:")) {
Daniel Stenberg
committed
/* only add Content-Length if not uploading chunked */
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer,
"Content-Length: %" CURL_FORMAT_CURL_OFF_T
"\r\n", 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;
}
/* convert the form data */
result = Curl_convert_form(data, http->sendit);
Daniel Stenberg
committed
if(result) {
Curl_formclean(&http->sendit); /* free that whole lot */
return result;
}
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->state.infilesize;
if((postsize != -1) && !data->req.upload_chunky &&
!Curl_checkheaders(conn, "Content-Length:")) {
Daniel Stenberg
committed
/* only add Content-Length if not uploading chunked */
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer,
"Content-Length: %" CURL_FORMAT_CURL_OFF_T
"\r\n", postsize);
if(result)
return result;
Daniel Stenberg
committed
}
if(postsize != 0) {
result = expect100(data, conn, req_buffer);
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);
}
/* We only set Content-Length and allow a custom Content-Length if
we don't upload data chunked, as RFC2616 forbids us to set both
kinds of headers (Transfer-Encoding: chunked and Content-Length) */
if((postsize != -1) && !data->req.upload_chunky &&
!Curl_checkheaders(conn, "Content-Length:")) {
/* we allow replacing this header if not during auth negotiation,
although it isn't very wise to actually set your own */
result = Curl_add_bufferf(req_buffer,
"Content-Length: %" CURL_FORMAT_CURL_OFF_T
"\r\n", postsize);
if(result)
return result;
}
Daniel Stenberg
committed
if(!Curl_checkheaders(conn, "Content-Type:")) {
Daniel Stenberg
committed
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. */
ptr = Curl_checkheaders(conn, "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
/* In HTTP2, we send request body in DATA frame regardless of
its size. */
if(conn->httpversion != 20 &&
!data->state.expect100header &&
(postsize < MAX_INITIAL_POST_SIZE)) {
/* if we don't use expect: 100 AND
postsize is less than MAX_INITIAL_POST_SIZE
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,
included_body = postsize;
}
else {
if(postsize) {
/* Append the POST data chunky-style */
result = Curl_add_bufferf(req_buffer, "%x\r\n", (int)postsize);
if(CURLE_OK == result) {
result = Curl_add_buffer(req_buffer, data->set.postfields,
(size_t)postsize);
if(CURLE_OK == result)
result = Curl_add_buffer(req_buffer, "\r\n", 2);
included_body = postsize + 2;
}
if(CURLE_OK == result)
Daniel Stenberg
committed
result = Curl_add_buffer(req_buffer,
"\x30\x0d\x0a\x0d\x0a", 5);
/* 0 CR LF CR LF */
included_body += 5;
}
if(result)
return result;
/* Make sure the progress information is accurate */
Curl_pgrsSetUploadSize(data, postsize);
}
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,
"\x30\x0d\x0a\x0d\x0a", 5);
/* 0 CR LF CR LF */
Daniel Stenberg
committed
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;
if(http->writebytecount >= postsize) {
/* already sent the entire request body, mark the "upload" as
complete */
infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
" out of %" CURL_FORMAT_CURL_OFF_T " bytes\n",
http->writebytecount, postsize);
data->req.upload_done = TRUE;
data->req.keepon &= ~KEEP_SEND; /* we're done writing */
data->req.exp100 = EXP100_SEND_DATA; /* already sent */
}
Daniel Stenberg
committed
}
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);
if(NULL == scratch) {
failf (data, "Failed to allocate memory for conversion!");
return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
}
if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
/* Curl_convert_from_network calls failf if unsuccessful */
free(scratch);
return FALSE; /* can't return CURLE_foobar so return FALSE */
}
s = scratch;
#endif /* CURL_DOES_CONVERSIONS */
while(head) {
if(checkprefix(head->data, s)) {
rc = TRUE;
break;
}
head = head->next;
}
if(!rc && (checkprefix("HTTP/", s)))
rc = TRUE;
#ifdef CURL_DOES_CONVERSIONS
free(scratch);
#endif /* CURL_DOES_CONVERSIONS */
return rc;
}
Daniel Stenberg
committed
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
#ifndef CURL_DISABLE_RTSP
static bool
checkrtspprefix(struct SessionHandle *data,
const char *s)
{
#ifdef CURL_DOES_CONVERSIONS
/* convert from the network encoding using a scratch area */
char *scratch = strdup(s);
if(NULL == scratch) {
failf (data, "Failed to allocate memory for conversion!");
return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
}
if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
/* Curl_convert_from_network calls failf if unsuccessful */
free(scratch);
return FALSE; /* can't return CURLE_foobar so return FALSE */
}
s = scratch;
#else
(void)data; /* unused */
#endif /* CURL_DOES_CONVERSIONS */
if(checkprefix("RTSP/", s))
return TRUE;
else
return FALSE;
}
#endif /* CURL_DISABLE_RTSP */
static bool
checkprotoprefix(struct SessionHandle *data, struct connectdata *conn,
const char *s)
{
#ifndef CURL_DISABLE_RTSP
if(conn->handler->protocol & CURLPROTO_RTSP)
Daniel Stenberg
committed
return checkrtspprefix(data, s);
Daniel Stenberg
committed
#endif /* CURL_DISABLE_RTSP */
return checkhttpprefix(data, s);
}
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
/*
* header_append() copies a chunk of data to the end of the already received
* header. We make sure that the full string fit in the allocated header
* buffer, or else we enlarge it.
*/
static CURLcode header_append(struct SessionHandle *data,
struct SingleRequest *k,
size_t length)
{
if(k->hbuflen + length >= data->state.headersize) {
/* We enlarge the header buffer as it is too small */
char *newbuff;
size_t hbufp_index;
size_t newsize;
if(k->hbuflen + length > CURL_MAX_HTTP_HEADER) {
/* The reason to have a max limit for this is to avoid the risk of a bad
server feeding libcurl with a never-ending header that will cause
reallocs infinitely */
failf (data, "Avoided giant realloc for header (max is %d)!",
CURL_MAX_HTTP_HEADER);
return CURLE_OUT_OF_MEMORY;
}
newsize=CURLMAX((k->hbuflen+ length)*3/2, data->state.headersize*2);
hbufp_index = k->hbufp - data->state.headerbuff;
newbuff = realloc(data->state.headerbuff, newsize);
if(!newbuff) {
failf (data, "Failed to alloc memory for big header!");
return CURLE_OUT_OF_MEMORY;
}
data->state.headersize=newsize;
data->state.headerbuff = newbuff;
k->hbufp = data->state.headerbuff + hbufp_index;
}
memcpy(k->hbufp, k->str_start, length);
k->hbufp += length;
k->hbuflen += length;
*k->hbufp = 0;
return CURLE_OK;
}
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
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
static void print_http_error(struct SessionHandle *data)
{
struct SingleRequest *k = &data->req;
char *beg = k->p;
/* make sure that data->req.p points to the HTTP status line */
if(!strncmp(beg, "HTTP", 4)) {
/* skip to HTTP status code */
beg = strchr(beg, ' ');
if(beg && *++beg) {
/* find trailing CR */
char end_char = '\r';
char *end = strchr(beg, end_char);
if(!end) {
/* try to find LF (workaround for non-compliant HTTP servers) */
end_char = '\n';
end = strchr(beg, end_char);
}
if(end) {
/* temporarily replace CR or LF by NUL and print the error message */
*end = '\0';
failf(data, "The requested URL returned error: %s", beg);
/* restore the previously replaced CR or LF */
*end = end_char;
return;
}
}
}
/* fall-back to printing the HTTP status code only */
failf(data, "The requested URL returned error: %d", k->httpcode);
}
/*
* Read any HTTP header lines from the server and pass them to the client app.
*/
CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
Daniel Stenberg
committed
struct connectdata *conn,
ssize_t *nread,
bool *stop_reading)
{
CURLcode result;
Daniel Stenberg
committed
struct SingleRequest *k = &data->req;
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
/* header line within buffer loop */
do {
size_t rest_length;
size_t full_length;
int writetype;
/* str_start is start of line within buf */
k->str_start = k->str;
/* data is in network encoding so use 0x0a instead of '\n' */
k->end_ptr = memchr(k->str_start, 0x0a, *nread);
if(!k->end_ptr) {
/* Not a complete header line within buffer, append the data to
the end of the headerbuff. */
result = header_append(data, k, *nread);
if(result)
return result;
if(!k->headerline && (k->hbuflen>5)) {
Daniel Stenberg
committed
/* make a first check that this looks like a protocol header */
if(!checkprotoprefix(data, conn, data->state.headerbuff)) {
/* this is not the beginning of a protocol first header line */
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
k->header = FALSE;
k->badheader = HEADER_ALLBAD;
break;
}
}
break; /* read more and try again */
}
/* decrease the size of the remaining (supposed) header line */
rest_length = (k->end_ptr - k->str)+1;
*nread -= (ssize_t)rest_length;
k->str = k->end_ptr + 1; /* move past new line */
full_length = k->str - k->str_start;
result = header_append(data, k, full_length);
if(result)
return result;
k->end_ptr = k->hbufp;
k->p = data->state.headerbuff;
/****
* We now have a FULL header line that p points to
*****/
if(!k->headerline) {
/* the first read header */
if((k->hbuflen>5) &&
Daniel Stenberg
committed
!checkprotoprefix(data, conn, data->state.headerbuff)) {
/* this is not the beginning of a protocol first header line */
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
k->header = FALSE;
if(*nread)
/* since there's more, this is a partial bad header */
k->badheader = HEADER_PARTHEADER;
else {
/* this was all we read so it's all a bad header */
k->badheader = HEADER_ALLBAD;
*nread = (ssize_t)rest_length;
}
break;
}
}
/* headers are in network encoding so
use 0x0a and 0x0d instead of '\n' and '\r' */
if((0x0a == *k->p) || (0x0d == *k->p)) {
size_t headerlen;
/* Zero-length header line means end of headers! */
#ifdef CURL_DOES_CONVERSIONS
if(0x0d == *k->p) {
*k->p = '\r'; /* replace with CR in host encoding */
k->p++; /* pass the CR byte */
}
if(0x0a == *k->p) {
*k->p = '\n'; /* replace with LF in host encoding */
k->p++; /* pass the LF byte */
}
#else
if('\r' == *k->p)
k->p++; /* pass the \r byte */
if('\n' == *k->p)
k->p++; /* pass the \n byte */
#endif /* CURL_DOES_CONVERSIONS */
if(100 <= k->httpcode && 199 >= k->httpcode) {
/*
* We have made a HTTP PUT or POST and this is 1.1-lingo
* that tells us that the server is OK with this and ready
* to receive the data.
* However, we'll get more headers now so we must get
* back into the header-parsing state!
*/
k->header = TRUE;
k->headerline = 0; /* restart the header line counter */
/* "A user agent MAY ignore unexpected 1xx status responses." */
switch(k->httpcode) {
case 100:
/* if we did wait for this do enable write now! */
if(k->exp100) {
k->exp100 = EXP100_SEND_DATA;
k->keepon |= KEEP_SEND;
}
break;