diff options
Diffstat (limited to 'lib/transfer.c')
-rw-r--r-- | lib/transfer.c | 153 |
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); |