From 18faa509403c39b4914114cfe2966241b62b2959 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 10 Jan 2008 10:30:19 +0000 Subject: Georg Lippitsch brought CURLOPT_SEEKFUNCTION and CURLOPT_SEEKDATA to allow libcurl to seek in a given input stream. This is particularly important when doing upload resumes when there's already a huge part of the file present remotely. Before, and still if this callback isn't used, libcurl will read and through away the entire file up to the point to where the resuming begins (which of course can be a slow opereration depending on file size, I/O bandwidth and more). This new function will also be preferred to get used instead of the CURLOPT_IOCTLFUNCTION for seeking back in a stream when doing multi-stage HTTP auth with POST/PUT. --- lib/ftp.c | 53 ++++++++++++++++++++++++++++++----------------------- lib/http.c | 55 ++++++++++++++++++++++++++++++++++--------------------- lib/transfer.c | 15 ++++++++++++--- lib/url.c | 18 ++++++++++++++++++ lib/urldata.h | 5 +++++ 5 files changed, 99 insertions(+), 47 deletions(-) (limited to 'lib') diff --git a/lib/ftp.c b/lib/ftp.c index 29cbcb0f8..af0fc2f00 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -1525,7 +1525,6 @@ static CURLcode ftp_state_ul_setup(struct connectdata *conn, struct FTP *ftp = conn->data->state.proto.ftp; struct SessionHandle *data = conn->data; struct ftp_conn *ftpc = &conn->proto.ftpc; - curl_off_t passed=0; if((data->state.resume_from && !sizechecked) || ((data->state.resume_from > 0) && sizechecked)) { @@ -1552,31 +1551,39 @@ static CURLcode ftp_state_ul_setup(struct connectdata *conn, /* enable append */ data->set.ftp_append = TRUE; - /* 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 */ + /* Let's read off the proper amount of bytes from the input. */ + if(conn->seek_func) { + curl_off_t readthisamountnow = data->state.resume_from; - /* TODO: allow the ioctlfunction to provide a fast forward function that - can be used here and use this method only as a fallback! */ - do { - curl_off_t readthisamountnow = (data->state.resume_from - passed); - curl_off_t actuallyread; - - if(readthisamountnow > BUFSIZE) - readthisamountnow = BUFSIZE; - - actuallyread = (curl_off_t) - conn->fread_func(data->state.buffer, 1, (size_t)readthisamountnow, - conn->fread_in); - - passed += actuallyread; - if((actuallyread <= 0) || (actuallyread > readthisamountnow)) { - /* this checks for greater-than only to make sure that the - CURL_READFUNC_ABORT return code still aborts */ - failf(data, "Failed to read data"); + if(conn->seek_func(conn->seek_client, + readthisamountnow, SEEK_SET) != 0) { + failf(data, "Could not seek stream"); return CURLE_FTP_COULDNT_USE_REST; } - } while(passed < data->state.resume_from); + } + + else { + curl_off_t passed=0; + do { + curl_off_t readthisamountnow = (data->state.resume_from - passed); + curl_off_t actuallyread; + + if(readthisamountnow > BUFSIZE) + readthisamountnow = BUFSIZE; + + actuallyread = (curl_off_t) + conn->fread_func(data->state.buffer, 1, (size_t)readthisamountnow, + conn->fread_in); + + passed += actuallyread; + if((actuallyread <= 0) || (actuallyread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Failed to read data"); + return CURLE_FTP_COULDNT_USE_REST; + } + } while(passed < data->state.resume_from); + } /* now, decrease the size of the read */ if(data->set.infilesize>0) { diff --git a/lib/http.c b/lib/http.c index e41a8f750..fd58c06cc 100644 --- a/lib/http.c +++ b/lib/http.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -1793,6 +1793,8 @@ CURLcode Curl_http_done(struct connectdata *conn, /* set the proper values (possibly modified on POST) */ conn->fread_func = data->set.fread_func; /* restore */ conn->fread_in = data->set.in; /* restore */ + conn->seek_func = data->set.seek_func; /* restore */ + conn->seek_client = data->set.seek_client; /* restore */ if(http == NULL) return CURLE_OK; @@ -2186,30 +2188,41 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if(data->state.resume_from && !data->state.this_is_a_follow) { /* do we still game? */ - curl_off_t passed=0; /* 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->state.resume_from - passed); - size_t actuallyread; - - if(readthisamountnow > BUFSIZE) - readthisamountnow = BUFSIZE; - - actuallyread = - data->set.fread_func(data->state.buffer, 1, (size_t)readthisamountnow, - data->set.in); - - passed += actuallyread; - if(actuallyread != readthisamountnow) { - failf(data, "Could only read %" FORMAT_OFF_T - " bytes from the input", - passed); + input. */ + if(conn->seek_func) { + curl_off_t readthisamountnow = data->state.resume_from; + + if(conn->seek_func(conn->seek_client, + readthisamountnow, SEEK_SET) != 0) { + failf(data, "Could not seek stream"); return CURLE_READ_ERROR; } - } while(passed != data->state.resume_from); /* loop until done */ + } + else { + curl_off_t passed=0; + + do { + size_t readthisamountnow = (size_t)(data->state.resume_from - passed); + size_t actuallyread; + + if(readthisamountnow > BUFSIZE) + readthisamountnow = BUFSIZE; + + actuallyread = data->set.fread_func(data->state.buffer, 1, + (size_t)readthisamountnow, + data->set.in); + + passed += actuallyread; + if(actuallyread != readthisamountnow) { + failf(data, "Could only read %" FORMAT_OFF_T + " bytes from the input", + passed); + return CURLE_READ_ERROR; + } + } while(passed != data->state.resume_from); /* loop until done */ + } /* now, decrease the size of the read */ if(data->set.infilesize>0) { diff --git a/lib/transfer.c b/lib/transfer.c index 77146dd6e..512eceff0 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -245,11 +245,20 @@ CURLcode Curl_readrewind(struct connectdata *conn) (data->set.httpreq == HTTPREQ_POST_FORM)) ; /* do nothing */ else { - if(data->set.ioctl_func) { + if(data->set.seek_func) { + int err; + + err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET); + if(err) { + failf(data, "seek callback returned error %d\n", (int)err); + return CURLE_SEND_FAIL_REWIND; + } + } + else if(data->set.ioctl_func) { curlioerr err; - err = (data->set.ioctl_func) (data, CURLIOCMD_RESTARTREAD, - data->set.ioctl_client); + err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD, + data->set.ioctl_client); infof(data, "the ioctl callback returned %d\n", (int)err); if(err) { diff --git a/lib/url.c b/lib/url.c index d7b4c512a..a5e4565ad 100644 --- a/lib/url.c +++ b/lib/url.c @@ -685,6 +685,10 @@ CURLcode Curl_open(struct SessionHandle **curl) /* use fread as default function to read input */ data->set.fread_func = (curl_read_callback)fread; + /* don't use a seek function by default */ + data->set.seek_func = ZERO_NULL; + data->set.seek_client = ZERO_NULL; + /* conversion callbacks for non-ASCII hosts */ data->set.convfromnetwork = ZERO_NULL; data->set.convtonetwork = ZERO_NULL; @@ -1627,6 +1631,18 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, /* When set to NULL, reset to our internal default function */ data->set.fread_func = (curl_read_callback)fread; break; + case CURLOPT_SEEKFUNCTION: + /* + * Seek callback. Might be NULL. + */ + data->set.seek_func = va_arg(param, curl_seek_callback); + break; + case CURLOPT_SEEKDATA: + /* + * Seek control callback. Might be NULL. + */ + data->set.seek_client = va_arg(param, void *); + break; case CURLOPT_CONV_FROM_NETWORK_FUNCTION: /* * "Convert from network encoding" callback @@ -4038,6 +4054,8 @@ static CURLcode CreateConnection(struct SessionHandle *data, * the persistent connection stuff */ conn->fread_func = data->set.fread_func; conn->fread_in = data->set.in; + conn->seek_func = data->set.seek_func; + conn->seek_client = data->set.seek_client; if((conn->protocol&PROT_HTTP) && data->set.upload && diff --git a/lib/urldata.h b/lib/urldata.h index 976bc3771..e918cd61d 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -961,6 +961,9 @@ struct connectdata { size_t buf_len; /* Length of the buffer?? */ + curl_seek_callback seek_func; /* function that seeks the input */ + void *seek_client; /* pointer to pass to the seek() above */ + /*************** Request - specific items ************/ /* previously this was in the urldata struct */ @@ -1324,6 +1327,7 @@ struct UserDefined { bool free_referer; /* set TRUE if 'referer' points to a string we allocated */ void *postfields; /* if POST, set the fields' values here */ + curl_seek_callback seek_func; /* function that seeks the input */ curl_off_t postfieldsize; /* if POST, this might have a size to use instead of strlen(), and then the data *may* be binary (contain zero bytes) */ @@ -1342,6 +1346,7 @@ struct UserDefined { the address and opening the socket */ void* opensocket_client; + void *seek_client; /* pointer to pass to the seek callback */ /* the 3 curl_conv_callback functions below are used on non-ASCII hosts */ /* function to convert from the network encoding: */ curl_conv_callback convfromnetwork; -- cgit v1.2.3