summaryrefslogtreecommitdiff
path: root/Utilities/cmcurl/lib/transfer.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmcurl/lib/transfer.c')
-rw-r--r--Utilities/cmcurl/lib/transfer.c240
1 files changed, 110 insertions, 130 deletions
diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c
index a07c7af8ce..2f29b29d8e 100644
--- a/Utilities/cmcurl/lib/transfer.c
+++ b/Utilities/cmcurl/lib/transfer.c
@@ -5,11 +5,11 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, 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 https://curl.haxx.se/docs/copyright.html.
+ * are also available at https://curl.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
@@ -78,6 +78,7 @@
#include "mime.h"
#include "strcase.h"
#include "urlapi-int.h"
+#include "hsts.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -92,12 +93,11 @@
*
* Returns a pointer to the first matching header or NULL if none matched.
*/
-char *Curl_checkheaders(const struct connectdata *conn,
+char *Curl_checkheaders(const struct Curl_easy *data,
const char *thisheader)
{
struct curl_slist *head;
size_t thislen = strlen(thisheader);
- struct Curl_easy *data = conn->data;
for(head = data->set.headers; head; head = head->next) {
if(strncasecompare(head->data, thisheader, thislen) &&
@@ -124,8 +124,8 @@ CURLcode Curl_get_upload_buffer(struct Curl_easy *data)
* This function will be called to loop through the trailers buffer
* until no more data is available for sending.
*/
-static size_t Curl_trailers_read(char *buffer, size_t size, size_t nitems,
- void *raw)
+static size_t trailers_read(char *buffer, size_t size, size_t nitems,
+ void *raw)
{
struct Curl_easy *data = (struct Curl_easy *)raw;
struct dynbuf *trailers_buf = &data->state.trailers_buf;
@@ -141,7 +141,7 @@ static size_t Curl_trailers_read(char *buffer, size_t size, size_t nitems,
return to_copy;
}
-static size_t Curl_trailers_left(void *raw)
+static size_t trailers_left(void *raw)
{
struct Curl_easy *data = (struct Curl_easy *)raw;
struct dynbuf *trailers_buf = &data->state.trailers_buf;
@@ -153,10 +153,9 @@ static size_t Curl_trailers_left(void *raw)
* This function will call the read callback to fill our buffer with data
* to upload.
*/
-CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
+CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
size_t *nreadp)
{
- struct Curl_easy *data = conn->data;
size_t buffersize = bytes;
size_t nread;
@@ -165,9 +164,10 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
#ifdef CURL_DOES_CONVERSIONS
bool sending_http_headers = FALSE;
+ struct connectdata *conn = data->conn;
if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
- const struct HTTP *http = data->req.protop;
+ const struct HTTP *http = data->req.p.http;
if(http->sending == HTTPSEND_REQUEST)
/* We're sending the HTTP request headers, not the data.
@@ -230,7 +230,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
simply return to the previous point in the state machine as if
nothing happened.
*/
- readfunc = Curl_trailers_read;
+ readfunc = trailers_read;
extra_data = (void *)data;
}
else
@@ -253,7 +253,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
if(nread == CURL_READFUNC_PAUSE) {
struct SingleRequest *k = &data->req;
- if(conn->handler->flags & PROTOPT_NONETWORK) {
+ if(data->conn->handler->flags & PROTOPT_NONETWORK) {
/* protocols that work without network cannot be paused. This is
actually only FILE:// just now, and it can't pause since the transfer
isn't done using the "normal" procedure. */
@@ -366,7 +366,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
#ifndef CURL_DISABLE_HTTP
if(data->state.trailers_state == TRAILERS_SENDING &&
- !Curl_trailers_left(data)) {
+ !trailers_left(data)) {
Curl_dyn_free(&data->state.trailers_buf);
data->state.trailers_state = TRAILERS_DONE;
data->set.trailer_data = NULL;
@@ -409,9 +409,9 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
* POST/PUT with multi-pass authentication when a sending was denied and a
* resend is necessary.
*/
-CURLcode Curl_readrewind(struct connectdata *conn)
+CURLcode Curl_readrewind(struct Curl_easy *data)
{
- struct Curl_easy *data = conn->data;
+ struct connectdata *conn = data->conn;
curl_mimepart *mimepart = &data->set.mimepost;
conn->bits.rewindaftersend = FALSE; /* we rewind now */
@@ -426,7 +426,7 @@ CURLcode Curl_readrewind(struct connectdata *conn)
CURLOPT_HTTPPOST, call app to rewind
*/
if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
- struct HTTP *http = data->req.protop;
+ struct HTTP *http = data->req.p.http;
if(http->sendit)
mimepart = http->sendit;
@@ -435,9 +435,10 @@ CURLcode Curl_readrewind(struct connectdata *conn)
; /* do nothing */
else if(data->state.httpreq == HTTPREQ_POST_MIME ||
data->state.httpreq == HTTPREQ_POST_FORM) {
- if(Curl_mime_rewind(mimepart)) {
+ CURLcode result = Curl_mime_rewind(mimepart);
+ if(result) {
failf(data, "Cannot rewind mime/post data");
- return CURLE_SEND_FAIL_REWIND;
+ return result;
}
}
else {
@@ -598,7 +599,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
if(bytestoread) {
/* receive data from the network! */
- result = Curl_read(conn, conn->sockfd, buf, bytestoread, &nread);
+ result = Curl_read(data, conn->sockfd, buf, bytestoread, &nread);
/* read would've blocked */
if(CURLE_AGAIN == result)
@@ -707,64 +708,10 @@ static CURLcode readwrite_data(struct Curl_easy *data,
write a piece of the body */
if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
/* 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;
- *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->state.httpreq == HTTPREQ_GET) &&
- !k->ignorebody) {
-
- if(k->size == data->state.resume_from) {
- /* The resume point is at the end of file, consider this fine
- even if it doesn't allow resume from here. */
- infof(data, "The entire document is already downloaded");
- connclose(conn, "already downloaded");
- /* Abort download */
- k->keepon &= ~KEEP_RECV;
- *done = TRUE;
- return CURLE_OK;
- }
-
- /* 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(!Curl_meets_timecondition(data, k->timeofdoc)) {
- *done = TRUE;
- /* We're simulating a http 304 from server so we return
- what should have been returned from the server */
- data->info.httpcode = 304;
- infof(data, "Simulate a HTTP 304 response!\n");
- /* we abort the transfer before it is completed == we ruin the
- re-use ability. Close the connection */
- connclose(conn, "Simulated 304 handling");
- return CURLE_OK;
- }
- } /* we have a time condition */
-
- } /* this is HTTP or RTSP */
+ result = Curl_http_firstwrite(data, conn, done);
+ if(result || *done)
+ return result;
+ }
} /* this is the first time we write a body part */
#endif /* CURL_DISABLE_HTTP */
@@ -795,7 +742,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
*/
CURLcode extra;
CHUNKcode res =
- Curl_httpchunk_read(conn, k->str, nread, &nread, &extra);
+ Curl_httpchunk_read(data, k->str, nread, &nread, &extra);
if(CHUNKE_OK < res) {
if(CHUNKE_PASSTHRU_ERROR == res) {
@@ -806,18 +753,15 @@ static CURLcode readwrite_data(struct Curl_easy *data,
return CURLE_RECV_ERROR;
}
if(CHUNKE_STOP == res) {
- size_t dataleft;
/* we're done reading chunks! */
k->keepon &= ~KEEP_RECV; /* read no more */
- /* There are now possibly N number of bytes at the end of the
- str buffer that weren't written to the client.
- Push it back to be read on the next pass. */
-
- dataleft = conn->chunk.dataleft;
- if(dataleft != 0) {
- infof(conn->data, "Leftovers after chunking: %zu bytes\n",
- dataleft);
+ /* N number of bytes at the end of the str buffer that weren't
+ written to the client. */
+ if(conn->chunk.datasize) {
+ infof(data, "Leftovers after chunking: % "
+ CURL_FORMAT_CURL_OFF_T "u bytes\n",
+ conn->chunk.datasize);
}
}
/* If it returned OK, we just keep going */
@@ -867,11 +811,11 @@ static CURLcode readwrite_data(struct Curl_easy *data,
/* Don't let excess data pollute body writes */
if(k->maxdownload == -1 || (curl_off_t)headlen <= k->maxdownload)
- result = Curl_client_write(conn, CLIENTWRITE_BODY,
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
Curl_dyn_ptr(&data->state.headerb),
headlen);
else
- result = Curl_client_write(conn, CLIENTWRITE_BODY,
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
Curl_dyn_ptr(&data->state.headerb),
(size_t)k->maxdownload);
@@ -884,19 +828,19 @@ static CURLcode readwrite_data(struct Curl_easy *data,
in http_chunks.c.
Make sure that ALL_CONTENT_ENCODINGS contains all the
encodings handled here. */
- if(conn->data->set.http_ce_skip || !k->writer_stack) {
+ if(data->set.http_ce_skip || !k->writer_stack) {
if(!k->ignorebody) {
#ifndef CURL_DISABLE_POP3
if(conn->handler->protocol & PROTO_FAMILY_POP3)
- result = Curl_pop3_write(conn, k->str, nread);
+ result = Curl_pop3_write(data, k->str, nread);
else
#endif /* CURL_DISABLE_POP3 */
- result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str,
+ result = Curl_client_write(data, CLIENTWRITE_BODY, k->str,
nread);
}
}
else if(!k->ignorebody)
- result = Curl_unencode_write(conn, k->writer_stack, k->str, nread);
+ result = Curl_unencode_write(data, k->writer_stack, k->str, nread);
}
k->badheader = HEADER_NORMAL; /* taken care of now */
@@ -957,17 +901,18 @@ static CURLcode readwrite_data(struct Curl_easy *data,
return CURLE_OK;
}
-CURLcode Curl_done_sending(struct connectdata *conn,
+CURLcode Curl_done_sending(struct Curl_easy *data,
struct SingleRequest *k)
{
+ struct connectdata *conn = data->conn;
k->keepon &= ~KEEP_SEND; /* we're done writing */
/* These functions should be moved into the handler struct! */
- Curl_http2_done_sending(conn);
- Curl_quic_done_sending(conn);
+ Curl_http2_done_sending(data, conn);
+ Curl_quic_done_sending(data);
if(conn->bits.rewindaftersend) {
- CURLcode result = Curl_readrewind(conn);
+ CURLcode result = Curl_readrewind(data);
if(result)
return result;
}
@@ -1015,6 +960,8 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
*didwhat |= KEEP_SEND;
do {
+ curl_off_t nbody;
+
/* only read more data if there's no upload data already
present in the upload buffer */
if(0 == k->upload_present) {
@@ -1028,7 +975,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
/* HTTP pollution, this should be written nicer to become more
protocol agnostic. */
size_t fillcount;
- struct HTTP *http = k->protop;
+ struct HTTP *http = k->p.http;
if((k->exp100 == EXP100_SENDING_REQUEST) &&
(http->sending == HTTPSEND_BODY)) {
@@ -1053,7 +1000,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
sending_http_headers = FALSE;
}
- result = Curl_fillreadbuffer(conn, data->set.upload_buffer_size,
+ result = Curl_fillreadbuffer(data, data->set.upload_buffer_size,
&fillcount);
if(result)
return result;
@@ -1068,7 +1015,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
break;
}
if(nread <= 0) {
- result = Curl_done_sending(conn, k);
+ result = Curl_done_sending(data, k);
if(result)
return result;
break;
@@ -1130,7 +1077,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
#ifndef CURL_DISABLE_SMTP
if(conn->handler->protocol & PROTO_FAMILY_SMTP) {
- result = Curl_smtp_escape_eob(conn, nread);
+ result = Curl_smtp_escape_eob(data, nread);
if(result)
return result;
}
@@ -1142,7 +1089,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
}
/* write to socket (send away data) */
- result = Curl_write(conn,
+ result = Curl_write(data,
conn->writesockfd, /* socket to send to */
k->upload_fromhere, /* buffer pointer */
k->upload_present, /* buffer size */
@@ -1152,13 +1099,26 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
win_update_buffer_size(conn->writesockfd);
- if(data->set.verbose)
+ if(k->pendingheader) {
+ /* parts of what was sent was header */
+ curl_off_t n = CURLMIN(k->pendingheader, bytes_written);
/* show the data before we change the pointer upload_fromhere */
- Curl_debug(data, CURLINFO_DATA_OUT, k->upload_fromhere,
- (size_t)bytes_written);
+ Curl_debug(data, CURLINFO_HEADER_OUT, k->upload_fromhere, (size_t)n);
+ k->pendingheader -= n;
+ nbody = bytes_written - n; /* size of the written body part */
+ }
+ else
+ nbody = bytes_written;
- k->writebytecount += bytes_written;
- Curl_pgrsSetUploadCounter(data, k->writebytecount);
+ if(nbody) {
+ /* show the data before we change the pointer upload_fromhere */
+ Curl_debug(data, CURLINFO_DATA_OUT,
+ &k->upload_fromhere[bytes_written - nbody],
+ (size_t)nbody);
+
+ k->writebytecount += nbody;
+ Curl_pgrsSetUploadCounter(data, k->writebytecount);
+ }
if((!k->upload_chunky || k->forbidchunk) &&
(k->writebytecount == data->state.infilesize)) {
@@ -1186,7 +1146,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
k->upload_present = 0; /* no more bytes left */
if(k->upload_done) {
- result = Curl_done_sending(conn, k);
+ result = Curl_done_sending(data, k);
if(result)
return result;
}
@@ -1233,7 +1193,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
else
fd_write = CURL_SOCKET_BAD;
- if(conn->data->state.drain) {
+ if(data->state.drain) {
select_res |= CURL_CSELECT_IN;
DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data\n"));
}
@@ -1247,6 +1207,10 @@ CURLcode Curl_readwrite(struct connectdata *conn,
return CURLE_SEND_ERROR;
}
+#ifdef USE_HYPER
+ if(conn->datastream)
+ return conn->datastream(data, conn, &didwhat, done, select_res);
+#endif
/* 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) */
@@ -1266,10 +1230,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
}
k->now = Curl_now();
- if(didwhat) {
- ;
- }
- else {
+ if(!didwhat) {
/* no read no write, this is a timeout? */
if(k->exp100 == EXP100_AWAITING_CONTINUE) {
/* This should allow some time for the header to arrive, but only a
@@ -1296,7 +1257,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
}
}
- if(Curl_pgrsUpdate(conn))
+ if(Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
else
result = Curl_speedcheck(data, k->now);
@@ -1355,7 +1316,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
failf(data, "transfer closed with outstanding read data remaining");
return CURLE_PARTIAL_FILE;
}
- if(Curl_pgrsUpdate(conn))
+ if(Curl_pgrsUpdate(data))
return CURLE_ABORTED_BY_CALLBACK;
}
@@ -1373,15 +1334,15 @@ CURLcode Curl_readwrite(struct connectdata *conn,
* keeps track of. This function will only be called for connections that are
* in the proper state to have this information available.
*/
-int Curl_single_getsock(const struct connectdata *conn,
+int Curl_single_getsock(struct Curl_easy *data,
+ struct connectdata *conn,
curl_socket_t *sock)
{
- const struct Curl_easy *data = conn->data;
int bitmap = GETSOCK_BLANK;
unsigned sockindex = 0;
if(conn->handler->perform_getsock)
- return conn->handler->perform_getsock(conn, sock);
+ return conn->handler->perform_getsock(data, conn, sock);
/* don't include HOLD and PAUSE connections */
if((data->req.keepon & KEEP_RECVBITS) == KEEP_RECV) {
@@ -1529,8 +1490,23 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
}
#endif
Curl_http2_init_state(&data->state);
+ Curl_hsts_loadcb(data, data->hsts);
+ }
+
+ /*
+ * Set user-agent. Used for HTTP, but since we can attempt to tunnel
+ * basically anything through a http proxy we can't limit this based on
+ * protocol.
+ */
+ if(data->set.str[STRING_USERAGENT]) {
+ Curl_safefree(data->state.aptr.uagent);
+ data->state.aptr.uagent =
+ aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]);
+ if(!data->state.aptr.uagent)
+ return CURLE_OUT_OF_MEMORY;
}
+ data->req.headerbytecount = 0;
return result;
}
@@ -1573,6 +1549,8 @@ CURLcode Curl_follow(struct Curl_easy *data,
bool reachedmax = FALSE;
CURLUcode uc;
+ DEBUGASSERT(type != FOLLOW_NONE);
+
if(type == FOLLOW_REDIR) {
if((data->set.maxredirs != -1) &&
(data->set.followlocation >= data->set.maxredirs)) {
@@ -1604,8 +1582,11 @@ CURLcode Curl_follow(struct Curl_easy *data,
}
}
- if(Curl_is_absolute_url(newurl, NULL, MAX_SCHEME_LEN))
- /* This is an absolute URL, don't allow the custom port number */
+ if((type != FOLLOW_RETRY) &&
+ (data->req.httpcode != 401) && (data->req.httpcode != 407) &&
+ Curl_is_absolute_url(newurl, NULL, MAX_SCHEME_LEN))
+ /* If this is not redirect due to a 401 or 407 response and an absolute
+ URL: don't allow a custom port number */
disallowport = TRUE;
DEBUGASSERT(data->state.uh);
@@ -1686,7 +1667,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
* request with an error page. To be sure that libcurl gets the page that
* most user agents would get, libcurl has to force GET.
*
- * This behaviour is forbidden by RFC1945 and the obsolete RFC2616, and
+ * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
* can be overridden with CURLOPT_POSTREDIR.
*/
if((data->state.httpreq == HTTPREQ_POST
@@ -1711,7 +1692,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
* request with an error page. To be sure that libcurl gets the page that
* most user agents would get, libcurl has to force GET.
*
- * This behaviour is forbidden by RFC1945 and the obsolete RFC2616, and
+ * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
* can be overridden with CURLOPT_POSTREDIR.
*/
if((data->state.httpreq == HTTPREQ_POST
@@ -1765,10 +1746,9 @@ CURLcode Curl_follow(struct Curl_easy *data,
/* Returns CURLE_OK *and* sets '*url' if a request retry is wanted.
NOTE: that the *url is malloc()ed. */
-CURLcode Curl_retry_request(struct connectdata *conn,
- char **url)
+CURLcode Curl_retry_request(struct Curl_easy *data, char **url)
{
- struct Curl_easy *data = conn->data;
+ struct connectdata *conn = data->conn;
bool retry = FALSE;
*url = NULL;
@@ -1798,7 +1778,7 @@ CURLcode Curl_retry_request(struct connectdata *conn,
to issue again, but the nghttp2 API can deliver the message to other
streams as well, which is why this adds the check the data counters
too. */
- infof(conn->data, "REFUSED_STREAM, retrying a fresh connect\n");
+ infof(data, "REFUSED_STREAM, retrying a fresh connect\n");
data->state.refused_stream = FALSE; /* clear again */
retry = TRUE;
}
@@ -1810,9 +1790,9 @@ CURLcode Curl_retry_request(struct connectdata *conn,
data->state.retrycount = 0;
return CURLE_SEND_ERROR;
}
- infof(conn->data, "Connection died, retrying a fresh connect\
+ infof(data, "Connection died, retrying a fresh connect\
(retry count: %d)\n", data->state.retrycount);
- *url = strdup(conn->data->change.url);
+ *url = strdup(data->change.url);
if(!*url)
return CURLE_OUT_OF_MEMORY;
@@ -1826,7 +1806,7 @@ CURLcode Curl_retry_request(struct connectdata *conn,
if(conn->handler->protocol&PROTO_FAMILY_HTTP) {
if(data->req.writebytecount) {
- CURLcode result = Curl_readrewind(conn);
+ CURLcode result = Curl_readrewind(data);
if(result) {
Curl_safefree(*url);
return result;
@@ -1853,7 +1833,7 @@ Curl_setup_transfer(
{
struct SingleRequest *k = &data->req;
struct connectdata *conn = data->conn;
- struct HTTP *http = data->req.protop;
+ struct HTTP *http = data->req.p.http;
bool httpsending = ((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
(http->sending == HTTPSEND_REQUEST));
DEBUGASSERT(conn != NULL);