Commit dbcced8e authored by Patrick Monnerat's avatar Patrick Monnerat
Browse files

HTTP: support multiple Content-Encodings

This is implemented as an output streaming stack of unencoders, the last
calling the client write procedure.

New test 230 checks this feature.

Bug: https://github.com/curl/curl/pull/2002
Reported-By: Daniel Bankhead
parent 462f3cac
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -673,7 +673,7 @@ Content Encoding


 where string is the intended value of the Accept-Encoding header.
 where string is the intended value of the Accept-Encoding header.


 Currently, libcurl does not support multiple encodings and only
 Currently, libcurl does support multiple encodings but only
 understands how to process responses that use the "deflate" or "gzip"
 understands how to process responses that use the "deflate" or "gzip"
 Content-Encoding, so the only values for [`CURLOPT_ACCEPT_ENCODING`][5]
 Content-Encoding, so the only values for [`CURLOPT_ACCEPT_ENCODING`][5]
 that will work (besides "identity," which does nothing) are "deflate"
 that will work (besides "identity," which does nothing) are "deflate"
+0 −7
Original line number Original line Diff line number Diff line
@@ -67,7 +67,6 @@
 5.7 Brotli compression
 5.7 Brotli compression
 5.8 QUIC
 5.8 QUIC
 5.9 Leave secure cookies alone
 5.9 Leave secure cookies alone
 5.10 Support Multiple Content-Encodings


 6. TELNET
 6. TELNET
 6.1 ditch stdin
 6.1 ditch stdin
@@ -538,12 +537,6 @@ This is not detailed in any FTP specification.


 https://tools.ietf.org/html/draft-ietf-httpbis-cookie-alone-01
 https://tools.ietf.org/html/draft-ietf-httpbis-cookie-alone-01


5.10 Support Multiple Content-Encodings

 RFC 7231 Section 3.1.2.2 allows multiple encodings for a single request. Using
 this may result in lower bandwidth and promotes a more resource-friendly web.
 Currently, Chrome and Firefox support multiple encodings.



6. TELNET
6. TELNET


+477 −97
Original line number Original line Diff line number Diff line
@@ -22,16 +22,23 @@


#include "curl_setup.h"
#include "curl_setup.h"


#ifdef HAVE_LIBZ

#include "urldata.h"
#include "urldata.h"
#include <curl/curl.h>
#include <curl/curl.h>
#include <stddef.h>
#include "sendf.h"
#include "sendf.h"
#include "http.h"
#include "content_encoding.h"
#include "content_encoding.h"
#include "strdup.h"
#include "strdup.h"
#include "strcase.h"
#include "curl_memory.h"
#include "curl_memory.h"
#include "memdebug.h"
#include "memdebug.h"


#define CONTENT_ENCODING_DEFAULT  "identity"

#ifndef CURL_DISABLE_HTTP

#ifdef HAVE_LIBZ

/* Comment this out if zlib is always going to be at least ver. 1.2.0.4
/* Comment this out if zlib is always going to be at least ver. 1.2.0.4
   (doing so will reduce code size slightly). */
   (doing so will reduce code size slightly). */
#define OLD_ZLIB_SUPPORT 1
#define OLD_ZLIB_SUPPORT 1
@@ -49,6 +56,21 @@
#define COMMENT      0x10 /* bit 4 set: file comment present */
#define COMMENT      0x10 /* bit 4 set: file comment present */
#define RESERVED     0xE0 /* bits 5..7: reserved */
#define RESERVED     0xE0 /* bits 5..7: reserved */


typedef enum {
  ZLIB_UNINIT,          /* uninitialized */
  ZLIB_INIT,            /* initialized */
  ZLIB_GZIP_HEADER,     /* reading gzip header */
  ZLIB_GZIP_INFLATING,  /* inflating gzip stream */
  ZLIB_INIT_GZIP        /* initialized in transparent gzip mode */
} zlibInitState;

