Newer
Older
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
Daniel Stenberg
committed
* Copyright (C) 1998 - 2006, 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>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#include <errno.h>
#include "strequal.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
#include <netdb.h>
#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
#include <signal.h>
#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
#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"
#include "memory.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 */
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;
if(conn->bits.upload_chunky) {
/* if chunked Transfer-Encoding */
buffersize -= (8 + 2 + 2); /* 32bit hex + CRLF + CRLF */
data->reqdata.upload_fromhere += 10; /* 32bit hex + CRLF */
Daniel Stenberg
committed
}
/* this function returns a size_t, so we typecast to int to prevent warnings
with picky compilers */
nread = (int)conn->fread(data->reqdata.upload_fromhere, 1,
if(nread == CURL_READFUNC_ABORT) {
failf(data, "operation aborted by callback\n");
return CURLE_ABORTED_BY_CALLBACK;
}
Daniel Stenberg
committed
if(!conn->bits.forbidchunk && conn->bits.upload_chunky) {
/* if chunked Transfer-Encoding */
char hexbuffer[11];
int hexlen = snprintf(hexbuffer, sizeof(hexbuffer),
"%x\r\n", nread);
/* move buffer pointer */
data->reqdata.upload_fromhere -= hexlen;
Daniel Stenberg
committed
nread += hexlen;
/* copy the prefix to the buffer */
memcpy(data->reqdata.upload_fromhere, hexbuffer, hexlen);
/* always append CRLF to the data */
memcpy(data->reqdata.upload_fromhere + nread, "\r\n", 2);
if((nread - hexlen) == 0) {
Daniel Stenberg
committed
/* mark this as done once this chunk is transfered */
data->reqdata.keep.upload_done = TRUE;
Daniel Stenberg
committed
}
nread+=2; /* for the added CRLF */
Daniel Stenberg
committed
}
*nreadp = nread;
#ifdef CURL_DOES_CONVERSIONS
if(data->set.prefer_ascii) {
CURLcode res;
res = Curl_convert_to_network(data, data->reqdata.upload_fromhere, nread);
/* Curl_convert_to_network calls failf if unsuccessful */
if(res != CURLE_OK) {
return(res);
}
}
#endif /* CURL_DOES_CONVERSIONS */
return CURLE_OK;
Daniel Stenberg
committed
}
/*
* 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;
while (head) {
if (checkprefix(head->data, s))
return TRUE;
head = head->next;
}
if(checkprefix("HTTP/", s))
return TRUE;
return FALSE;
}
Daniel Stenberg
committed
/*
* Curl_readrewind() rewinds the read stream. This typically (so far) only
* 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 */
/* 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 {
if(data->set.ioctl) {
Daniel Stenberg
committed
err = (data->set.ioctl) (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 */
failf(data, "ioctl callback returned error %d\n", (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 == (curl_read_callback)fread) {
if(-1 != fseek(data->set.in, 0, SEEK_SET))
/* successful rewind */
return CURLE_OK;
}
/* no callback set or failure aboe, makes us fail at once */
failf(data, "necessary data rewind wasn't possible\n");
return CURLE_SEND_FAIL_REWIND;
}
}
return CURLE_OK;
}
#ifdef USE_SSLEAY
Daniel Stenberg
committed
/* FIX: this is nasty OpenSSL-specific code that really shouldn't be here */
static int data_pending(struct connectdata *conn)
{
if(conn->ssl[FIRSTSOCKET].handle)
/* SSL is in use */
return SSL_pending(conn->ssl[FIRSTSOCKET].handle);
return 0; /* nothing */
}
#else
/* non-SSL never have pending data */
#define data_pending(x) 0
#endif
/*
* Curl_readwrite() is the low-level function to be called when data is to
* be read and written to/from the connection.
*/
CURLcode Curl_readwrite(struct connectdata *conn,
bool *done)
struct SessionHandle *data = conn->data;
struct Curl_transfer_keeper *k = &data->reqdata.keep;
ssize_t nread; /* number of bytes read */
int didwhat=0;
curl_socket_t fd_read;
curl_socket_t fd_write;
Daniel Stenberg
committed
curl_off_t contentlength;
if(k->keepon & KEEP_READ)
fd_read = conn->sockfd;
else
fd_read = CURL_SOCKET_BAD;
if(k->keepon & KEEP_WRITE)
fd_write = conn->writesockfd;
else
fd_write = CURL_SOCKET_BAD;
select_res = Curl_select(fd_read, fd_write, 0);
Daniel Stenberg
committed
if(select_res == CSELECT_ERR) {
failf(data, "select/poll returned error");
return CURLE_SEND_ERROR;
}
/* We 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) */
if((k->keepon & KEEP_READ) &&
((select_res & CSELECT_IN) || conn->bits.stream_was_rewound)) {
/* read */
Daniel Stenberg
committed
bool is_empty_data = FALSE;
Daniel Stenberg
committed
/* 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)
bytestoread = k->size - k->bytecount;
Daniel Stenberg
committed
/* receive data from the network! */
readrc = Curl_read(conn, conn->sockfd, k->buf, bytestoread, &nread);
Daniel Stenberg
committed
/* 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;
Daniel Stenberg
committed
if(result>0)
return result;
if ((k->bytecount == 0) && (k->writebytecount == 0)) {
Daniel Stenberg
committed
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
if(k->wait100_after_headers)
/* set time stamp to compare with when waiting for the 100 */
k->start100 = Curl_tvnow();
}
Daniel Stenberg
committed
didwhat |= KEEP_READ;
Daniel Stenberg
committed
/* indicates data of zero size, i.e. empty file */
is_empty_data = (bool)((nread == 0) && (k->bodywrites == 0));
Daniel Stenberg
committed
/* NULL terminate, allowing string ops to be used */
if (0 < nread || is_empty_data) {
Daniel Stenberg
committed
k->buf[nread] = 0;
}
Daniel Stenberg
committed
else if (0 >= nread) {
/* if we receive 0 or less here, the server closed the connection
and we bail out from this! */
Daniel Stenberg
committed
k->keepon &= ~KEEP_READ;
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;
/* Since this is a two-state thing, we check if we are parsing
headers at the moment or not. */
Daniel Stenberg
committed
if (k->header) {
/* we are in parse-the-header-mode */
bool stop_reading = FALSE;
Daniel Stenberg
committed
/* header line within buffer loop */
do {
size_t hbufp_index;
size_t rest_length;
Daniel Stenberg
committed
/* str_start is start of line within buf */
k->str_start = k->str;
k->end_ptr = memchr(k->str_start, '\n', nread);
Daniel Stenberg
committed
if (!k->end_ptr) {
/* Not a complete header line within buffer, append the data to
the end of the headerbuff. */
if (k->hbuflen + nread >= data->state.headersize) {
/* We enlarge the header buffer as it is too small */
char *newbuff;
size_t newsize=CURLMAX((k->hbuflen+nread)*3/2,
data->state.headersize*2);
Daniel Stenberg
committed
hbufp_index = k->hbufp - data->state.headerbuff;
newbuff = (char *)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, nread);
k->hbufp += nread;
k->hbuflen += nread;
if (!k->headerline && (k->hbuflen>5)) {
/* make a first check that this looks like a HTTP header */
if(!checkhttpprefix(data, data->state.headerbuff)) {
Daniel Stenberg
committed
/* this is not the beginning of a HTTP first header line */
k->header = FALSE;
k->badheader = HEADER_ALLBAD;
break;
}
}
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
Daniel Stenberg
committed
k->str = k->end_ptr + 1; /* move past new line */
full_length = k->str - k->str_start;
Daniel Stenberg
committed
/*
* We're about to copy 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
Daniel Stenberg
committed
* it.
*/
if (k->hbuflen + full_length >=
Daniel Stenberg
committed
data->state.headersize) {
size_t newsize=CURLMAX((k->hbuflen+full_length)*3/2,
data->state.headersize*2);
hbufp_index = k->hbufp - data->state.headerbuff;
newbuff = (char *)realloc(data->state.headerbuff, newsize);
if(!newbuff) {
failf (data, "Failed to alloc memory for big header!");
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
data->state.headersize= newsize;
data->state.headerbuff = newbuff;
k->hbufp = data->state.headerbuff + hbufp_index;
}
Daniel Stenberg
committed
/* copy to end of line */
memcpy(k->hbufp, k->str_start, full_length);
k->hbufp += full_length;
k->hbuflen += full_length;
Daniel Stenberg
committed
*k->hbufp = 0;
k->end_ptr = k->hbufp;
Daniel Stenberg
committed
k->p = data->state.headerbuff;
Daniel Stenberg
committed
/****
* We now have a FULL header line that p points to
*****/
if(!k->headerline) {
/* the first read header */
if((k->hbuflen>5) &&
!checkhttpprefix(data, data->state.headerbuff)) {
Daniel Stenberg
committed
/* 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 its all a bad header */
k->badheader = HEADER_ALLBAD;
nread = (ssize_t)rest_length;
Daniel Stenberg
committed
break;
}
}
Daniel Stenberg
committed
if (('\n' == *k->p) || ('\r' == *k->p)) {
size_t headerlen;
Daniel Stenberg
committed
/* Zero-length header line means end of headers! */
if ('\r' == *k->p)
k->p++; /* pass the \r byte */
if ('\n' == *k->p)
k->p++; /* pass the \n byte */
if(100 == k->httpcode) {
/*
* We have made a HTTP PUT or POST and this is 1.1-lingo
Daniel Stenberg
committed
* that tells us that the server is OK with this and ready
* to receive the data.
Daniel Stenberg
committed
* 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->write_after_100_header) {
k->write_after_100_header = FALSE;
k->keepon |= KEEP_WRITE;
}
}
else
k->header = FALSE; /* no more header to parse! */
if (417 == k->httpcode) {
/*
* 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->write_after_100_header = FALSE;
k->keepon &= ~KEEP_WRITE;
}
#ifndef CURL_DISABLE_HTTP
Daniel Stenberg
committed
/*
* When all the headers have been parsed, see if we should give
* up and return an error.
*/
Daniel Stenberg
committed
if (Curl_http_should_fail(conn)) {
failf (data, "The requested URL returned error: %d",
k->httpcode);
return CURLE_HTTP_RETURNED_ERROR;
}
#endif /* CURL_DISABLE_HTTP */
Daniel Stenberg
committed
Daniel Stenberg
committed
/* now, only output this if the header AND body are requested:
*/
if (data->set.include_header)
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)
return result;
data->info.header_size += (long)headerlen;
k->headerbytecount += (long)headerlen;
Daniel Stenberg
committed
k->deductheadercount =
(100 == k->httpcode)?k->headerbytecount:0;
if (data->reqdata.resume_from &&
(data->set.httpreq==HTTPREQ_GET) &&
(k->httpcode == 416)) {
/* "Requested Range Not Satisfiable" */
stop_reading = TRUE;
#ifndef CURL_DISABLE_HTTP
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 metod was picked. */
result = Curl_http_auth_act(conn);
if(result)
return result;
Daniel Stenberg
committed
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_WRITE;
}
#endif /* CURL_DISABLE_HTTP */
Daniel Stenberg
committed
if(!k->header) {
/*
* really end-of-headers.
*
* If we requested a "no body", this is a good time to get
* out and return home.
*/
Daniel Stenberg
committed
if(conn->bits.no_body)
Daniel Stenberg
committed
stop_reading = TRUE;
Daniel Stenberg
committed
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(conn->bits.chunk)
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)
Daniel Stenberg
committed
stop_reading = TRUE;
Daniel Stenberg
committed
if(stop_reading) {
/* we make sure that this socket isn't read more now */
k->keepon &= ~KEEP_READ;
}
Daniel Stenberg
committed
Daniel Stenberg
committed
}
Daniel Stenberg
committed
/* We continue reading headers, so reset the line-based
header parsing variables hbufp && hbuflen */
k->hbufp = data->state.headerbuff;
k->hbuflen = 0;
continue;
Daniel Stenberg
committed
/*
* Checks for special headers coming up.
*/
Daniel Stenberg
committed
if (!k->headerline++) {
/* This is the first header, it MUST be the error code line
or else we consiser this to be the body right away! */
int httpversion_major;
Daniel Stenberg
committed
int nc=sscanf(k->p, " HTTP/%d.%d %3d",
&httpversion_major,
&k->httpversion,
&k->httpcode);
Daniel Stenberg
committed
if (nc==3) {
k->httpversion += 10 * httpversion_major;
}
else {
/* this is the real world, not a Nirvana
NCSA 1.5.x returns this crap when asked for HTTP/1.1
*/
Daniel Stenberg
committed
nc=sscanf(k->p, " HTTP %3d", &k->httpcode);
Daniel Stenberg
committed
k->httpversion = 10;
/* If user has set option HTTP200ALIASES,
compare header line against list of aliases
*/
if (!nc) {
if (checkhttpprefix(data, k->p)) {
nc = 1;
k->httpcode = 200;
k->httpversion =
(data->set.httpversion==CURL_HTTP_VERSION_1_0)? 10 : 11;
}
}
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if (nc) {
data->info.httpcode = k->httpcode;
data->info.httpversion = k->httpversion;
Daniel Stenberg
committed
/*
* This code executes as part of processing the header. As a
* result, it's not totally clear how to interpret the
* response code yet as that depends on what other headers may
* be present. 401 and 407 may be errors, but may be OK
* depending on how authentication is working. Other codes
* are definitely errors, so give up here.
*/
Daniel Stenberg
committed
if (data->set.http_fail_on_error &&
Daniel Stenberg
committed
(k->httpcode >= 400) &&
(k->httpcode != 401) &&
(k->httpcode != 407)) {
if (data->reqdata.resume_from &&
(data->set.httpreq==HTTPREQ_GET) &&
(k->httpcode == 416)) {
/* "Requested Range Not Satisfiable", just proceed and
pretend this is no error */
}
else {
/* serious error, go home! */
failf (data, "The requested URL returned error: %d",
k->httpcode);
return CURLE_HTTP_RETURNED_ERROR;
}
Daniel Stenberg
committed
}
if(k->httpversion == 10)
/* Default action for HTTP/1.0 must be to close, unless
we get one of those fancy headers that tell us the
server keeps it open for us! */
conn->bits.close = TRUE;
switch(k->httpcode) {
case 204:
/* (quote from RFC2616, section 10.2.5): The server has
* fulfilled the request but does not need to return an
* entity-body ... The 204 response MUST NOT include a
* message-body, and thus is always terminated by the first
* empty line after the header fields. */
/* FALLTHROUGH */
Daniel Stenberg
committed
case 416: /* Requested Range Not Satisfiable, it has the
Content-Length: set as the "real" document but no
actual response is sent. */
Daniel Stenberg
committed
case 304:
Daniel Stenberg
committed
/* (quote from RFC2616, section 10.3.5): The 304 response
* MUST NOT contain a message-body, and thus is always
* terminated by the first empty line after the header
* fields. */
k->size=0;
k->maxdownload=0;
Daniel Stenberg
committed
k->ignorecl = TRUE; /* ignore Content-Length headers */
Daniel Stenberg
committed
break;
default:
/* nothing */
break;
}
}
else {
k->header = FALSE; /* this is not a header line */
break;
Daniel Stenberg
committed
/* Check for Content-Length: header lines to get size. Ignore
the header completely if we get a 416 response as then we're
resuming a document that we don't get, and this header contains
Daniel Stenberg
committed
info about the true size of the document we didn't get now. */
Daniel Stenberg
committed
if (!k->ignorecl && !data->set.ignorecl &&
checkprefix("Content-Length:", k->p)) {
Daniel Stenberg
committed
contentlength = curlx_strtoofft(k->p+15, NULL, 10);
if (data->set.max_filesize &&
contentlength > data->set.max_filesize) {
failf(data, "Maximum file size exceeded");
return CURLE_FILESIZE_EXCEEDED;
}
if(contentlength >= 0)
k->size = contentlength;
else {
/* Negative Content-Length is really odd, and we know it
happens for example when older Apache servers send large
files */
conn->bits.close = TRUE;
infof(data, "Negative content-length: %" FORMAT_OFF_T
", closing after transfer\n", contentlength);
}
Daniel Stenberg
committed
}
/* check for Content-Type: header lines to get the mime-type */
else if (checkprefix("Content-Type:", k->p)) {
char *start;
char *end;
Daniel Stenberg
committed
/* Find the first non-space letter */
for(start=k->p+13;
Daniel Stenberg
committed
*start && isspace((int)*start);
Daniel Stenberg
committed
start++)
; /* empty loop */
Daniel Stenberg
committed
Daniel Stenberg
committed
end = strchr(start, '\r');
if(!end)
end = strchr(start, '\n');
if(end) {
/* skip all trailing space letters */
Daniel Stenberg
committed
for(; isspace((int)*end) && (end > start); end--)
; /* empty loop */
Daniel Stenberg
committed
/* get length of the type */
len = end-start+1;
Daniel Stenberg
committed
/* allocate memory of a cloned copy */
Daniel Stenberg
committed
Curl_safefree(data->info.contenttype);
Daniel Stenberg
committed
data->info.contenttype = malloc(len + 1);
if (NULL == data->info.contenttype)
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
Daniel Stenberg
committed
/* copy the content-type string */
memcpy(data->info.contenttype, start, len);
data->info.contenttype[len] = 0; /* zero terminate */
}
Daniel Stenberg
committed
}
#ifndef CURL_DISABLE_HTTP
Daniel Stenberg
committed
else if((k->httpversion == 10) &&
conn->bits.httpproxy &&
Curl_compareheader(k->p,
"Proxy-Connection:", "keep-alive")) {
Daniel Stenberg
committed
* When a HTTP/1.0 reply comes when using a proxy, the
* 'Proxy-Connection: keep-alive' line tells us the
* connection will be kept alive for our pleasure.
* Default action for 1.0 is to close.
Daniel Stenberg
committed
conn->bits.close = FALSE; /* don't close when done */
infof(data, "HTTP/1.0 proxy connection set to keep alive!\n");
else if((k->httpversion == 11) &&
conn->bits.httpproxy &&
Curl_compareheader(k->p,
"Proxy-Connection:", "close")) {
/*
* We get a HTTP/1.1 response from a proxy and it says it'll
* close down after this transfer.
*/
conn->bits.close = TRUE; /* close when done */
infof(data, "HTTP/1.1 proxy connection set close!\n");
}
Daniel Stenberg
committed
else if((k->httpversion == 10) &&
Curl_compareheader(k->p, "Connection:", "keep-alive")) {
Daniel Stenberg
committed
* A HTTP/1.0 reply with the 'Connection: keep-alive' line
* tells us the connection will be kept alive for our
* pleasure. Default action for 1.0 is to close.
Daniel Stenberg
committed
* [RFC2068, section 19.7.1] */
conn->bits.close = FALSE; /* don't close when done */
infof(data, "HTTP/1.0 connection set to keep alive!\n");
}
else if (Curl_compareheader(k->p, "Connection:", "close")) {
/*
* [RFC 2616, section 8.1.2.1]
* "Connection: close" is HTTP/1.1 language and means that
* the connection will close when this request has been
* served.
Daniel Stenberg
committed
conn->bits.close = TRUE; /* close when done */
Daniel Stenberg
committed
else if (Curl_compareheader(k->p,
"Transfer-Encoding:", "chunked")) {
/*
* [RFC 2616, section 3.6.1] A 'chunked' transfer encoding
* means that the server will send a series of "chunks". Each
* chunk starts with line with info (including size of the
* coming block) (terminated with CRLF), then a block of data
* with the previously mentioned size. There can be any amount
* of chunks, and a chunk-data set to zero signals the
* end-of-chunks. */
conn->bits.chunk = TRUE; /* chunks coming our way */
/* init our chunky engine */
Curl_httpchunk_init(conn);
else if (checkprefix("Trailer:", k->p) ||
checkprefix("Trailers:", k->p)) {
/*
* This test helps Curl_httpchunk_read() to determine to look
* for well formed trailers after the zero chunksize record. In
* this case a CRLF is required after the zero chunksize record
* when no trailers are sent, or after the last trailer record.
*
* It seems both Trailer: and Trailers: occur in the wild.
*/
conn->bits.trailerHdrPresent = TRUE;
}
Daniel Stenberg
committed
else if (checkprefix("Content-Encoding:", k->p) &&
data->set.encoding) {
/*
* Process Content-Encoding. Look for the values: identity,
* gzip, deflate, compress, x-gzip and x-compress. x-gzip and
Daniel Stenberg
committed
* x-compress are the same as gzip and compress. (Sec 3.5 RFC
* 2616). zlib cannot handle compress. However, errors are
* handled further down when the response body is processed
*/
Daniel Stenberg
committed
char *start;
/* Find the first non-space letter */
for(start=k->p+17;
*start && isspace((int)*start);
Daniel Stenberg
committed
start++)
; /* empty loop */
Daniel Stenberg
committed
/* Record the content-encoding for later use */
Daniel Stenberg
committed
if (checkprefix("identity", start))
k->content_encoding = IDENTITY;
else if (checkprefix("deflate", start))
k->content_encoding = DEFLATE;
else if (checkprefix("gzip", start)
Daniel Stenberg
committed
|| checkprefix("x-gzip", start))
k->content_encoding = GZIP;
else if (checkprefix("compress", start)
Daniel Stenberg
committed
|| checkprefix("x-compress", start))
k->content_encoding = COMPRESS;
else if (Curl_compareheader(k->p, "Content-Range:", "bytes")) {
/* Content-Range: bytes [num]-
Content-Range: bytes: [num]-
The second format was added since Sun's webserver
JavaWebServer/1.1.1 obviously sends the header this way!
*/
char *ptr = Curl_strcasestr(k->p, "bytes");
ptr+=5;
if(*ptr == ':')
/* stupid colon skip */
ptr++;
Daniel Stenberg
committed
k->offset = curlx_strtoofft(ptr, NULL, 10);
if (data->reqdata.resume_from == k->offset)
/* we asked for a resume and we got it */
k->content_range = TRUE;
Daniel Stenberg
committed
}
#if !defined(CURL_DISABLE_COOKIES)
Daniel Stenberg
committed
else if(data->cookies &&
checkprefix("Set-Cookie:", k->p)) {
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
CURL_LOCK_ACCESS_SINGLE);
Curl_cookie_add(data,
data->cookies, TRUE, k->p+11,
/* If there is a custom-set Host: name, use it
here, or else use real peer host name. */
conn->allocptr.cookiehost?
conn->allocptr.cookiehost:conn->host.name,
data->reqdata.path);
Daniel Stenberg
committed
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
#endif
Daniel Stenberg
committed
else if(checkprefix("Last-Modified:", k->p) &&
(data->set.timecondition || data->set.get_filetime) ) {
time_t secs=time(NULL);
k->timeofdoc = curl_getdate(k->p+strlen("Last-Modified:"),
&secs);
if(data->set.get_filetime)
data->info.filetime = (long)k->timeofdoc;
}
else if((checkprefix("WWW-Authenticate:", k->p) &&
(401 == k->httpcode)) ||
(checkprefix("Proxy-authenticate:", k->p) &&
(407 == k->httpcode))) {
result = Curl_http_input_auth(conn, k->httpcode, k->p);
if(result)
return result;
else if ((k->httpcode >= 300 && k->httpcode < 400) &&
checkprefix("Location:", k->p)) {
if(data->set.http_follow_location) {
/* this is the URL that the server advices us to get instead */
char *ptr;
char *start=k->p;
char backup;
start += 9; /* pass "Location:" */
/* Skip spaces and tabs. We do this to support multiple
white spaces after the "Location:" keyword. */
while(*start && isspace((int)*start ))
start++;
/* Scan through the string from the end to find the last
non-space. k->end_ptr points to the actual terminating zero
letter, move pointer one letter back and start from
there. This logic strips off trailing whitespace, but keeps
any embedded whitespace. */
ptr = k->end_ptr-1;
while((ptr>=start) && isspace((int)*ptr))
ptr--;
ptr++;
backup = *ptr; /* store the ending letter */
if(ptr != start) {
*ptr = '\0'; /* zero terminate */
data->reqdata.newurl = strdup(start); /* clone string */
*ptr = backup; /* restore ending letter */
if(!data->reqdata.newurl)
return CURLE_OUT_OF_MEMORY;
}
#endif /* CURL_DISABLE_HTTP */
/*
* End of header-checks. Write them to the client.
*/
if (data->set.include_header)
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN,
k->p, k->hbuflen, conn);
Daniel Stenberg
committed
Daniel Stenberg
committed
result = Curl_client_write(conn, writetype, k->p, k->hbuflen);
if(result)
return result;