mirror of
https://github.com/curl/curl.git
synced 2025-04-12 16:20:35 +08:00
lib: client writer, part 2, accounting + logging
This PR has these changes: Renaming of unencode_* to cwriter, e.g. client writers - documentation of sendf.h functions - move max decode stack checks back to content_encoding.c - define writer phase which was used as order before - introduce phases for monitoring inbetween decode phases - offering default implementations for init/write/close Add type paramter to client writer's do_write() - always pass all writes through the writer stack - writers who only care about BODY data will pass other writes unchanged add RAW and PROTOCOL client writers - RAW used for Curl_debug() logging of CURLINFO_DATA_IN - PROTOCOL used for updates to data->req.bytecount, max_filesize checks and Curl_pgrsSetDownloadCounter() - remove all updates of data->req.bytecount and calls to Curl_pgrsSetDownloadCounter() and Curl_debug() from other code - adjust test457 expected output to no longer see the excess write Closes #12184
This commit is contained in:
parent
2b16b86bb6
commit
ad051e1cbe
@ -172,17 +172,15 @@ static int hyper_each_header(void *userdata,
|
||||
|
||||
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);
|
||||
@ -201,7 +199,7 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
|
||||
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;
|
||||
@ -241,11 +239,6 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
|
||||
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) {
|
||||
@ -253,12 +246,6 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -310,13 +297,14 @@ static CURLcode status_line(struct Curl_easy *data,
|
||||
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;
|
||||
}
|
||||
|
@ -374,7 +374,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
|
||||
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
|
||||
@ -467,15 +467,12 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
|
||||
/* 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)
|
||||
|
@ -63,6 +63,9 @@
|
||||
|
||||
#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 */
|
||||
|
||||
|
||||
@ -95,7 +98,7 @@ typedef enum {
|
||||
|
||||
/* 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. */
|
||||
@ -171,7 +174,7 @@ static CURLcode process_trailer(struct Curl_easy *data,
|
||||
}
|
||||
|
||||
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;
|
||||
@ -196,7 +199,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
|
||||
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;
|
||||
@ -217,7 +220,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
|
||||
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);
|
||||
@ -274,8 +277,8 @@ static CURLcode inflate_stream(struct Curl_easy *data,
|
||||
|
||||
|
||||
/* 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 */
|
||||
@ -290,13 +293,16 @@ static CURLcode deflate_init_writer(struct Curl_easy *data,
|
||||
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;
|
||||
@ -305,11 +311,11 @@ static CURLcode deflate_unencode_write(struct Curl_easy *data,
|
||||
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 */
|
||||
@ -317,19 +323,19 @@ static void deflate_close_writer(struct Curl_easy *data,
|
||||
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 */
|
||||
@ -441,19 +447,22 @@ static enum {
|
||||
}
|
||||
#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
|
||||
@ -565,12 +574,12 @@ static CURLcode gzip_unencode_write(struct Curl_easy *data,
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
@ -578,12 +587,12 @@ static void gzip_close_writer(struct Curl_easy *data,
|
||||
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)
|
||||
};
|
||||
|
||||
@ -593,7 +602,7 @@ static const struct content_encoding gzip_encoding = {
|
||||
#ifdef HAVE_BROTLI
|
||||
/* Brotli writer. */
|
||||
struct brotli_writer {
|
||||
struct contenc_writer super;
|
||||
struct Curl_cwriter super;
|
||||
BrotliDecoderState *br; /* State structure for brotli. */
|
||||
};
|
||||
|
||||
@ -635,8 +644,8 @@ static CURLcode brotli_map_error(BrotliDecoderErrorCode be)
|
||||
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;
|
||||
@ -645,8 +654,8 @@ static CURLcode brotli_init_writer(struct Curl_easy *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;
|
||||
@ -657,6 +666,9 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data,
|
||||
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. */
|
||||
|
||||
@ -670,7 +682,7 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data,
|
||||
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;
|
||||
@ -693,8 +705,8 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data,
|
||||
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;
|
||||
|
||||
@ -706,12 +718,12 @@ static void brotli_close_writer(struct Curl_easy *data,
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@ -720,13 +732,13 @@ static const struct content_encoding brotli_encoding = {
|
||||
#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;
|
||||
|
||||
@ -737,8 +749,8 @@ static CURLcode zstd_init_writer(struct Curl_easy *data,
|
||||
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;
|
||||
@ -747,6 +759,9 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data,
|
||||
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)
|
||||
@ -766,7 +781,7 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data,
|
||||
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;
|
||||
@ -778,8 +793,8 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data,
|
||||
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;
|
||||
|
||||
@ -795,52 +810,30 @@ static void zstd_close_writer(struct Curl_easy *data,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@ -860,8 +853,8 @@ static const struct content_encoding * const encodings[] = {
|
||||
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++) {
|
||||
@ -893,16 +886,16 @@ char *Curl_all_content_encodings(void)
|
||||
|
||||
|
||||
/* 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();
|
||||
@ -911,6 +904,9 @@ static CURLcode error_unencode_write(struct Curl_easy *data,
|
||||
(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. "
|
||||
@ -919,43 +915,30 @@ static CURLcode error_unencode_write(struct Curl_easy *data,
|
||||
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 = {
|
||||
static const struct Curl_cwtype error_writer = {
|
||||
"ce-error",
|
||||
NULL,
|
||||
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;
|
||||
@ -969,7 +952,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
|
||||
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 {
|
||||
@ -992,23 +976,32 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
|
||||
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;
|
||||
}
|
||||
|
||||
encoding = find_encoding(name, namelen);
|
||||
if(!encoding)
|
||||
encoding = &error_encoding; /* Defer error at stack use. */
|
||||
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;
|
||||
}
|
||||
|
||||
result = Curl_client_create_writer(&writer, data, encoding, order);
|
||||
cwt = find_encoding(name, namelen);
|
||||
if(!cwt)
|
||||
cwt = &error_writer; /* Defer error at use. */
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1028,17 +1021,6 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
|
||||
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. */
|
||||
|
@ -25,15 +25,10 @@
|
||||
***************************************************************************/
|
||||
#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 */
|
||||
|
@ -414,7 +414,6 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
|
||||
bool size_known;
|
||||
bool fstated = FALSE;
|
||||
char *buf = data->state.buffer;
|
||||
curl_off_t bytecount = 0;
|
||||
int fd;
|
||||
struct FILEPROTO *file;
|
||||
|
||||
@ -563,7 +562,6 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
|
||||
if(nread <= 0 || (size_known && (expected_size == 0)))
|
||||
break;
|
||||
|
||||
bytecount += nread;
|
||||
if(size_known)
|
||||
expected_size -= nread;
|
||||
|
||||
@ -571,10 +569,6 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_pgrsSetDownloadCounter(data, bytecount);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(Curl_pgrsUpdate(data))
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
else
|
||||
|
@ -1194,8 +1194,6 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
|
||||
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);
|
||||
|
||||
|
16
lib/ldap.c
16
lib/ldap.c
@ -313,7 +313,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
|
||||
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
|
||||
@ -535,6 +534,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
|
||||
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);
|
||||
|
||||
@ -596,8 +596,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
|
||||
goto quit;
|
||||
}
|
||||
|
||||
dlsize += name_len + 5;
|
||||
|
||||
FREE_ON_WINLDAP(name);
|
||||
ldap_memfree(dn);
|
||||
}
|
||||
@ -659,8 +657,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
|
||||
goto quit;
|
||||
}
|
||||
|
||||
dlsize += attr_len + 3;
|
||||
|
||||
if((attr_len > 7) &&
|
||||
(strcmp(";binary", attr + (attr_len - 7)) == 0)) {
|
||||
/* Binary attribute, encode to base64. */
|
||||
@ -689,8 +685,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
|
||||
|
||||
goto quit;
|
||||
}
|
||||
|
||||
dlsize += val_b64_sz;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -705,8 +699,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
|
||||
|
||||
goto quit;
|
||||
}
|
||||
|
||||
dlsize += vals[i]->bv_len;
|
||||
}
|
||||
|
||||
result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
|
||||
@ -719,8 +711,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
|
||||
|
||||
goto quit;
|
||||
}
|
||||
|
||||
dlsize++;
|
||||
}
|
||||
|
||||
/* Free memory used to store values */
|
||||
@ -734,10 +724,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
|
||||
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)
|
||||
|
@ -677,7 +677,6 @@ MQTT_SUBACK_COMING:
|
||||
/* 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;
|
||||
@ -693,13 +692,8 @@ MQTT_SUBACK_COMING:
|
||||
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 */
|
||||
|
||||
|
@ -953,18 +953,12 @@ static CURLcode client_write(struct Curl_easy *data,
|
||||
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;
|
||||
}
|
||||
|
@ -319,12 +319,6 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
@ -655,7 +655,6 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
|
||||
* 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;
|
||||
|
265
lib/sendf.c
265
lib/sendf.c
@ -50,6 +50,7 @@
|
||||
#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 */
|
||||
@ -57,6 +58,9 @@
|
||||
#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
|
||||
@ -385,17 +389,17 @@ static CURLcode chop_write(struct Curl_easy *data,
|
||||
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 */
|
||||
@ -405,14 +409,14 @@ CURLcode Curl_client_write(struct Curl_easy *data,
|
||||
/* 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)
|
||||
@ -449,12 +453,12 @@ 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;
|
||||
}
|
||||
@ -466,58 +470,152 @@ void Curl_client_cleanup(struct Curl_easy *data)
|
||||
|
||||
}
|
||||
|
||||
/* 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,
|
||||
client_cew_init,
|
||||
client_cew_write,
|
||||
client_cew_close,
|
||||
sizeof(struct contenc_writer)
|
||||
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,
|
||||
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;
|
||||
@ -526,55 +624,74 @@ out:
|
||||
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
|
||||
|
||||
|
||||
static CURLcode init_writer_stack(struct Curl_easy *data)
|
||||
size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase)
|
||||
{
|
||||
DEBUGASSERT(!data->req.writer_stack);
|
||||
return Curl_client_create_writer(&data->req.writer_stack,
|
||||
data, &client_cew, 0);
|
||||
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;
|
||||
}
|
||||
|
||||
CURLcode Curl_client_add_writer(struct Curl_easy *data,
|
||||
struct contenc_writer *writer)
|
||||
static CURLcode do_init_stack(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_cwriter *writer;
|
||||
CURLcode result;
|
||||
|
||||
if(!data->req.writer_stack) {
|
||||
result = init_writer_stack(data);
|
||||
DEBUGASSERT(!data->req.writer_stack);
|
||||
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_cwriter_add(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer)
|
||||
{
|
||||
CURLcode result;
|
||||
struct Curl_cwriter **anchor = &data->req.writer_stack;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
127
lib/sendf.h
127
lib/sendf.h
@ -50,43 +50,122 @@
|
||||
#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 */
|
||||
|
@ -1047,14 +1047,7 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
|
||||
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;
|
||||
|
||||
|
@ -1107,7 +1107,6 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data)
|
||||
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);
|
||||
@ -1141,11 +1140,6 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data)
|
||||
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;
|
||||
|
@ -423,6 +423,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
|
||||
{
|
||||
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?
|
||||
@ -578,23 +579,6 @@ static CURLcode readwrite_data(struct Curl_easy *data,
|
||||
} /* 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) {
|
||||
/*
|
||||
@ -634,16 +618,26 @@ static CURLcode readwrite_data(struct Curl_easy *data,
|
||||
#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;
|
||||
@ -657,17 +651,12 @@ static CURLcode readwrite_data(struct Curl_easy *data,
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@ -691,10 +680,12 @@ static CURLcode readwrite_data(struct Curl_easy *data,
|
||||
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,
|
||||
|
@ -685,7 +685,7 @@ struct SingleRequest {
|
||||
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:
|
||||
|
@ -1466,13 +1466,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
|
||||
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)) {
|
||||
@ -1564,12 +1558,6 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
|
||||
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;
|
||||
|
||||
|
@ -2341,14 +2341,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
|
||||
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);
|
||||
@ -2427,13 +2420,6 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
|
||||
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);
|
||||
|
@ -20,7 +20,8 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
30
|
||||
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
21;heresatest=moooo
|
||||
cccccccccccccccccccccccccccccccc
|
||||
cccccccccccccccccccccccccccccc
|
||||
c
|
||||
|
||||
0
|
||||
|
||||
@ -31,7 +32,7 @@ Server: fakeit/0.9 fakeitbad/1.0
|
||||
Transfer-Encoding: chunked
|
||||
Connection: mooo
|
||||
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccc
|
||||
</datacheck>
|
||||
</reply>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user