Newer
Older
free(in);
return CURLE_OUT_OF_MEMORY;
}
/*
* add_buffer() appends a memory chunk to the existing buffer
*/
static
CURLcode add_buffer(send_buffer *in, const void *inptr, size_t size)
{
char *new_rb;
if(!in->buffer ||
((in->size_used + size) > (in->size_max - 1))) {
new_size = (in->size_used+size)*2;
if(in->buffer)
/* we have a buffer, enlarge the existing one */
new_rb = (char *)realloc(in->buffer, new_size);
else
/* create a new buffer */
new_rb = (char *)malloc(new_size);
if(!new_rb)
return CURLE_OUT_OF_MEMORY;
in->buffer = new_rb;
in->size_max = new_size;
memcpy(&in->buffer[in->size_used], inptr, size);
in->size_used += size;
return CURLE_OK;
}
/* end of the add_buffer functions */
/* ------------------------------------------------------------------------- */
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
/*
* Curl_compareheader()
*
* Returns TRUE if 'headerline' contains the 'header' with given 'content'.
* Pass headers WITH the colon.
*/
bool
Curl_compareheader(char *headerline, /* line to check */
const char *header, /* header keyword _with_ colon */
const char *content) /* content string to find */
{
/* RFC2616, section 4.2 says: "Each header field consists of a name followed
* by a colon (":") and the field value. Field names are case-insensitive.
* The field value MAY be preceded by any amount of LWS, though a single SP
* is preferred." */
size_t hlen = strlen(header);
size_t clen;
size_t len;
char *start;
char *end;
if(!strnequal(headerline, header, hlen))
return FALSE; /* doesn't start with header */
/* pass the header */
start = &headerline[hlen];
/* pass all white spaces */
Daniel Stenberg
committed
while(*start && ISSPACE(*start))
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
start++;
/* find the end of the header line */
end = strchr(start, '\r'); /* lines end with CRLF */
if(!end) {
/* in case there's a non-standard compliant line here */
end = strchr(start, '\n');
if(!end)
/* hm, there's no line ending here, use the zero byte! */
end = strchr(start, '\0');
}
len = end-start; /* length of the content part of the input line */
clen = strlen(content); /* length of the word to find */
/* find the content string in the rest of the line */
for(;len>=clen;len--, start++) {
if(strnequal(start, content, clen))
return TRUE; /* match! */
}
return FALSE; /* no match */
}
Daniel Stenberg
committed
* Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
* function will issue the necessary commands to get a seamless tunnel through
* this proxy. After that, the socket can be used just as a normal socket.
*
* This badly needs to be rewritten. CONNECT should be sent and dealt with
* like any ordinary HTTP request, and not specially crafted like this. This
* function only remains here like this for now since the rewrite is a bit too
* much work to do at the moment.
Daniel Stenberg
committed
*
* This function is BLOCKING which is nasty for all multi interface using apps.
Daniel Stenberg
committed
CURLcode Curl_proxyCONNECT(struct connectdata *conn,
int sockindex,
char *hostname,
int remote_port)
{
int subversion=0;
Daniel Stenberg
committed
struct SessionHandle *data=conn->data;
struct Curl_transfer_keeper *k = &data->reqdata.keep;
Daniel Stenberg
committed
CURLcode result;
int res;
Daniel Stenberg
committed
long timeout =
Daniel Stenberg
committed
data->set.timeout?data->set.timeout:3600000; /* in milliseconds */
curl_socket_t tunnelsocket = conn->sock[sockindex];
Daniel Stenberg
committed
curl_off_t cl=0;
Daniel Stenberg
committed
bool closeConnection = FALSE;
Daniel Stenberg
committed
long check;
#define SELECT_OK 0
#define SELECT_ERROR 1
#define SELECT_TIMEOUT 2
int error = SELECT_OK;
Daniel Stenberg
committed
conn->bits.proxy_connect_closed = FALSE;
do {
Daniel Stenberg
committed
if (!conn->bits.tunnel_connecting) { /* BEGIN CONNECT PHASE */
char *host_port;
send_buffer *req_buffer;
infof(data, "Establish HTTP proxy tunnel to %s:%d\n",
hostname, remote_port);
if(data->reqdata.newurl) {
/* This only happens if we've looped here due to authentication
reasons, and we don't really use the newly cloned URL here
then. Just free() it. */
free(data->reqdata.newurl);
data->reqdata.newurl = NULL;
}
Daniel Stenberg
committed
/* initialize a dynamic send-buffer */
req_buffer = add_buffer_init();
Daniel Stenberg
committed
if(!req_buffer)
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
host_port = aprintf("%s:%d", hostname, remote_port);
if(!host_port) {
free(req_buffer);
Daniel Stenberg
committed
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
Daniel Stenberg
committed
/* Setup the proxy-authorization header, if any */
result = Curl_http_output_auth(conn, (char *)"CONNECT", host_port, TRUE);
if(CURLE_OK == result) {
Daniel Stenberg
committed
char *host=(char *)"";
const char *proxyconn="";
const char *useragent="";
if(!checkheaders(data, "Host:")) {
host = aprintf("Host: %s\r\n", host_port);
if(!host) {
free(req_buffer);
free(host_port);
Daniel Stenberg
committed
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
}
if(!checkheaders(data, "Proxy-Connection:"))
proxyconn = "Proxy-Connection: Keep-Alive\r\n";
if(!checkheaders(data, "User-Agent:") && data->set.useragent)
useragent = conn->allocptr.uagent;
if(CURLE_OK == result) {
/* Send the connect request to the proxy */
/* BLOCKING */
result =
add_bufferf(req_buffer,
"CONNECT %s:%d HTTP/1.0\r\n"
"%s" /* Host: */
"%s" /* Proxy-Authorization */
"%s" /* User-Agent */
"%s", /* Proxy-Connection */
hostname, remote_port,
host,
conn->allocptr.proxyuserpwd?
conn->allocptr.proxyuserpwd:"",
useragent,
proxyconn);
if(CURLE_OK == result)
result = add_custom_headers(conn, req_buffer);
if(host && *host)
free(host);
if(CURLE_OK == result)
/* CRLF terminate the request */
result = add_bufferf(req_buffer, "\r\n");
Daniel Stenberg
committed
/* Now send off the request */
result = add_buffer_send(req_buffer, conn,
&data->info.request_size, 0, sockindex);
Daniel Stenberg
committed
}
if(result)
failf(data, "Failed sending CONNECT to proxy");
Daniel Stenberg
committed
free(host_port);
if(result)
Daniel Stenberg
committed
return result;
Daniel Stenberg
committed
conn->bits.tunnel_connecting = TRUE;
} /* END CONNECT PHASE */
Daniel Stenberg
committed
/* now we've issued the CONNECT and we're waiting to hear back -
we try not to block here in multi-mode because that might be a LONG
wait if the proxy cannot connect-through to the remote host. */
Daniel Stenberg
committed
/* if timeout is requested, find out how much remaining time we have */
check = timeout - /* timeout time */
Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
if(check <=0 ) {
failf(data, "Proxy CONNECT aborted due to timeout");
error = SELECT_TIMEOUT; /* already too little time */
break;
}
Daniel Stenberg
committed
/* if we're in multi-mode and we would block, return instead for a retry */
if (Curl_if_multi == data->state.used_interface) {
if (0 == Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD, 0))
Daniel Stenberg
committed
/* return so we'll be called again polling-style */
return CURLE_OK;
else {
DEBUGF(infof(data,
"Multi mode finished polling for response from "
"proxy CONNECT."));
}
Daniel Stenberg
committed
}
else {
DEBUGF(infof(data, "Easy mode waiting for response from proxy CONNECT."));
}
Daniel Stenberg
committed
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
/* at this point, either:
1) we're in easy-mode and so it's okay to block waiting for a CONNECT
response
2) we're in multi-mode and we didn't block - it's either an error or we
now have some data waiting.
In any case, the tunnel_connecting phase is over. */
conn->bits.tunnel_connecting = FALSE;
{ /* BEGIN NEGOTIATION PHASE */
size_t nread; /* total size read */
int perline; /* count bytes per line */
int keepon=TRUE;
ssize_t gotbytes;
char *ptr;
char *line_start;
ptr=data->state.buffer;
line_start = ptr;
nread=0;
perline=0;
keepon=TRUE;
while((nread<BUFSIZE) && (keepon && !error)) {
/* if timeout is requested, find out how much remaining time we have */
check = timeout - /* timeout time */
Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
if(check <= 0) {
failf(data, "Proxy CONNECT aborted due to timeout");
error = SELECT_TIMEOUT; /* already too little time */
break;
}
Daniel Stenberg
committed
/* loop every second at least, less if the timeout is near */
switch (Curl_socket_ready(tunnelsocket, CURL_SOCKET_BAD,
Daniel Stenberg
committed
check<1000L?(int)check:1000)) {
case -1: /* select() error, stop reading */
error = SELECT_ERROR;
failf(data, "Proxy CONNECT aborted due to select/poll error");
Daniel Stenberg
committed
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
break;
case 0: /* timeout */
break;
default:
res = Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread, &gotbytes);
if(res< 0)
/* EWOULDBLOCK */
continue; /* go loop yourself */
else if(res)
keepon = FALSE;
else if(gotbytes <= 0) {
keepon = FALSE;
error = SELECT_ERROR;
failf(data, "Proxy CONNECT aborted");
}
else {
/*
* We got a whole chunk of data, which can be anything from one
* byte to a set of lines and possibly just a piece of the last
* line.
*/
int i;
nread += gotbytes;
if(keepon > TRUE) {
/* This means we are currently ignoring a response-body, so we
simply count down our counter and make sure to break out of
the loop when we're done! */
cl -= gotbytes;
if(cl<=0) {
keepon = FALSE;
break;
}
}
Daniel Stenberg
committed
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
else
for(i = 0; i < gotbytes; ptr++, i++) {
perline++; /* amount of bytes in this line so far */
if(*ptr=='\n') {
char letter;
int writetype;
/* output debug if that is requested */
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN,
line_start, (size_t)perline, conn);
/* send the header to the callback */
writetype = CLIENTWRITE_HEADER;
if(data->set.include_header)
writetype |= CLIENTWRITE_BODY;
result = Curl_client_write(conn, writetype, line_start, perline);
if(result)
return result;
/* Newlines are CRLF, so the CR is ignored as the line isn't
really terminated until the LF comes. Treat a following CR
as end-of-headers as well.*/
if(('\r' == line_start[0]) ||
('\n' == line_start[0])) {
/* end of response-headers from the proxy */
if(cl && (407 == k->httpcode) &&
!data->state.authproblem) {
/* If we get a 407 response code with content length
* when we have no auth problem, we must ignore the
* whole response-body */
keepon = 2;
infof(data, "Ignore %" FORMAT_OFF_T
" bytes of response-body\n", cl);
cl -= (gotbytes - i);/* remove the remaining chunk of
what we already read */
if(cl<=0)
/* if the whole thing was already read, we are done! */
keepon=FALSE;
}
else
keepon = FALSE;
break; /* breaks out of for-loop, not switch() */
}
/* keep a backup of the position we are about to blank */
letter = line_start[perline];
line_start[perline]=0; /* zero terminate the buffer */
if((checkprefix("WWW-Authenticate:", line_start) &&
(401 == k->httpcode)) ||
(checkprefix("Proxy-authenticate:", line_start) &&
(407 == k->httpcode))) {
result = Curl_http_input_auth(conn, k->httpcode,
line_start);
if(result)
return result;
}
else if(checkprefix("Content-Length:", line_start)) {
cl = curlx_strtoofft(line_start + strlen("Content-Length:"),
NULL, 10);
}
else if(Curl_compareheader(line_start,
"Connection:", "close"))
closeConnection = TRUE;
else if(2 == sscanf(line_start, "HTTP/1.%d %d",
&subversion,
&k->httpcode)) {
/* store the HTTP code from the proxy */
data->info.httpproxycode = k->httpcode;
}
/* put back the letter we blanked out before */
line_start[perline]= letter;
perline=0; /* line starts over here */
line_start = ptr+1; /* this skips the zero byte we wrote */
}
}
}
Daniel Stenberg
committed
break;
} /* switch */
} /* while there's buffer left and loop is requested */
if(error)
return CURLE_RECV_ERROR;
if(data->info.httpproxycode != 200)
/* Deal with the possibly already received authenticate
headers. 'newurl' is set to a new URL if we must loop. */
Curl_http_auth_act(conn);
if (closeConnection && data->reqdata.newurl) {
/* Connection closed by server. Don't use it anymore */
sclose(conn->sock[sockindex]);
conn->sock[sockindex] = CURL_SOCKET_BAD;
break;
Daniel Stenberg
committed
}
} /* END NEGOTIATION PHASE */
} while(data->reqdata.newurl);
if(200 != k->httpcode) {
failf(data, "Received HTTP code %d from proxy after CONNECT",
k->httpcode);
Daniel Stenberg
committed
if (closeConnection && data->reqdata.newurl)
conn->bits.proxy_connect_closed = TRUE;
return CURLE_RECV_ERROR;
Daniel Stenberg
committed
/* If a proxy-authorization header was used for the proxy, then we should
make sure that it isn't accidentally used for the document request
after we've connected. So let's free and clear it here. */
Curl_safefree(conn->allocptr.proxyuserpwd);
conn->allocptr.proxyuserpwd = NULL;
data->state.authproxy.done = TRUE;
infof (data, "Proxy replied OK to CONNECT request\n");
return CURLE_OK;
}
* Curl_http_connect() performs HTTP stuff to do at connect-time, called from
* the generic Curl_connect().
CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
Daniel Stenberg
committed
struct SessionHandle *data;
Daniel Stenberg
committed
/* We default to persistent connections. We set this already in this connect
function to make the re-use checks properly be able to check this bit. */
conn->bits.close = FALSE;
/* If we are not using a proxy and we want a secure connection, perform SSL
* initialization & connection now. If using a proxy with https, then we
* must tell the proxy to CONNECT to the host we want to talk to. Only
* after the connect has occurred, can we start talking SSL
Daniel Stenberg
committed
if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
/* either SSL over proxy, or explicitly asked for */
Daniel Stenberg
committed
result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
conn->host.name,
conn->remote_port);
if(CURLE_OK != result)
return result;
}
Daniel Stenberg
committed
if (conn->bits.tunnel_connecting) {
/* nothing else to do except wait right now - we're not done here. */
return CURLE_OK;
}
Daniel Stenberg
committed
if(!data->state.this_is_a_follow) {
/* this is not a followed location, get the original host name */
if (data->state.first_host)
/* Free to avoid leaking memory on multiple requests*/
Daniel Stenberg
committed
free(data->state.first_host);
Daniel Stenberg
committed
data->state.first_host = strdup(conn->host.name);
if(!data->state.first_host)
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
Daniel Stenberg
committed
if(conn->protocol & PROT_HTTPS) {
/* perform SSL initialization */
if(data->state.used_interface == Curl_if_multi) {
result = Curl_https_connecting(conn, done);
if(result)
return result;
}
else {
/* BLOCKING */
result = Curl_ssl_connect(conn, FIRSTSOCKET);
if(result)
return result;
*done = TRUE;
}
}
else {
*done = TRUE;
}
Daniel Stenberg
committed
Daniel Stenberg
committed
CURLcode Curl_https_connecting(struct connectdata *conn, bool *done)
{
CURLcode result;
DEBUGASSERT((conn) && (conn->protocol & PROT_HTTPS));
Daniel Stenberg
committed
/* perform SSL initialization for this socket */
result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
if(result)
return result;
return CURLE_OK;
}
#ifdef USE_SSLEAY
Daniel Stenberg
committed
/* This function is OpenSSL-specific. It should be made to query the generic
SSL layer instead. */
Daniel Stenberg
committed
int Curl_https_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
Daniel Stenberg
committed
{
if (conn->protocol & PROT_HTTPS) {
struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
Daniel Stenberg
committed
if(!numsocks)
return GETSOCK_BLANK;
Daniel Stenberg
committed
if (connssl->connecting_state == ssl_connect_2_writing) {
/* write mode */
Daniel Stenberg
committed
socks[0] = conn->sock[FIRSTSOCKET];
return GETSOCK_WRITESOCK(0);
Daniel Stenberg
committed
}
else if (connssl->connecting_state == ssl_connect_2_reading) {
/* read mode */
Daniel Stenberg
committed
socks[0] = conn->sock[FIRSTSOCKET];
return GETSOCK_READSOCK(0);
Daniel Stenberg
committed
}
}
return CURLE_OK;
}
Daniel Stenberg
committed
#else
#ifdef USE_GNUTLS
int Curl_https_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
(void)conn;
(void)socks;
(void)numsocks;
return GETSOCK_BLANK;
}
#else
#ifdef USE_NSS
int Curl_https_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
(void)conn;
(void)socks;
(void)numsocks;
return GETSOCK_BLANK;
}
#endif
Daniel Stenberg
committed
#endif
Daniel Stenberg
committed
#endif
/*
* Curl_http_done() gets called from Curl_done() after a single HTTP request
* has been performed.
*/
CURLcode Curl_http_done(struct connectdata *conn,
Daniel Stenberg
committed
CURLcode status, bool premature)
struct SessionHandle *data = conn->data;
struct HTTP *http =data->reqdata.proto.http;
struct Curl_transfer_keeper *k = &data->reqdata.keep;
Daniel Stenberg
committed
(void)premature; /* not used */
/* set the proper values (possibly modified on POST) */
conn->fread = data->set.fread; /* restore */
conn->fread_in = data->set.in; /* restore */
if (http == NULL)
return CURLE_OK;
Daniel Stenberg
committed
if(http->send_buffer) {
send_buffer *buff = http->send_buffer;
Daniel Stenberg
committed
free(buff->buffer);
free(buff);
http->send_buffer = NULL; /* clear the pointer */
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if(HTTPREQ_POST_FORM == data->set.httpreq) {
k->bytecount = http->readbytecount + http->writebytecount;
Curl_formclean(&http->sendit); /* Now free that whole lot */
/* a file being uploaded was left opened, close it! */
fclose(http->form.fp);
http->form.fp = NULL;
}
else if(HTTPREQ_PUT == data->set.httpreq)
k->bytecount = http->readbytecount + http->writebytecount;
if (status != CURLE_OK)
return (status);
if(!conn->bits.retry &&
((http->readbytecount +
data->reqdata.keep.headerbytecount -
data->reqdata.keep.deductheadercount)) <= 0) {
/* If this connection isn't simply closed to be retried, AND nothing was
read from the HTTP server (that counts), this can't be right so we
return an error here */
failf(data, "Empty reply from server");
Daniel Stenberg
committed
return CURLE_GOT_NOTHING;
}
/* check and possibly add an Expect: header */
static CURLcode expect100(struct SessionHandle *data,
send_buffer *req_buffer)
{
CURLcode result = CURLE_OK;
Daniel Stenberg
committed
data->state.expect100header = FALSE; /* default to false unless it is set
to TRUE below */
if((data->set.httpversion != CURL_HTTP_VERSION_1_0) &&
!checkheaders(data, "Expect:")) {
/* if not doing HTTP 1.0 or disabled explicitly, we add a Expect:
100-continue to the headers which actually speeds up post
operations (as there is one packet coming back from the web
server) */
result = add_bufferf(req_buffer,
"Expect: 100-continue\r\n");
if(result == CURLE_OK)
Daniel Stenberg
committed
data->state.expect100header = TRUE;
}
return result;
}
static CURLcode add_custom_headers(struct connectdata *conn,
send_buffer *req_buffer)
{
CURLcode result = CURLE_OK;
char *ptr;
struct curl_slist *headers=conn->data->set.headers;
while(headers) {
ptr = strchr(headers->data, ':');
if(ptr) {
/* we require a colon for this to be a true header */
ptr++; /* pass the colon */
Daniel Stenberg
committed
while(*ptr && ISSPACE(*ptr))
ptr++;
if(*ptr) {
/* only send this if the contents was non-blank */
if(conn->allocptr.host &&
/* a Host: header was sent already, don't pass on any custom Host:
header as that will produce *two* in the same request! */
curl_strnequal("Host:", headers->data, 5))
;
else if(conn->data->set.httpreq == HTTPREQ_POST_FORM &&
/* this header (extended by formdata.c) is sent later */
curl_strnequal("Content-Type:", headers->data,
strlen("Content-Type:")))
;
else {
result = add_bufferf(req_buffer, "%s\r\n", headers->data);
if(result)
return result;
}
}
}
headers = headers->next;
}
return result;
}
/*
* Curl_http() gets called from the generic Curl_do() function when a HTTP
* request is to be performed. This creates and sends a properly constructed
CURLcode Curl_http(struct connectdata *conn, bool *done)
Daniel Stenberg
committed
struct SessionHandle *data=conn->data;
char *buf = data->state.buffer; /* this is a short cut to the buffer */
CURLcode result=CURLE_OK;
char *ppath = data->reqdata.path;
char *host = conn->host.name;
char *ptr;
Daniel Stenberg
committed
Curl_HttpReq httpreq = data->set.httpreq;
char *addcookies = NULL;
/* Always consider the DO phase done after this function call, even if there
may be parts of the request that is not yet sent, since we can deal with
the rest of the request in the PERFORM phase. */
*done = TRUE;
if(!data->reqdata.proto.http) {
/* Only allocate this struct if we don't already have it! */
http = (struct HTTP *)malloc(sizeof(struct HTTP));
if(!http)
return CURLE_OUT_OF_MEMORY;
memset(http, 0, sizeof(struct HTTP));
data->reqdata.proto.http = http;
http = data->reqdata.proto.http;
if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) &&
Daniel Stenberg
committed
data->set.upload) {
Daniel Stenberg
committed
httpreq = HTTPREQ_PUT;
Daniel Stenberg
committed
/* Now set the 'request' pointer to the proper request string */
if(data->set.customrequest)
request = data->set.customrequest;
else {
if(conn->bits.no_body)
request = (char *)"HEAD";
else {
DEBUGASSERT((httpreq > HTTPREQ_NONE) && (httpreq < HTTPREQ_LAST));
Daniel Stenberg
committed
switch(httpreq) {
case HTTPREQ_POST:
case HTTPREQ_POST_FORM:
request = (char *)"POST";
break;
case HTTPREQ_PUT:
request = (char *)"PUT";
break;
Daniel Stenberg
committed
default: /* this should never happen */
Daniel Stenberg
committed
case HTTPREQ_GET:
request = (char *)"GET";
break;
case HTTPREQ_HEAD:
request = (char *)"HEAD";
break;
}
}
}
Daniel Stenberg
committed
/* The User-Agent string might have been allocated in url.c already, because
it might have been used in the proxy connect, but if we have got a header
with the user-agent string specified, we erase the previously made string
if(checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
free(conn->allocptr.uagent);
conn->allocptr.uagent=NULL;
/* setup the authentication headers */
result = Curl_http_output_auth(conn, request, ppath, FALSE);
if(result)
return result;
if((data->state.authhost.multi || data->state.authproxy.multi) &&
(httpreq != HTTPREQ_GET) &&
(httpreq != HTTPREQ_HEAD)) {
/* Auth is required and we are not authenticated yet. Make a PUT or POST
with content-length zero as a "probe". */
conn->bits.authneg = TRUE;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
else
conn->bits.authneg = FALSE;
Daniel Stenberg
committed
Daniel Stenberg
committed
Curl_safefree(conn->allocptr.ref);
if(data->change.referer && !checkheaders(data, "Referer:"))
conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
Daniel Stenberg
committed
else
conn->allocptr.ref = NULL;
if(data->set.cookie && !checkheaders(data, "Cookie:"))
addcookies = data->set.cookie;
if(!checkheaders(data, "Accept-Encoding:") &&
data->set.encoding) {
Curl_safefree(conn->allocptr.accept_encoding);
conn->allocptr.accept_encoding =
aprintf("Accept-Encoding: %s\r\n", data->set.encoding);
if(!conn->allocptr.accept_encoding)
return CURLE_OUT_OF_MEMORY;
}
ptr = checkheaders(data, "Transfer-Encoding:");
if(ptr) {
/* Some kind of TE is requested, check if 'chunked' is chosen */
conn->bits.upload_chunky =
Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
}
else {
if (httpreq == HTTPREQ_GET)
conn->bits.upload_chunky = FALSE;
if(conn->bits.upload_chunky)
Daniel Stenberg
committed
te = "Transfer-Encoding: chunked\r\n";
}
Daniel Stenberg
committed
Curl_safefree(conn->allocptr.host);
ptr = checkheaders(data, "Host:");
Daniel Stenberg
committed
if(ptr && (!data->state.this_is_a_follow ||
curl_strequal(data->state.first_host, conn->host.name))) {
#if !defined(CURL_DISABLE_COOKIES)
/* If we have a given custom Host: header, we extract the host name in
order to possibly use it for cookie reasons later on. We only allow the
custom Host: header if this is NOT a redirect, as setting Host: in the
Daniel Stenberg
committed
redirected request is being out on thin ice. Except if the host name
is the same as the first one! */
char *start = ptr+strlen("Host:");
Daniel Stenberg
committed
while(*start && ISSPACE(*start ))
start++;
ptr = start; /* start host-scanning here */
/* scan through the string to find the end (space or colon) */
Daniel Stenberg
committed
while(*ptr && !ISSPACE(*ptr) && !(':'==*ptr))
ptr++;
if(ptr != start) {
Daniel Stenberg
committed
Curl_safefree(conn->allocptr.cookiehost);
conn->allocptr.cookiehost = malloc(len+1);
if(!conn->allocptr.cookiehost)
return CURLE_OUT_OF_MEMORY;
memcpy(conn->allocptr.cookiehost, start, len);
conn->allocptr.cookiehost[len]=0;
}
#endif
Daniel Stenberg
committed
conn->allocptr.host = NULL;
}
else {
/* When building Host: headers, we must put the host name within
[brackets] if the host name is a plain IPv6-address. RFC2732-style. */
if(((conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTPS)) ||
(!(conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTP)) )
/* If (HTTPS on port 443) OR (non-HTTPS on port 80) then don't include
the port number in the host string */
conn->allocptr.host = aprintf("Host: %s%s%s\r\n",
conn->bits.ipv6_ip?"[":"",
host,
conn->bits.ipv6_ip?"]":"");
conn->allocptr.host = aprintf("Host: %s%s%s:%d\r\n",
conn->bits.ipv6_ip?"[":"",
host,
conn->bits.ipv6_ip?"]":"",
conn->remote_port);
if(!conn->allocptr.host)
/* without Host: we can't make a nice request */
return CURLE_OUT_OF_MEMORY;
if (conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
/* Using a proxy but does not tunnel through it */
Daniel Stenberg
committed
/* The path sent to the proxy is in fact the entire URL. But if the remote
host is a IDN-name, we must make sure that the request we produce only
uses the encoded host name! */
if(conn->host.dispname != conn->host.name) {
char *url = data->change.url;
ptr = strstr(url, conn->host.dispname);
Daniel Stenberg
committed
if(ptr) {
/* This is where the display name starts in the URL, now replace this
part with the encoded name. TODO: This method of replacing the host
name is rather crude as I believe there's a slight risk that the
user has entered a user name or password that contain the host name
string. */
size_t currlen = strlen(conn->host.dispname);
size_t newlen = strlen(conn->host.name);
size_t urllen = strlen(url);
Daniel Stenberg
committed
char *newurl;
Daniel Stenberg
committed
newurl = malloc(urllen + newlen - currlen + 1);
if(newurl) {
/* copy the part before the host name */
memcpy(newurl, url, ptr - url);
/* append the new host name instead of the old */
memcpy(newurl + (ptr - url), conn->host.name, newlen);
/* append the piece after the host name */
memcpy(newurl + newlen + (ptr - url),
ptr + currlen, /* copy the trailing zero byte too */
urllen - (ptr-url) - currlen + 1);
if(data->change.url_alloc)
free(data->change.url);
data->change.url = newurl;
data->change.url_alloc = TRUE;
}
else
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
}
}
ppath = data->change.url;
}
Daniel Stenberg
committed
if(HTTPREQ_POST_FORM == httpreq) {
/* we must build the whole darned post sequence first, so that we have
a size of the whole shebang before we start to send it */
result = Curl_getFormData(&http->sendit, data->set.httppost,
checkheaders(data, "Content-Type:"),
&http->postsize);
if(CURLE_OK != result) {
/* Curl_getFormData() doesn't use failf() */
failf(data, "failed creating formpost data");
return result;
}
}
http->p_pragma =
(!checkheaders(data, "Pragma:") &&
(conn->bits.httpproxy && !conn->bits.tunnel_proxy) )?
"Pragma: no-cache\r\n":NULL;
http->p_accept = "Accept: */*\r\n";
Daniel Stenberg
committed
if(( (HTTPREQ_POST == httpreq) ||
(HTTPREQ_POST_FORM == httpreq) ||
(HTTPREQ_PUT == httpreq) ) &&
data->reqdata.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.
*********************************************************************/
if(data->reqdata.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->reqdata.resume_from = 0;
if(data->reqdata.resume_from) {
/* do we still game? */
/* Now, let's read off the proper amount of bytes from the
input. If we knew it was a proper file we could've just
fseek()ed but we only have a stream here */
do {
size_t readthisamountnow = (size_t)(data->reqdata.resume_from - passed);
if(readthisamountnow > BUFSIZE)
readthisamountnow = BUFSIZE;
actuallyread =
data->set.fread(data->state.buffer, 1, (size_t)readthisamountnow,
Daniel Stenberg
committed
data->set.in);
passed += actuallyread;
if(actuallyread != readthisamountnow) {
Daniel Stenberg
committed
failf(data, "Could only read %" FORMAT_OFF_T
" bytes from the input",
passed);
return CURLE_READ_ERROR;
}
} while(passed != data->reqdata.resume_from); /* loop until done */
/* now, decrease the size of the read */
Daniel Stenberg
committed
if(data->set.infilesize>0) {