Newer
Older
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
Daniel Stenberg
committed
* Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
***************************************************************************/
/* -- WIN32 approved -- */
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include "strequal.h"
Daniel Stenberg
committed
#include "rawstr.h"
#include <time.h>
#include <io.h>
#else
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_NETDB_H
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_SIGNAL_H
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifndef HAVE_SOCKET
#error "We can't compile without socket() support!"
#endif
#endif /* WIN32 */
#include "urldata.h"
#include <curl/curl.h>
#include "netrc.h"
#include "transfer.h"
#include "sendf.h"
#include "speedcheck.h"
#include "progress.h"
#include "url.h"
#include "getinfo.h"
Daniel Stenberg
committed
#include "sslgen.h"
#include "http_digest.h"
#include "http_ntlm.h"
#include "http_negotiate.h"
Daniel Stenberg
committed
#include "multiif.h"
#include "easyif.h" /* for Curl_convert_to_network prototype */
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
/* The last #include file should be: */
#include "memdebug.h"
#define CURL_TIMEOUT_EXPECT_100 1000 /* counting ms here */
#ifndef CURL_DISABLE_HTTP
static CURLcode readwrite_http_headers(struct SessionHandle *data,
struct connectdata *conn,
struct SingleRequest *k,
ssize_t *nread,
bool *stop_reading);
#endif /* CURL_DISABLE_HTTP */
Daniel Stenberg
committed
/*
* This function will call the read callback to fill our buffer with data
* to upload.
*/
CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
Daniel Stenberg
committed
{
struct SessionHandle *data = conn->data;
size_t buffersize = (size_t)bytes;
Daniel Stenberg
committed
int nread;
#ifdef CURL_DOES_CONVERSIONS
Yang Tse
committed
bool sending_http_headers = FALSE;
Daniel Stenberg
committed
Yang Tse
committed
if((conn->protocol&PROT_HTTP) &&
(data->state.proto.http->sending == HTTPSEND_REQUEST)) {
/* We're sending the HTTP request headers, not the data.
Remember that so we don't re-translate them into garbage. */
sending_http_headers = TRUE;
}
#endif
if(data->req.upload_chunky) {
/* if chunked Transfer-Encoding */
buffersize -= (8 + 2 + 2); /* 32bit hex + CRLF + CRLF */
data->req.upload_fromhere += (8 + 2); /* 32bit hex + CRLF */
}
/* this function returns a size_t, so we typecast to int to prevent warnings
with picky compilers */
Daniel Stenberg
committed
nread = (int)conn->fread_func(data->req.upload_fromhere, 1,
Daniel Stenberg
committed
buffersize, conn->fread_in);
if(nread == CURL_READFUNC_ABORT) {
Daniel Stenberg
committed
failf(data, "operation aborted by callback");
Daniel Stenberg
committed
*nreadp = 0;
return CURLE_ABORTED_BY_CALLBACK;
}
Daniel Stenberg
committed
else if(nread == CURL_READFUNC_PAUSE) {
struct SingleRequest *k = &data->req;
Daniel Stenberg
committed
/* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
k->keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
if(data->req.upload_chunky) {
/* Back out the preallocation done above */
data->req.upload_fromhere -= (8 + 2);
}
Daniel Stenberg
committed
*nreadp = 0;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
else if((size_t)nread > buffersize) {
Daniel Stenberg
committed
/* the read function returned a too large value */
Daniel Stenberg
committed
*nreadp = 0;
failf(data, "read function returned funny value");
Daniel Stenberg
committed
return CURLE_READ_ERROR;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if(!data->req.forbidchunk && data->req.upload_chunky) {
/* if chunked Transfer-Encoding
* build chunk:
*
* <HEX SIZE> CRLF
* <DATA> CRLF
*/
/* On non-ASCII platforms the <DATA> may or may not be
translated based on set.prefer_ascii while the protocol
portion must always be translated to the network encoding.
To further complicate matters, line end conversion might be
done later on, so we need to prevent CRLFs from becoming
CRCRLFs if that's the case. To do this we use bare LFs
here, knowing they'll become CRLFs later on.
*/
Daniel Stenberg
committed
char hexbuffer[11];
const char *endofline_native;
const char *endofline_network;
int hexlen;
#ifdef CURL_DO_LINEEND_CONV
if((data->set.crlf) || (data->set.prefer_ascii)) {
#else
if(data->set.crlf) {
#endif /* CURL_DO_LINEEND_CONV */
/* \n will become \r\n later on */
endofline_native = "\n";
endofline_network = "\x0a";
} else {
endofline_native = "\r\n";
endofline_network = "\x0d\x0a";
}
hexlen = snprintf(hexbuffer, sizeof(hexbuffer),
"%x%s", nread, endofline_native);
Daniel Stenberg
committed
/* move buffer pointer */
Daniel Stenberg
committed
data->req.upload_fromhere -= hexlen;
Daniel Stenberg
committed
nread += hexlen;
/* copy the prefix to the buffer, leaving out the NUL */
Daniel Stenberg
committed
memcpy(data->req.upload_fromhere, hexbuffer, hexlen);
/* always append ASCII CRLF to the data */
memcpy(data->req.upload_fromhere + nread,
endofline_network,
strlen(endofline_network));
#ifdef CURL_DOES_CONVERSIONS
CURLcode res;
int length;
if(data->set.prefer_ascii) {
/* translate the protocol and data */
length = nread;
} else {
/* just translate the protocol portion */
length = strlen(hexbuffer);
}
res = Curl_convert_to_network(data, data->req.upload_fromhere, length);
/* Curl_convert_to_network calls failf if unsuccessful */
if(res != CURLE_OK) {
return(res);
}
#endif /* CURL_DOES_CONVERSIONS */
if((nread - hexlen) == 0) {
Daniel Stenberg
committed
/* mark this as done once this chunk is transfered */
Daniel Stenberg
committed
data->req.upload_done = TRUE;
Daniel Stenberg
committed
}
nread+=(int)strlen(endofline_native); /* for the added end of line */
Yang Tse
committed
}
#ifdef CURL_DOES_CONVERSIONS
Yang Tse
committed
else if((data->set.prefer_ascii) && (!sending_http_headers)) {
CURLcode res;
res = Curl_convert_to_network(data, data->req.upload_fromhere, nread);
/* Curl_convert_to_network calls failf if unsuccessful */
if(res != CURLE_OK)
return(res);
}
Yang Tse
committed
#endif /* CURL_DOES_CONVERSIONS */
*nreadp = nread;
return CURLE_OK;
Daniel Stenberg
committed
}
#ifndef CURL_DISABLE_HTTP
/*
* 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 */
char *scratch = calloc(1, strlen(s)+1);
Daniel Stenberg
committed
if(NULL == scratch) {
failf (data, "Failed to calloc memory for conversion!");
return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
}
strcpy(scratch, s);
Daniel Stenberg
committed
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 */
Daniel Stenberg
committed
while(head) {
if(checkprefix(head->data, s)) {
rc = TRUE;
break;
}
Daniel Stenberg
committed
if((rc != TRUE) && (checkprefix("HTTP/", s))) {
rc = TRUE;
}
#ifdef CURL_DOES_CONVERSIONS
free(scratch);
#endif /* CURL_DOES_CONVERSIONS */
return rc;
#endif /* CURL_DISABLE_HTTP */
Daniel Stenberg
committed
Daniel Stenberg
committed
* Curl_readrewind() rewinds the read stream. This is typically used for HTTP
* POST/PUT with multi-pass authentication when a sending was denied and a
* resend is necessary.
*/
CURLcode Curl_readrewind(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
conn->bits.rewindaftersend = FALSE; /* we rewind now */
/* explicitly switch off sending data on this connection now since we are
about to restart a new transfer and thus we want to avoid inadvertently
sending more data on the existing connection until the next transfer
starts */
data->req.keepon &= ~KEEP_SEND;
Daniel Stenberg
committed
/* We have sent away data. If not using CURLOPT_POSTFIELDS or
CURLOPT_HTTPPOST, call app to rewind
*/
if(data->set.postfields ||
(data->set.httpreq == HTTPREQ_POST_FORM))
; /* do nothing */
else {
Daniel Stenberg
committed
if(data->set.seek_func) {
int err;
err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
if(err) {
Daniel Stenberg
committed
failf(data, "seek callback returned error %d", (int)err);
Daniel Stenberg
committed
return CURLE_SEND_FAIL_REWIND;
}
}
else if(data->set.ioctl_func) {
Daniel Stenberg
committed
err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
data->set.ioctl_client);
infof(data, "the ioctl callback returned %d\n", (int)err);
if(err) {
/* FIXME: convert to a human readable error message */
Daniel Stenberg
committed
failf(data, "ioctl callback returned error %d", (int)err);
return CURLE_SEND_FAIL_REWIND;
}
}
else {
/* If no CURLOPT_READFUNCTION is used, we know that we operate on a
given FILE * stream and we can actually attempt to rewind that
ourself with fseek() */
if(data->set.fread_func == (curl_read_callback)fread) {
if(-1 != fseek(data->set.in, 0, SEEK_SET))
/* successful rewind */
return CURLE_OK;
}
/* no callback set or failure above, makes us fail at once */
Daniel Stenberg
committed
failf(data, "necessary data rewind wasn't possible");
return CURLE_SEND_FAIL_REWIND;
}
}
return CURLE_OK;
}
static int data_pending(const struct connectdata *conn)
{
Daniel Stenberg
committed
/* in the case of libssh2, we can never be really sure that we have emptied
its internal buffers so we MUST always try until we get EAGAIN back */
return conn->protocol&(PROT_SCP|PROT_SFTP) ||
Curl_ssl_data_pending(conn, FIRSTSOCKET);
}
Daniel Stenberg
committed
static void read_rewind(struct connectdata *conn,
size_t thismuch)
{
DEBUGASSERT(conn->read_pos >= thismuch);
Daniel Stenberg
committed
conn->read_pos -= thismuch;
conn->bits.stream_was_rewound = TRUE;
Daniel Stenberg
committed
{
char buf[512 + 1];
size_t show;
show = CURLMIN(conn->buf_len - conn->read_pos, sizeof(buf)-1);
Daniel Stenberg
committed
if(conn->master_buffer) {
Daniel Stenberg
committed
memcpy(buf, conn->master_buffer + conn->read_pos, show);
buf[show] = '\0';
}
else {
buf[0] = '\0';
}
Daniel Stenberg
committed
DEBUGF(infof(conn->data,
"Buffer after stream rewind (read_pos = %d): [%s]",
conn->read_pos, buf));
}
#endif
}
* Go ahead and do a read if we have a readable socket or if
* the stream was rewound (in which case we have data in a
* buffer)
static CURLcode readwrite_data(struct SessionHandle *data,
struct connectdata *conn,
struct SingleRequest *k,
int *didwhat, bool *done)
CURLcode result = CURLE_OK;
ssize_t nread; /* number of bytes read */
bool is_empty_data = FALSE;
/* This is where we loop until we have read everything there is to
read or we get a EWOULDBLOCK */
do {
size_t buffersize = data->set.buffer_size?
data->set.buffer_size : BUFSIZE;
size_t bytestoread = buffersize;
int readrc;
if(k->size != -1 && !k->header) {
/* make sure we don't read "too much" if we can help it since we
Daniel Stenberg
committed
might be pipelining and then someone else might want to read what
follows! */
curl_off_t totalleft = k->size - k->bytecount;
if(totalleft < (curl_off_t)bytestoread)
Daniel Stenberg
committed
bytestoread = (size_t)totalleft;
Daniel Stenberg
committed
if(bytestoread) {
/* receive data from the network! */
readrc = Curl_read(conn, conn->sockfd, k->buf, bytestoread, &nread);
/* subzero, this would've blocked */
if(0 > readrc)
Daniel Stenberg
committed
break; /* get out of loop */
/* get the CURLcode from the int */
result = (CURLcode)readrc;
if(result>0)
Daniel Stenberg
committed
return result;
}
else {
/* read nothing but since we wanted nothing we consider this an OK
Daniel Stenberg
committed
situation to proceed from */
nread = 0;
}
if((k->bytecount == 0) && (k->writebytecount == 0)) {
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
if(k->exp100 > EXP100_SEND_DATA)
Daniel Stenberg
committed
/* set time stamp to compare with when waiting for the 100 */
k->start100 = Curl_tvnow();
James Housley
committed
*didwhat |= KEEP_RECV;
/* indicates data of zero size, i.e. empty file */
is_empty_data = (bool)((nread == 0) && (k->bodywrites == 0));
/* NUL terminate, allowing string ops to be used */
if(0 < nread || is_empty_data) {
k->buf[nread] = 0;
}
else if(0 >= nread) {
/* if we receive 0 or less here, the server closed the connection
Daniel Stenberg
committed
and we bail out from this! */
DEBUGF(infof(data, "nread <= 0, server closed connection, bailing\n"));
k->keepon &= ~KEEP_RECV;
break;
}
/* Default buffer to use when we write the buffer, it may be changed
in the flow below before the actual storing is done. */
k->str = k->buf;
Daniel Stenberg
committed
#ifndef CURL_DISABLE_HTTP
/* Since this is a two-state thing, we check if we are parsing
headers at the moment or not. */
if(k->header) {
/* we are in parse-the-header-mode */
bool stop_reading = FALSE;
result = readwrite_http_headers(data, conn, k, &nread, &stop_reading);
if(result)
Daniel Stenberg
committed
return result;
if(stop_reading)
Daniel Stenberg
committed
/* We've stopped dealing with input, get out of the do-while loop */
break;
#endif /* CURL_DISABLE_HTTP */
/* This is not an 'else if' since it may be a rest from the header
parsing, where the beginning of the buffer is headers and the end
is non-headers. */
if(k->str && !k->header && (nread > 0 || is_empty_data)) {
#ifndef CURL_DISABLE_HTTP
if(0 == k->bodywrites && !is_empty_data) {
Daniel Stenberg
committed
/* These checks are only made the first time we are about to
write a piece of the body */
if(conn->protocol&PROT_HTTP) {
/* HTTP-only checks */
if(data->req.newurl) {
if(conn->bits.close) {
/* Abort after the headers if "follow Location" is set
and we're set to close anyway. */
k->keepon &= ~KEEP_RECV;
Daniel Stenberg
committed
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
*done = TRUE;
return CURLE_OK;
}
/* We have a new url to load, but since we want to be able
to re-use this connection properly, we read the full
response in "ignore more" */
k->ignorebody = TRUE;
infof(data, "Ignoring the response-body\n");
}
if(data->state.resume_from && !k->content_range &&
(data->set.httpreq==HTTPREQ_GET) &&
!k->ignorebody) {
/* we wanted to resume a download, although the server doesn't
* seem to support this and we did this with a GET (if it
* wasn't a GET we did a POST or PUT resume) */
failf(data, "HTTP server doesn't seem to support "
"byte ranges. Cannot resume.");
return CURLE_RANGE_ERROR;
}
if(data->set.timecondition && !data->state.range) {
/* A time condition has been set AND no ranges have been
requested. This seems to be what chapter 13.3.4 of
RFC 2616 defines to be the correct action for a
HTTP/1.1 client */
if((k->timeofdoc > 0) && (data->set.timevalue > 0)) {
switch(data->set.timecondition) {
case CURL_TIMECOND_IFMODSINCE:
default:
if(k->timeofdoc < data->set.timevalue) {
infof(data,
"The requested document is not new enough\n");
*done = TRUE;
Daniel Stenberg
committed
data->info.timecond = TRUE;
Daniel Stenberg
committed
return CURLE_OK;
}
break;
case CURL_TIMECOND_IFUNMODSINCE:
if(k->timeofdoc > data->set.timevalue) {
infof(data,
"The requested document is not old enough\n");
*done = TRUE;
Daniel Stenberg
committed
data->info.timecond = TRUE;
Daniel Stenberg
committed
return CURLE_OK;
}
break;
} /* switch */
} /* two valid time strings */
} /* we have a time condition */
} /* this is HTTP */
} /* this is the first time we write a body part */
#endif /* CURL_DISABLE_HTTP */
k->bodywrites++;
/* pass data to the debug function before it gets "dechunked" */
if(data->set.verbose) {
Daniel Stenberg
committed
if(k->badheader) {
Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff,
(size_t)k->hbuflen, conn);
if(k->badheader == HEADER_PARTHEADER)
Curl_debug(data, CURLINFO_DATA_IN,
k->str, (size_t)nread, conn);
}
else
Curl_debug(data, CURLINFO_DATA_IN,
k->str, (size_t)nread, conn);
}
#ifndef CURL_DISABLE_HTTP
if(k->chunk) {
Daniel Stenberg
committed
/*
* Here comes a chunked transfer flying and we need to decode this
* properly. While the name says read, this function both reads
* and writes away the data. The returned 'nread' holds the number
* of actual data it wrote to the client.
*/
CHUNKcode res =
Curl_httpchunk_read(conn, k->str, nread, &nread);
if(CHUNKE_OK < res) {
if(CHUNKE_WRITE_ERROR == res) {
failf(data, "Failed writing data");
return CURLE_WRITE_ERROR;
}
failf(data, "Received problem %d in the chunky parser", res);
return CURLE_RECV_ERROR;
}
else if(CHUNKE_STOP == res) {
size_t dataleft;
/* we're done reading chunks! */
k->keepon &= ~KEEP_RECV; /* read no more */
Daniel Stenberg
committed
/* There are now possibly N number of bytes at the end of the
str buffer that weren't written to the client.
We DO care about this data if we are pipelining.
Push it back to be read on the next pass. */
dataleft = conn->chunk.dataleft;
if(dataleft != 0) {
infof(conn->data, "Leftovers after chunking. "
" Rewinding %d bytes\n",dataleft);
read_rewind(conn, dataleft);
}
}
/* If it returned OK, we just keep going */
}
#endif /* CURL_DISABLE_HTTP */
if((-1 != k->maxdownload) &&
Daniel Stenberg
committed
(k->bytecount + nread >= k->maxdownload)) {
if(conn->data->multi && Curl_multi_canPipeline(conn->data->multi)) {
/* The 'excess' amount below can't be more than BUFSIZE which
always will fit in a size_t */
size_t excess = (size_t)(k->bytecount + nread - k->maxdownload);
if(excess > 0 && !k->ignorebody) {
infof(data,
"Rewinding stream by : %d"
" bytes on url %s (size = %" FORMAT_OFF_T
", maxdownload = %" FORMAT_OFF_T
", bytecount = %" FORMAT_OFF_T ", nread = %d)\n",
excess, data->state.path,
k->size, k->maxdownload, k->bytecount, nread);
read_rewind(conn, excess);
}
Daniel Stenberg
committed
}
nread = (ssize_t) (k->maxdownload - k->bytecount);
if(nread < 0 ) /* this should be unusual */
nread = 0;
k->keepon &= ~KEEP_RECV; /* we're done reading */
}
Daniel Stenberg
committed
k->bytecount += nread;
Curl_pgrsSetDownloadCounter(data, k->bytecount);
if(!k->chunk && (nread || k->badheader || is_empty_data)) {
Daniel Stenberg
committed
/* If this is chunky transfer, it was already written */
if(k->badheader && !k->ignorebody) {
/* we parsed a piece of data wrongly assuming it was a header
and now we output it as body instead */
result = Curl_client_write(conn, CLIENTWRITE_BODY,
data->state.headerbuff,
k->hbuflen);
if(result)
return result;
}
if(k->badheader < HEADER_ALLBAD) {
/* This switch handles various content encodings. If there's an
error here, be sure to check over the almost identical code
in http_chunks.c.
Make sure that ALL_CONTENT_ENCODINGS contains all the
encodings handled here. */
#ifdef HAVE_LIBZ
Daniel Stenberg
committed
switch (conn->data->set.http_ce_skip ?
IDENTITY : k->content_encoding) {
case IDENTITY:
Daniel Stenberg
committed
/* This is the default when the server sends no
Content-Encoding header. See Curl_readwrite_init; the
memset() call initializes k->content_encoding to zero. */
if(!k->ignorebody)
result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str,
nread);
#ifdef HAVE_LIBZ
Daniel Stenberg
committed
break;
case DEFLATE:
/* Assume CLIENTWRITE_BODY; headers are not encoded. */
if(!k->ignorebody)
result = Curl_unencode_deflate_write(conn, k, nread);
break;
case GZIP:
/* Assume CLIENTWRITE_BODY; headers are not encoded. */
if(!k->ignorebody)
result = Curl_unencode_gzip_write(conn, k, nread);
break;
case COMPRESS:
default:
failf (data, "Unrecognized content encoding type. "
"libcurl understands `identity', `deflate' and `gzip' "
"content encodings.");
result = CURLE_BAD_CONTENT_ENCODING;
break;
}
Daniel Stenberg
committed
}
k->badheader = HEADER_NORMAL; /* taken care of now */
Daniel Stenberg
committed
if(result)
return result;
}
Daniel Stenberg
committed
} /* if(! header and data to read ) */
Daniel Stenberg
committed
if(is_empty_data) {
/* if we received nothing, the server closed the connection and we
Daniel Stenberg
committed
are done */
k->keepon &= ~KEEP_RECV;
}
} while(data_pending(conn));
if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) &&
conn->bits.close ) {
/* When we've read the entire thing and the close bit is set, the server
may now close the connection. If there's now any kind of sending going
on from our side, we need to stop that immediately. */
infof(data, "we are done reading and this is set to close, stop send\n");
k->keepon &= ~KEEP_SEND; /* no writing anymore either */
}
return CURLE_OK;
}
#ifndef CURL_DISABLE_HTTP
Daniel Stenberg
committed
/*
* 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)
{
Daniel Stenberg
committed
/* We enlarge the header buffer as it is too small */
char *newbuff;
size_t hbufp_index;
Daniel Stenberg
committed
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);
Daniel Stenberg
committed
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.
static CURLcode readwrite_http_headers(struct SessionHandle *data,
struct connectdata *conn,
struct SingleRequest *k,
ssize_t *nread,
bool *stop_reading)
{
CURLcode result;
/* 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
Daniel Stenberg
committed
the end of the headerbuff. */
Daniel Stenberg
committed
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 HTTP header */
if(!checkhttpprefix(data, data->state.headerbuff)) {
/* this is not the beginning of a HTTP first header line */
k->header = FALSE;
k->badheader = HEADER_ALLBAD;
break;
}
}
Daniel Stenberg
committed
break; /* read more and try again */
}
Daniel Stenberg
committed
/* decrease the size of the remaining (supposed) header line */
rest_length = (k->end_ptr - k->str)+1;
*nread -= (ssize_t)rest_length;
Daniel Stenberg
committed
k->str = k->end_ptr + 1; /* move past new line */
full_length = k->str - k->str_start;
Daniel Stenberg
committed
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
!checkhttpprefix(data, data->state.headerbuff)) {
/* this is not the beginning of a HTTP first header line */
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) {
Daniel Stenberg
committed
*k->p = '\r'; /* replace with CR in host encoding */
k->p++; /* pass the CR byte */
}
if(0x0a == *k->p) {
Daniel Stenberg
committed
*k->p = '\n'; /* replace with LF in host encoding */
k->p++; /* pass the LF byte */
#else
if('\r' == *k->p)
Daniel Stenberg
committed
k->p++; /* pass the \r byte */
if('\n' == *k->p)
Daniel Stenberg
committed
k->p++; /* pass the \n byte */
#endif /* CURL_DOES_CONVERSIONS */
Daniel Stenberg
committed
if(100 <= k->httpcode && 199 >= k->httpcode) {
Daniel Stenberg
committed
/*
* 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;
Daniel Stenberg
committed
}
}
else {
Daniel Stenberg
committed
k->header = FALSE; /* no more header to parse! */
if((k->size == -1) && !k->chunk && !conn->bits.close &&
(conn->httpversion >= 11) ) {
/* 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;
}
if(417 == k->httpcode) {
Daniel Stenberg
committed
/*
* we got: "417 Expectation Failed" this means:
* we have made a HTTP call and our Expect Header
* seems to cause a problem => abort the write operations
* (or prevent them from starting).
*/
k->exp100 = EXP100_FAILED;
k->keepon &= ~KEEP_SEND;
/*
* When all the headers have been parsed, see if we should give
* up and return an error.
*/
if(Curl_http_should_fail(conn)) {
Daniel Stenberg
committed
failf (data, "The requested URL returned error: %d",
k->httpcode);
return CURLE_HTTP_RETURNED_ERROR;
Daniel Stenberg
committed
/* now, only output this if the header AND body are requested:
*/
writetype = CLIENTWRITE_HEADER;
if(data->set.include_header)
Daniel Stenberg
committed
writetype |= CLIENTWRITE_BODY;
Daniel Stenberg
committed
headerlen = k->p - data->state.headerbuff;
Daniel Stenberg
committed
result = Curl_client_write(conn, writetype,
Daniel Stenberg
committed
data->state.headerbuff,
headerlen);
if(result)
Daniel Stenberg
committed
return result;
Daniel Stenberg
committed
data->info.header_size += (long)headerlen;
data->req.headerbytecount += (long)headerlen;
Daniel Stenberg
committed
data->req.deductheadercount =
Daniel Stenberg
committed
(100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;
if(data->state.resume_from &&
Daniel Stenberg
committed
(data->set.httpreq==HTTPREQ_GET) &&
(k->httpcode == 416)) {
/* "Requested Range Not Satisfiable" */
*stop_reading = TRUE;
if(!*stop_reading) {
Daniel Stenberg
committed
/* 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;
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;
Daniel Stenberg
committed
}
if(!k->header) {
Daniel Stenberg
committed
/*
* really end-of-headers.
*
* If we requested a "no body", this is a good time to get
* out and return home.