Newer
Older
* 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 */
Daniel Stenberg
committed
failf(data, "Could only read %" FORMAT_OFF_T
" bytes from the input",
passed);
return CURLE_READ_ERROR;
}
} while(passed < data->state.resume_from);
Daniel Stenberg
committed
}
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",
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",
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);
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;
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
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_checkheaders(data, "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 !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
}
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;
if(!data->req.upload_chunky &&
!Curl_checkheaders(data, "Content-Length:")) {
Daniel Stenberg
committed
/* only add Content-Length if not uploading chunked */
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer,
"Content-Length: %" FORMAT_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->set.infilesize;
if((postsize != -1) && !data->req.upload_chunky &&
!Curl_checkheaders(data, "Content-Length:")) {
Daniel Stenberg
committed
/* only add Content-Length if not uploading chunked */
Daniel Stenberg
committed
result = Curl_add_bufferf(req_buffer,
"Content-Length: %" FORMAT_OFF_T "\r\n",
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,
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,
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;
/* 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,
"\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7);
Daniel Stenberg
committed
/* 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;
if(http->writebytecount >= postsize) {
/* already sent the entire request body, mark the "upload" as
complete */
infof(data, "upload completely sent off: %" FORMAT_OFF_T " out of "
"%" FORMAT_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
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
#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);
}
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
/*
* 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;
}
/*
* 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;
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
/* 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 */
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
2790
2791
2792
2793
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 */
2796
2797
2798
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
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
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 */
/* if we did wait for this do enable write now! */
if(k->exp100) {
k->exp100 = EXP100_SEND_DATA;
k->keepon |= KEEP_SEND;
}
}
else {
k->header = FALSE; /* no more header to parse! */
if((k->size == -1) && !k->chunk && !conn->bits.close &&
!(conn->handler->protocol & CURLPROTO_RTSP)) {
/* On HTTP 1.1, when connection is not to get closed, but no
Content-Length nor Content-Encoding chunked have been
received, according to RFC2616 section 4.4 point 5, we
assume that the server will close the connection to
signal the end of the document. */
infof(data, "no chunk, no close, no size. Assume close to "
"signal end\n");
conn->bits.close = TRUE;
}
}
/*
* When all the headers have been parsed, see if we should give
* up and return an error.
*/
if(http_should_fail(conn)) {
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
failf (data, "The requested URL returned error: %d",
k->httpcode);
return CURLE_HTTP_RETURNED_ERROR;
}
/* now, only output this if the header AND body are requested:
*/
writetype = CLIENTWRITE_HEADER;
if(data->set.include_header)
writetype |= CLIENTWRITE_BODY;
headerlen = k->p - data->state.headerbuff;
result = Curl_client_write(conn, writetype,
data->state.headerbuff,
headerlen);
if(result)
return result;
data->info.header_size += (long)headerlen;
data->req.headerbytecount += (long)headerlen;
data->req.deductheadercount =
(100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;
if(!*stop_reading) {
/* Curl_http_auth_act() checks what authentication methods
* that are available and decides which one (if any) to
* use. It will set 'newurl' if an auth method was picked. */
result = Curl_http_auth_act(conn);
if(result)
return result;
2904
2905
2906
2907
2908
2909
2910
2911
2912
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
if(k->httpcode >= 300) {
if((!conn->bits.authneg) && !conn->bits.close &&
!conn->bits.rewindaftersend) {
/*
* General treatment of errors when about to send data. Including :
* "417 Expectation Failed", while waiting for 100-continue.
*
* The check for close above is done simply because of something
* else has already deemed the connection to get closed then
* something else should've considered the big picture and we
* avoid this check.
*
* rewindaftersend indicates that something has told libcurl to
* continue sending even if it gets discarded
*/
switch(data->set.httpreq) {
case HTTPREQ_PUT:
case HTTPREQ_POST:
case HTTPREQ_POST_FORM:
/* We got an error response. If this happened before the whole
* request body has been sent we stop sending and mark the
* connection for closure after we've read the entire response.
*/
if(!k->upload_done) {
infof(data, "HTTP error before end of send, stop sending\n");
conn->bits.close = TRUE; /* close after this */
k->upload_done = TRUE;
k->keepon &= ~KEEP_SEND; /* don't send */
if(data->state.expect100header)
k->exp100 = EXP100_FAILED;
}
break;
default: /* default label present to avoid compiler warnings */
break;
}
}
}
2944
2945
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
if(conn->bits.rewindaftersend) {
/* We rewind after a complete send, so thus we continue
sending now */
infof(data, "Keep sending data to get tossed away!\n");
k->keepon |= KEEP_SEND;
}
}
if(!k->header) {
/*
* really end-of-headers.
*
* If we requested a "no body", this is a good time to get
* out and return home.
*/
if(data->set.opt_no_body)
*stop_reading = TRUE;
else {
/* If we know the expected size of this document, we set the
maximum download size to the size of the expected
document or else, we won't know when to stop reading!
Note that we set the download maximum even if we read a
"Connection: close" header, to make sure that
"Content-Length: 0" still prevents us from attempting to
read the (missing) response-body.
*/
/* According to RFC2616 section 4.4, we MUST ignore
Content-Length: headers if we are now receiving data
using chunked Transfer-Encoding.
*/
if(k->chunk)
k->maxdownload = k->size = -1;
}
if(-1 != k->size) {
/* We do this operation even if no_body is true, since this
data might be retrieved later with curl_easy_getinfo()
and its CURLINFO_CONTENT_LENGTH_DOWNLOAD option. */
Curl_pgrsSetDownloadSize(data, k->size);
k->maxdownload = k->size;
}
Daniel Stenberg
committed
/* If max download size is *zero* (nothing) we already
have nothing and can safely return ok now! */
if(0 == k->maxdownload)
*stop_reading = TRUE;
if(*stop_reading) {
/* we make sure that this socket isn't read more now */
k->keepon &= ~KEEP_RECV;
}
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN,
k->str_start, headerlen, conn);
break; /* exit header line loop */