/* Writer parameters. */
typedef struct {
  zlibInitState zlib_init;    /* zlib init state */
  z_stream z;                /* State structure for zlib. */
}  zlib_params;


static voidpf
static voidpf
zalloc_cb(voidpf opaque, unsigned int items, unsigned int size)
zalloc_cb(voidpf opaque, unsigned int items, unsigned int size)
{
{
@@ -79,19 +101,27 @@ process_zlib_error(struct connectdata *conn, z_stream *z)
}
}


static CURLcode
static CURLcode
exit_zlib(z_stream *z, zlibInitState *zlib_init, CURLcode result)
exit_zlib(struct connectdata *conn,
          z_stream *z, zlibInitState *zlib_init, CURLcode result)
{
{
  inflateEnd(z);
  if(*zlib_init == ZLIB_GZIP_HEADER)
    Curl_safefree(z->next_in);

  if(*zlib_init != ZLIB_UNINIT) {
    if(inflateEnd(z) != Z_OK && result == CURLE_OK)
      result = process_zlib_error(conn, z);
    *zlib_init = ZLIB_UNINIT;
    *zlib_init = ZLIB_UNINIT;
  }

  return result;
  return result;
}
}


static CURLcode
static CURLcode
inflate_stream(struct connectdata *conn,
inflate_stream(struct connectdata *conn, contenc_writer *writer)
               struct SingleRequest *k)
{
{
  zlib_params *zp = (zlib_params *) &writer->params;
  int allow_restart = 1;
  int allow_restart = 1;
  z_stream *z = &k->z;          /* zlib state structure */
  z_stream *z = &zp->z;         /* zlib state structure */
  uInt nread = z->avail_in;
  uInt nread = z->avail_in;
  Bytef *orig_in = z->next_in;
  Bytef *orig_in = z->next_in;
  int status;                   /* zlib status */
  int status;                   /* zlib status */
@@ -102,7 +132,7 @@ inflate_stream(struct connectdata *conn,
     large to hold on the stack */
     large to hold on the stack */
  decomp = malloc(DSIZ);
  decomp = malloc(DSIZ);
  if(decomp == NULL) {
  if(decomp == NULL) {
    return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
    return exit_zlib(conn, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
  }
  }


  /* because the buffer size is fixed, iteratively decompress and transfer to
  /* because the buffer size is fixed, iteratively decompress and transfer to
@@ -115,22 +145,18 @@ inflate_stream(struct connectdata *conn,
    status = inflate(z, Z_SYNC_FLUSH);
    status = inflate(z, Z_SYNC_FLUSH);
    if(status == Z_OK || status == Z_STREAM_END) {
    if(status == Z_OK || status == Z_STREAM_END) {
      allow_restart = 0;
      allow_restart = 0;
      if((DSIZ - z->avail_out) && (!k->ignorebody)) {
      result = Curl_unencode_write(conn, writer->downstream, decomp,
        result = Curl_client_write(conn, CLIENTWRITE_BODY, decomp,
                                   DSIZ - z->avail_out);
                                   DSIZ - z->avail_out);
      /* if !CURLE_OK, clean up, return */
      /* if !CURLE_OK, clean up, return */
      if(result) {
      if(result) {
        free(decomp);
        free(decomp);
          return exit_zlib(z, &k->zlib_init, result);
        return exit_zlib(conn, z, &zp->zlib_init, result);
        }
      }
      }


      /* Done? clean up, return */
      /* Done? clean up, return */
      if(status == Z_STREAM_END) {
      if(status == Z_STREAM_END) {
        free(decomp);
        free(decomp);
        if(inflateEnd(z) == Z_OK)
        return exit_zlib(conn, z, &zp->zlib_init, result);
          return exit_zlib(z, &k->zlib_init, result);
        return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
      }
      }


      /* Done with these bytes, exit */
      /* Done with these bytes, exit */
@@ -148,7 +174,8 @@ inflate_stream(struct connectdata *conn,
      (void) inflateEnd(z);     /* don't care about the return code */
      (void) inflateEnd(z);     /* don't care about the return code */
      if(inflateInit2(z, -MAX_WBITS) != Z_OK) {
      if(inflateInit2(z, -MAX_WBITS) != Z_OK) {
        free(decomp);
        free(decomp);
        return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
        zp->zlib_init = ZLIB_UNINIT;  /* inflateEnd() already called. */
        return exit_zlib(conn, z, &zp->zlib_init, process_zlib_error(conn, z));
      }
      }
      z->next_in = orig_in;
      z->next_in = orig_in;
      z->avail_in = nread;
      z->avail_in = nread;
@@ -157,36 +184,97 @@ inflate_stream(struct connectdata *conn,
    }
    }
    else {                      /* Error; exit loop, handle below */
    else {                      /* Error; exit loop, handle below */
      free(decomp);
      free(decomp);
      return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
      return exit_zlib(conn, z, &zp->zlib_init, process_zlib_error(conn, z));
    }
    }
  }
  }
  /* Will never get here */
  /* UNREACHED */
}
}


