]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
lib: client writer, part 2, accounting + logging
authorStefan Eissing <stefan@eissing.org>
Mon, 23 Oct 2023 08:33:07 +0000 (10:33 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 6 Nov 2023 12:14:06 +0000 (13:14 +0100)
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

20 files changed:
lib/c-hyper.c
lib/cf-h1-proxy.c
lib/content_encoding.c
lib/content_encoding.h
lib/file.c
lib/imap.c
lib/ldap.c
lib/mqtt.c
lib/openldap.c
lib/progress.c
lib/rtsp.c
lib/sendf.c
lib/sendf.h
lib/smb.c
lib/tftp.c
lib/transfer.c
lib/urldata.h
lib/vssh/libssh.c
lib/vssh/libssh2.c
tests/data/test457

index 5726ff1cc3a0b565b446fc57e15fac368ede7e56..f34c44cc7376787852747fbfb6b2e42242f7acc4 100644 (file)
@@ -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;
 }
index c46fb1e31a85c498e9213016eb519a9241de92e3..5ecd7707fca2a6913ac5f5f94c8a9dde787a9e13 100644 (file)
@@ -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)
index be7c075e949855695b75b2b9309ef9e4c4f561eb..c7553e15a23302432aee0fa8e71cba0c8056d3e5 100644 (file)
@@ -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 = {
-  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;
@@ -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;
+      }
+
+      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;
       }
     }
@@ -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. */
index ef7930cb32cdd5d6854fa679411e028c36fe342a..7b71c813c57ce62d0ab449c91c52d7c15c9b9717 100644 (file)
  ***************************************************************************/
 #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 */
index ffa9fb76d06fc1b966d1ca8a43d80a1718babe9e..e7e24e345178a1b4cc8ecb5f89d9f9b37cfb88eb 100644 (file)
@@ -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
index de64c2a7f26df420c0c66d2ae875e2f10dc54d41..eee8624e9ec9da1cbf80fc7ab885e4825b14907d 100644 (file)
@@ -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);
 
index f3339290bbf45ece1d1c0c103097b5fdacc545a0..eb5fe795e882e791adb1f290ada0ee4ecdff4be8 100644 (file)
@@ -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)
index 54f88822c0df225ab115aa8612396ce0947ac14e..2cf18f9adfe8f2dcce4eb733ee939e1f1a50e709 100644 (file)
@@ -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 */
 
index eae01b9d2f7e0dd1a08114129f3f17de19e972f1..131f4741428b112e4732d2c890477697d827db6e 100644 (file)
@@ -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;
 }
index e783a9c86dfa3d2c75283462469597cd7b673a15..b0fc4aa48fdf646bff0fd4803e72b6933962bbda 100644 (file)
@@ -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;
 }
index f963391062969f4b0467e9a9211ec493c598f694..cc0c3621fdb27adf4b7970b52569da31891e906c 100644 (file)
@@ -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;
index 0482c5da4e2ce0bd79aa59b261df536d1ae64633..ac5f9197d0a76f72f919deb0a9c1aea7fbf3aaaa 100644 (file)
@@ -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,
+  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;
@@ -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
+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;
 }
 
index 9ee00bb0df2abb7c764c433a5d96588e03ed7b8a..a70189f2f5e97eed5c4382e989cfcffada428681 100644 (file)
 #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 */
index 32c5137a44f9060ba9f2e490eec20e1415b61bbd..b432e46b24726bf89564c4b298e2bcf0624b0248 100644 (file)
--- a/lib/smb.c
+++ b/lib/smb.c
@@ -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;
 
index e78140d520394a960eb312b70ae930029d0e0791..33281356ae287d831a65882c817b5bd9ace0d6c7 100644 (file)
@@ -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;
index bbef7b5f0a2013b75593862e61b6725fcc975ad6..8e8158e64ab1178c3c4a55f01e4be974ce454449 100644 (file)
@@ -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,
index 2f927a393ef57d2c53225ed9ee0237ab1a0d661a..1828fc4cd48c9f983c59e05592ffb1a1bd2dcab9 100644 (file)
@@ -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:
index 80588d07312162da249cac8613e090de3b95f02b..4d5445be49b802891ad1737d0f6f776d6c0f2b61 100644 (file)
@@ -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;
 
index f539b393b38c973b4cfeb41cf4569833a1984d87..543b6fb43221642942ea129d9e3945d0f882db4d 100644 (file)
@@ -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);
index 77eb9c8553eb9c17ad6b746224ea1ac3f5eb5ec5..aa391d7fd00908aac3e433f0ae878e277073220c 100644 (file)
@@ -20,7 +20,8 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 30\r
 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r
 21;heresatest=moooo\r
-cccccccccccccccccccccccccccccccc
+cccccccccccccccccccccccccccccc
+c
 \r
 0\r
 \r
@@ -31,7 +32,7 @@ Server: fakeit/0.9 fakeitbad/1.0
 Transfer-Encoding: chunked
 Connection: mooo
 
-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccc
 </datacheck>
 </reply>