aboutsummaryrefslogtreecommitdiff
path: root/lib/transfer.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/transfer.c')
-rw-r--r--lib/transfer.c153
1 files changed, 107 insertions, 46 deletions
diff --git a/lib/transfer.c b/lib/transfer.c
index d0d4aeb50..ca6031724 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -157,8 +157,15 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
size_t buffersize = bytes;
size_t nread;
+#ifndef CURL_DISABLE_HTTP
+ struct curl_slist *trailers = NULL;
+ CURLcode c;
+ int trailers_ret_code;
+#endif
+
curl_read_callback readfunc = NULL;
void *extra_data = NULL;
+ bool added_crlf = FALSE;
#ifdef CURL_DOES_CONVERSIONS
bool sending_http_headers = FALSE;
@@ -175,10 +182,6 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
#ifndef CURL_DISABLE_HTTP
if(data->state.trailers_state == TRAILERS_INITIALIZED) {
- struct curl_slist *trailers = NULL;
- CURLcode result;
- int trailers_ret_code;
-
/* at this point we already verified that the callback exists
so we compile and store the trailers buffer, then proceed */
infof(data,
@@ -195,18 +198,17 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
data->set.trailer_data);
Curl_set_in_callback(data, false);
if(trailers_ret_code == CURL_TRAILERFUNC_OK) {
- result = Curl_http_compile_trailers(trailers, &data->state.trailers_buf,
- data);
+ c = Curl_http_compile_trailers(trailers, data->state.trailers_buf, data);
}
else {
failf(data, "operation aborted by trailing headers callback");
*nreadp = 0;
- result = CURLE_ABORTED_BY_CALLBACK;
+ c = CURLE_ABORTED_BY_CALLBACK;
}
- if(result) {
+ if(c != CURLE_OK) {
Curl_add_buffer_free(&data->state.trailers_buf);
curl_slist_free_all(trailers);
- return result;
+ return c;
}
infof(data, "Successfully compiled trailers.\r\n");
curl_slist_free_all(trailers);
@@ -226,7 +228,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
if(data->state.trailers_state == TRAILERS_SENDING) {
/* if we're here then that means that we already sent the last empty chunk
but we didn't send a final CR LF, so we sent 0 CR LF. We then start
- pulling trailing data until we have no more at which point we
+ pulling trailing data until we ²have no more at which point we
simply return to the previous point in the state machine as if
nothing happened.
*/
@@ -294,7 +296,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
here, knowing they'll become CRLFs later on.
*/
- bool added_crlf = FALSE;
+ char hexbuffer[11] = "";
int hexlen = 0;
const char *endofline_native;
const char *endofline_network;
@@ -315,7 +317,6 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
/* if we're not handling trailing data, proceed as usual */
if(data->state.trailers_state != TRAILERS_SENDING) {
- char hexbuffer[11] = "";
hexlen = msnprintf(hexbuffer, sizeof(hexbuffer),
"%zx%s", nread, endofline_native);
@@ -462,6 +463,7 @@ CURLcode Curl_readrewind(struct connectdata *conn)
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", (int)err);
return CURLE_SEND_FAIL_REWIND;
}
@@ -498,12 +500,41 @@ static int data_pending(const struct connectdata *conn)
TRUE. The thing is if we read everything, then http2_recv won't
be called and we cannot signal the HTTP/2 stream has closed. As
a workaround, we return nonzero here to call http2_recv. */
- ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion >= 20);
+ ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion == 20);
#else
Curl_ssl_data_pending(conn, FIRSTSOCKET);
#endif
}
+static void read_rewind(struct connectdata *conn,
+ size_t thismuch)
+{
+ DEBUGASSERT(conn->read_pos >= thismuch);
+
+ conn->read_pos -= thismuch;
+ conn->bits.stream_was_rewound = TRUE;
+
+#ifdef DEBUGBUILD
+ {
+ char buf[512 + 1];
+ size_t show;
+
+ show = CURLMIN(conn->buf_len - conn->read_pos, sizeof(buf)-1);
+ if(conn->master_buffer) {
+ memcpy(buf, conn->master_buffer + conn->read_pos, show);
+ buf[show] = '\0';
+ }
+ else {
+ buf[0] = '\0';
+ }
+
+ DEBUGF(infof(conn->data,
+ "Buffer after stream rewind (read_pos = %zu): [%s]\n",
+ conn->read_pos, buf));
+ }
+#endif
+}
+
/*
* Check to see if CURLOPT_TIMECONDITION was met by comparing the time of the
* remote document with the time provided by CURLOPT_TIMEVAL
@@ -578,7 +609,9 @@ static CURLcode readwrite_data(struct Curl_easy *data,
conn->httpversion == 20) &&
#endif
k->size != -1 && !k->header) {
- /* make sure we don't read too much */
+ /* make sure we don't read "too much" if we can help it since we
+ 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)
bytestoread = (size_t)totalleft;
@@ -602,7 +635,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
nread = 0;
}
- if(!k->bytecount) {
+ if((k->bytecount == 0) && (k->writebytecount == 0)) {
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
if(k->exp100 > EXP100_SEND_DATA)
/* set time stamp to compare with when waiting for the 100 */
@@ -617,7 +650,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
if(0 < nread || is_empty_data) {
k->buf[nread] = 0;
}
- else {
+ else if(0 >= nread) {
/* if we receive 0 or less here, the server closed the connection
and we bail out from this! */
DEBUGF(infof(data, "nread <= 0, server closed connection, bailing\n"));
@@ -660,11 +693,20 @@ static CURLcode readwrite_data(struct Curl_easy *data,
/* We've stopped dealing with input, get out of the do-while loop */
if(nread > 0) {
- infof(data,
- "Excess found:"
- " excess = %zd"
- " url = %s (zero-length body)\n",
- nread, data->state.up.path);
+ if(Curl_pipeline_wanted(conn->data->multi, CURLPIPE_HTTP1)) {
+ infof(data,
+ "Rewinding stream by : %zd"
+ " bytes on url %s (zero-length body)\n",
+ nread, data->state.up.path);
+ read_rewind(conn, (size_t)nread);
+ }
+ else {
+ infof(data,
+ "Excess found in a non pipelined read:"
+ " excess = %zd"
+ " url = %s (zero-length body)\n",
+ nread, data->state.up.path);
+ }
}
break;
@@ -776,14 +818,14 @@ static CURLcode readwrite_data(struct Curl_easy *data,
* and writes away the data. The returned 'nread' holds the number
* of actual data it wrote to the client.
*/
- CURLcode extra;
+
CHUNKcode res =
- Curl_httpchunk_read(conn, k->str, nread, &nread, &extra);
+ Curl_httpchunk_read(conn, k->str, nread, &nread);
if(CHUNKE_OK < res) {
- if(CHUNKE_PASSTHRU_ERROR == res) {
- failf(data, "Failed reading the chunked-encoded stream");
- return extra;
+ if(CHUNKE_WRITE_ERROR == res) {
+ failf(data, "Failed writing data");
+ return CURLE_WRITE_ERROR;
}
failf(data, "%s in chunked-encoding", Curl_chunked_strerror(res));
return CURLE_RECV_ERROR;
@@ -795,12 +837,19 @@ static CURLcode readwrite_data(struct Curl_easy *data,
/* 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: %zu bytes\n",
dataleft);
+ if(Curl_pipeline_wanted(conn->data->multi, CURLPIPE_HTTP1)) {
+ /* only attempt the rewind if we truly are pipelining */
+ infof(conn->data, "Rewinding %zu bytes\n",dataleft);
+ read_rewind(conn, dataleft);
+ }
}
}
/* If it returned OK, we just keep going */
@@ -819,13 +868,25 @@ static CURLcode readwrite_data(struct Curl_easy *data,
excess = (size_t)(k->bytecount + nread - k->maxdownload);
if(excess > 0 && !k->ignorebody) {
- infof(data,
- "Excess found in a read:"
- " excess = %zu"
- ", size = %" CURL_FORMAT_CURL_OFF_T
- ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
- ", bytecount = %" CURL_FORMAT_CURL_OFF_T "\n",
- excess, k->size, k->maxdownload, k->bytecount);
+ if(Curl_pipeline_wanted(conn->data->multi, CURLPIPE_HTTP1)) {
+ infof(data,
+ "Rewinding stream by : %zu"
+ " bytes on url %s (size = %" CURL_FORMAT_CURL_OFF_T
+ ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
+ ", bytecount = %" CURL_FORMAT_CURL_OFF_T ", nread = %zd)\n",
+ excess, data->state.up.path,
+ k->size, k->maxdownload, k->bytecount, nread);
+ read_rewind(conn, excess);
+ }
+ else {
+ infof(data,
+ "Excess found in a non pipelined read:"
+ " excess = %zu"
+ ", size = %" CURL_FORMAT_CURL_OFF_T
+ ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
+ ", bytecount = %" CURL_FORMAT_CURL_OFF_T "\n",
+ excess, k->size, k->maxdownload, k->bytecount);
+ }
}
nread = (ssize_t) (k->maxdownload - k->bytecount);
@@ -938,14 +999,12 @@ static CURLcode readwrite_data(struct Curl_easy *data,
return CURLE_OK;
}
-CURLcode Curl_done_sending(struct connectdata *conn,
- struct SingleRequest *k)
+static CURLcode done_sending(struct connectdata *conn,
+ struct SingleRequest *k)
{
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);
if(conn->bits.rewindaftersend) {
CURLcode result = Curl_readrewind(conn);
@@ -1049,7 +1108,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
break;
}
if(nread <= 0) {
- result = Curl_done_sending(conn, k);
+ result = done_sending(conn, k);
if(result)
return result;
break;
@@ -1167,7 +1226,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 = done_sending(conn, k);
if(result)
return result;
}
@@ -1357,14 +1416,20 @@ CURLcode Curl_readwrite(struct connectdata *conn,
* in the proper state to have this information available.
*/
int Curl_single_getsock(const struct connectdata *conn,
- curl_socket_t *sock)
+ curl_socket_t *sock, /* points to numsocks number
+ of sockets */
+ int numsocks)
{
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(conn, sock, numsocks);
+
+ if(numsocks < 2)
+ /* simple check but we might need two slots */
+ return GETSOCK_BLANK;
/* don't include HOLD and PAUSE connections */
if((data->req.keepon & KEEP_RECVBITS) == KEEP_RECV) {
@@ -1500,7 +1565,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
data->state.authhost.picked &= data->state.authhost.want;
data->state.authproxy.picked &= data->state.authproxy.want;
-#ifndef CURL_DISABLE_FTP
if(data->state.wildcardmatch) {
struct WildcardData *wc = &data->wildcard;
if(wc->state < CURLWC_INIT) {
@@ -1509,8 +1573,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
return CURLE_OUT_OF_MEMORY;
}
}
-#endif
- Curl_http2_init_state(&data->state);
}
return result;
@@ -1592,8 +1654,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
DEBUGASSERT(data->state.uh);
uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl,
- (type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME :
- ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) );
+ (type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : 0);
if(uc) {
if(type != FOLLOW_FAKE)
return Curl_uc_to_curlcode(uc);