CURLcode

Curl_unencode_deflate_write(struct connectdata *conn,
/* Deflate handler. */
                            struct SingleRequest *k,
static CURLcode deflate_init_writer(struct connectdata *conn,
                            ssize_t nread)
                                    contenc_writer *writer)
{
{
  z_stream *z = &k->z;          /* zlib state structure */
  zlib_params *zp = (zlib_params *) &writer->params;
  z_stream *z = &zp->z;     /* zlib state structure */

  if(!writer->downstream)
    return CURLE_WRITE_ERROR;


  /* Initialize zlib? */
  /* Initialize zlib */
  if(k->zlib_init == ZLIB_UNINIT) {
    memset(z, 0, sizeof(z_stream));
  z->zalloc = (alloc_func) zalloc_cb;
  z->zalloc = (alloc_func) zalloc_cb;
  z->zfree = (free_func) zfree_cb;
  z->zfree = (free_func) zfree_cb;


  if(inflateInit(z) != Z_OK)
  if(inflateInit(z) != Z_OK)
    return process_zlib_error(conn, z);
    return process_zlib_error(conn, z);
    k->zlib_init = ZLIB_INIT;
  zp->zlib_init = ZLIB_INIT;
  return CURLE_OK;
}
}


static CURLcode deflate_unencode_write(struct connectdata *conn,
                                       contenc_writer *writer,
                                       const char *buf, size_t nbytes)
{
  zlib_params *zp = (zlib_params *) &writer->params;
  z_stream *z = &zp->z;     /* zlib state structure */

  /* Set the compressed input when this function is called */
  /* Set the compressed input when this function is called */
  z->next_in = (Bytef *)k->str;
  z->next_in = (Bytef *) buf;
  z->avail_in = (uInt)nread;
  z->avail_in = (uInt) nbytes;


  /* Now uncompress the data */
  /* Now uncompress the data */
  return inflate_stream(conn, k);
  return inflate_stream(conn, writer);
}

static void deflate_close_writer(struct connectdata *conn,
                                 contenc_writer *writer)
{
  zlib_params *zp = (zlib_params *) &writer->params;
  z_stream *z = &zp->z;     /* zlib state structure */

  exit_zlib(conn, z, &zp->zlib_init, CURLE_OK);
}

static const content_encoding deflate_encoding = {
  "deflate",
  NULL,
  deflate_init_writer,
  deflate_unencode_write,
  deflate_close_writer,
  sizeof(zlib_params)
};


/* Gzip handler. */
static CURLcode gzip_init_writer(struct connectdata *conn,
                                 contenc_writer *writer)
{
  zlib_params *zp = (zlib_params *) &writer->params;
  z_stream *z = &zp->z;     /* zlib state structure */

  if(!writer->downstream)
    return CURLE_WRITE_ERROR;

  /* Initialize zlib */
  z->zalloc = (alloc_func) zalloc_cb;
  z->zfree = (free_func) zfree_cb;

  if(strcmp(zlibVersion(), "1.2.0.4") >= 0) {
    /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */
    if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) {
      return process_zlib_error(conn, z);
    }
    zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
  }
  else {
    /* we must parse the gzip header ourselves */
    if(inflateInit2(z, -MAX_WBITS) != Z_OK) {
      return process_zlib_error(conn, z);
    }
    zp->zlib_init = ZLIB_INIT;   /* Initial call state */
  }

  return CURLE_OK;
}
}


