Curl_debug(data, CURLINFO_HEADER_IN, headp, len);
- if(!data->state.hconnect || !data->set.suppress_connect_headers) {
- writetype = CLIENTWRITE_HEADER;
- if(data->state.hconnect)
- writetype |= CLIENTWRITE_CONNECT;
- if(data->req.httpcode/100 == 1)
- writetype |= CLIENTWRITE_1XX;
- result = Curl_client_write(data, writetype, headp, len);
- if(result) {
- data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
- return HYPER_ITER_BREAK;
- }
+ writetype = CLIENTWRITE_HEADER;
+ if(data->state.hconnect)
+ writetype |= CLIENTWRITE_CONNECT;
+ if(data->req.httpcode/100 == 1)
+ writetype |= CLIENTWRITE_1XX;
+ result = Curl_client_write(data, writetype, headp, len);
+ if(result) {
+ data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
+ return HYPER_ITER_BREAK;
}
result = Curl_bump_headersize(data, len, FALSE);
struct SingleRequest *k = &data->req;
CURLcode result = CURLE_OK;
- if(0 == k->bodywrites++) {
+ if(0 == k->bodywrites) {
bool done = FALSE;
#if defined(USE_NTLM)
struct connectdata *conn = data->conn;
return HYPER_ITER_BREAK;
}
}
- if(k->ignorebody)
- return HYPER_ITER_CONTINUE;
- if(0 == len)
- return HYPER_ITER_CONTINUE;
- Curl_debug(data, CURLINFO_DATA_IN, buf, len);
result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len);
if(result) {
return HYPER_ITER_BREAK;
}
- data->req.bytecount += len;
- result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
- if(result) {
- data->state.hresult = result;
- return HYPER_ITER_BREAK;
- }
return HYPER_ITER_CONTINUE;
}
Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb),
len);
- if(!data->state.hconnect || !data->set.suppress_connect_headers) {
- writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS;
- result = Curl_client_write(data, writetype,
- Curl_dyn_ptr(&data->state.headerb), len);
- if(result)
- return result;
- }
+ writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS;
+ if(data->state.hconnect)
+ writetype |= CLIENTWRITE_CONNECT;
+ result = Curl_client_write(data, writetype,
+ Curl_dyn_ptr(&data->state.headerb), len);
+ if(result)
+ return result;
+
result = Curl_bump_headersize(data, len, FALSE);
return result;
}
curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
char *linep;
size_t perline;
- int error;
+ int error, writetype;
#define SELECT_OK 0
#define SELECT_ERROR 1
/* output debug if that is requested */
Curl_debug(data, CURLINFO_HEADER_IN, linep, perline);
- if(!data->set.suppress_connect_headers) {
- /* send the header to the callback */
- int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
- (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
-
- result = Curl_client_write(data, writetype, linep, perline);
- if(result)
- return result;
- }
+ /* send the header to the callback */
+ writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
+ (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
+ result = Curl_client_write(data, writetype, linep, perline);
+ if(result)
+ return result;
result = Curl_bump_headersize(data, perline, TRUE);
if(result)
#ifndef CURL_DISABLE_HTTP
+/* allow no more than 5 "chained" compression steps */
+#define MAX_ENCODE_STACK 5
+
#define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */
/* Deflate and gzip writer. */
struct zlib_writer {
- struct contenc_writer super;
+ struct Curl_cwriter super;
zlibInitState zlib_init; /* zlib init state */
uInt trailerlen; /* Remaining trailer byte count. */
z_stream z; /* State structure for zlib. */
}
static CURLcode inflate_stream(struct Curl_easy *data,
- struct contenc_writer *writer,
+ struct Curl_cwriter *writer, int type,
zlibInitState started)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
/* because the buffer size is fixed, iteratively decompress and transfer to
- the client via downstream_write function. */
+ the client via next_write function. */
while(!done) {
int status; /* zlib status */
done = TRUE;
if(z->avail_out != DSIZ) {
if(status == Z_OK || status == Z_STREAM_END) {
zp->zlib_init = started; /* Data started. */
- result = Curl_unencode_write(data, writer->downstream, decomp,
+ result = Curl_cwriter_write(data, writer->next, type, decomp,
DSIZ - z->avail_out);
if(result) {
exit_zlib(data, z, &zp->zlib_init, result);
/* Deflate handler. */
-static CURLcode deflate_init_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
+static CURLcode deflate_do_init(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
return CURLE_OK;
}
-static CURLcode deflate_unencode_write(struct Curl_easy *data,
- struct contenc_writer *writer,
+static CURLcode deflate_do_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
+ if(!(type & CLIENTWRITE_BODY))
+ return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
/* Set the compressed input when this function is called */
z->next_in = (Bytef *) buf;
z->avail_in = (uInt) nbytes;
return process_trailer(data, zp);
/* Now uncompress the data */
- return inflate_stream(data, writer, ZLIB_INFLATING);
+ return inflate_stream(data, writer, type, ZLIB_INFLATING);
}
-static void deflate_close_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
+static void deflate_do_close(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
}
-static const struct content_encoding deflate_encoding = {
+static const struct Curl_cwtype deflate_encoding = {
"deflate",
NULL,
- deflate_init_writer,
- deflate_unencode_write,
- deflate_close_writer,
+ deflate_do_init,
+ deflate_do_write,
+ deflate_do_close,
sizeof(struct zlib_writer)
};
/* Gzip handler. */
-static CURLcode gzip_init_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
+static CURLcode gzip_do_init(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
}
#endif
-static CURLcode gzip_unencode_write(struct Curl_easy *data,
- struct contenc_writer *writer,
+static CURLcode gzip_do_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
+ if(!(type & CLIENTWRITE_BODY))
+ return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
if(zp->zlib_init == ZLIB_INIT_GZIP) {
/* Let zlib handle the gzip decompression entirely */
z->next_in = (Bytef *) buf;
z->avail_in = (uInt) nbytes;
/* Now uncompress the data */
- return inflate_stream(data, writer, ZLIB_INIT_GZIP);
+ return inflate_stream(data, writer, type, ZLIB_INIT_GZIP);
}
#ifndef OLD_ZLIB_SUPPORT
}
/* We've parsed the header, now uncompress the data */
- return inflate_stream(data, writer, ZLIB_GZIP_INFLATING);
+ return inflate_stream(data, writer, type, ZLIB_GZIP_INFLATING);
#endif
}
-static void gzip_close_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
+static void gzip_do_close(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
}
-static const struct content_encoding gzip_encoding = {
+static const struct Curl_cwtype gzip_encoding = {
"gzip",
"x-gzip",
- gzip_init_writer,
- gzip_unencode_write,
- gzip_close_writer,
+ gzip_do_init,
+ gzip_do_write,
+ gzip_do_close,
sizeof(struct zlib_writer)
};
#ifdef HAVE_BROTLI
/* Brotli writer. */
struct brotli_writer {
- struct contenc_writer super;
+ struct Curl_cwriter super;
BrotliDecoderState *br; /* State structure for brotli. */
};
return CURLE_WRITE_ERROR;
}
-static CURLcode brotli_init_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
+static CURLcode brotli_do_init(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
{
struct brotli_writer *bp = (struct brotli_writer *) writer;
(void) data;
return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY;
}
-static CURLcode brotli_unencode_write(struct Curl_easy *data,
- struct contenc_writer *writer,
+static CURLcode brotli_do_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
struct brotli_writer *bp = (struct brotli_writer *) writer;
CURLcode result = CURLE_OK;
BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
+ if(!(type & CLIENTWRITE_BODY))
+ return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
if(!bp->br)
return CURLE_WRITE_ERROR; /* Stream already ended. */
dstleft = DSIZ;
r = BrotliDecoderDecompressStream(bp->br,
&nbytes, &src, &dstleft, &dst, NULL);
- result = Curl_unencode_write(data, writer->downstream,
+ result = Curl_cwriter_write(data, writer->next, type,
decomp, DSIZ - dstleft);
if(result)
break;
return result;
}
-static void brotli_close_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
+static void brotli_do_close(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
{
struct brotli_writer *bp = (struct brotli_writer *) writer;
}
}
-static const struct content_encoding brotli_encoding = {
+static const struct Curl_cwtype brotli_encoding = {
"br",
NULL,
- brotli_init_writer,
- brotli_unencode_write,
- brotli_close_writer,
+ brotli_do_init,
+ brotli_do_write,
+ brotli_do_close,
sizeof(struct brotli_writer)
};
#endif
#ifdef HAVE_ZSTD
/* Zstd writer. */
struct zstd_writer {
- struct contenc_writer super;
+ struct Curl_cwriter super;
ZSTD_DStream *zds; /* State structure for zstd. */
void *decomp;
};
-static CURLcode zstd_init_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
+static CURLcode zstd_do_init(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
{
struct zstd_writer *zp = (struct zstd_writer *) writer;
return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY;
}
-static CURLcode zstd_unencode_write(struct Curl_easy *data,
- struct contenc_writer *writer,
+static CURLcode zstd_do_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
CURLcode result = CURLE_OK;
ZSTD_outBuffer out;
size_t errorCode;
+ if(!(type & CLIENTWRITE_BODY))
+ return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
if(!zp->decomp) {
zp->decomp = malloc(DSIZ);
if(!zp->decomp)
return CURLE_BAD_CONTENT_ENCODING;
}
if(out.pos > 0) {
- result = Curl_unencode_write(data, writer->downstream,
+ result = Curl_cwriter_write(data, writer->next, type,
zp->decomp, out.pos);
if(result)
break;
return result;
}
-static void zstd_close_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
+static void zstd_do_close(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
{
struct zstd_writer *zp = (struct zstd_writer *) writer;
}
}
-static const struct content_encoding zstd_encoding = {
+static const struct Curl_cwtype zstd_encoding = {
"zstd",
NULL,
- zstd_init_writer,
- zstd_unencode_write,
- zstd_close_writer,
+ zstd_do_init,
+ zstd_do_write,
+ zstd_do_close,
sizeof(struct zstd_writer)
};
#endif
/* Identity handler. */
-static CURLcode identity_init_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
-{
- (void)data;
- (void)writer;
- return CURLE_OK;
-}
-
-static CURLcode identity_unencode_write(struct Curl_easy *data,
- struct contenc_writer *writer,
- const char *buf, size_t nbytes)
-{
- return Curl_unencode_write(data, writer->downstream, buf, nbytes);
-}
-
-static void identity_close_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
-{
- (void) data;
- (void) writer;
-}
-
-static const struct content_encoding identity_encoding = {
+static const struct Curl_cwtype identity_encoding = {
"identity",
"none",
- identity_init_writer,
- identity_unencode_write,
- identity_close_writer,
- sizeof(struct contenc_writer)
+ Curl_cwriter_def_init,
+ Curl_cwriter_def_write,
+ Curl_cwriter_def_close,
+ sizeof(struct Curl_cwriter)
};
/* supported content encodings table. */
-static const struct content_encoding * const encodings[] = {
+static const struct Curl_cwtype * const encodings[] = {
&identity_encoding,
#ifdef HAVE_LIBZ
&deflate_encoding,
char *Curl_all_content_encodings(void)
{
size_t len = 0;
- const struct content_encoding * const *cep;
- const struct content_encoding *ce;
+ const struct Curl_cwtype * const *cep;
+ const struct Curl_cwtype *ce;
char *ace;
for(cep = encodings; *cep; cep++) {
/* Deferred error dummy writer. */
-static CURLcode error_init_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
+static CURLcode error_do_init(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
{
(void)data;
(void)writer;
return CURLE_OK;
}
-static CURLcode error_unencode_write(struct Curl_easy *data,
- struct contenc_writer *writer,
+static CURLcode error_do_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
char *all = Curl_all_content_encodings();
(void) buf;
(void) nbytes;
+ if(!(type & CLIENTWRITE_BODY))
+ return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
if(!all)
return CURLE_OUT_OF_MEMORY;
failf(data, "Unrecognized content encoding type. "
return CURLE_BAD_CONTENT_ENCODING;
}
-static void error_close_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
+static void error_do_close(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
{
(void) data;
(void) writer;
}
-static const struct content_encoding error_encoding = {
- NULL,
+static const struct Curl_cwtype error_writer = {
+ "ce-error",
NULL,
- error_init_writer,
- error_unencode_write,
- error_close_writer,
- sizeof(struct contenc_writer)
+ error_do_init,
+ error_do_write,
+ error_do_close,
+ sizeof(struct Curl_cwriter)
};
-/* Write data using an unencoding writer stack. "nbytes" is not
- allowed to be 0. */
-CURLcode Curl_unencode_write(struct Curl_easy *data,
- struct contenc_writer *writer,
- const char *buf, size_t nbytes)
-{
- if(!nbytes)
- return CURLE_OK;
- if(!writer)
- return CURLE_WRITE_ERROR;
- return writer->handler->unencode_write(data, writer, buf, nbytes);
-}
-
/* Find the content encoding by name. */
-static const struct content_encoding *find_encoding(const char *name,
+static const struct Curl_cwtype *find_encoding(const char *name,
size_t len)
{
- const struct content_encoding * const *cep;
+ const struct Curl_cwtype * const *cep;
for(cep = encodings; *cep; cep++) {
- const struct content_encoding *ce = *cep;
+ const struct Curl_cwtype *ce = *cep;
if((strncasecompare(name, ce->name, len) && !ce->name[len]) ||
(ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len]))
return ce;
const char *enclist, int is_transfer)
{
struct SingleRequest *k = &data->req;
- unsigned int order = is_transfer? 2: 1;
+ Curl_cwriter_phase phase = is_transfer?
+ CURL_CW_TRANSFER_DECODE:CURL_CW_CONTENT_DECODE;
CURLcode result;
do {
Curl_httpchunk_init(data); /* init our chunky engine. */
}
else if(namelen) {
- const struct content_encoding *encoding;
- struct contenc_writer *writer;
- if(is_transfer && !data->set.http_transfer_encoding)
+ const struct Curl_cwtype *cwt;
+ struct Curl_cwriter *writer;
+
+ if((is_transfer && !data->set.http_transfer_encoding) ||
+ (!is_transfer && data->set.http_ce_skip)) {
/* not requested, ignore */
return CURLE_OK;
+ }
+
+ if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) {
+ failf(data, "Reject response due to more than %u content encodings",
+ MAX_ENCODE_STACK);
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
- encoding = find_encoding(name, namelen);
- if(!encoding)
- encoding = &error_encoding; /* Defer error at stack use. */
+ cwt = find_encoding(name, namelen);
+ if(!cwt)
+ cwt = &error_writer; /* Defer error at use. */
- result = Curl_client_create_writer(&writer, data, encoding, order);
+ result = Curl_cwriter_create(&writer, data, cwt, phase);
if(result)
return result;
- result = Curl_client_add_writer(data, writer);
+ result = Curl_cwriter_add(data, writer);
if(result) {
- Curl_client_free_writer(data, writer);
+ Curl_cwriter_free(data, writer);
return result;
}
}
return CURLE_NOT_BUILT_IN;
}
-CURLcode Curl_unencode_write(struct Curl_easy *data,
- struct contenc_writer *writer,
- const char *buf, size_t nbytes)
-{
- (void) data;
- (void) writer;
- (void) buf;
- (void) nbytes;
- return CURLE_NOT_BUILT_IN;
-}
-
char *Curl_all_content_encodings(void)
{
return strdup(CONTENT_ENCODING_DEFAULT); /* Satisfy caller. */
***************************************************************************/
#include "curl_setup.h"
-struct contenc_writer;
+struct Curl_cwriter;
char *Curl_all_content_encodings(void);
CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
const char *enclist, int is_transfer);
-CURLcode Curl_unencode_write(struct Curl_easy *data,
- struct contenc_writer *writer,
- const char *buf, size_t nbytes);
-void Curl_unencode_cleanup(struct Curl_easy *data);
-
#endif /* HEADER_CURL_CONTENT_ENCODING_H */
bool size_known;
bool fstated = FALSE;
char *buf = data->state.buffer;
- curl_off_t bytecount = 0;
int fd;
struct FILEPROTO *file;
if(nread <= 0 || (size_known && (expected_size == 0)))
break;
- bytecount += nread;
if(size_known)
expected_size -= nread;
if(result)
return result;
- result = Curl_pgrsSetDownloadCounter(data, bytecount);
- if(result)
- return result;
-
if(Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
else
if(result)
return result;
- data->req.bytecount += chunk;
-
infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU
" bytes are left for transfer", chunk, size - chunk);
int ldap_ssl = 0;
char *val_b64 = NULL;
size_t val_b64_sz = 0;
- curl_off_t dlsize = 0;
#ifdef LDAP_OPT_NETWORK_TIMEOUT
struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */
#endif
goto quit;
}
+ Curl_pgrsSetDownloadCounter(data, 0);
rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope,
ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg);
goto quit;
}
- dlsize += name_len + 5;
-
FREE_ON_WINLDAP(name);
ldap_memfree(dn);
}
goto quit;
}
- dlsize += attr_len + 3;
-
if((attr_len > 7) &&
(strcmp(";binary", attr + (attr_len - 7)) == 0)) {
/* Binary attribute, encode to base64. */
goto quit;
}
-
- dlsize += val_b64_sz;
}
}
else {
goto quit;
}
-
- dlsize += vals[i]->bv_len;
}
result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
goto quit;
}
-
- dlsize++;
}
/* Free memory used to store values */
result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
if(result)
goto quit;
- dlsize++;
- result = Curl_pgrsSetDownloadCounter(data, dlsize);
- if(result)
- goto quit;
}
if(ber)
/* FALLTHROUGH */
case MQTT_PUB_REMAIN: {
/* read rest of packet, but no more. Cap to buffer size */
- struct SingleRequest *k = &data->req;
size_t rest = mq->npacket;
if(rest > (size_t)data->set.buffer_size)
rest = (size_t)data->set.buffer_size;
result = CURLE_PARTIAL_FILE;
goto end;
}
- Curl_debug(data, CURLINFO_DATA_IN, (char *)pkt, (size_t)nread);
mq->npacket -= nread;
- k->bytecount += nread;
- result = Curl_pgrsSetDownloadCounter(data, k->bytecount);
- if(result)
- goto end;
/* if QoS is set, message contains packet id */
if(!len && plen && prefix[plen - 1] == ' ')
plen--;
result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen);
- if(!result)
- data->req.bytecount += plen;
}
if(!result && value) {
result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len);
- if(!result)
- data->req.bytecount += len;
}
if(!result && suffix) {
result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen);
- if(!result)
- data->req.bytecount += slen;
}
return result;
}
*/
CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
{
- if(data->set.max_filesize && (size > data->set.max_filesize)) {
- failf(data, "Exceeded the maximum allowed file size "
- "(%" CURL_FORMAT_CURL_OFF_T ")",
- data->set.max_filesize);
- return CURLE_FILESIZE_EXCEEDED;
- }
data->progress.downloaded = size;
return CURLE_OK;
}
* to write it directly as BODY data */
result = Curl_client_write(data, CLIENTWRITE_BODY,
Curl_dyn_ptr(&rtspc->buf), 1);
- ++data->req.bytecount;
Curl_dyn_free(&rtspc->buf);
if(result)
goto out;
#include "strdup.h"
#include "http2.h"
#include "headers.h"
+#include "progress.h"
#include "ws.h"
/* The last 3 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
+
+static CURLcode do_init_stack(struct Curl_easy *data);
+
#if defined(CURL_DO_LINEEND_CONV) && !defined(CURL_DISABLE_FTP)
/*
* convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
the future to leave the original data alone.
*/
CURLcode Curl_client_write(struct Curl_easy *data,
- int type,
- char *ptr,
- size_t len)
+ int type, char *buf, size_t blen)
{
+ CURLcode result;
+
#if !defined(CURL_DISABLE_FTP) && defined(CURL_DO_LINEEND_CONV)
/* FTP data may need conversion. */
if((type & CLIENTWRITE_BODY) &&
(data->conn->handler->protocol & PROTO_FAMILY_FTP) &&
data->conn->proto.ftpc.transfertype == 'A') {
/* convert end-of-line markers */
- len = convert_lineends(data, ptr, len);
+ blen = convert_lineends(data, buf, blen);
}
#endif
/* it is one of those, at least */
/* INFO is only INFO */
DEBUGASSERT(!(type & CLIENTWRITE_INFO) || (type == CLIENTWRITE_INFO));
- if(type == CLIENTWRITE_BODY) {
- if(data->req.ignorebody)
- return CURLE_OK;
-
- if(data->req.writer_stack && !data->set.http_ce_skip)
- return Curl_unencode_write(data, data->req.writer_stack, ptr, len);
+ if(!data->req.writer_stack) {
+ result = do_init_stack(data);
+ if(result)
+ return result;
+ DEBUGASSERT(data->req.writer_stack);
}
- return chop_write(data, type, FALSE, ptr, len);
+
+ return Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
}
CURLcode Curl_client_unpause(struct Curl_easy *data)
void Curl_client_cleanup(struct Curl_easy *data)
{
- struct contenc_writer *writer = data->req.writer_stack;
+ struct Curl_cwriter *writer = data->req.writer_stack;
size_t i;
while(writer) {
- data->req.writer_stack = writer->downstream;
- writer->handler->close_writer(data, writer);
+ data->req.writer_stack = writer->next;
+ writer->cwt->do_close(data, writer);
free(writer);
writer = data->req.writer_stack;
}
}
-/* Real client writer: no downstream. */
-static CURLcode client_cew_init(struct Curl_easy *data,
- struct contenc_writer *writer)
+/* Write data using an unencoding writer stack. "nbytes" is not
+ allowed to be 0. */
+CURLcode Curl_cwriter_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes)
{
- (void) data;
+ if(!nbytes)
+ return CURLE_OK;
+ if(!writer)
+ return CURLE_WRITE_ERROR;
+ return writer->cwt->do_write(data, writer, type, buf, nbytes);
+}
+
+CURLcode Curl_cwriter_def_init(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
+{
+ (void)data;
(void)writer;
return CURLE_OK;
}
-static CURLcode client_cew_write(struct Curl_easy *data,
- struct contenc_writer *writer,
- const char *buf, size_t nbytes)
+CURLcode Curl_cwriter_def_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes)
{
- (void)writer;
- if(!nbytes || data->req.ignorebody)
- return CURLE_OK;
- return chop_write(data, CLIENTWRITE_BODY, FALSE, (char *)buf, nbytes);
+ return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
}
-static void client_cew_close(struct Curl_easy *data,
- struct contenc_writer *writer)
+void Curl_cwriter_def_close(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
{
(void) data;
(void) writer;
}
-static const struct content_encoding client_cew = {
+/* Real client writer to installed callbacks. */
+static CURLcode cw_client_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes)
+{
+ (void)writer;
+ if(!nbytes)
+ return CURLE_OK;
+ return chop_write(data, type, FALSE, (char *)buf, nbytes);
+}
+
+static const struct Curl_cwtype cw_client = {
+ "client",
+ NULL,
+ Curl_cwriter_def_init,
+ cw_client_write,
+ Curl_cwriter_def_close,
+ sizeof(struct Curl_cwriter)
+};
+
+/* Download client writer in phase CURL_CW_PROTOCOL that
+ * sees the "real" download body data. */
+static CURLcode cw_download_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes)
+{
+ CURLcode result;
+ size_t nwrite;
+
+ if(!(type & CLIENTWRITE_BODY)) {
+ if((type & CLIENTWRITE_CONNECT) && data->set.suppress_connect_headers)
+ return CURLE_OK;
+ return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+ }
+
+ nwrite = nbytes;
+ data->req.bytecount += nbytes;
+ ++data->req.bodywrites;
+ /* Enforce `max_filesize` also for downloads where we ignore the body.
+ * Also, write body data up to the max size. This ensures that we
+ * always produce the same result, even when buffers vary due to
+ * connection timings. test457 fails in CI randomly otherwise. */
+ if(data->set.max_filesize &&
+ (data->req.bytecount > data->set.max_filesize)) {
+ curl_off_t nexcess;
+ failf(data, "Exceeded the maximum allowed file size "
+ "(%" CURL_FORMAT_CURL_OFF_T ")",
+ data->set.max_filesize);
+ nexcess = data->req.bytecount - data->set.max_filesize;
+ nwrite = (nexcess >= (curl_off_t)nbytes)? 0 : (nbytes - (size_t)nexcess);
+ }
+
+ if(!data->req.ignorebody && nwrite) {
+ result = Curl_cwriter_write(data, writer->next, type, buf, nwrite);
+ if(result)
+ return result;
+ }
+ result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
+ if(result)
+ return result;
+
+ return (nwrite == nbytes)? CURLE_OK : CURLE_FILESIZE_EXCEEDED;
+}
+
+static const struct Curl_cwtype cw_download = {
+ "download",
NULL,
+ Curl_cwriter_def_init,
+ cw_download_write,
+ Curl_cwriter_def_close,
+ sizeof(struct Curl_cwriter)
+};
+
+/* RAW client writer in phase CURL_CW_RAW that
+ * enabled tracing of raw data. */
+static CURLcode cw_raw_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes)
+{
+ if(type & CLIENTWRITE_BODY && data->set.verbose && !data->req.ignorebody) {
+ Curl_debug(data, CURLINFO_DATA_IN, (char *)buf, nbytes);
+ }
+ return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+}
+
+static const struct Curl_cwtype cw_raw = {
+ "raw",
NULL,
- client_cew_init,
- client_cew_write,
- client_cew_close,
- sizeof(struct contenc_writer)
+ Curl_cwriter_def_init,
+ cw_raw_write,
+ Curl_cwriter_def_close,
+ sizeof(struct Curl_cwriter)
};
/* Create an unencoding writer stage using the given handler. */
-CURLcode Curl_client_create_writer(struct contenc_writer **pwriter,
+CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
struct Curl_easy *data,
- const struct content_encoding *ce_handler,
- int order)
+ const struct Curl_cwtype *cwt,
+ Curl_cwriter_phase phase)
{
- struct contenc_writer *writer;
+ struct Curl_cwriter *writer;
CURLcode result = CURLE_OUT_OF_MEMORY;
- DEBUGASSERT(ce_handler->writersize >= sizeof(struct contenc_writer));
- writer = (struct contenc_writer *) calloc(1, ce_handler->writersize);
+ DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter));
+ writer = (struct Curl_cwriter *) calloc(1, cwt->cwriter_size);
if(!writer)
goto out;
- writer->handler = ce_handler;
- writer->order = order;
- result = ce_handler->init_writer(data, writer);
+ writer->cwt = cwt;
+ writer->phase = phase;
+ result = cwt->do_init(data, writer);
out:
*pwriter = result? NULL : writer;
return result;
}
-void Curl_client_free_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
+void Curl_cwriter_free(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
{
if(writer) {
- writer->handler->close_writer(data, writer);
+ writer->cwt->do_close(data, writer);
free(writer);
}
}
-/* allow no more than 5 "chained" compression steps */
-#define MAX_ENCODE_STACK 5
+size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase)
+{
+ struct Curl_cwriter *w;
+ size_t n = 0;
+ for(w = data->req.writer_stack; w; w = w->next) {
+ if(w->phase == phase)
+ ++n;
+ }
+ return n;
+}
-static CURLcode init_writer_stack(struct Curl_easy *data)
+static CURLcode do_init_stack(struct Curl_easy *data)
{
+ struct Curl_cwriter *writer;
+ CURLcode result;
+
DEBUGASSERT(!data->req.writer_stack);
- return Curl_client_create_writer(&data->req.writer_stack,
- data, &client_cew, 0);
+ result = Curl_cwriter_create(&data->req.writer_stack,
+ data, &cw_client, CURL_CW_CLIENT);
+ if(result)
+ return result;
+
+ result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
+ if(result)
+ return result;
+ result = Curl_cwriter_add(data, writer);
+ if(result) {
+ Curl_cwriter_free(data, writer);
+ }
+
+ result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW);
+ if(result)
+ return result;
+ result = Curl_cwriter_add(data, writer);
+ if(result) {
+ Curl_cwriter_free(data, writer);
+ }
+ return result;
}
-CURLcode Curl_client_add_writer(struct Curl_easy *data,
- struct contenc_writer *writer)
+CURLcode Curl_cwriter_add(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
{
CURLcode result;
+ struct Curl_cwriter **anchor = &data->req.writer_stack;
- if(!data->req.writer_stack) {
- result = init_writer_stack(data);
+ if(!*anchor) {
+ result = do_init_stack(data);
if(result)
return result;
}
- if(data->req.writer_stack_depth++ >= MAX_ENCODE_STACK) {
- failf(data, "Reject response due to more than %u content encodings",
- MAX_ENCODE_STACK);
- return CURLE_BAD_CONTENT_ENCODING;
- }
-
- /* Stack the unencoding stage. */
- if(writer->order >= data->req.writer_stack->order) {
- writer->downstream = data->req.writer_stack;
- data->req.writer_stack = writer;
- }
- else {
- struct contenc_writer *w = data->req.writer_stack;
- while(w->downstream && writer->order < w->downstream->order)
- w = w->downstream;
- writer->downstream = w->downstream;
- w->downstream = writer;
- }
+ /* Insert the writer as first in its phase.
+ * Skip existing writers of lower phases. */
+ while(*anchor && (*anchor)->phase < writer->phase)
+ anchor = &((*anchor)->next);
+ writer->next = *anchor;
+ *anchor = writer;
return CURLE_OK;
}
#define CLIENTWRITE_1XX (1<<5) /* a 1xx response related HEADER */
#define CLIENTWRITE_TRAILER (1<<6) /* a trailer HEADER */
+/**
+ * Write `len` bytes at `prt` to the client. `type` indicates what
+ * kind of data is being written.
+ */
CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr,
size_t len) WARN_UNUSED_RESULT;
+/**
+ * For a paused transfer, there might be buffered data held back.
+ * Attempt to flush this data to the client. This *may* trigger
+ * another pause of the transfer.
+ */
CURLcode Curl_client_unpause(struct Curl_easy *data);
+
+/**
+ * Free all resources related to client writing.
+ */
void Curl_client_cleanup(struct Curl_easy *data);
-struct contenc_writer {
- const struct content_encoding *handler; /* Encoding handler. */
- struct contenc_writer *downstream; /* Downstream writer. */
- unsigned int order; /* Ordering within writer stack. */
+/**
+ * Client Writers - a chain passing transfer BODY data to the client.
+ * Main application: HTTP and related protocols
+ * Other uses: monitoring of download progress
+ *
+ * Writers in the chain are order by their `phase`. First come all
+ * writers in CURL_CW_RAW, followed by any in CURL_CW_TRANSFER_DECODE,
+ * followed by any in CURL_CW_PROTOCOL, etc.
+ *
+ * When adding a writer, it is inserted as first in its phase. This means
+ * the order of adding writers of the same phase matters, but writers for
+ * different phases may be added in any order.
+ *
+ * Writers which do modify the BODY data written are expected to be of
+ * phases TRANSFER_DECODE or CONTENT_DECODE. The other phases are intended
+ * for monitoring writers. Which do *not* modify the data but gather
+ * statistics or update progress reporting.
+ */
+
+/* Phase a writer operates at. */
+typedef enum {
+ CURL_CW_RAW, /* raw data written, before any decoding */
+ CURL_CW_TRANSFER_DECODE, /* remove transfer-encodings */
+ CURL_CW_PROTOCOL, /* after transfer, but before content decoding */
+ CURL_CW_CONTENT_DECODE, /* remove content-encodings */
+ CURL_CW_CLIENT /* data written to client */
+} Curl_cwriter_phase;
+
+/* Client Writer Type, provides the implementation */
+struct Curl_cwtype {
+ const char *name; /* writer name. */
+ const char *alias; /* writer name alias, maybe NULL. */
+ CURLcode (*do_init)(struct Curl_easy *data,
+ struct Curl_cwriter *writer);
+ CURLcode (*do_write)(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes);
+ void (*do_close)(struct Curl_easy *data,
+ struct Curl_cwriter *writer);
+ size_t cwriter_size; /* sizeof() allocated struct Curl_cwriter */
};
-/* Content encoding writer. */
-struct content_encoding {
- const char *name; /* Encoding name. */
- const char *alias; /* Encoding name alias. */
- CURLcode (*init_writer)(struct Curl_easy *data,
- struct contenc_writer *writer);
- CURLcode (*unencode_write)(struct Curl_easy *data,
- struct contenc_writer *writer,
- const char *buf, size_t nbytes);
- void (*close_writer)(struct Curl_easy *data,
- struct contenc_writer *writer);
- size_t writersize;
+/* Client writer instance */
+struct Curl_cwriter {
+ const struct Curl_cwtype *cwt; /* type implementation */
+ struct Curl_cwriter *next; /* Downstream writer. */
+ Curl_cwriter_phase phase; /* phase at which it operates */
};
+/**
+ * Create a new cwriter instance with given type and phase. Is not
+ * inserted into the writer chain by this call.
+ * Invokes `writer->do_init()`.
+ */
+CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
+ struct Curl_easy *data,
+ const struct Curl_cwtype *ce_handler,
+ Curl_cwriter_phase phase);
-CURLcode Curl_client_create_writer(struct contenc_writer **pwriter,
- struct Curl_easy *data,
- const struct content_encoding *ce_handler,
- int order);
+/**
+ * Free a cwriter instance.
+ * Invokes `writer->do_close()`.
+ */
+void Curl_cwriter_free(struct Curl_easy *data,
+ struct Curl_cwriter *writer);
-void Curl_client_free_writer(struct Curl_easy *data,
- struct contenc_writer *writer);
+/**
+ * Count the number of writers installed of the given phase.
+ */
+size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase);
-CURLcode Curl_client_add_writer(struct Curl_easy *data,
- struct contenc_writer *writer);
+/**
+ * Adds a writer to the transfer's writer chain.
+ * The writers `phase` determines where in the chain it is inserted.
+ */
+CURLcode Curl_cwriter_add(struct Curl_easy *data,
+ struct Curl_cwriter *writer);
+
+/**
+ * Convenience method for calling `writer->do_write()` that
+ * checks for NULL writer.
+ */
+CURLcode Curl_cwriter_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes);
+
+/**
+ * Default implementations for do_init, do_write, do_close that
+ * do nothing and pass the data through.
+ */
+CURLcode Curl_cwriter_def_init(struct Curl_easy *data,
+ struct Curl_cwriter *writer);
+CURLcode Curl_cwriter_def_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes);
+void Curl_cwriter_def_close(struct Curl_easy *data,
+ struct Curl_cwriter *writer);
/* internal read-function, does plain socket, SSL and krb4 */
break;
}
}
- data->req.bytecount += len;
data->req.offset += len;
- result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
- if(result) {
- req->result = result;
- next_state = SMB_CLOSE;
- break;
- }
next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD;
break;
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
struct tftp_state_data *state = conn->proto.tftpc;
- struct SingleRequest *k = &data->req;
/* Receive the packet */
fromlen = sizeof(fromaddr);
result = Curl_client_write(data, CLIENTWRITE_BODY,
(char *)state->rpacket.data + 4,
state->rbytes-4);
- if(!result) {
- k->bytecount += state->rbytes-4;
- result = Curl_pgrsSetDownloadCounter(data,
- (curl_off_t) k->bytecount);
- }
if(result) {
tftp_state_machine(state, TFTP_EVENT_ERROR);
return result;
{
CURLcode result = CURLE_OK;
ssize_t nread; /* number of bytes read */
+ ssize_t n_to_write;
bool readmore = FALSE; /* used by RTP to signal for more data */
int maxloops = 100;
curl_off_t max_recv = data->set.max_recv_speed?
} /* this is the first time we write a body part */
#endif /* CURL_DISABLE_HTTP */
- k->bodywrites++;
-
- /* pass data to the debug function before it gets "dechunked" */
- if(data->set.verbose) {
- if(k->badheader) {
- Curl_debug(data, CURLINFO_DATA_IN,
- Curl_dyn_ptr(&data->state.headerb),
- Curl_dyn_len(&data->state.headerb));
- if(k->badheader == HEADER_PARTHEADER)
- Curl_debug(data, CURLINFO_DATA_IN,
- k->str, (size_t)nread);
- }
- else
- Curl_debug(data, CURLINFO_DATA_IN,
- k->str, (size_t)nread);
- }
-
#ifndef CURL_DISABLE_HTTP
if(k->chunk) {
/*
#endif /* CURL_DISABLE_HTTP */
/* Account for body content stored in the header buffer */
+ n_to_write = nread;
if((k->badheader == HEADER_PARTHEADER) && !k->ignorebody) {
- size_t headlen = Curl_dyn_len(&data->state.headerb);
- DEBUGF(infof(data, "Increasing bytecount by %zu", headlen));
- k->bytecount += headlen;
+ n_to_write += Curl_dyn_len(&data->state.headerb);
}
if((-1 != k->maxdownload) &&
- (k->bytecount + nread >= k->maxdownload)) {
+ (k->bytecount + n_to_write >= k->maxdownload)) {
+
+ excess = (size_t)(k->bytecount + n_to_write - 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,
+ excess, k->size, k->maxdownload, k->bytecount);
+ connclose(conn, "excess found in a read");
+ }
- excess = (size_t)(k->bytecount + nread - k->maxdownload);
nread = (ssize_t) (k->maxdownload - k->bytecount);
if(nread < 0) /* this should be unusual */
nread = 0;
}
}
- k->bytecount += nread;
max_recv -= nread;
- result = Curl_pgrsSetDownloadCounter(data, k->bytecount);
- if(result)
- goto out;
-
if(!k->chunk && (nread || k->badheader || is_empty_data)) {
/* If this is chunky transfer, it was already written */
- if(k->badheader && !k->ignorebody) {
+ if(k->badheader) {
/* we parsed a piece of data wrongly assuming it was a header
and now we output it as body instead */
size_t headlen = Curl_dyn_len(&data->state.headerb);
in http_chunks.c.
Make sure that ALL_CONTENT_ENCODINGS contains all the
encodings handled here. */
- if(!k->ignorebody && nread) {
+ if(nread) {
#ifndef CURL_DISABLE_POP3
- if(conn->handler->protocol & PROTO_FAMILY_POP3)
- result = Curl_pop3_write(data, k->str, nread);
+ if(conn->handler->protocol & PROTO_FAMILY_POP3) {
+ result = k->ignorebody? CURLE_OK :
+ Curl_pop3_write(data, k->str, nread);
+ }
else
#endif /* CURL_DISABLE_POP3 */
result = Curl_client_write(data, CLIENTWRITE_BODY, k->str,
enum upgrade101 upgr101; /* 101 upgrade state */
/* Content unencoding stack. See sec 3.5, RFC2616. */
- struct contenc_writer *writer_stack;
+ struct Curl_cwriter *writer_stack;
time_t timeofdoc;
long bodywrites;
char *location; /* This points to an allocated version of the Location:
state(data, SSH_STOP);
break;
}
- /* since this counts what we send to the client, we include the
- newline in this counter */
- data->req.bytecount += sshc->readdir_len + 1;
- /* output debug output if that is requested */
- Curl_debug(data, CURLINFO_DATA_OUT, (char *)sshc->readdir_filename,
- sshc->readdir_len);
}
else {
if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) {
Curl_dyn_ptr(&sshc->readdir_buf),
Curl_dyn_len(&sshc->readdir_buf));
- if(!result) {
- /* output debug output if that is requested */
- Curl_debug(data, CURLINFO_DATA_OUT, Curl_dyn_ptr(&sshc->readdir_buf),
- Curl_dyn_len(&sshc->readdir_buf));
- data->req.bytecount += Curl_dyn_len(&sshc->readdir_buf);
- }
ssh_string_free_char(sshc->readdir_tmp);
sshc->readdir_tmp = NULL;
state(data, SSH_STOP);
break;
}
- /* since this counts what we send to the client, we include the
- newline in this counter */
- data->req.bytecount += readdir_len + 1;
- /* output debug output if that is requested */
- Curl_debug(data, CURLINFO_DATA_IN, sshp->readdir_filename,
- readdir_len);
- Curl_debug(data, CURLINFO_DATA_IN, (char *)"\n", 1);
}
else {
result = Curl_dyn_add(&sshp->readdir, sshp->readdir_longentry);
Curl_dyn_ptr(&sshp->readdir),
Curl_dyn_len(&sshp->readdir));
- if(!result) {
- /* output debug output if that is requested */
- Curl_debug(data, CURLINFO_DATA_IN,
- Curl_dyn_ptr(&sshp->readdir),
- Curl_dyn_len(&sshp->readdir));
- data->req.bytecount += Curl_dyn_len(&sshp->readdir);
- }
if(result) {
Curl_dyn_free(&sshp->readdir);
state(data, SSH_STOP);
30\r
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r
21;heresatest=moooo\r
-cccccccccccccccccccccccccccccccc
+cccccccccccccccccccccccccccccc
+c
\r
0\r
\r
Transfer-Encoding: chunked
Connection: mooo
-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccc
</datacheck>
</reply>