diff --git a/lib/file.c b/lib/file.c index 3b68eb229..1461de224 100644 --- a/lib/file.c +++ b/lib/file.c @@ -378,7 +378,6 @@ static CURLcode Curl_file(struct connectdata *conn, bool *done) *done = TRUE; /* unconditionally */ - Curl_readwrite_init(conn); Curl_initinfo(data); Curl_pgrsStartNow(data); diff --git a/lib/http_chunks.c b/lib/http_chunks.c index 71b9b9791..305a8a97a 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -178,7 +178,7 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, if(*datap == 0x0a) { /* we're now expecting data to come, unless size was zero! */ if(0 == ch->datasize) { - if(conn->bits.trailerHdrPresent!=TRUE) { + if(conn->bits.trailerhdrpresent!=TRUE) { /* No Trailer: header found - revert to original Curl processing */ ch->state = CHUNK_STOPCR; diff --git a/lib/multi.c b/lib/multi.c index 1b5dd1869..061e7b5e4 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -1121,11 +1121,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else { /* we're done with the DO, now DO_DONE */ - easy->result = Curl_readwrite_init(easy->easy_conn); - if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_DO_DONE); - result = CURLM_CALL_MULTI_PERFORM; - } + multistate(easy, CURLM_STATE_DO_DONE); + result = CURLM_CALL_MULTI_PERFORM; } } else { @@ -1152,11 +1149,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } else { /* we're done with the DO, now DO_DONE */ - easy->result = Curl_readwrite_init(easy->easy_conn); - if(CURLE_OK == easy->result) { - multistate(easy, CURLM_STATE_DO_DONE); - result = CURLM_CALL_MULTI_PERFORM; - } + multistate(easy, CURLM_STATE_DO_DONE); + result = CURLM_CALL_MULTI_PERFORM; } } /* dophase_done */ } @@ -1179,9 +1173,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, */ easy->result = Curl_do_more(easy->easy_conn); - if(CURLE_OK == easy->result) - easy->result = Curl_readwrite_init(easy->easy_conn); - /* No need to remove ourselves from the send pipeline here since that is done for us in Curl_done() */ @@ -1207,9 +1198,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, easy->easy_conn->recv_pipe); multistate(easy, CURLM_STATE_WAITPERFORM); result = CURLM_CALL_MULTI_PERFORM; - - Curl_pre_readwrite(easy->easy_conn); - break; case CURLM_STATE_WAITPERFORM: diff --git a/lib/tftp.c b/lib/tftp.c index c4603934c..d315aadde 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -725,10 +725,6 @@ static CURLcode Curl_tftp(struct connectdata *conn, bool *done) } state = (tftp_state_data_t *)data->reqdata.proto.tftp; - code = Curl_readwrite_init(conn); - if(code) - return code; - /* Run the TFTP State Machine */ for(code=tftp_state_machine(state, TFTP_EVENT_INIT); (state->state != TFTP_STATE_FIN) && (code == CURLE_OK); diff --git a/lib/transfer.c b/lib/transfer.c index 64513e003..01a5ee33b 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -992,7 +992,7 @@ CURLcode Curl_readwrite(struct connectdata *conn, * * It seems both Trailer: and Trailers: occur in the wild. */ - conn->bits.trailerHdrPresent = TRUE; + conn->bits.trailerhdrpresent = TRUE; } else if(checkprefix("Content-Encoding:", k->p) && @@ -1640,102 +1640,6 @@ CURLcode Curl_readwrite(struct connectdata *conn, return CURLE_OK; } - -/* - * Curl_readwrite_init() inits the readwrite session. This is inited each time - * for a transfer, sometimes multiple times on the same SessionHandle - */ - -CURLcode Curl_readwrite_init(struct connectdata *conn) -{ - struct SessionHandle *data = conn->data; - struct Curl_transfer_keeper *k = &data->reqdata.keep; - - /* NB: the content encoding software depends on this initialization of - Curl_transfer_keeper.*/ - memset(k, 0, sizeof(struct Curl_transfer_keeper)); - - k->start = Curl_tvnow(); /* start time */ - k->now = k->start; /* current time is now */ - k->header = TRUE; /* assume header */ - k->httpversion = -1; /* unknown at this point */ - - k->size = data->reqdata.size; - k->maxdownload = data->reqdata.maxdownload; - k->bytecountp = data->reqdata.bytecountp; - k->writebytecountp = data->reqdata.writebytecountp; - - k->bytecount = 0; - - k->buf = data->state.buffer; - k->uploadbuf = data->state.uploadbuffer; - k->maxfd = (conn->sockfd>conn->writesockfd? - conn->sockfd:conn->writesockfd)+1; - k->hbufp = data->state.headerbuff; - k->ignorebody=FALSE; - - Curl_pgrsTime(data, TIMER_PRETRANSFER); - Curl_speedinit(data); - - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - - if(!conn->bits.getheader) { - k->header = FALSE; - if(k->size > 0) - Curl_pgrsSetDownloadSize(data, k->size); - } - /* we want header and/or body, if neither then don't do this! */ - if(conn->bits.getheader || !conn->bits.no_body) { - - if(conn->sockfd != CURL_SOCKET_BAD) { - k->keepon |= KEEP_READ; - } - - if(conn->writesockfd != CURL_SOCKET_BAD) { - /* HTTP 1.1 magic: - - Even if we require a 100-return code before uploading data, we might - need to write data before that since the REQUEST may not have been - finished sent off just yet. - - Thus, we must check if the request has been sent before we set the - state info where we wait for the 100-return code - */ - if(data->state.expect100header && - (data->reqdata.proto.http->sending == HTTPSEND_BODY)) { - /* wait with write until we either got 100-continue or a timeout */ - k->write_after_100_header = TRUE; - k->start100 = k->start; - } - else { - if(data->state.expect100header) - /* when we've sent off the rest of the headers, we must await a - 100-continue */ - k->wait100_after_headers = TRUE; - k->keepon |= KEEP_WRITE; - } - } - } - - return CURLE_OK; -} - -/* - * Curl_readwrite may get called multiple times. This function is called - * immediately before the first Curl_readwrite. Note that this can't be moved - * to Curl_readwrite_init since that function can get called while another - * pipeline request is in the middle of receiving data. - * - * We init chunking and trailer bits to their default values here immediately - * before receiving any header data for the current request in the pipeline. - */ -void Curl_pre_readwrite(struct connectdata *conn) -{ - conn->bits.chunk=FALSE; - conn->bits.trailerHdrPresent=FALSE; -} - /* * Curl_single_getsock() gets called by the multi interface code when the app * has requested to get the sockets for the current connection. This function @@ -1757,6 +1661,9 @@ int Curl_single_getsock(const struct connectdata *conn, return GETSOCK_BLANK; if(data->reqdata.keep.keepon & KEEP_READ) { + + DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD); + bitmap |= GETSOCK_READSOCK(sockindex); sock[sockindex] = conn->sockfd; } @@ -1769,6 +1676,9 @@ int Curl_single_getsock(const struct connectdata *conn, one, we increase index */ if(data->reqdata.keep.keepon & KEEP_READ) sockindex++; /* increase index if we need two entries */ + + DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD); + sock[sockindex] = conn->writesockfd; } @@ -1810,15 +1720,6 @@ Transfer(struct connectdata *conn) if(!conn->bits.getheader && conn->bits.no_body) return CURLE_OK; - if(!(conn->protocol & (PROT_FILE|PROT_TFTP))) { - /* Only do this if we are not transferring FILE or TFTP, since those - transfers are treated differently. They do their entire transfers in - the DO function and just returns from this. That is ugly indeed. - */ - Curl_readwrite_init(conn); - Curl_pre_readwrite(conn); - } - while(!done) { curl_socket_t fd_read; curl_socket_t fd_write; @@ -2523,25 +2424,23 @@ CURLcode Curl_perform(struct SessionHandle *data) */ CURLcode Curl_setup_transfer( - struct connectdata *c_conn, /* connection data */ - int sockindex, /* socket index to read from or -1 */ - curl_off_t size, /* -1 if unknown at this point */ - bool getheader, /* TRUE if header parsing is wanted */ - curl_off_t *bytecountp, /* return number of bytes read or NULL */ - int writesockindex, /* socket index to write to, it may very - well be the same we read from. -1 - disables */ - curl_off_t *writecountp /* return number of bytes written or - NULL */ - ) + struct connectdata *conn, /* connection data */ + int sockindex, /* socket index to read from or -1 */ + curl_off_t size, /* -1 if unknown at this point */ + bool getheader, /* TRUE if header parsing is wanted */ + curl_off_t *bytecountp, /* return number of bytes read or NULL */ + int writesockindex, /* socket index to write to, it may very well be + the same we read from. -1 disables */ + curl_off_t *writecountp /* return number of bytes written or NULL */ + ) { - struct connectdata *conn = (struct connectdata *)c_conn; struct SessionHandle *data; + struct Curl_transfer_keeper *k; - if(!conn) - return CURLE_BAD_FUNCTION_ARGUMENT; + DEBUGASSERT(conn != NULL); data = conn->data; + k = &data->reqdata.keep; DEBUGASSERT((sockindex <= 1) && (sockindex >= -1)); @@ -2556,5 +2455,47 @@ Curl_setup_transfer( data->reqdata.bytecountp = bytecountp; data->reqdata.writebytecountp = writecountp; + /* The code sequence below is placed in this function just because all + necessary input is not always known in do_complete() as this function may + be called after that */ + + if(!conn->bits.getheader) { + k->header = FALSE; + if(k->size > 0) + Curl_pgrsSetDownloadSize(data, k->size); + } + /* we want header and/or body, if neither then don't do this! */ + if(conn->bits.getheader || !conn->bits.no_body) { + + if(conn->sockfd != CURL_SOCKET_BAD) { + k->keepon |= KEEP_READ; + } + + if(conn->writesockfd != CURL_SOCKET_BAD) { + /* HTTP 1.1 magic: + + Even if we require a 100-return code before uploading data, we might + need to write data before that since the REQUEST may not have been + finished sent off just yet. + + Thus, we must check if the request has been sent before we set the + state info where we wait for the 100-return code + */ + if(data->state.expect100header && + (data->reqdata.proto.http->sending == HTTPSEND_BODY)) { + /* wait with write until we either got 100-continue or a timeout */ + k->write_after_100_header = TRUE; + k->start100 = k->start; + } + else { + if(data->state.expect100header) + /* when we've sent off the rest of the headers, we must await a + 100-continue */ + k->wait100_after_headers = TRUE; + k->keepon |= KEEP_WRITE; + } + } + } + return CURLE_OK; } diff --git a/lib/transfer.h b/lib/transfer.h index a2fcd11e2..c368c4682 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -31,8 +31,6 @@ CURLcode Curl_readwrite(struct connectdata *conn, bool *done); int Curl_single_getsock(const struct connectdata *conn, curl_socket_t *socks, int numsocks); -CURLcode Curl_readwrite_init(struct connectdata *conn); -void Curl_pre_readwrite(struct connectdata *conn); CURLcode Curl_readrewind(struct connectdata *conn); CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp); bool Curl_retry_request(struct connectdata *conn, char **url); diff --git a/lib/url.c b/lib/url.c index 4ecfeee3e..5d8d042ed 100644 --- a/lib/url.c +++ b/lib/url.c @@ -121,6 +121,7 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by #include "select.h" #include "multiif.h" #include "easyif.h" +#include "speedcheck.h" /* And now for the protocols */ #include "ftp.h" @@ -164,6 +165,8 @@ static void conn_free(struct connectdata *conn); static void signalPipeClose(struct curl_llist *pipeline); static struct SessionHandle* gethandleathead(struct curl_llist *pipeline); +static CURLcode do_init(struct connectdata *conn); +static void do_complete(struct connectdata *conn); #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) static void flush_cookies(struct SessionHandle *data, int cleanup); @@ -4449,21 +4452,83 @@ CURLcode Curl_done(struct connectdata **connp, return result; } +/* + * do_init() inits the readwrite session. This is inited each time (in the DO + * function before the protocol-specific DO functions are invoked) for a + * transfer, sometimes multiple times on the same SessionHandle. Make sure + * nothing in here depends on stuff that are setup dynamicly for the transfer. + */ + +static CURLcode do_init(struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + struct Curl_transfer_keeper *k = &data->reqdata.keep; + + conn->bits.done = FALSE; /* Curl_done() is not called yet */ + conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */ + + /* NB: the content encoding software depends on this initialization of + Curl_transfer_keeper.*/ + memset(k, 0, sizeof(struct Curl_transfer_keeper)); + + k->start = Curl_tvnow(); /* start time */ + k->now = k->start; /* current time is now */ + k->header = TRUE; /* assume header */ + k->httpversion = -1; /* unknown at this point */ + + k->bytecount = 0; + + k->buf = data->state.buffer; + k->uploadbuf = data->state.uploadbuffer; + k->hbufp = data->state.headerbuff; + k->ignorebody=FALSE; + + Curl_pgrsTime(data, TIMER_PRETRANSFER); + Curl_speedinit(data); + + Curl_pgrsSetUploadCounter(data, 0); + Curl_pgrsSetDownloadCounter(data, 0); + + return CURLE_OK; +} + +/* + * do_complete is called when the DO actions are complete. + * + * We init chunking and trailer bits to their default values here immediately + * before receiving any header data for the current request in the pipeline. + */ +static void do_complete(struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + struct Curl_transfer_keeper *k = &data->reqdata.keep; + conn->bits.chunk=FALSE; + conn->bits.trailerhdrpresent=FALSE; + + k->maxfd = (conn->sockfd>conn->writesockfd? + conn->sockfd:conn->writesockfd)+1; + + k->size = data->reqdata.size; + k->maxdownload = data->reqdata.maxdownload; + k->bytecountp = data->reqdata.bytecountp; + k->writebytecountp = data->reqdata.writebytecountp; + +} + CURLcode Curl_do(struct connectdata **connp, bool *done) { CURLcode result=CURLE_OK; struct connectdata *conn = *connp; struct SessionHandle *data = conn->data; - conn->bits.done = FALSE; /* Curl_done() is not called yet */ - conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */ + /* setup and init stuff before DO starts, in preparing for the transfer */ + do_init(conn); if(conn->handler->do_it) { /* generic protocol-specific function pointer set in curl_connect() */ result = conn->handler->do_it(conn, done); /* This was formerly done in transfer.c, but we better do it here */ - if((CURLE_SEND_ERROR == result) && conn->bits.reuse) { /* This was a re-use of a connection and we got a write error in the * DO-phase. Then we DISCONNECT this connection and have another attempt @@ -4513,6 +4578,10 @@ CURLcode Curl_do(struct connectdata **connp, bool *done) } } } + + if(result == CURLE_OK) + /* pre readwrite must be called after the protocol-specific DO function */ + do_complete(conn); } return result; } diff --git a/lib/urldata.h b/lib/urldata.h index d28108d34..2ca78e256 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -600,7 +600,7 @@ struct ConnectBits { requests */ bool netrc; /* name+password provided by netrc */ - bool trailerHdrPresent; /* Set when Trailer: header found in HTTP response. + bool trailerhdrpresent; /* Set when Trailer: header found in HTTP response. Required to determine whether to look for trailers in case of Transfer-Encoding: chunking */ bool done; /* set to FALSE when Curl_do() is called and set to TRUE