#ifdef OLD_ZLIB_SUPPORT
#ifdef OLD_ZLIB_SUPPORT
@@ -273,47 +361,25 @@ static enum {
}
}
#endif
#endif


CURLcode
static CURLcode gzip_unencode_write(struct connectdata *conn,
Curl_unencode_gzip_write(struct connectdata *conn,
                                    contenc_writer *writer,
                         struct SingleRequest *k,
                                    const char *buf, size_t nbytes)
                         ssize_t nread)
{
{
  z_stream *z = &k->z;          /* zlib state structure */
  zlib_params *zp = (zlib_params *) &writer->params;

  z_stream *z = &zp->z;     /* zlib state structure */
  /* Initialize zlib? */
  if(k->zlib_init == ZLIB_UNINIT) {
    memset(z, 0, sizeof(z_stream));
    z->zalloc = (alloc_func)zalloc_cb;
    z->zfree = (free_func)zfree_cb;

    if(strcmp(zlibVersion(), "1.2.0.4") >= 0) {
      /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */
      if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) {
        return process_zlib_error(conn, z);
      }
      k->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
    }
    else {
      /* we must parse the gzip header ourselves */
      if(inflateInit2(z, -MAX_WBITS) != Z_OK) {
        return process_zlib_error(conn, z);
      }
      k->zlib_init = ZLIB_INIT;   /* Initial call state */
    }
  }


  if(k->zlib_init == ZLIB_INIT_GZIP) {
  if(zp->zlib_init == ZLIB_INIT_GZIP) {
    /* Let zlib handle the gzip decompression entirely */
    /* Let zlib handle the gzip decompression entirely */
    z->next_in = (Bytef *)k->str;
    z->next_in = (Bytef *) buf;
    z->avail_in = (uInt)nread;
    z->avail_in = (uInt) nbytes;
    /* Now uncompress the data */
    /* Now uncompress the data */
    return inflate_stream(conn, k);
    return inflate_stream(conn, writer);
  }
  }


#ifndef OLD_ZLIB_SUPPORT
#ifndef OLD_ZLIB_SUPPORT
  /* Support for old zlib versions is compiled away and we are running with
  /* Support for old zlib versions is compiled away and we are running with
     an old version, so return an error. */
     an old version, so return an error. */
  return exit_zlib(z, &k->zlib_init, CURLE_WRITE_ERROR);
  return exit_zlib(conn, z, &zp->zlib_init, CURLE_WRITE_ERROR);


#else
#else
  /* This next mess is to get around the potential case where there isn't
  /* This next mess is to get around the potential case where there isn't
@@ -326,18 +392,18 @@ Curl_unencode_gzip_write(struct connectdata *conn,
   * can handle the gzip header themselves.
   * can handle the gzip header themselves.
   */
   */


  switch(k->zlib_init) {
  switch(zp->zlib_init) {
  /* Skip over gzip header? */
  /* Skip over gzip header? */
  case ZLIB_INIT:
  case ZLIB_INIT:
  {
  {
    /* Initial call state */
    /* Initial call state */
    ssize_t hlen;
    ssize_t hlen;


    switch(check_gzip_header((unsigned char *)k->str, nread, &hlen)) {
    switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) {
    case GZIP_OK:
    case GZIP_OK:
      z->next_in = (Bytef *)k->str + hlen;
      z->next_in = (Bytef *) buf + hlen;
      z->avail_in = (uInt)(nread - hlen);
      z->avail_in = (uInt) (nbytes - hlen);
      k->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
      zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
      break;
      break;


    case GZIP_UNDERFLOW:
    case GZIP_UNDERFLOW:
@@ -348,19 +414,19 @@ Curl_unencode_gzip_write(struct connectdata *conn,
       * the first place, and it's even more unlikely for a transfer to fail
       * the first place, and it's even more unlikely for a transfer to fail
       * immediately afterwards, it should seldom be a problem.
       * immediately afterwards, it should seldom be a problem.
       */
       */
      z->avail_in = (uInt)nread;
      z->avail_in = (uInt) nbytes;
      z->next_in = malloc(z->avail_in);
      z->next_in = malloc(z->avail_in);
      if(z->next_in == NULL) {
      if(z->next_in == NULL) {
        return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
        return exit_zlib(conn, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
      }
      }
      memcpy(z->next_in, k->str, z->avail_in);
      memcpy(z->next_in, buf, z->avail_in);
      k->zlib_init = ZLIB_GZIP_HEADER;   /* Need more gzip header data state */
      zp->zlib_init = ZLIB_GZIP_HEADER;  /* Need more gzip header data state */
      /* We don't have any data to inflate yet */
      /* We don't have any data to inflate yet */
      return CURLE_OK;
      return CURLE_OK;


    case GZIP_BAD:
    case GZIP_BAD:
    default:
    default:
      return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
      return exit_zlib(conn, z, &zp->zlib_init, process_zlib_error(conn, z));
    }
    }


  }
  }
@@ -370,22 +436,22 @@ Curl_unencode_gzip_write(struct connectdata *conn,
  {
  {
    /* Need more gzip header data state */
    /* Need more gzip header data state */
    ssize_t hlen;
    ssize_t hlen;
    z->avail_in += (uInt)nread;
    z->avail_in += (uInt) nbytes;
    z->next_in = Curl_saferealloc(z->next_in, z->avail_in);
    z->next_in = Curl_saferealloc(z->next_in, z->avail_in);
    if(z->next_in == NULL) {
    if(z->next_in == NULL) {
      return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
      return exit_zlib(conn, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
    }
    }
    /* Append the new block of data to the previous one */
    /* Append the new block of data to the previous one */
    memcpy(z->next_in + z->avail_in - nread, k->str, nread);
    memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes);


    switch(check_gzip_header(z->next_in, z->avail_in, &hlen)) {
    switch(check_gzip_header(z->next_in, z->avail_in, &hlen)) {
    case GZIP_OK:
    case GZIP_OK:
      /* This is the zlib stream data */
      /* This is the zlib stream data */
      free(z->next_in);
      free(z->next_in);
      /* Don't point into the malloced block since we just freed it */
      /* Don't point into the malloced block since we just freed it */
      z->next_in = (Bytef *)k->str + hlen + nread - z->avail_in;
      z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in;
      z->avail_in = (uInt) (z->avail_in - hlen);
      z->avail_in = (uInt) (z->avail_in - hlen);
      k->zlib_init = ZLIB_GZIP_INFLATING;   /* Inflating stream state */
      zp->zlib_init = ZLIB_GZIP_INFLATING;   /* Inflating stream state */
      break;
      break;


    case GZIP_UNDERFLOW:
    case GZIP_UNDERFLOW:
@@ -394,8 +460,7 @@ Curl_unencode_gzip_write(struct connectdata *conn,


    case GZIP_BAD:
    case GZIP_BAD:
    default:
    default:
      free(z->next_in);
      return exit_zlib(conn, z, &zp->zlib_init, process_zlib_error(conn, z));
      return exit_zlib(z, &k->zlib_init, process_zlib_error(conn, z));
    }
    }


  }
  }
@@ -404,8 +469,8 @@ Curl_unencode_gzip_write(struct connectdata *conn,
  case ZLIB_GZIP_INFLATING:
  case ZLIB_GZIP_INFLATING:
  default:
  default:
    /* Inflating stream state */
    /* Inflating stream state */
    z->next_in = (Bytef *)k->str;
    z->next_in = (Bytef *) buf;
    z->avail_in = (uInt)nread;
    z->avail_in = (uInt) nbytes;
    break;
    break;
  }
  }


@@ -415,17 +480,332 @@ Curl_unencode_gzip_write(struct connectdata *conn,
  }
  }


  /* We've parsed the header, now uncompress the data */
  /* We've parsed the header, now uncompress the data */
  return inflate_stream(conn, k);
  return inflate_stream(conn, writer);
#endif
#endif
}
}


static void gzip_close_writer(struct connectdata *conn,
                              contenc_writer *writer)
{
  zlib_params *zp = (zlib_params *) &writer->params;
  z_stream *z = &zp->z;     /* zlib state structure */

  exit_zlib(conn, z, &zp->zlib_init, CURLE_OK);
}

static const content_encoding gzip_encoding = {
  "gzip",
  "x-gzip",
  gzip_init_writer,
  gzip_unencode_write,
  gzip_close_writer,
  sizeof(zlib_params)
};

#endif /* HAVE_LIBZ */


/* Identity handler. */
static CURLcode identity_init_writer(struct connectdata *conn,
                                     contenc_writer *writer)
{
  (void) conn;
  return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR;
}

static CURLcode identity_unencode_write(struct connectdata *conn,
                                        contenc_writer *writer,
                                        const char *buf, size_t nbytes)
{
  return Curl_unencode_write(conn, writer->downstream, buf, nbytes);
}

static void identity_close_writer(struct connectdata *conn,
                                  contenc_writer *writer)
{
  (void) conn;
  (void) writer;
}

static const content_encoding identity_encoding = {
  "identity",
  NULL,
  identity_init_writer,
  identity_unencode_write,
  identity_close_writer,
  0
};


/* supported content encodings table. */
static const content_encoding * const encodings[] = {
  &identity_encoding,
#ifdef HAVE_LIBZ
  &deflate_encoding,
  &gzip_encoding,
#endif
  NULL
};


/* Return a list of comma-separated names of supported encodings. */
char *Curl_all_content_encodings(void)
{
  size_t len = 0;
  const content_encoding * const *cep;
  const content_encoding *ce;
  char *ace;
  char *p;

  for(cep = encodings; *cep; cep++) {
    ce = *cep;
    if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT))
      len += strlen(ce->name) + 2;
  }

  if(!len)
    return strdup(CONTENT_ENCODING_DEFAULT);

  ace = malloc(len);
  if(ace) {
    p = ace;
    for(cep = encodings; *cep; cep++) {
      ce = *cep;
      if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) {
        strcpy(p, ce->name);
        p += strlen(p);
        *p++ = ',';
        *p++ = ' ';
      }
    }
    p[-2] = '\0';
  }

  return ace;
}


