/* Unpause parts in active mime tree. */
if((k->keepon & ~newstate & KEEP_SEND_PAUSE) &&
(data->mstate == MSTATE_PERFORMING ||
- data->mstate == MSTATE_RATELIMITING) &&
- data->state.fread_func == (curl_read_callback) Curl_mime_read) {
- Curl_mime_unpause(data->state.in);
+ data->mstate == MSTATE_RATELIMITING)) {
+ result = Curl_creader_unpause(data);
+ if(result)
+ return result;
}
/* put it back in the keepon */
break;
}
+ switch(httpreq) {
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ /* This is form posting using mime data. */
#ifndef CURL_DISABLE_MIME
- if(data->state.mimepost) {
- const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type"));
+ if(data->state.mimepost) {
+ const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type"));
- /* Read and seek body only. */
- data->state.mimepost->flags |= MIME_BODY_ONLY;
+ /* Read and seek body only. */
+ data->state.mimepost->flags |= MIME_BODY_ONLY;
- /* Prepare the mime structure headers & set content type. */
+ /* Prepare the mime structure headers & set content type. */
- if(cthdr)
- for(cthdr += 13; *cthdr == ' '; cthdr++)
- ;
- else if(data->state.mimepost->kind == MIMEKIND_MULTIPART)
- cthdr = "multipart/form-data";
+ if(cthdr)
+ for(cthdr += 13; *cthdr == ' '; cthdr++)
+ ;
+ else if(data->state.mimepost->kind == MIMEKIND_MULTIPART)
+ cthdr = "multipart/form-data";
- curl_mime_headers(data->state.mimepost, data->set.headers, 0);
- result = Curl_mime_prepare_headers(data, data->state.mimepost, cthdr,
- NULL, MIMESTRATEGY_FORM);
- curl_mime_headers(data->state.mimepost, NULL, 0);
- if(!result)
- result = Curl_mime_rewind(data->state.mimepost);
- if(result)
- return result;
- postsize = Curl_mime_size(data->state.mimepost);
- }
+ curl_mime_headers(data->state.mimepost, data->set.headers, 0);
+ result = Curl_mime_prepare_headers(data, data->state.mimepost, cthdr,
+ NULL, MIMESTRATEGY_FORM);
+ if(result)
+ return result;
+ curl_mime_headers(data->state.mimepost, NULL, 0);
+ result = Curl_creader_set_mime(data, data->state.mimepost);
+ if(result)
+ return result;
+ }
+ else
#endif
-
- switch(httpreq) {
- case HTTPREQ_POST_FORM:
- case HTTPREQ_POST_MIME:
- /* This is form posting using mime data. */
- data->state.infilesize = postsize;
- if(!postsize)
+ {
result = Curl_creader_set_null(data);
- else {
- /* Read from mime structure. We could do a special client reader
- * for this, but replacing the callback seems to work fine. */
- data->state.fread_func = (curl_read_callback) Curl_mime_read;
- data->state.in = (void *) data->state.mimepost;
- result = Curl_creader_set_fread(data, postsize);
}
+ data->state.infilesize = Curl_creader_total_length(data);
return result;
default:
if(!postsize)
cr_chunked_total_length,
Curl_creader_def_resume_from,
Curl_creader_def_rewind,
+ Curl_creader_def_unpause,
sizeof(struct chunked_reader)
};
result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
"Mime-Version: 1.0");
- /* Make sure we will read the entire mime structure. */
if(!result)
- result = Curl_mime_rewind(&data->set.mimepost);
-
+ result = Curl_creader_set_mime(data, &data->set.mimepost);
if(result)
return result;
-
- data->state.infilesize = Curl_mime_size(&data->set.mimepost);
-
- /* Read from mime structure. */
- data->state.fread_func = (curl_read_callback) Curl_mime_read;
- data->state.in = (void *) &data->set.mimepost;
+ data->state.infilesize = Curl_creader_client_length(data);
}
+ else
#endif
+ {
+ result = Curl_creader_set_fread(data, data->state.infilesize);
+ if(result)
+ return result;
+ }
/* Check we know the size of the upload */
if(data->state.infilesize < 0) {
static size_t encoder_qp_read(char *buffer, size_t size, bool ateof,
curl_mimepart *part);
static curl_off_t encoder_qp_size(curl_mimepart *part);
+static curl_off_t mime_size(curl_mimepart *part);
static const struct mime_encoder encoders[] = {
{"binary", encoder_nop_read, encoder_nop_size},
}
/* Rewind mime stream. */
-CURLcode Curl_mime_rewind(curl_mimepart *part)
+static CURLcode mime_rewind(curl_mimepart *part)
{
return mime_part_rewind(part) == CURL_SEEKFUNC_OK?
CURLE_OK: CURLE_SEND_FAIL_REWIND;
size = boundarysize; /* Final boundary - CRLF after headers. */
for(part = mime->firstpart; part; part = part->nextpart) {
- curl_off_t sz = Curl_mime_size(part);
+ curl_off_t sz = mime_size(part);
if(sz < 0)
size = sz;
}
/* Get/compute mime size. */
-curl_off_t Curl_mime_size(curl_mimepart *part)
+static curl_off_t mime_size(curl_mimepart *part)
{
curl_off_t size;
}
/* Recursively reset paused status in the given part. */
-void Curl_mime_unpause(curl_mimepart *part)
+static void mime_unpause(curl_mimepart *part)
{
if(part) {
if(part->lastreadstatus == CURL_READFUNC_PAUSE)
curl_mimepart *subpart;
for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart)
- Curl_mime_unpause(subpart);
+ mime_unpause(subpart);
}
}
}
}
+struct cr_mime_ctx {
+ struct Curl_creader super;
+ curl_mimepart *part;
+ curl_off_t total_len;
+ curl_off_t read_len;
+ CURLcode error_result;
+ BIT(seen_eos);
+ BIT(errored);
+};
+
+static CURLcode cr_mime_init(struct Curl_easy *data,
+ struct Curl_creader *reader)
+{
+ struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+ (void)data;
+ ctx->total_len = -1;
+ ctx->read_len = 0;
+ return CURLE_OK;
+}
+
+/* Real client reader to installed client callbacks. */
+static CURLcode cr_mime_read(struct Curl_easy *data,
+ struct Curl_creader *reader,
+ char *buf, size_t blen,
+ size_t *pnread, bool *peos)
+{
+ struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+ size_t nread;
+
+ /* Once we have errored, we will return the same error forever */
+ if(ctx->errored) {
+ *pnread = 0;
+ *peos = FALSE;
+ return ctx->error_result;
+ }
+ if(ctx->seen_eos) {
+ *pnread = 0;
+ *peos = TRUE;
+ return CURLE_OK;
+ }
+ /* respect length limitations */
+ if(ctx->total_len >= 0) {
+ curl_off_t remain = ctx->total_len - ctx->read_len;
+ if(remain <= 0)
+ blen = 0;
+ else if(remain < (curl_off_t)blen)
+ blen = (size_t)remain;
+ }
+ nread = 0;
+ if(blen) {
+ nread = Curl_mime_read(buf, 1, blen, ctx->part);
+ }
+
+ switch(nread) {
+ case 0:
+ if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) {
+ failf(data, "client mime read EOF fail, only "
+ "only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T
+ " of needed bytes read", ctx->read_len, ctx->total_len);
+ return CURLE_READ_ERROR;
+ }
+ *pnread = 0;
+ *peos = TRUE;
+ ctx->seen_eos = TRUE;
+ break;
+
+ case CURL_READFUNC_ABORT:
+ failf(data, "operation aborted by callback");
+ *pnread = 0;
+ *peos = FALSE;
+ ctx->errored = TRUE;
+ ctx->error_result = CURLE_ABORTED_BY_CALLBACK;
+ return CURLE_ABORTED_BY_CALLBACK;
+
+ case CURL_READFUNC_PAUSE:
+ /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
+ data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
+ *pnread = 0;
+ *peos = FALSE;
+ break; /* nothing was read */
+
+ default:
+ if(nread > blen) {
+ /* the read function returned a too large value */
+ failf(data, "read function returned funny value");
+ *pnread = 0;
+ *peos = FALSE;
+ ctx->errored = TRUE;
+ ctx->error_result = CURLE_READ_ERROR;
+ return CURLE_READ_ERROR;
+ }
+ ctx->read_len += nread;
+ if(ctx->total_len >= 0)
+ ctx->seen_eos = (ctx->read_len >= ctx->total_len);
+ *pnread = nread;
+ *peos = ctx->seen_eos;
+ break;
+ }
+ DEBUGF(infof(data, "cr_mime_read(len=%zu, total=%"CURL_FORMAT_CURL_OFF_T
+ ", read=%"CURL_FORMAT_CURL_OFF_T") -> %d, %zu, %d",
+ blen, ctx->total_len, ctx->read_len, CURLE_OK, *pnread, *peos));
+ return CURLE_OK;
+}
+
+static bool cr_mime_needs_rewind(struct Curl_easy *data,
+ struct Curl_creader *reader)
+{
+ struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+ (void)data;
+ return ctx->read_len > 0;
+}
+
+static curl_off_t cr_mime_total_length(struct Curl_easy *data,
+ struct Curl_creader *reader)
+{
+ struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+ (void)data;
+ return ctx->total_len;
+}
+
+static CURLcode cr_mime_resume_from(struct Curl_easy *data,
+ struct Curl_creader *reader,
+ curl_off_t offset)
+{
+ struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+
+ if(offset > 0) {
+ curl_off_t passed = 0;
+
+ do {
+ char scratch[4*1024];
+ size_t readthisamountnow =
+ (offset - passed > (curl_off_t)sizeof(scratch)) ?
+ sizeof(scratch) :
+ curlx_sotouz(offset - passed);
+ size_t nread;
+
+ nread = Curl_mime_read(scratch, 1, readthisamountnow, ctx->part);
+ passed += (curl_off_t)nread;
+ if((nread == 0) || (nread > readthisamountnow)) {
+ /* this checks for greater-than only to make sure that the
+ CURL_READFUNC_ABORT return code still aborts */
+ failf(data, "Could only read %" CURL_FORMAT_CURL_OFF_T
+ " bytes from the mime post", passed);
+ return CURLE_READ_ERROR;
+ }
+ } while(passed < offset);
+
+ /* now, decrease the size of the read */
+ if(ctx->total_len > 0) {
+ ctx->total_len -= offset;
+
+ if(ctx->total_len <= 0) {
+ failf(data, "Mime post already completely uploaded");
+ return CURLE_PARTIAL_FILE;
+ }
+ }
+ /* we've passed, proceed as normal */
+ }
+ return CURLE_OK;
+}
+
+static CURLcode cr_mime_rewind(struct Curl_easy *data,
+ struct Curl_creader *reader)
+{
+ struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+ CURLcode result = mime_rewind(ctx->part);
+ if(result)
+ failf(data, "Cannot rewind mime/post data");
+ return result;
+}
+
+static CURLcode cr_mime_unpause(struct Curl_easy *data,
+ struct Curl_creader *reader)
+{
+ struct cr_mime_ctx *ctx = (struct cr_mime_ctx *)reader;
+ (void)data;
+ mime_unpause(ctx->part);
+ return CURLE_OK;
+}
+
+static const struct Curl_crtype cr_mime = {
+ "cr-mime",
+ cr_mime_init,
+ cr_mime_read,
+ Curl_creader_def_close,
+ cr_mime_needs_rewind,
+ cr_mime_total_length,
+ cr_mime_resume_from,
+ cr_mime_rewind,
+ cr_mime_unpause,
+ sizeof(struct cr_mime_ctx)
+};
+
+CURLcode Curl_creader_set_mime(struct Curl_easy *data, curl_mimepart *part)
+{
+ struct Curl_creader *r;
+ struct cr_mime_ctx *ctx;
+ CURLcode result;
+
+ result = Curl_creader_create(&r, data, &cr_mime, CURL_CR_CLIENT);
+ if(result)
+ return result;
+ ctx = (struct cr_mime_ctx *)r;
+ ctx->part = part;
+ /* Make sure we will read the entire mime structure. */
+ result = mime_rewind(ctx->part);
+ if(result) {
+ Curl_creader_free(data, r);
+ return result;
+ }
+ ctx->total_len = mime_size(ctx->part);
+
+ return Curl_creader_set(data, r);
+}
#else /* !CURL_DISABLE_MIME && (!CURL_DISABLE_HTTP ||
!CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP) */
const char *contenttype,
const char *disposition,
enum mimestrategy strategy);
-curl_off_t Curl_mime_size(struct curl_mimepart *part);
size_t Curl_mime_read(char *buffer, size_t size, size_t nitems,
void *instream);
-CURLcode Curl_mime_rewind(struct curl_mimepart *part);
const char *Curl_mime_contenttype(const char *filename);
-void Curl_mime_unpause(struct curl_mimepart *part);
+
+/**
+ * Install a client reader as upload source that reads the given
+ * mime part.
+ */
+CURLcode Curl_creader_set_mime(struct Curl_easy *data, curl_mimepart *part);
#else
/* if disabled */
#define Curl_mime_duppart(x,y,z) CURLE_OK /* Nothing to duplicate. Succeed */
#define Curl_mime_set_subparts(a,b,c) CURLE_NOT_BUILT_IN
#define Curl_mime_prepare_headers(a,b,c,d,e) CURLE_NOT_BUILT_IN
-#define Curl_mime_size(x) (curl_off_t) -1
#define Curl_mime_read NULL
-#define Curl_mime_rewind(x) ((void)x, CURLE_NOT_BUILT_IN)
-#define Curl_mime_unpause(x)
+#define Curl_creader_set_mime(x,y) ((void)x, CURLE_NOT_BUILT_IN)
#endif
req->ignore_cl = FALSE;
req->upload_chunky = FALSE;
req->getheader = FALSE;
- req->forbidchunk = FALSE;
req->no_body = data->set.opt_no_body;
req->authneg = FALSE;
}
BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding
on upload */
BIT(getheader); /* TRUE if header parsing is wanted */
- BIT(forbidchunk); /* used only to explicitly forbid chunk-upload for
- specific upload buffers. See readmoredata() in http.c
- for details. */
BIT(no_body); /* the response has no body */
BIT(authneg); /* TRUE when the auth phase has started, which means
that we are creating a request with an auth header,
goto out;
}
- /* RTSP never allows chunked transfer */
- data->req.forbidchunk = TRUE;
/* Finish the request buffer */
result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n"));
if(result)
return CURLE_OK;
}
+CURLcode Curl_creader_def_unpause(struct Curl_easy *data,
+ struct Curl_creader *reader)
+{
+ (void)data;
+ (void)reader;
+ return CURLE_OK;
+}
+
struct cr_in_ctx {
struct Curl_creader super;
curl_read_callback read_cb;
struct Curl_creader *reader)
{
struct cr_in_ctx *ctx = (struct cr_in_ctx *)reader;
- /* TODO: I wonder if we should rather give mime its own client
- * reader type. This is messy. */
-#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API)
- curl_mimepart *mimepart = &data->set.mimepost;
-#endif
/* If we never invoked the callback, there is noting to rewind */
if(!ctx->has_used_cb)
return CURLE_OK;
- /* We have sent away data. If not using CURLOPT_POSTFIELDS or
- CURLOPT_HTTPPOST, call app to rewind
- */
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)
- if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) {
- if(data->state.mimepost)
- mimepart = data->state.mimepost;
- }
-#endif
-#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API)
- if(ctx->read_cb == (curl_read_callback)Curl_mime_read) {
- CURLcode result = Curl_mime_rewind(mimepart);
- DEBUGF(infof(data, "cr_in, rewind mime/post data -> %d", result));
- if(result) {
- failf(data, "Cannot rewind mime/post data");
- }
- return result;
- }
-#endif
-
- /* With mime out of the way, handle "normal" fread callbacks */
if(data->set.seek_func) {
int err;
cr_in_total_length,
cr_in_resume_from,
cr_in_rewind,
+ Curl_creader_def_unpause,
sizeof(struct cr_in_ctx)
};
cr_lc_total_length,
Curl_creader_def_resume_from,
Curl_creader_def_rewind,
+ Curl_creader_def_unpause,
sizeof(struct cr_lc_ctx)
};
}
static CURLcode do_init_reader_stack(struct Curl_easy *data,
- const struct Curl_crtype *crt,
- struct Curl_creader **preader,
- curl_off_t clen)
+ struct Curl_creader *r)
{
- CURLcode result;
+ CURLcode result = CURLE_OK;
+ curl_off_t clen;
+ DEBUGASSERT(r);
+ DEBUGASSERT(r->crt);
+ DEBUGASSERT(r->phase == CURL_CR_CLIENT);
DEBUGASSERT(!data->req.reader_stack);
- result = Curl_creader_create(preader, data, crt, CURL_CR_CLIENT);
- if(result)
- return result;
- data->req.reader_stack = *preader;
+ data->req.reader_stack = r;
+ clen = r->crt->total_length(data, r);
/* if we do not have 0 length init, and crlf conversion is wanted,
* add the reader for it */
if(clen && (data->set.crlf
{
CURLcode result;
struct Curl_creader *r;
+ struct cr_in_ctx *ctx;
+
+ result = Curl_creader_create(&r, data, &cr_in, CURL_CR_CLIENT);
+ if(result)
+ return result;
+ ctx = (struct cr_in_ctx *)r;
+ ctx->total_len = len;
cl_reset_reader(data);
- result = do_init_reader_stack(data, &cr_in, &r, len);
- if(!result && r) {
- struct cr_in_ctx *ctx = (struct cr_in_ctx *)r;
- DEBUGASSERT(r->crt == &cr_in);
- ctx->total_len = len;
- }
- return result;
+ return do_init_reader_stack(data, r);
}
CURLcode Curl_creader_add(struct Curl_easy *data,
return CURLE_OK;
}
+CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r)
+{
+ CURLcode result;
+
+ DEBUGASSERT(r);
+ DEBUGASSERT(r->crt);
+ DEBUGASSERT(r->phase == CURL_CR_CLIENT);
+
+ cl_reset_reader(data);
+ result = do_init_reader_stack(data, r);
+ if(result)
+ Curl_creader_free(data, r);
+ return result;
+}
+
CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
size_t *nread, bool *eos)
{
cr_null_total_length,
Curl_creader_def_resume_from,
Curl_creader_def_rewind,
+ Curl_creader_def_unpause,
sizeof(struct Curl_creader)
};
CURLcode Curl_creader_set_null(struct Curl_easy *data)
{
struct Curl_creader *r;
+ CURLcode result;
+
+ result = Curl_creader_create(&r, data, &cr_null, CURL_CR_CLIENT);
+ if(result)
+ return result;
cl_reset_reader(data);
- return do_init_reader_stack(data, &cr_null, &r, 0);
+ return do_init_reader_stack(data, r);
}
struct cr_buf_ctx {
cr_buf_total_length,
cr_buf_resume_from,
Curl_creader_def_rewind,
+ Curl_creader_def_unpause,
sizeof(struct cr_buf_ctx)
};
{
CURLcode result;
struct Curl_creader *r;
+ struct cr_buf_ctx *ctx;
+
+ result = Curl_creader_create(&r, data, &cr_buf, CURL_CR_CLIENT);
+ if(result)
+ return result;
+ ctx = (struct cr_buf_ctx *)r;
+ ctx->buf = buf;
+ ctx->blen = blen;
+ ctx->index = 0;
cl_reset_reader(data);
- result = do_init_reader_stack(data, &cr_buf, &r, blen);
- if(!result && r) {
- struct cr_buf_ctx *ctx = (struct cr_buf_ctx *)r;
- DEBUGASSERT(r->crt == &cr_buf);
- ctx->buf = buf;
- ctx->blen = blen;
- ctx->index = 0;
- }
- return result;
+ return do_init_reader_stack(data, r);
}
curl_off_t Curl_creader_total_length(struct Curl_easy *data)
r = r->next;
return r? r->crt->resume_from(data, r, offset) : CURLE_READ_ERROR;
}
+
+CURLcode Curl_creader_unpause(struct Curl_easy *data)
+{
+ struct Curl_creader *reader = data->req.reader_stack;
+ CURLcode result = CURLE_OK;
+
+ while(reader) {
+ result = reader->crt->unpause(data, reader);
+ if(result)
+ break;
+ reader = reader->next;
+ }
+ return result;
+}
struct Curl_cwriter *writer);
+
/* Client Reader Type, provides the implementation */
struct Curl_crtype {
const char *name; /* writer name. */
CURLcode (*resume_from)(struct Curl_easy *data,
struct Curl_creader *reader, curl_off_t offset);
CURLcode (*rewind)(struct Curl_easy *data, struct Curl_creader *reader);
+ CURLcode (*unpause)(struct Curl_easy *data, struct Curl_creader *reader);
size_t creader_size; /* sizeof() allocated struct Curl_creader */
};
curl_off_t offset);
CURLcode Curl_creader_def_rewind(struct Curl_easy *data,
struct Curl_creader *reader);
+CURLcode Curl_creader_def_unpause(struct Curl_easy *data,
+ struct Curl_creader *reader);
/**
* Convenience method for calling `reader->do_read()` that
CURLcode Curl_creader_add(struct Curl_easy *data,
struct Curl_creader *reader);
+/**
+ * Set the given reader, which needs to be of type CURL_CR_CLIENT,
+ * as the new first reader. Discard any installed readers and init
+ * the reader chain anew.
+ * The function takes ownership of `r`.
+ */
+CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r);
+
/**
* Read at most `blen` bytes at `buf` from the client.
* @param date the transfer to read client bytes for
*/
CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset);
+/**
+ * Unpause all installed readers.
+ */
+CURLcode Curl_creader_unpause(struct Curl_easy *data);
+
/**
* Set the client reader to provide 0 bytes, immediate EOS.
*/
result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
"Mime-Version: 1.0");
- /* Make sure we will read the entire mime structure. */
if(!result)
- result = Curl_mime_rewind(&data->set.mimepost);
-
+ result = Curl_creader_set_mime(data, &data->set.mimepost);
if(result)
goto out;
-
- data->state.infilesize = Curl_mime_size(&data->set.mimepost);
-
- /* Read from mime structure. */
- data->state.fread_func = (curl_read_callback) Curl_mime_read;
- data->state.in = (void *) &data->set.mimepost;
+ data->state.infilesize = Curl_creader_total_length(data);
}
+ else
#endif
+ {
+ result = Curl_creader_set_fread(data, data->state.infilesize);
+ if(result)
+ goto out;
+ }
/* Calculate the optional SIZE parameter */
if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
}
}
- /* Setup client reader for size and EOB conversion */
- result = Curl_creader_set_fread(data, data->state.infilesize);
- if(result)
- goto out;
/* Add the client reader doing STMP EOB escaping */
result = cr_eob_add(data);
if(result)
cr_eob_total_length,
Curl_creader_def_resume_from,
Curl_creader_def_rewind,
+ Curl_creader_def_unpause,
sizeof(struct cr_eob_ctx)
};