Annotation of embedaddon/curl/lib/content_encoding.c, revision 1.1
1.1 ! misho 1: /***************************************************************************
! 2: * _ _ ____ _
! 3: * Project ___| | | | _ \| |
! 4: * / __| | | | |_) | |
! 5: * | (__| |_| | _ <| |___
! 6: * \___|\___/|_| \_\_____|
! 7: *
! 8: * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
! 9: *
! 10: * This software is licensed as described in the file COPYING, which
! 11: * you should have received as part of this distribution. The terms
! 12: * are also available at https://curl.haxx.se/docs/copyright.html.
! 13: *
! 14: * You may opt to use, copy, modify, merge, publish, distribute and/or sell
! 15: * copies of the Software, and permit persons to whom the Software is
! 16: * furnished to do so, under the terms of the COPYING file.
! 17: *
! 18: * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
! 19: * KIND, either express or implied.
! 20: *
! 21: ***************************************************************************/
! 22:
! 23: #include "curl_setup.h"
! 24:
! 25: #include "urldata.h"
! 26: #include <curl/curl.h>
! 27: #include <stddef.h>
! 28:
! 29: #ifdef HAVE_ZLIB_H
! 30: #include <zlib.h>
! 31: #ifdef __SYMBIAN32__
! 32: /* zlib pollutes the namespace with this definition */
! 33: #undef WIN32
! 34: #endif
! 35: #endif
! 36:
! 37: #ifdef HAVE_BROTLI
! 38: #include <brotli/decode.h>
! 39: #endif
! 40:
! 41: #include "sendf.h"
! 42: #include "http.h"
! 43: #include "content_encoding.h"
! 44: #include "strdup.h"
! 45: #include "strcase.h"
! 46: #include "curl_memory.h"
! 47: #include "memdebug.h"
! 48:
! 49: #define CONTENT_ENCODING_DEFAULT "identity"
! 50:
! 51: #ifndef CURL_DISABLE_HTTP
! 52:
! 53: #define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */
! 54:
! 55:
! 56: #ifdef HAVE_LIBZ
! 57:
! 58: /* Comment this out if zlib is always going to be at least ver. 1.2.0.4
! 59: (doing so will reduce code size slightly). */
! 60: #define OLD_ZLIB_SUPPORT 1
! 61:
! 62: #define GZIP_MAGIC_0 0x1f
! 63: #define GZIP_MAGIC_1 0x8b
! 64:
! 65: /* gzip flag byte */
! 66: #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
! 67: #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
! 68: #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
! 69: #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
! 70: #define COMMENT 0x10 /* bit 4 set: file comment present */
! 71: #define RESERVED 0xE0 /* bits 5..7: reserved */
! 72:
! 73: typedef enum {
! 74: ZLIB_UNINIT, /* uninitialized */
! 75: ZLIB_INIT, /* initialized */
! 76: ZLIB_INFLATING, /* inflating started. */
! 77: ZLIB_EXTERNAL_TRAILER, /* reading external trailer */
! 78: ZLIB_GZIP_HEADER, /* reading gzip header */
! 79: ZLIB_GZIP_INFLATING, /* inflating gzip stream */
! 80: ZLIB_INIT_GZIP /* initialized in transparent gzip mode */
! 81: } zlibInitState;
! 82:
! 83: /* Writer parameters. */
! 84: typedef struct {
! 85: zlibInitState zlib_init; /* zlib init state */
! 86: uInt trailerlen; /* Remaining trailer byte count. */
! 87: z_stream z; /* State structure for zlib. */
! 88: } zlib_params;
! 89:
! 90:
! 91: static voidpf
! 92: zalloc_cb(voidpf opaque, unsigned int items, unsigned int size)
! 93: {
! 94: (void) opaque;
! 95: /* not a typo, keep it calloc() */
! 96: return (voidpf) calloc(items, size);
! 97: }
! 98:
! 99: static void
! 100: zfree_cb(voidpf opaque, voidpf ptr)
! 101: {
! 102: (void) opaque;
! 103: free(ptr);
! 104: }
! 105:
! 106: static CURLcode
! 107: process_zlib_error(struct connectdata *conn, z_stream *z)
! 108: {
! 109: struct Curl_easy *data = conn->data;
! 110: if(z->msg)
! 111: failf(data, "Error while processing content unencoding: %s",
! 112: z->msg);
! 113: else
! 114: failf(data, "Error while processing content unencoding: "
! 115: "Unknown failure within decompression software.");
! 116:
! 117: return CURLE_BAD_CONTENT_ENCODING;
! 118: }
! 119:
! 120: static CURLcode
! 121: exit_zlib(struct connectdata *conn,
! 122: z_stream *z, zlibInitState *zlib_init, CURLcode result)
! 123: {
! 124: if(*zlib_init == ZLIB_GZIP_HEADER)
! 125: Curl_safefree(z->next_in);
! 126:
! 127: if(*zlib_init != ZLIB_UNINIT) {
! 128: if(inflateEnd(z) != Z_OK && result == CURLE_OK)
! 129: result = process_zlib_error(conn, z);
! 130: *zlib_init = ZLIB_UNINIT;
! 131: }
! 132:
! 133: return result;
! 134: }
! 135:
! 136: static CURLcode process_trailer(struct connectdata *conn, zlib_params *zp)
! 137: {
! 138: z_stream *z = &zp->z;
! 139: CURLcode result = CURLE_OK;
! 140: uInt len = z->avail_in < zp->trailerlen? z->avail_in: zp->trailerlen;
! 141:
! 142: /* Consume expected trailer bytes. Terminate stream if exhausted.
! 143: Issue an error if unexpected bytes follow. */
! 144:
! 145: zp->trailerlen -= len;
! 146: z->avail_in -= len;
! 147: z->next_in += len;
! 148: if(z->avail_in)
! 149: result = CURLE_WRITE_ERROR;
! 150: if(result || !zp->trailerlen)
! 151: result = exit_zlib(conn, z, &zp->zlib_init, result);
! 152: else {
! 153: /* Only occurs for gzip with zlib < 1.2.0.4 or raw deflate. */
! 154: zp->zlib_init = ZLIB_EXTERNAL_TRAILER;
! 155: }
! 156: return result;
! 157: }
! 158:
! 159: static CURLcode inflate_stream(struct connectdata *conn,
! 160: contenc_writer *writer, zlibInitState started)
! 161: {
! 162: zlib_params *zp = (zlib_params *) &writer->params;
! 163: z_stream *z = &zp->z; /* zlib state structure */
! 164: uInt nread = z->avail_in;
! 165: Bytef *orig_in = z->next_in;
! 166: bool done = FALSE;
! 167: CURLcode result = CURLE_OK; /* Curl_client_write status */
! 168: char *decomp; /* Put the decompressed data here. */
! 169:
! 170: /* Check state. */
! 171: if(zp->zlib_init != ZLIB_INIT &&
! 172: zp->zlib_init != ZLIB_INFLATING &&
! 173: zp->zlib_init != ZLIB_INIT_GZIP &&
! 174: zp->zlib_init != ZLIB_GZIP_INFLATING)
! 175: return exit_zlib(conn, z, &zp->zlib_init, CURLE_WRITE_ERROR);
! 176:
! 177: /* Dynamically allocate a buffer for decompression because it's uncommonly
! 178: large to hold on the stack */
! 179: decomp = malloc(DSIZ);
! 180: if(decomp == NULL)
! 181: return exit_zlib(conn, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
! 182:
! 183: /* because the buffer size is fixed, iteratively decompress and transfer to
! 184: the client via downstream_write function. */
! 185: while(!done) {
! 186: int status; /* zlib status */
! 187: done = TRUE;
! 188:
! 189: /* (re)set buffer for decompressed output for every iteration */
! 190: z->next_out = (Bytef *) decomp;
! 191: z->avail_out = DSIZ;
! 192:
! 193: #ifdef Z_BLOCK
! 194: /* Z_BLOCK is only available in zlib ver. >= 1.2.0.5 */
! 195: status = inflate(z, Z_BLOCK);
! 196: #else
! 197: /* fallback for zlib ver. < 1.2.0.5 */
! 198: status = inflate(z, Z_SYNC_FLUSH);
! 199: #endif
! 200:
! 201: /* Flush output data if some. */
! 202: if(z->avail_out != DSIZ) {
! 203: if(status == Z_OK || status == Z_STREAM_END) {
! 204: zp->zlib_init = started; /* Data started. */
! 205: result = Curl_unencode_write(conn, writer->downstream, decomp,
! 206: DSIZ - z->avail_out);
! 207: if(result) {
! 208: exit_zlib(conn, z, &zp->zlib_init, result);
! 209: break;
! 210: }
! 211: }
! 212: }
! 213:
! 214: /* Dispatch by inflate() status. */
! 215: switch(status) {
! 216: case Z_OK:
! 217: /* Always loop: there may be unflushed latched data in zlib state. */
! 218: done = FALSE;
! 219: break;
! 220: case Z_BUF_ERROR:
! 221: /* No more data to flush: just exit loop. */
! 222: break;
! 223: case Z_STREAM_END:
! 224: result = process_trailer(conn, zp);
! 225: break;
! 226: case Z_DATA_ERROR:
! 227: /* some servers seem to not generate zlib headers, so this is an attempt
! 228: to fix and continue anyway */
! 229: if(zp->zlib_init == ZLIB_INIT) {
! 230: /* Do not use inflateReset2(): only available since zlib 1.2.3.4. */
! 231: (void) inflateEnd(z); /* don't care about the return code */
! 232: if(inflateInit2(z, -MAX_WBITS) == Z_OK) {
! 233: z->next_in = orig_in;
! 234: z->avail_in = nread;
! 235: zp->zlib_init = ZLIB_INFLATING;
! 236: zp->trailerlen = 4; /* Tolerate up to 4 unknown trailer bytes. */
! 237: done = FALSE;
! 238: break;
! 239: }
! 240: zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */
! 241: }
! 242: /* FALLTHROUGH */
! 243: default:
! 244: result = exit_zlib(conn, z, &zp->zlib_init, process_zlib_error(conn, z));
! 245: break;
! 246: }
! 247: }
! 248: free(decomp);
! 249:
! 250: /* We're about to leave this call so the `nread' data bytes won't be seen
! 251: again. If we are in a state that would wrongly allow restart in raw mode
! 252: at the next call, assume output has already started. */
! 253: if(nread && zp->zlib_init == ZLIB_INIT)
! 254: zp->zlib_init = started; /* Cannot restart anymore. */
! 255:
! 256: return result;
! 257: }
! 258:
! 259:
! 260: /* Deflate handler. */
! 261: static CURLcode deflate_init_writer(struct connectdata *conn,
! 262: contenc_writer *writer)
! 263: {
! 264: zlib_params *zp = (zlib_params *) &writer->params;
! 265: z_stream *z = &zp->z; /* zlib state structure */
! 266:
! 267: if(!writer->downstream)
! 268: return CURLE_WRITE_ERROR;
! 269:
! 270: /* Initialize zlib */
! 271: z->zalloc = (alloc_func) zalloc_cb;
! 272: z->zfree = (free_func) zfree_cb;
! 273:
! 274: if(inflateInit(z) != Z_OK)
! 275: return process_zlib_error(conn, z);
! 276: zp->zlib_init = ZLIB_INIT;
! 277: return CURLE_OK;
! 278: }
! 279:
! 280: static CURLcode deflate_unencode_write(struct connectdata *conn,
! 281: contenc_writer *writer,
! 282: const char *buf, size_t nbytes)
! 283: {
! 284: zlib_params *zp = (zlib_params *) &writer->params;
! 285: z_stream *z = &zp->z; /* zlib state structure */
! 286:
! 287: /* Set the compressed input when this function is called */
! 288: z->next_in = (Bytef *) buf;
! 289: z->avail_in = (uInt) nbytes;
! 290:
! 291: if(zp->zlib_init == ZLIB_EXTERNAL_TRAILER)
! 292: return process_trailer(conn, zp);
! 293:
! 294: /* Now uncompress the data */
! 295: return inflate_stream(conn, writer, ZLIB_INFLATING);
! 296: }
! 297:
! 298: static void deflate_close_writer(struct connectdata *conn,
! 299: contenc_writer *writer)
! 300: {
! 301: zlib_params *zp = (zlib_params *) &writer->params;
! 302: z_stream *z = &zp->z; /* zlib state structure */
! 303:
! 304: exit_zlib(conn, z, &zp->zlib_init, CURLE_OK);
! 305: }
! 306:
! 307: static const content_encoding deflate_encoding = {
! 308: "deflate",
! 309: NULL,
! 310: deflate_init_writer,
! 311: deflate_unencode_write,
! 312: deflate_close_writer,
! 313: sizeof(zlib_params)
! 314: };
! 315:
! 316:
! 317: /* Gzip handler. */
! 318: static CURLcode gzip_init_writer(struct connectdata *conn,
! 319: contenc_writer *writer)
! 320: {
! 321: zlib_params *zp = (zlib_params *) &writer->params;
! 322: z_stream *z = &zp->z; /* zlib state structure */
! 323:
! 324: if(!writer->downstream)
! 325: return CURLE_WRITE_ERROR;
! 326:
! 327: /* Initialize zlib */
! 328: z->zalloc = (alloc_func) zalloc_cb;
! 329: z->zfree = (free_func) zfree_cb;
! 330:
! 331: if(strcmp(zlibVersion(), "1.2.0.4") >= 0) {
! 332: /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */
! 333: if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) {
! 334: return process_zlib_error(conn, z);
! 335: }
! 336: zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
! 337: }
! 338: else {
! 339: /* we must parse the gzip header and trailer ourselves */
! 340: if(inflateInit2(z, -MAX_WBITS) != Z_OK) {
! 341: return process_zlib_error(conn, z);
! 342: }
! 343: zp->trailerlen = 8; /* A CRC-32 and a 32-bit input size (RFC 1952, 2.2) */
! 344: zp->zlib_init = ZLIB_INIT; /* Initial call state */
! 345: }
! 346:
! 347: return CURLE_OK;
! 348: }
! 349:
! 350: #ifdef OLD_ZLIB_SUPPORT
! 351: /* Skip over the gzip header */
! 352: static enum {
! 353: GZIP_OK,
! 354: GZIP_BAD,
! 355: GZIP_UNDERFLOW
! 356: } check_gzip_header(unsigned char const *data, ssize_t len, ssize_t *headerlen)
! 357: {
! 358: int method, flags;
! 359: const ssize_t totallen = len;
! 360:
! 361: /* The shortest header is 10 bytes */
! 362: if(len < 10)
! 363: return GZIP_UNDERFLOW;
! 364:
! 365: if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1))
! 366: return GZIP_BAD;
! 367:
! 368: method = data[2];
! 369: flags = data[3];
! 370:
! 371: if(method != Z_DEFLATED || (flags & RESERVED) != 0) {
! 372: /* Can't handle this compression method or unknown flag */
! 373: return GZIP_BAD;
! 374: }
! 375:
! 376: /* Skip over time, xflags, OS code and all previous bytes */
! 377: len -= 10;
! 378: data += 10;
! 379:
! 380: if(flags & EXTRA_FIELD) {
! 381: ssize_t extra_len;
! 382:
! 383: if(len < 2)
! 384: return GZIP_UNDERFLOW;
! 385:
! 386: extra_len = (data[1] << 8) | data[0];
! 387:
! 388: if(len < (extra_len + 2))
! 389: return GZIP_UNDERFLOW;
! 390:
! 391: len -= (extra_len + 2);
! 392: data += (extra_len + 2);
! 393: }
! 394:
! 395: if(flags & ORIG_NAME) {
! 396: /* Skip over NUL-terminated file name */
! 397: while(len && *data) {
! 398: --len;
! 399: ++data;
! 400: }
! 401: if(!len || *data)
! 402: return GZIP_UNDERFLOW;
! 403:
! 404: /* Skip over the NUL */
! 405: --len;
! 406: ++data;
! 407: }
! 408:
! 409: if(flags & COMMENT) {
! 410: /* Skip over NUL-terminated comment */
! 411: while(len && *data) {
! 412: --len;
! 413: ++data;
! 414: }
! 415: if(!len || *data)
! 416: return GZIP_UNDERFLOW;
! 417:
! 418: /* Skip over the NUL */
! 419: --len;
! 420: }
! 421:
! 422: if(flags & HEAD_CRC) {
! 423: if(len < 2)
! 424: return GZIP_UNDERFLOW;
! 425:
! 426: len -= 2;
! 427: }
! 428:
! 429: *headerlen = totallen - len;
! 430: return GZIP_OK;
! 431: }
! 432: #endif
! 433:
! 434: static CURLcode gzip_unencode_write(struct connectdata *conn,
! 435: contenc_writer *writer,
! 436: const char *buf, size_t nbytes)
! 437: {
! 438: zlib_params *zp = (zlib_params *) &writer->params;
! 439: z_stream *z = &zp->z; /* zlib state structure */
! 440:
! 441: if(zp->zlib_init == ZLIB_INIT_GZIP) {
! 442: /* Let zlib handle the gzip decompression entirely */
! 443: z->next_in = (Bytef *) buf;
! 444: z->avail_in = (uInt) nbytes;
! 445: /* Now uncompress the data */
! 446: return inflate_stream(conn, writer, ZLIB_INIT_GZIP);
! 447: }
! 448:
! 449: #ifndef OLD_ZLIB_SUPPORT
! 450: /* Support for old zlib versions is compiled away and we are running with
! 451: an old version, so return an error. */
! 452: return exit_zlib(conn, z, &zp->zlib_init, CURLE_WRITE_ERROR);
! 453:
! 454: #else
! 455: /* This next mess is to get around the potential case where there isn't
! 456: * enough data passed in to skip over the gzip header. If that happens, we
! 457: * malloc a block and copy what we have then wait for the next call. If
! 458: * there still isn't enough (this is definitely a worst-case scenario), we
! 459: * make the block bigger, copy the next part in and keep waiting.
! 460: *
! 461: * This is only required with zlib versions < 1.2.0.4 as newer versions
! 462: * can handle the gzip header themselves.
! 463: */
! 464:
! 465: switch(zp->zlib_init) {
! 466: /* Skip over gzip header? */
! 467: case ZLIB_INIT:
! 468: {
! 469: /* Initial call state */
! 470: ssize_t hlen;
! 471:
! 472: switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) {
! 473: case GZIP_OK:
! 474: z->next_in = (Bytef *) buf + hlen;
! 475: z->avail_in = (uInt) (nbytes - hlen);
! 476: zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
! 477: break;
! 478:
! 479: case GZIP_UNDERFLOW:
! 480: /* We need more data so we can find the end of the gzip header. It's
! 481: * possible that the memory block we malloc here will never be freed if
! 482: * the transfer abruptly aborts after this point. Since it's unlikely
! 483: * that circumstances will be right for this code path to be followed in
! 484: * the first place, and it's even more unlikely for a transfer to fail
! 485: * immediately afterwards, it should seldom be a problem.
! 486: */
! 487: z->avail_in = (uInt) nbytes;
! 488: z->next_in = malloc(z->avail_in);
! 489: if(z->next_in == NULL) {
! 490: return exit_zlib(conn, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
! 491: }
! 492: memcpy(z->next_in, buf, z->avail_in);
! 493: zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */
! 494: /* We don't have any data to inflate yet */
! 495: return CURLE_OK;
! 496:
! 497: case GZIP_BAD:
! 498: default:
! 499: return exit_zlib(conn, z, &zp->zlib_init, process_zlib_error(conn, z));
! 500: }
! 501:
! 502: }
! 503: break;
! 504:
! 505: case ZLIB_GZIP_HEADER:
! 506: {
! 507: /* Need more gzip header data state */
! 508: ssize_t hlen;
! 509: z->avail_in += (uInt) nbytes;
! 510: z->next_in = Curl_saferealloc(z->next_in, z->avail_in);
! 511: if(z->next_in == NULL) {
! 512: return exit_zlib(conn, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
! 513: }
! 514: /* Append the new block of data to the previous one */
! 515: memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes);
! 516:
! 517: switch(check_gzip_header(z->next_in, z->avail_in, &hlen)) {
! 518: case GZIP_OK:
! 519: /* This is the zlib stream data */
! 520: free(z->next_in);
! 521: /* Don't point into the malloced block since we just freed it */
! 522: z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in;
! 523: z->avail_in = (uInt) (z->avail_in - hlen);
! 524: zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
! 525: break;
! 526:
! 527: case GZIP_UNDERFLOW:
! 528: /* We still don't have any data to inflate! */
! 529: return CURLE_OK;
! 530:
! 531: case GZIP_BAD:
! 532: default:
! 533: return exit_zlib(conn, z, &zp->zlib_init, process_zlib_error(conn, z));
! 534: }
! 535:
! 536: }
! 537: break;
! 538:
! 539: case ZLIB_EXTERNAL_TRAILER:
! 540: z->next_in = (Bytef *) buf;
! 541: z->avail_in = (uInt) nbytes;
! 542: return process_trailer(conn, zp);
! 543:
! 544: case ZLIB_GZIP_INFLATING:
! 545: default:
! 546: /* Inflating stream state */
! 547: z->next_in = (Bytef *) buf;
! 548: z->avail_in = (uInt) nbytes;
! 549: break;
! 550: }
! 551:
! 552: if(z->avail_in == 0) {
! 553: /* We don't have any data to inflate; wait until next time */
! 554: return CURLE_OK;
! 555: }
! 556:
! 557: /* We've parsed the header, now uncompress the data */
! 558: return inflate_stream(conn, writer, ZLIB_GZIP_INFLATING);
! 559: #endif
! 560: }
! 561:
! 562: static void gzip_close_writer(struct connectdata *conn,
! 563: contenc_writer *writer)
! 564: {
! 565: zlib_params *zp = (zlib_params *) &writer->params;
! 566: z_stream *z = &zp->z; /* zlib state structure */
! 567:
! 568: exit_zlib(conn, z, &zp->zlib_init, CURLE_OK);
! 569: }
! 570:
! 571: static const content_encoding gzip_encoding = {
! 572: "gzip",
! 573: "x-gzip",
! 574: gzip_init_writer,
! 575: gzip_unencode_write,
! 576: gzip_close_writer,
! 577: sizeof(zlib_params)
! 578: };
! 579:
! 580: #endif /* HAVE_LIBZ */
! 581:
! 582:
! 583: #ifdef HAVE_BROTLI
! 584:
! 585: /* Writer parameters. */
! 586: typedef struct {
! 587: BrotliDecoderState *br; /* State structure for brotli. */
! 588: } brotli_params;
! 589:
! 590:
! 591: static CURLcode brotli_map_error(BrotliDecoderErrorCode be)
! 592: {
! 593: switch(be) {
! 594: case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE:
! 595: case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE:
! 596: case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET:
! 597: case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME:
! 598: case BROTLI_DECODER_ERROR_FORMAT_CL_SPACE:
! 599: case BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE:
! 600: case BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT:
! 601: case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1:
! 602: case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2:
! 603: case BROTLI_DECODER_ERROR_FORMAT_TRANSFORM:
! 604: case BROTLI_DECODER_ERROR_FORMAT_DICTIONARY:
! 605: case BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS:
! 606: case BROTLI_DECODER_ERROR_FORMAT_PADDING_1:
! 607: case BROTLI_DECODER_ERROR_FORMAT_PADDING_2:
! 608: #ifdef BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY
! 609: case BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY:
! 610: #endif
! 611: #ifdef BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET
! 612: case BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET:
! 613: #endif
! 614: case BROTLI_DECODER_ERROR_INVALID_ARGUMENTS:
! 615: return CURLE_BAD_CONTENT_ENCODING;
! 616: case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES:
! 617: case BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS:
! 618: case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP:
! 619: case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1:
! 620: case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2:
! 621: case BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES:
! 622: return CURLE_OUT_OF_MEMORY;
! 623: default:
! 624: break;
! 625: }
! 626: return CURLE_WRITE_ERROR;
! 627: }
! 628:
! 629: static CURLcode brotli_init_writer(struct connectdata *conn,
! 630: contenc_writer *writer)
! 631: {
! 632: brotli_params *bp = (brotli_params *) &writer->params;
! 633:
! 634: (void) conn;
! 635:
! 636: if(!writer->downstream)
! 637: return CURLE_WRITE_ERROR;
! 638:
! 639: bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL);
! 640: return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY;
! 641: }
! 642:
! 643: static CURLcode brotli_unencode_write(struct connectdata *conn,
! 644: contenc_writer *writer,
! 645: const char *buf, size_t nbytes)
! 646: {
! 647: brotli_params *bp = (brotli_params *) &writer->params;
! 648: const uint8_t *src = (const uint8_t *) buf;
! 649: char *decomp;
! 650: uint8_t *dst;
! 651: size_t dstleft;
! 652: CURLcode result = CURLE_OK;
! 653: BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
! 654:
! 655: if(!bp->br)
! 656: return CURLE_WRITE_ERROR; /* Stream already ended. */
! 657:
! 658: decomp = malloc(DSIZ);
! 659: if(!decomp)
! 660: return CURLE_OUT_OF_MEMORY;
! 661:
! 662: while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) &&
! 663: result == CURLE_OK) {
! 664: dst = (uint8_t *) decomp;
! 665: dstleft = DSIZ;
! 666: r = BrotliDecoderDecompressStream(bp->br,
! 667: &nbytes, &src, &dstleft, &dst, NULL);
! 668: result = Curl_unencode_write(conn, writer->downstream,
! 669: decomp, DSIZ - dstleft);
! 670: if(result)
! 671: break;
! 672: switch(r) {
! 673: case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
! 674: case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
! 675: break;
! 676: case BROTLI_DECODER_RESULT_SUCCESS:
! 677: BrotliDecoderDestroyInstance(bp->br);
! 678: bp->br = NULL;
! 679: if(nbytes)
! 680: result = CURLE_WRITE_ERROR;
! 681: break;
! 682: default:
! 683: result = brotli_map_error(BrotliDecoderGetErrorCode(bp->br));
! 684: break;
! 685: }
! 686: }
! 687: free(decomp);
! 688: return result;
! 689: }
! 690:
! 691: static void brotli_close_writer(struct connectdata *conn,
! 692: contenc_writer *writer)
! 693: {
! 694: brotli_params *bp = (brotli_params *) &writer->params;
! 695:
! 696: (void) conn;
! 697:
! 698: if(bp->br) {
! 699: BrotliDecoderDestroyInstance(bp->br);
! 700: bp->br = NULL;
! 701: }
! 702: }
! 703:
! 704: static const content_encoding brotli_encoding = {
! 705: "br",
! 706: NULL,
! 707: brotli_init_writer,
! 708: brotli_unencode_write,
! 709: brotli_close_writer,
! 710: sizeof(brotli_params)
! 711: };
! 712: #endif
! 713:
! 714:
! 715: /* Identity handler. */
! 716: static CURLcode identity_init_writer(struct connectdata *conn,
! 717: contenc_writer *writer)
! 718: {
! 719: (void) conn;
! 720: return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR;
! 721: }
! 722:
! 723: static CURLcode identity_unencode_write(struct connectdata *conn,
! 724: contenc_writer *writer,
! 725: const char *buf, size_t nbytes)
! 726: {
! 727: return Curl_unencode_write(conn, writer->downstream, buf, nbytes);
! 728: }
! 729:
! 730: static void identity_close_writer(struct connectdata *conn,
! 731: contenc_writer *writer)
! 732: {
! 733: (void) conn;
! 734: (void) writer;
! 735: }
! 736:
! 737: static const content_encoding identity_encoding = {
! 738: "identity",
! 739: "none",
! 740: identity_init_writer,
! 741: identity_unencode_write,
! 742: identity_close_writer,
! 743: 0
! 744: };
! 745:
! 746:
! 747: /* supported content encodings table. */
! 748: static const content_encoding * const encodings[] = {
! 749: &identity_encoding,
! 750: #ifdef HAVE_LIBZ
! 751: &deflate_encoding,
! 752: &gzip_encoding,
! 753: #endif
! 754: #ifdef HAVE_BROTLI
! 755: &brotli_encoding,
! 756: #endif
! 757: NULL
! 758: };
! 759:
! 760:
! 761: /* Return a list of comma-separated names of supported encodings. */
! 762: char *Curl_all_content_encodings(void)
! 763: {
! 764: size_t len = 0;
! 765: const content_encoding * const *cep;
! 766: const content_encoding *ce;
! 767: char *ace;
! 768:
! 769: for(cep = encodings; *cep; cep++) {
! 770: ce = *cep;
! 771: if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT))
! 772: len += strlen(ce->name) + 2;
! 773: }
! 774:
! 775: if(!len)
! 776: return strdup(CONTENT_ENCODING_DEFAULT);
! 777:
! 778: ace = malloc(len);
! 779: if(ace) {
! 780: char *p = ace;
! 781: for(cep = encodings; *cep; cep++) {
! 782: ce = *cep;
! 783: if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) {
! 784: strcpy(p, ce->name);
! 785: p += strlen(p);
! 786: *p++ = ',';
! 787: *p++ = ' ';
! 788: }
! 789: }
! 790: p[-2] = '\0';
! 791: }
! 792:
! 793: return ace;
! 794: }
! 795:
! 796:
! 797: /* Real client writer: no downstream. */
! 798: static CURLcode client_init_writer(struct connectdata *conn,
! 799: contenc_writer *writer)
! 800: {
! 801: (void) conn;
! 802: return writer->downstream? CURLE_WRITE_ERROR: CURLE_OK;
! 803: }
! 804:
! 805: static CURLcode client_unencode_write(struct connectdata *conn,
! 806: contenc_writer *writer,
! 807: const char *buf, size_t nbytes)
! 808: {
! 809: struct Curl_easy *data = conn->data;
! 810: struct SingleRequest *k = &data->req;
! 811:
! 812: (void) writer;
! 813:
! 814: if(!nbytes || k->ignorebody)
! 815: return CURLE_OK;
! 816:
! 817: return Curl_client_write(conn, CLIENTWRITE_BODY, (char *) buf, nbytes);
! 818: }
! 819:
! 820: static void client_close_writer(struct connectdata *conn,
! 821: contenc_writer *writer)
! 822: {
! 823: (void) conn;
! 824: (void) writer;
! 825: }
! 826:
! 827: static const content_encoding client_encoding = {
! 828: NULL,
! 829: NULL,
! 830: client_init_writer,
! 831: client_unencode_write,
! 832: client_close_writer,
! 833: 0
! 834: };
! 835:
! 836:
! 837: /* Deferred error dummy writer. */
! 838: static CURLcode error_init_writer(struct connectdata *conn,
! 839: contenc_writer *writer)
! 840: {
! 841: (void) conn;
! 842: return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR;
! 843: }
! 844:
! 845: static CURLcode error_unencode_write(struct connectdata *conn,
! 846: contenc_writer *writer,
! 847: const char *buf, size_t nbytes)
! 848: {
! 849: char *all = Curl_all_content_encodings();
! 850:
! 851: (void) writer;
! 852: (void) buf;
! 853: (void) nbytes;
! 854:
! 855: if(!all)
! 856: return CURLE_OUT_OF_MEMORY;
! 857: failf(conn->data, "Unrecognized content encoding type. "
! 858: "libcurl understands %s content encodings.", all);
! 859: free(all);
! 860: return CURLE_BAD_CONTENT_ENCODING;
! 861: }
! 862:
! 863: static void error_close_writer(struct connectdata *conn,
! 864: contenc_writer *writer)
! 865: {
! 866: (void) conn;
! 867: (void) writer;
! 868: }
! 869:
! 870: static const content_encoding error_encoding = {
! 871: NULL,
! 872: NULL,
! 873: error_init_writer,
! 874: error_unencode_write,
! 875: error_close_writer,
! 876: 0
! 877: };
! 878:
! 879: /* Create an unencoding writer stage using the given handler. */
! 880: static contenc_writer *new_unencoding_writer(struct connectdata *conn,
! 881: const content_encoding *handler,
! 882: contenc_writer *downstream)
! 883: {
! 884: size_t sz = offsetof(contenc_writer, params) + handler->paramsize;
! 885: contenc_writer *writer = (contenc_writer *) calloc(1, sz);
! 886:
! 887: if(writer) {
! 888: writer->handler = handler;
! 889: writer->downstream = downstream;
! 890: if(handler->init_writer(conn, writer)) {
! 891: free(writer);
! 892: writer = NULL;
! 893: }
! 894: }
! 895:
! 896: return writer;
! 897: }
! 898:
! 899: /* Write data using an unencoding writer stack. */
! 900: CURLcode Curl_unencode_write(struct connectdata *conn, contenc_writer *writer,
! 901: const char *buf, size_t nbytes)
! 902: {
! 903: if(!nbytes)
! 904: return CURLE_OK;
! 905: return writer->handler->unencode_write(conn, writer, buf, nbytes);
! 906: }
! 907:
! 908: /* Close and clean-up the connection's writer stack. */
! 909: void Curl_unencode_cleanup(struct connectdata *conn)
! 910: {
! 911: struct Curl_easy *data = conn->data;
! 912: struct SingleRequest *k = &data->req;
! 913: contenc_writer *writer = k->writer_stack;
! 914:
! 915: while(writer) {
! 916: k->writer_stack = writer->downstream;
! 917: writer->handler->close_writer(conn, writer);
! 918: free(writer);
! 919: writer = k->writer_stack;
! 920: }
! 921: }
! 922:
! 923: /* Find the content encoding by name. */
! 924: static const content_encoding *find_encoding(const char *name, size_t len)
! 925: {
! 926: const content_encoding * const *cep;
! 927:
! 928: for(cep = encodings; *cep; cep++) {
! 929: const content_encoding *ce = *cep;
! 930: if((strncasecompare(name, ce->name, len) && !ce->name[len]) ||
! 931: (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len]))
! 932: return ce;
! 933: }
! 934: return NULL;
! 935: }
! 936:
! 937: /* Set-up the unencoding stack from the Content-Encoding header value.
! 938: * See RFC 7231 section 3.1.2.2. */
! 939: CURLcode Curl_build_unencoding_stack(struct connectdata *conn,
! 940: const char *enclist, int maybechunked)
! 941: {
! 942: struct Curl_easy *data = conn->data;
! 943: struct SingleRequest *k = &data->req;
! 944:
! 945: do {
! 946: const char *name;
! 947: size_t namelen;
! 948:
! 949: /* Parse a single encoding name. */
! 950: while(ISSPACE(*enclist) || *enclist == ',')
! 951: enclist++;
! 952:
! 953: name = enclist;
! 954:
! 955: for(namelen = 0; *enclist && *enclist != ','; enclist++)
! 956: if(!ISSPACE(*enclist))
! 957: namelen = enclist - name + 1;
! 958:
! 959: /* Special case: chunked encoding is handled at the reader level. */
! 960: if(maybechunked && namelen == 7 && strncasecompare(name, "chunked", 7)) {
! 961: k->chunk = TRUE; /* chunks coming our way. */
! 962: Curl_httpchunk_init(conn); /* init our chunky engine. */
! 963: }
! 964: else if(namelen) {
! 965: const content_encoding *encoding = find_encoding(name, namelen);
! 966: contenc_writer *writer;
! 967:
! 968: if(!k->writer_stack) {
! 969: k->writer_stack = new_unencoding_writer(conn, &client_encoding, NULL);
! 970:
! 971: if(!k->writer_stack)
! 972: return CURLE_OUT_OF_MEMORY;
! 973: }
! 974:
! 975: if(!encoding)
! 976: encoding = &error_encoding; /* Defer error at stack use. */
! 977:
! 978: /* Stack the unencoding stage. */
! 979: writer = new_unencoding_writer(conn, encoding, k->writer_stack);
! 980: if(!writer)
! 981: return CURLE_OUT_OF_MEMORY;
! 982: k->writer_stack = writer;
! 983: }
! 984: } while(*enclist);
! 985:
! 986: return CURLE_OK;
! 987: }
! 988:
! 989: #else
! 990: /* Stubs for builds without HTTP. */
! 991: CURLcode Curl_build_unencoding_stack(struct connectdata *conn,
! 992: const char *enclist, int maybechunked)
! 993: {
! 994: (void) conn;
! 995: (void) enclist;
! 996: (void) maybechunked;
! 997: return CURLE_NOT_BUILT_IN;
! 998: }
! 999:
! 1000: CURLcode Curl_unencode_write(struct connectdata *conn, contenc_writer *writer,
! 1001: const char *buf, size_t nbytes)
! 1002: {
! 1003: (void) conn;
! 1004: (void) writer;
! 1005: (void) buf;
! 1006: (void) nbytes;
! 1007: return CURLE_NOT_BUILT_IN;
! 1008: }
! 1009:
! 1010: void Curl_unencode_cleanup(struct connectdata *conn)
! 1011: {
! 1012: (void) conn;
! 1013: }
! 1014:
! 1015: char *Curl_all_content_encodings(void)
! 1016: {
! 1017: return strdup(CONTENT_ENCODING_DEFAULT); /* Satisfy caller. */
! 1018: }
! 1019:
! 1020: #endif /* CURL_DISABLE_HTTP */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>