/* Real client writer: no downstream. */
static CURLcode client_init_writer(struct connectdata *conn,
                                   contenc_writer *writer)
{
  (void) conn;
  return writer->downstream? CURLE_WRITE_ERROR: CURLE_OK;
}

static CURLcode client_unencode_write(struct connectdata *conn,
                                      contenc_writer *writer,
                                      const char *buf, size_t nbytes)
{
  struct Curl_easy *data = conn->data;
  struct SingleRequest *k = &data->req;

  (void) writer;

  if(!nbytes || k->ignorebody)
    return CURLE_OK;

  return Curl_client_write(conn, CLIENTWRITE_BODY, (char *) buf, nbytes);
}

static void client_close_writer(struct connectdata *conn,
                                contenc_writer *writer)
{
  (void) conn;
  (void) writer;
}

static const content_encoding client_encoding = {
  NULL,
  NULL,
  client_init_writer,
  client_unencode_write,
  client_close_writer,
  0
};


/* Deferred error dummy writer. */
static CURLcode error_init_writer(struct connectdata *conn,
                                  contenc_writer *writer)
{
  (void) conn;
  return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR;
}

static CURLcode error_unencode_write(struct connectdata *conn,
                                     contenc_writer *writer,
                                     const char *buf, size_t nbytes)
{
  char *all = Curl_all_content_encodings();

  (void) writer;
  (void) buf;
  (void) nbytes;

  if(!all)
    return CURLE_OUT_OF_MEMORY;
  failf(conn->data, "Unrecognized content encoding type. "
                    "libcurl understands %s content encodings.", all);
  free(all);
  return CURLE_BAD_CONTENT_ENCODING;
}

