diff options
author | Thomas E. Dickey <dickey@invisible-island.net> | 2022-03-30 00:29:50 +0000 |
---|---|---|
committer | Thomas E. Dickey <dickey@invisible-island.net> | 2022-03-30 00:29:50 +0000 |
commit | 04fd5be50c369e986053e7bfcc4b9eb2fa5ac937 (patch) | |
tree | 840e3e3dca02952345abbb2614b27ddbb0025bd5 /WWW/Library/Implementation | |
parent | baa72f144c15896a40c794b967854f0508459a20 (diff) | |
download | lynx-snapshots-04fd5be50c369e986053e7bfcc4b9eb2fa5ac937.tar.gz |
snapshot of project "lynx", label v2-9-0dev_10d
Diffstat (limited to 'WWW/Library/Implementation')
-rw-r--r-- | WWW/Library/Implementation/HTFTP.c | 5 | ||||
-rw-r--r-- | WWW/Library/Implementation/HTFile.c | 87 | ||||
-rw-r--r-- | WWW/Library/Implementation/HTFile.h | 4 | ||||
-rw-r--r-- | WWW/Library/Implementation/HTFormat.c | 251 | ||||
-rw-r--r-- | WWW/Library/Implementation/HTFormat.h | 24 | ||||
-rw-r--r-- | WWW/Library/Implementation/HTTP.c | 5 |
6 files changed, 350 insertions, 26 deletions
diff --git a/WWW/Library/Implementation/HTFTP.c b/WWW/Library/Implementation/HTFTP.c index 092c2076..cb058cc9 100644 --- a/WWW/Library/Implementation/HTFTP.c +++ b/WWW/Library/Implementation/HTFTP.c @@ -1,5 +1,5 @@ /* - * $LynxId: HTFTP.c,v 1.143 2022/03/12 12:06:13 tom Exp $ + * $LynxId: HTFTP.c,v 1.144 2022/03/17 20:04:17 tom Exp $ * * File Transfer Protocol (FTP) Client * for a WorldWideWeb browser @@ -4089,6 +4089,9 @@ int HTFTPLoad(const char *name, case cftBzip2: StrAllocCopy(anchor->content_encoding, "x-bzip2"); break; + case cftBrotli: + StrAllocCopy(anchor->content_encoding, "x-brotli"); + break; case cftNone: break; } diff --git a/WWW/Library/Implementation/HTFile.c b/WWW/Library/Implementation/HTFile.c index 1624e5c2..4da14c1a 100644 --- a/WWW/Library/Implementation/HTFile.c +++ b/WWW/Library/Implementation/HTFile.c @@ -1,5 +1,5 @@ /* - * $LynxId: HTFile.c,v 1.152 2019/08/16 22:53:10 tom Exp $ + * $LynxId: HTFile.c,v 1.153 2022/03/29 23:48:38 tom Exp $ * * File Access HTFile.c * =========== @@ -1281,9 +1281,14 @@ CompressFileType HTCompressFileType(const char *filename, len = strlen(filename); ftype = filename + len; - if ((len > 4) - && !strcasecomp((ftype - 3), "bz2") - && StrChr(dots, ftype[-4]) != 0) { + if ((len > 3) + && !strcasecomp((ftype - 2), "br") + && StrChr(dots, ftype[-3]) != 0) { + result = cftBrotli; + ftype -= 3; + } else if ((len > 4) + && !strcasecomp((ftype - 3), "bz2") + && StrChr(dots, ftype[-4]) != 0) { result = cftBzip2; ftype -= 4; } else if ((len > 3) @@ -1335,6 +1340,9 @@ const char *HTCompressTypeToSuffix(CompressFileType method) case cftDeflate: result = ".zz"; break; + case cftBrotli: + result = ".br"; + break; } return result; } @@ -1363,6 +1371,9 @@ const char *HTCompressTypeToEncoding(CompressFileType method) case cftDeflate: result = "deflate"; break; + case cftBrotli: + result = "brotli"; + break; } return result; } @@ -1390,6 +1401,10 @@ CompressFileType HTEncodingToCompressType(const char *coding) } else if (!strcasecomp(coding, "bzip2") || !strcasecomp(coding, "x-bzip2")) { result = cftBzip2; + } else if (!strcasecomp(coding, "br") || /* actual */ + !strcasecomp(coding, "brotli") || /* expected */ + !strcasecomp(coding, "x-brotli")) { + result = cftBrotli; } else if (!strcasecomp(coding, "deflate") || !strcasecomp(coding, "x-deflate")) { result = cftDeflate; @@ -1412,6 +1427,10 @@ CompressFileType HTContentTypeToCompressType(const char *ct) } else if (!strncasecomp(ct, "application/bzip2", 17) || !strncasecomp(ct, "application/x-bzip2", 19)) { method = cftBzip2; + } else if (!strncasecomp(ct, "application/br", 14) || + !strncasecomp(ct, "application/brotli", 18) || + !strncasecomp(ct, "application/x-brotli", 20)) { + method = cftBrotli; } return method; } @@ -2420,6 +2439,15 @@ static BOOL isBzip2Stream(FILE *fp) #define DOT_STRING "." #endif +#ifdef USE_BROTLI +static FILE * +brotli_open(const char *localname, const char *mode) +{ + CTRACE((tfp, "brotli_open file=%s, mode=%s\n", localname, mode)); + return fopen(localname, mode); +} +#endif + static int decompressAndParse(HTParentAnchor *anchor, HTFormat format_out, HTStream *sink, @@ -2437,7 +2465,10 @@ static int decompressAndParse(HTParentAnchor *anchor, #endif /* USE_ZLIB */ #ifdef USE_BZLIB BZFILE *bzfp = 0; -#endif /* USE_ZLIB */ +#endif /* USE_BZLIB */ +#ifdef USE_BROTLI + FILE *brfp = 0; +#endif /* USE_BROTLI */ #if defined(USE_ZLIB) || defined(USE_BZLIB) CompressFileType internal_decompress = cftNone; BOOL failed_decompress = NO; @@ -2536,6 +2567,17 @@ static int decompressAndParse(HTParentAnchor *anchor, internal_decompress = cftBzip2; } else #endif /* USE_BZLIB */ +#ifdef USE_BROTLI + if (isDOWNLOAD(cftBrotli)) { + fclose(fp); + fp = 0; + brfp = brotli_open(localname, BIN_R); + + CTRACE((tfp, "HTLoadFile: brotli_open of `%s' gives %p\n", + localname, (void *) brfp)); + internal_decompress = cftBrotli; + } else +#endif /* USE_BROTLI */ { StrAllocCopy(anchor->content_type, format->name); StrAllocCopy(anchor->content_encoding, HTAtom_name(myEncoding)); @@ -2614,6 +2656,22 @@ static int decompressAndParse(HTParentAnchor *anchor, format = HTAtom_for("www/compressed"); #endif /* USE_BZLIB */ break; + case cftBrotli: + StrAllocCopy(anchor->content_encoding, "x-brotli"); +#ifdef USE_BROTLI + if (strcmp(format_out->name, "www/download") != 0) { + fclose(fp); + fp = 0; + brfp = brotli_open(localname, BIN_R); + + CTRACE((tfp, "HTLoadFile: brotli_open of `%s' gives %p\n", + localname, (void *) brfp)); + internal_decompress = cftBrotli; + } +#else /* USE_BROTLI */ + format = HTAtom_for("www/compressed"); +#endif /* USE_BROTLI */ + break; case cftNone: break; } @@ -2635,6 +2693,11 @@ static int decompressAndParse(HTParentAnchor *anchor, failed_decompress = (BOOLEAN) (bzfp == NULL); break; #endif +#ifdef USE_BROTLI + case cftBrotli: + failed_decompress = (BOOLEAN) (brfp == NULL); + break; +#endif default: failed_decompress = YES; break; @@ -2666,6 +2729,12 @@ static int decompressAndParse(HTParentAnchor *anchor, if (sugfname && *sugfname) StrAllocCopy(anchor->SugFname, sugfname); FREE(sugfname); +#ifdef USE_BROTLI + if (brfp) + *statusp = HTParseBrFile(format, format_out, + anchor, + brfp, sink); +#endif #ifdef USE_BZLIB if (bzfp) *statusp = HTParseBzFile(format, format_out, @@ -2954,6 +3023,9 @@ int HTLoadFile(const char *addr, case cftBzip2: atomname = "application/x-bzip2"; break; + case cftBrotli: + atomname = "application/x-brotli"; + break; case cftNone: break; } @@ -3182,6 +3254,11 @@ void HTInitProgramPaths(BOOL init) for (n = (int) ppUnknown + 1; n < (int) pp_Last; ++n) { switch (code = (ProgramPaths) n) { +#ifdef BROTLI_PATH + case ppBROTLI: + path = BROTLI_PATH; + break; +#endif #ifdef BZIP2_PATH case ppBZIP2: path = BZIP2_PATH; diff --git a/WWW/Library/Implementation/HTFile.h b/WWW/Library/Implementation/HTFile.h index 204f5404..0bdfb794 100644 --- a/WWW/Library/Implementation/HTFile.h +++ b/WWW/Library/Implementation/HTFile.h @@ -1,5 +1,5 @@ /* - * $LynxId: HTFile.h,v 1.34 2020/01/21 22:08:07 tom Exp $ + * $LynxId: HTFile.h,v 1.35 2021/07/29 22:54:21 tom Exp $ * File access in libwww * FILE ACCESS * @@ -213,6 +213,7 @@ extern "C" { ,cftGzip ,cftBzip2 ,cftDeflate + ,cftBrotli } CompressFileType; /* @@ -307,6 +308,7 @@ extern "C" { */ typedef enum { ppUnknown = 0 + ,ppBROTLI ,ppBZIP2 ,ppCHMOD ,ppCOMPRESS diff --git a/WWW/Library/Implementation/HTFormat.c b/WWW/Library/Implementation/HTFormat.c index a1ad71ae..4f48e5d5 100644 --- a/WWW/Library/Implementation/HTFormat.c +++ b/WWW/Library/Implementation/HTFormat.c @@ -1,5 +1,5 @@ /* - * $LynxId: HTFormat.c,v 1.92 2022/03/12 14:40:38 tom Exp $ + * $LynxId: HTFormat.c,v 1.95 2022/03/30 00:29:50 tom Exp $ * * Manage different file formats HTFormat.c * ============================= @@ -57,6 +57,10 @@ static float HTMaxSecs = 1e10; /* No effective limit */ #include <LYMainLoop.h> #endif +#ifdef USE_BROTLI +#include <brotli/decode.h> +#endif + BOOL HTOutputSource = NO; /* Flag: shortcut parser to stdout */ /* this version used by the NetToText stream */ @@ -375,6 +379,8 @@ static HTPresentation *HTFindPresentation(HTFormat rep_in, HTPresentation *fill_in, HTParentAnchor *anchor) { +#undef THIS_FUNC +#define THIS_FUNC "HTFindPresentation" HTAtom *wildcard = NULL; /* = HTAtom_for("*"); lookup when needed - kw */ int n; int i; @@ -385,7 +391,7 @@ static HTPresentation *HTFindPresentation(HTFormat rep_in, HTPresentation *last_default_match = 0; HTPresentation *strong_subtype_wildcard_match = 0; - CTRACE((tfp, "HTFormat: Looking up presentation for %s to %s\n", + CTRACE((tfp, THIS_FUNC ": Looking up presentation for %s to %s\n", HTAtom_name(rep_in), HTAtom_name(rep_out))); n = HTList_count(HTPresentations); @@ -395,7 +401,7 @@ static HTPresentation *HTFindPresentation(HTFormat rep_in, if (pres->rep_out == rep_out) { if (failsMailcap(pres, anchor)) continue; - CTRACE((tfp, "FindPresentation: found exact match: %s -> %s\n", + CTRACE((tfp, THIS_FUNC ": found exact match: %s -> %s\n", HTAtom_name(pres->rep), HTAtom_name(pres->rep_out))); return pres; @@ -411,8 +417,8 @@ static HTPresentation *HTFindPresentation(HTFormat rep_in, if (!strong_wildcard_match) strong_wildcard_match = pres; /* otherwise use the first one */ - CTRACE((tfp, - "StreamStack: found strong wildcard match: %s -> %s\n", + CTRACE((tfp, THIS_FUNC + ": found strong wildcard match: %s -> %s\n", HTAtom_name(pres->rep), HTAtom_name(pres->rep_out))); } @@ -429,8 +435,8 @@ static HTPresentation *HTFindPresentation(HTFormat rep_in, if (!strong_subtype_wildcard_match) strong_subtype_wildcard_match = pres; /* otherwise use the first one */ - CTRACE((tfp, - "StreamStack: found strong subtype wildcard match: %s -> %s\n", + CTRACE((tfp, THIS_FUNC + ": found strong subtype wildcard match: %s -> %s\n", HTAtom_name(pres->rep), HTAtom_name(pres->rep_out))); } @@ -444,7 +450,7 @@ static HTPresentation *HTFindPresentation(HTFormat rep_in, weak_wildcard_match = pres; /* otherwise use the first one */ CTRACE((tfp, - "StreamStack: found weak wildcard match: %s\n", + THIS_FUNC ": found weak wildcard match: %s\n", HTAtom_name(pres->rep_out))); } else if (!last_default_match) { @@ -476,6 +482,7 @@ static HTPresentation *HTFindPresentation(HTFormat rep_in, } return NULL; +#undef THIS_FUNC } /* Create a filter stack @@ -493,11 +500,13 @@ HTStream *HTStreamStack(HTFormat rep_in, HTStream *sink, HTParentAnchor *anchor) { +#undef THIS_FUNC +#define THIS_FUNC "HTStreamStack" HTPresentation temp; HTPresentation *match; HTStream *result; - CTRACE((tfp, "StreamStack: Constructing stream stack for %s to %s (%s)\n", + CTRACE((tfp, THIS_FUNC ": Constructing stream stack for %s to %s (%s)\n", HTAtom_name(rep_in), HTAtom_name(rep_out), NONNULL(anchor->content_type_params))); @@ -507,9 +516,9 @@ HTStream *HTStreamStack(HTFormat rep_in, } else if ((match = HTFindPresentation(rep_in, rep_out, &temp, anchor))) { if (match == &temp) { - CTRACE((tfp, "StreamStack: Using %s\n", HTAtom_name(temp.rep_out))); + CTRACE((tfp, THIS_FUNC ": Using %s\n", HTAtom_name(temp.rep_out))); } else { - CTRACE((tfp, "StreamStack: found exact match: %s -> %s\n", + CTRACE((tfp, THIS_FUNC ": found exact match: %s -> %s\n", HTAtom_name(match->rep), HTAtom_name(match->rep_out))); } @@ -519,15 +528,16 @@ HTStream *HTStreamStack(HTFormat rep_in, } if (TRACE) { if (result && result->isa && result->isa->name) { - CTRACE((tfp, "StreamStack: Returning \"%s\"\n", result->isa->name)); + CTRACE((tfp, THIS_FUNC ": Returning \"%s\"\n", result->isa->name)); } else if (result) { - CTRACE((tfp, "StreamStack: Returning *unknown* stream!\n")); + CTRACE((tfp, THIS_FUNC ": Returning *unknown* stream!\n")); } else { - CTRACE((tfp, "StreamStack: Returning NULL!\n")); + CTRACE((tfp, THIS_FUNC ": Returning NULL!\n")); CTRACE_FLUSH(tfp); /* a crash may be imminent... - kw */ } } return result; +#undef THIS_FUNC } /* Put a presentation near start of list @@ -590,7 +600,7 @@ void HTFilterPresentations(void) /* Find the cost of a filter stack * ------------------------------- * - * Must return the cost of the same stack which StreamStack would set up. + * Must return the cost of the same stack which HTStreamStack would set up. * * On entry, * length The size of the data to be converted @@ -1330,6 +1340,142 @@ static int HTBzFileCopy(BZFILE * bzfp, HTStream *sink) } #endif /* USE_BZLIB */ +#ifdef USE_BROTLI +/* Push data from a brotli file pointer down a stream + * ------------------------------------- + * + * This routine is responsible for creating and PRESENTING any + * graphic (or other) objects described by the file. + * + * + * State of file and target stream on entry: + * BZFILE (bzfp) assumed open (should have bzipped content), + * target (sink) assumed valid. + * + * Return values: + * HT_INTERRUPTED Interruption after some data read. + * HT_PARTIAL_CONTENT Error after some data read. + * -1 Error before any data read. + * HT_LOADED Normal end of file indication on reading. + * + * State of file and target stream on return: + * always bzfp still open, target stream still valid. + */ +static int HTBrFileCopy(FILE *brfp, HTStream *sink) +{ +#undef THIS_FUNC +#define THIS_FUNC "HTBrFileCopy" + HTStreamClass targetClass; + int status; + off_t bytes; + int rv = HT_OK; + BrotliDecoderResult status2 = BROTLI_DECODER_RESULT_ERROR; + + char *brotli_buffer = NULL; + char *normal_buffer = NULL; + size_t brotli_size; + size_t brotli_limit = 0; + size_t brotli_offset = brotli_limit; + size_t normal_size; + size_t normal_limit = 0; + + /* Push the data down the stream + */ + targetClass = *(sink->isa); /* Copy pointers to procedures */ + + /* read and inflate brotli'd file, and push binary down sink + */ + HTReadProgress(bytes = 0, (off_t) 0); + /* + * first, read all of the brotli'd file into memory, to work with the + * library's limitations. + */ + for (;;) { + size_t input_chunk = INPUT_BUFFER_SIZE; + + brotli_offset = brotli_limit; + brotli_limit += input_chunk; + brotli_buffer = realloc(brotli_buffer, brotli_limit); + if (brotli_buffer == NULL) + outofmem(__FILE__, THIS_FUNC); + status = (int) fread(brotli_buffer + brotli_offset, sizeof(char), + input_chunk, brfp); + + if (status <= 0) { /* EOF or error */ + if (status == 0) { + rv = HT_LOADED; + break; + } + CTRACE((tfp, THIS_FUNC ": Read error, fread returns %d\n", status)); + if (bytes) { + if (!feof(brfp)) + rv = HT_PARTIAL_CONTENT; + } else { + rv = -1; + } + break; + } + bytes += status; + } + + /* + * next, unless we encountered an error (and have no data), try + * decompressing with increasing output buffer sizes until the brotli + * library succeeds. + */ + if (bytes > 0) { + do { + if (normal_limit == 0) + normal_limit = (10 * brotli_limit) + INPUT_BUFFER_SIZE; + else + normal_limit *= 2; + normal_buffer = realloc(normal_buffer, normal_limit); + if (normal_buffer == NULL) + outofmem(__FILE__, THIS_FUNC); + brotli_size = (size_t) bytes; + normal_size = normal_limit; + status2 = BrotliDecoderDecompress(brotli_size, + (uint8_t *) brotli_buffer, + &normal_size, + (uint8_t *) normal_buffer); + /* + * brotli library should return + * BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT, + * but actually returns + * BROTLI_DECODER_RESULT_ERROR + * + * Accommodate possible improvements... + */ + } while (status2 != BROTLI_DECODER_RESULT_SUCCESS); + } + + /* + * finally, pump that data into the output stream. + */ + if (status2 == BROTLI_DECODER_RESULT_SUCCESS) { + CTRACE((tfp, THIS_FUNC ": decompressed %ld -> %ld (1:%.1f)\n", + brotli_size, normal_size, + (double) normal_size / (double) brotli_size)); + (*targetClass.put_block) (sink, normal_buffer, (int) normal_size); + bytes += status; + HTReadProgress(bytes, (off_t) -1); + HTDisplayPartial(); + + if (HTCheckForInterrupt()) { + _HTProgress(TRANSFER_INTERRUPTED); + rv = HT_INTERRUPTED; + } + } + free(brotli_buffer); + free(normal_buffer); + + /* next bufferload */ + HTFinishDisplayPartial(); + return rv; +#undef THIS_FUNC +} +#endif /* USE_BZLIB */ + /* Push data from a socket down a stream STRIPPING CR * -------------------------------------------------- * @@ -1817,6 +1963,81 @@ int HTParseBzFile(HTFormat rep_in, } #endif /* USE_BZLIB */ +#ifdef USE_BROTLI +/* HTParseBrFile + * + * State of file and target stream on entry: + * FILE* (brfp) assumed open, + * target (sink) usually NULL (will call stream stack). + * + * Return values: + * -501 Stream stack failed (cannot present or convert). + * -1 Download cancelled. + * HT_NO_DATA Error before any data read. + * HT_PARTIAL_CONTENT Interruption or error after some data read. + * HT_LOADED Normal end of file indication on reading. + * + * State of file and target stream on return: + * always bzfp closed; target freed, aborted, or NULL. + */ +int HTParseBrFile(HTFormat rep_in, + HTFormat format_out, + HTParentAnchor *anchor, + FILE *brfp, + HTStream *sink) +{ + HTStream *stream; + HTStreamClass targetClass; + int rv; + int result; + + stream = HTStreamStack(rep_in, format_out, sink, anchor); + + if (!stream || !stream->isa) { + char *buffer = 0; + + fclose(brfp); + if (LYCancelDownload) { + LYCancelDownload = FALSE; + result = -1; + } else { + HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O, + HTAtom_name(rep_in), HTAtom_name(format_out)); + CTRACE((tfp, "HTFormat(in HTParseBzFile): %s\n", buffer)); + rv = HTLoadError(sink, 501, buffer); + FREE(buffer); + result = rv; + } + } else { + + /* + * Push the data down the stream + * + * @@ Bug: This decision ought to be made based on "encoding" rather than + * on content-type. @@@ When we handle encoding. The current method + * smells anyway. + */ + targetClass = *(stream->isa); /* Copy pointers to procedures */ + rv = HTBrFileCopy(brfp, stream); + if (rv == -1 || rv == HT_INTERRUPTED) { + (*targetClass._abort) (stream, NULL); + } else { + (*targetClass._free) (stream); + } + + fclose(brfp); + if (rv == -1) { + result = HT_NO_DATA; + } else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED)) { + result = HT_PARTIAL_CONTENT; + } else { + result = HT_LOADED; + } + } + return result; +} +#endif /* USE_BROTLI */ + /* Converter stream: Network Telnet to internal character text * ----------------------------------------------------------- * diff --git a/WWW/Library/Implementation/HTFormat.h b/WWW/Library/Implementation/HTFormat.h index 20e718a8..76a8b253 100644 --- a/WWW/Library/Implementation/HTFormat.h +++ b/WWW/Library/Implementation/HTFormat.h @@ -1,5 +1,5 @@ /* - * $LynxId: HTFormat.h,v 1.37 2020/01/21 22:02:59 tom Exp $ + * $LynxId: HTFormat.h,v 1.38 2022/03/28 08:09:44 tom Exp $ * * HTFormat: The format manager in the WWW Library * MANAGE DIFFERENT DOCUMENT FORMATS @@ -229,10 +229,12 @@ The HTPresentation and HTConverter types ,encodingDEFLATE = 2 ,encodingCOMPRESS = 4 ,encodingBZIP2 = 8 + ,encodingBROTLI = 16 ,encodingALL = (encodingGZIP + encodingDEFLATE + encodingCOMPRESS - + encodingBZIP2) + + encodingBZIP2 + + encodingBROTLI) } AcceptEncoding; /* @@ -512,7 +514,7 @@ HTParseZzFile: Parse a deflate'd File through a file pointer HTParseBzFile: Parse a bzip2'ed File through a file pointer This routine is called by protocols modules to load an object. uses - HTStreamStack and HTGzFileCopy. Returns HT_LOADED if successful, can also + HTStreamStack and HTBzFileCopy. Returns HT_LOADED if successful, can also return HT_PARTIAL_CONTENT, HT_NO_DATA, or other <0 for failure. */ extern int HTParseBzFile(HTFormat format_in, @@ -523,6 +525,22 @@ HTParseBzFile: Parse a bzip2'ed File through a file pointer #endif /* USE_BZLIB */ +#ifdef USE_BROTLI +/* +HTParseBzFile: Parse a brotli'ed File through a file pointer + + This routine is called by protocols modules to load an object. uses + HTStreamStack and HTBrFileCopy. Returns HT_LOADED if successful, can also + return HT_PARTIAL_CONTENT, HT_NO_DATA, or other <0 for failure. + */ + extern int HTParseBrFile(HTFormat format_in, + HTFormat format_out, + HTParentAnchor *anchor, + FILE * brfp, + HTStream *sink); + +#endif /* USE_BROTLI */ + /* HTNetToText: Convert Net ASCII to local representation diff --git a/WWW/Library/Implementation/HTTP.c b/WWW/Library/Implementation/HTTP.c index e405e46f..46f1924e 100644 --- a/WWW/Library/Implementation/HTTP.c +++ b/WWW/Library/Implementation/HTTP.c @@ -1,5 +1,5 @@ /* - * $LynxId: HTTP.c,v 1.181 2021/11/04 22:15:26 Sylvain.Bertrand Exp $ + * $LynxId: HTTP.c,v 1.182 2022/03/17 20:02:41 tom Exp $ * * HyperText Transfer Protocol - Client implementation HTTP.c * =========================== @@ -710,6 +710,9 @@ static BOOL acceptEncoding(int code) case encodingBZIP2: program = HTGetProgramPath(ppBZIP2); break; + case encodingBROTLI: + program = HTGetProgramPath(ppBROTLI); + break; default: break; } |