static void error_close_writer(struct connectdata *conn,
                               contenc_writer *writer)
{
  (void) conn;
  (void) writer;
}

static const content_encoding error_encoding = {
  NULL,
  NULL,
  error_init_writer,
  error_unencode_write,
  error_close_writer,
  0
};

/* Create an unencoding writer stage using the given handler. */
static contenc_writer *new_unencoding_writer(struct connectdata *conn,
                                             const content_encoding *handler,
                                             contenc_writer *downstream)
{
  size_t sz = offsetof(contenc_writer, params) + handler->paramsize;
  contenc_writer *writer = (contenc_writer *) malloc(sz);

  if(writer) {
    memset(writer, 0, sz);
    writer->handler = handler;
    writer->downstream = downstream;
    if(handler->init_writer(conn, writer)) {
      free(writer);
      writer = NULL;
    }
  }

  return writer;
}

/* Write data using an unencoding writer stack. */
CURLcode Curl_unencode_write(struct connectdata *conn, contenc_writer *writer,
                             const char *buf, size_t nbytes)
{
  return writer->handler->unencode_write(conn, writer, buf, nbytes);
}

/* Close and clean-up the connection's writer stack. */
void Curl_unencode_cleanup(struct connectdata *conn)
void Curl_unencode_cleanup(struct connectdata *conn)
{
{
  struct Curl_easy *data = conn->data;
  struct Curl_easy *data = conn->data;
  struct SingleRequest *k = &data->req;
  struct SingleRequest *k = &data->req;
  z_stream *z = &k->z;
  contenc_writer *writer = k->writer_stack;
  if(k->zlib_init != ZLIB_UNINIT)

    (void) exit_zlib(z, &k->zlib_init, CURLE_OK);
  while(writer) {
    k->writer_stack = writer->downstream;
    writer->handler->close_writer(conn, writer);
    free(writer);
    writer = k->writer_stack;
  }
}
}


#endif /* HAVE_LIBZ */
/* Find the content encoding by name. */
static const content_encoding *find_encoding(const char *name, size_t len)
{
  const content_encoding * const *cep;
  const content_encoding *ce;

  for(cep = encodings; *cep; cep++) {
    ce = *cep;
    if((strncasecompare(name, ce->name, len) && !ce->name[len]) ||
       (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len]))
      return ce;
  }
  return NULL;
}

/* Set-up the unencoding stack from the Content-Encoding header value.
 * See RFC 7231 section 3.1.2.2. */
CURLcode Curl_build_unencoding_stack(struct connectdata *conn,
                                     const char *enclist, int maybechunked)
{
  struct Curl_easy *data = conn->data;
  struct SingleRequest *k = &data->req;

  do {
    const char *name;
    size_t namelen;

    /* Parse a single encoding name. */
    while(ISSPACE(*enclist) || *enclist == ',')
      enclist++;

    name = enclist;

    for(namelen = 0; *enclist && *enclist != ','; enclist++)
      if(!ISSPACE(*enclist))
        namelen = enclist - name + 1;

    /* Special case: chunked encoding is handled at the reader level. */
    if(maybechunked && namelen == 7 && strncasecompare(name, "chunked", 7)) {
      k->chunk = TRUE;             /* chunks coming our way. */
      Curl_httpchunk_init(conn);   /* init our chunky engine. */
    }
    else if(namelen) {
      const content_encoding *encoding = find_encoding(name, namelen);
      contenc_writer *writer;

      if(!k->writer_stack) {
        k->writer_stack = new_unencoding_writer(conn, &client_encoding, NULL);

        if(!k->writer_stack)
          return CURLE_OUT_OF_MEMORY;
      }

      if(!encoding)
        encoding = &error_encoding;  /* Defer error at stack use. */

      /* Stack the unencoding stage. */
      writer = new_unencoding_writer(conn, encoding, k->writer_stack);
      if(!writer)
        return CURLE_OUT_OF_MEMORY;
      k->writer_stack = writer;
    }
  } while(*enclist);

  return CURLE_OK;
}

#else
/* Stubs for builds without HTTP. */
CURLcode Curl_build_unencoding_stack(struct connectdata *conn,
                                     const char *enclist, int maybechunked)
{
  (void) conn;
  (void) enclist;
  (void) maybechunked;
  return CURLE_NOT_BUILT_IN;
}

CURLcode Curl_unencode_write(struct connectdata *conn, contenc_writer *writer,
                             const char *buf, size_t nbytes)
{
  (void) conn;
  (void) writer;
  (void) buf;
  (void) nbytes;
  return CURLE_NOT_BUILT_IN;
}

void Curl_unencode_cleanup(struct connectdata *conn)
{
  (void) conn;
}

char *Curl_all_content_encodings(void)
{
  return strdup(CONTENT_ENCODING_DEFAULT);  /* Satisfy caller. */
}

#endif /* CURL_DISABLE_HTTP */
+26 −19
Original line number Original line Diff line number Diff line
@@ -7,7 +7,7 @@
 *                            | (__| |_| |  _ <| |___
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *                             \___|\___/|_| \_\_____|
 *
 *
 * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 *
 * This software is licensed as described in the file COPYING, which
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * you should have received as part of this distribution. The terms
@@ -23,26 +23,33 @@
 ***************************************************************************/
 ***************************************************************************/
#include "curl_setup.h"
#include "curl_setup.h"


/*
/* Decoding writer. */
 * Comma-separated list all supported Content-Encodings ('identity' is implied)
typedef struct contenc_writer_s contenc_writer;
 */
typedef struct content_encoding_s content_encoding;
#ifdef HAVE_LIBZ

#define ALL_CONTENT_ENCODINGS "deflate, gzip"
struct contenc_writer_s {
/* force a cleanup */
  const content_encoding *handler;  /* Encoding handler. */
void Curl_unencode_cleanup(struct connectdata *conn);
  contenc_writer *downstream;  /* Downstream writer. */
#else
  void *params;  /* Encoding-specific storage (variable length). */
#define ALL_CONTENT_ENCODINGS "identity"
};
#define Curl_unencode_cleanup(x) Curl_nop_stmt
#endif


CURLcode Curl_unencode_deflate_write(struct connectdata *conn,
/* Content encoding writer. */
                                     struct SingleRequest *req,
struct content_encoding_s {
                                     ssize_t nread);
  const char *name;        /* Encoding name. */
  const char *alias;       /* Encoding name alias. */
  CURLcode (*init_writer)(struct connectdata *conn, contenc_writer *writer);
  CURLcode (*unencode_write)(struct connectdata *conn, contenc_writer *writer,
                             const char *buf, size_t nbytes);
  void (*close_writer)(struct connectdata *conn, contenc_writer *writer);
  size_t paramsize;
};


CURLcode
Curl_unencode_gzip_write(struct connectdata *conn,
                         struct SingleRequest *k,
                         ssize_t nread);


CURLcode Curl_build_unencoding_stack(struct connectdata *conn,
                                     const char *enclist, int maybechunked);
CURLcode Curl_unencode_write(struct connectdata *conn, contenc_writer *writer,
                             const char *buf, size_t nbytes);
void Curl_unencode_cleanup(struct connectdata *conn);
char *Curl_all_content_encodings(void);


#endif /* HEADER_CURL_CONTENT_ENCODING_H */
#endif /* HEADER_CURL_CONTENT_ENCODING_H */
+7 −61

File changed.

Preview size limit exceeded, changes collapsed.

Loading