├── .gitignore ├── img ├── br.png └── brh2.png ├── .gitmodules ├── src ├── brotli.def ├── CMakeLists.txt ├── brotli.h ├── brotli.rc.in └── brotli.c ├── .editorconfig ├── license ├── azure-pipelines.yml └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | out 3 | -------------------------------------------------------------------------------- /img/br.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saucecontrol/Brotli-IIS/HEAD/img/br.png -------------------------------------------------------------------------------- /img/brh2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saucecontrol/Brotli-IIS/HEAD/img/brh2.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vcpkg"] 2 | path = vcpkg 3 | url = https://github.com/microsoft/vcpkg 4 | branch = master 5 | -------------------------------------------------------------------------------- /src/brotli.def: -------------------------------------------------------------------------------- 1 | LIBRARY BROTLI 2 | EXPORTS 3 | Compress 4 | CreateCompression 5 | DeInitCompression 6 | DestroyCompression 7 | InitCompression 8 | ResetCompression 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = crlf 6 | indent_style = tab 7 | indent_size = 4 8 | 9 | [*.yml] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [*.{c,h}] 14 | file_header_template = Copyright © Clinton Ingram. Licensed under the MIT License. 15 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | set(BROTLI_VERSION_MAJOR 1) 4 | set(BROTLI_VERSION_MINOR 1) 5 | set(BROTLI_VERSION_PATCH 0) 6 | set(BROTLI_VERSION_REVIS 0) 7 | set(BROTLI_VERSION_PARCH " (${VCPKG_TARGET_ARCHITECTURE})") 8 | set(BROTLI_LINK_LIBS brotlienc brotlicommon) 9 | 10 | string(TIMESTAMP DATE_YEAR "%Y") 11 | 12 | project(brotli C) 13 | 14 | find_package(Brotli NAMES unofficial-brotli REQUIRED) 15 | foreach(brlib IN LISTS BROTLI_LINK_LIBS) 16 | add_library(${brlib} ALIAS unofficial::brotli::${brlib}) 17 | endforeach() 18 | 19 | configure_file(brotli.rc.in brotli.rc) 20 | 21 | add_library(brotli brotli.c brotli.def brotli.rc) 22 | 23 | target_link_libraries(brotli PRIVATE ${BROTLI_LINK_LIBS}) 24 | 25 | install(TARGETS brotli RUNTIME DESTINATION bin) 26 | -------------------------------------------------------------------------------- /src/brotli.h: -------------------------------------------------------------------------------- 1 | // Copyright © Clinton Ingram and Contributors. Licensed under the MIT License. 2 | 3 | #pragma once 4 | 5 | #define _WIN32_WINNT 0x0600 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #pragma warning (disable: 4100) 13 | 14 | // 15 | // The following function exports are required by IIS but aren't needed for Brotli. 16 | // 17 | 18 | // Startup code, called once as soon as compression scheme dll is loaded by IIS compression module. 19 | HRESULT WINAPI InitCompression(VOID) { return S_OK; } 20 | 21 | // Shutdown code, called before compression scheme dll is unloaded by IIS compression module. 22 | VOID WINAPI DeInitCompression(VOID) { } 23 | 24 | // Reset compression context, export required for IIS 7.0 (only) but never actually called. 25 | HRESULT WINAPI ResetCompression(IN OUT PVOID context) { return S_OK; } 26 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018-2024 Clinton Ingram 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/brotli.rc.in: -------------------------------------------------------------------------------- 1 | #include "winres.h" 2 | 3 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 4 | 5 | VS_VERSION_INFO VERSIONINFO 6 | FILEVERSION ${BROTLI_VERSION_MAJOR},${BROTLI_VERSION_MINOR},${BROTLI_VERSION_PATCH},${BROTLI_VERSION_REVIS} 7 | PRODUCTVERSION ${BROTLI_VERSION_MAJOR},${BROTLI_VERSION_MINOR},${BROTLI_VERSION_PATCH} 8 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 9 | #ifdef _DEBUG 10 | FILEFLAGS VS_FF_DEBUG 11 | #else 12 | FILEFLAGS 0x0L 13 | #endif 14 | FILEOS VOS_NT 15 | FILETYPE VFT_DLL 16 | FILESUBTYPE VFT2_UNKNOWN 17 | BEGIN 18 | BLOCK "StringFileInfo" 19 | BEGIN 20 | BLOCK "040904b0" 21 | BEGIN 22 | VALUE "FileVersion", "${BROTLI_VERSION_MAJOR}.${BROTLI_VERSION_MINOR}.${BROTLI_VERSION_PATCH}.${BROTLI_VERSION_REVIS}" 23 | VALUE "ProductVersion", "${BROTLI_VERSION_MAJOR}.${BROTLI_VERSION_MINOR}.${BROTLI_VERSION_PATCH}" 24 | VALUE "OriginalFilename", "brotli.dll" 25 | VALUE "LegalCopyright", "Copyright © 2018-${DATE_YEAR} Clinton Ingram" 26 | VALUE "FileDescription", "Brotli Compression DLL${BROTLI_VERSION_PARCH}" 27 | VALUE "ProductName", "Brotli IIS Compression Scheme Plugin" 28 | END 29 | END 30 | BLOCK "VarFileInfo" 31 | BEGIN 32 | VALUE "Translation", 0x409, 1200 33 | END 34 | END 35 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | name: $(Year:yy)$(DayOfYear)$(Rev:r) 2 | trigger: 3 | branches: 4 | include: 5 | - master 6 | paths: 7 | exclude: 8 | - readme.md 9 | 10 | pool: 11 | vmImage: windows-latest 12 | 13 | variables: 14 | vcpkgNuGet: https://pkgs.dev.azure.com/saucecontrol/Brotli-IIS/_packaging/brotli-iis_vcpkg/nuget/v3/index.json 15 | VCPKG_BINARY_SOURCES: clear;nuget,$(vcpkgNuGet),readwrite 16 | 17 | steps: 18 | - checkout: self 19 | submodules: true 20 | fetchDepth: 1 21 | fetchTags: false 22 | 23 | - task: NuGetAuthenticate@1 24 | 25 | - script: vcpkg\bootstrap-vcpkg.bat 26 | displayName: Setup 27 | 28 | - script: vcpkg\vcpkg install brotli-iis:win-arm64ec @build\vcpkg\response 29 | displayName: Build arm64EC 30 | 31 | - script: vcpkg\vcpkg install brotli-iis:win-x64 @build\vcpkg\response 32 | displayName: Build x64 33 | 34 | - script: vcpkg\vcpkg install brotli-iis:win-x86 @build\vcpkg\response 35 | displayName: Build x86 36 | 37 | - publish: out/vcpkg/buildtrees 38 | displayName: Publish Build Logs 39 | artifact: logs.$(System.JobIdentifier) 40 | condition: failed() 41 | 42 | - publish: out/vcpkg/install 43 | displayName: Publish Native Binaries 44 | artifact: $(System.JobIdentifier) 45 | -------------------------------------------------------------------------------- /src/brotli.c: -------------------------------------------------------------------------------- 1 | // Copyright © Clinton Ingram and Contributors. Licensed under the MIT License. 2 | // IIS Compression Scheme DLL export function definitions. See 3 | 4 | #include "brotli.h" 5 | 6 | // Create a new compression context, called at the start of each response to be compressed. 7 | HRESULT WINAPI CreateCompression(OUT PVOID *context, IN ULONG reserved) 8 | { 9 | // Passing null pointers here tells the Brotli encoder to allocate its own memory. 10 | *context = BrotliEncoderCreateInstance(NULL, NULL, NULL); 11 | return *context ? S_OK : E_FAIL; 12 | } 13 | 14 | // Destroy compression context, called at the end of each compressed response. 15 | VOID WINAPI DestroyCompression(IN PVOID context) 16 | { 17 | // Any memory the encoder allocated is freed here. 18 | BrotliEncoderDestroyInstance((BrotliEncoderState*)context); 19 | } 20 | 21 | // Compress data, called in a loop until full response is processed. 22 | HRESULT WINAPI Compress( 23 | IN OUT PVOID context, // compression context 24 | IN CONST BYTE * input_buffer, // input buffer 25 | IN LONG input_buffer_size, // size of input buffer 26 | IN PBYTE output_buffer, // output buffer 27 | IN LONG output_buffer_size, // size of output buffer 28 | OUT PLONG input_used, // amount of input buffer used 29 | OUT PLONG output_used, // amount of output buffer used 30 | IN INT compression_level // compression level (0..11) 31 | ) 32 | { 33 | if (compression_level < BROTLI_MIN_QUALITY || compression_level > BROTLI_MAX_QUALITY) 34 | return E_INVALIDARG; 35 | 36 | BrotliEncoderState* encoderState = (BrotliEncoderState*)context; 37 | 38 | // This should succeed on the first call and fail once any data has been processed. 39 | // Since IIS won't change compression level between calls, we can ignore the return. 40 | BrotliEncoderSetParameter(encoderState, BROTLI_PARAM_QUALITY, compression_level); 41 | 42 | *input_used = 0; 43 | *output_used = 0; 44 | 45 | size_t size_in = input_buffer_size; 46 | size_t size_out = output_buffer_size; 47 | 48 | // Compress input data. If input is empty, we're at the end of the stream, so finish up. 49 | BrotliEncoderOperation op = input_buffer_size ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH; 50 | if (!BrotliEncoderCompressStream(encoderState, op, &size_in, &input_buffer, &size_out, &output_buffer, NULL)) 51 | return E_FAIL; 52 | 53 | *input_used = input_buffer_size - (LONG)size_in; 54 | *output_used = output_buffer_size - (LONG)size_out; 55 | 56 | // Return S_OK to continue looping, S_FALSE to complete the response. 57 | return input_buffer_size || !BrotliEncoderIsFinished(encoderState) ? S_OK : S_FALSE; 58 | } 59 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

Brotli

2 |

Latest Release Downloads

3 | 4 | --- 5 | **Note**: After this project was created, Microsoft [released their own](https://docs.microsoft.com/en-us/iis/extensions/iis-compression/iis-compression-overview) new compression plugin for Brotli and even [borrowed my workaround](https://docs.microsoft.com/en-us/iis/extensions/iis-compression/using-iis-compression#before-iis-100-version-1803) for `Accept-Encoding` priority. You may wish to use that plugin instead. 6 | 7 | Brotli IIS Compression Scheme Plugin 8 | ==================================== 9 | 10 | Brotli is a [new-ish](https://opensource.googleblog.com/2015/09/introducing-brotli-new-compression.html) open-sourced compression algorithm specifically designed for HTTP content encoding. The algorithm and reference encoder/decoder libraries were [created by Google](https://github.com/google/brotli). 11 | 12 | Brotli offers significantly [better compression than gzip](https://samsaffron.com/archive/2016/06/15/the-current-state-of-brotli-compression) with very little additional compression cost and almost no additional decompression cost. 13 | 14 | This plugin is a very thin wrapper around Google's Brotli encoding library. There is no license management code, no automagic configuration, no unnecessary processing. This plugin contains only what is absolutely necessary to cleanly and reliably integrate Google's Brotli encoder with IIS's built-in Static and Dynamic Compression Modules. 15 | 16 | Of course, that means you have to configure it yourself. But a proper HTTP compression design requires that you know what you're doing anyway, so this should not be a problem. If you're new to this, you may find the following links useful for learning about IIS compression and the configuration thereof. 17 | 18 | * [Built-in GZip/Deflate Compression on IIS 7.x](https://weblog.west-wind.com/posts/2011/May/05/Builtin-GZipDeflate-Compression-on-IIS-7x) 19 | * [IIS 7 Compression. Good? Bad? How much?](https://weblogs.asp.net/owscott/iis-7-compression-good-bad-how-much) 20 | * [Changes to compression in IIS7](http://www.ksingla.net/2006/06/changes_to_compression_in_iis7/) 21 | 22 | Very little has changed since IIS 7 was released, but here's one more article highlighting some improvements to dynamic compression and compression config in IIS 10 23 | 24 | * [IIS Dynamic Compression and new Dynamic Compression features in IIS 10](https://blogs.msdn.microsoft.com/friis/2017/09/05/iis-dynamic-compression-and-new-dynamic-compression-features-in-iis-10/) 25 | 26 | Features 27 | -------- 28 | 29 | * Integrates with the built-in IIS Static and Dynamic Compression Modules. 30 | * Uses the latest version of Google's Brotli encoder (v1.1.0). 31 | 32 | Requirements 33 | ------------ 34 | 35 | IIS 7 or later (Windows Vista/Windows Server 2008). You must have admin permissions to modify the root `applicationHost.config` file. 36 | 37 | Installation 38 | ------------ 39 | 40 | The Brotli IIS Compression Scheme Plugin is packaged as a single DLL file per platform architecture, with no external dependencies. The simplest way to install it is to copy it to your `inetsrv` folder, alongside the built-in `gzip.dll`. This allows configuration for Brotli to mirror the built-in schemes and allows for easy support of both 64-bit and 32-bit Application Pools. 41 | 42 | Binaries are available on the [releases page](https://github.com/saucecontrol/BrotliIIS/releases). A sample installation script is included in the .zip file. 43 | 44 | The Compression Scheme must be registered in the `applicationHost.config` file. You can do this manually or with appcmd.exe or IIS Manager. Final configuration will look something like this: 45 | 46 | ``` 47 | 48 | 49 | 50 | 51 | 52 | ... 53 | 54 | 55 | 56 | ... 57 | 58 | 59 | ``` 60 | 61 | Note that the name `br` shown above is important. This name must match the `Accept-Encoding` header value sent by the client and will be returned to the client in the `Content-Encoding` header. `br` is the [official](https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#content-coding) designator for Brotli. 62 | 63 | Note also that if you need to support 32-bit Application Pools on 64-bit IIS, you will need to deploy the x64 version of the DLL to `%windir%\system32\inetsrv` and the x86 DLL to `%windir%\syswow64\inetsrv`. The WoW64 subsystem will automatically load the correct platform version of the DLL if its path is listed under `system32` in the `applicationHost.config`, just as it does with `gzip.dll`. 64 | 65 | Configuration 66 | ------------- 67 | 68 | The only configuration accepted by the plugin is the compression level (or 'quality' in Brotli terms) to use. This value is configured separately for the IIS Static and Dynamic Compression Modules as demonstrated in the sample config above. 69 | 70 | Brotli accepts quality values from `0` to `11`. Configured values outside that range will cause the compression DLL to raise an error during processing. 71 | 72 | Be aware that the default values for compression level in IIS are `0` for dynamic content and `7` for static content. These values are based on the `0` to `9` scale used by `gzip` and `deflate` and aren't normally ideal settings anyway. The values in the sample above represent a good starting point for most modern servers. 73 | 74 | Browser Support 75 | --------------- 76 | 77 | Since the end of 2017, Brotli has been supported in [all modern browsers](https://caniuse.com/#feat=brotli). 78 | 79 | There are however, some gotchas related to the way the browser support is implemented. 80 | 81 | ### HTTPS is Required 82 | 83 | Current browsers will only request and accept Brotli encoding over HTTPS. Due to some poorly-behaved intermediate software/devices (proxies, caches, etc) in the wild, the Chrome dev team [decided](https://bugs.chromium.org/p/chromium/issues/detail?id=452335#c87) to only advertise Brotli support over HTTPS so that these poorly-behaved intermediaries couldn't mangle Brotli-encoded responses. Other vendors followed suit. 84 | 85 | If you aren't using HTTPS, you can't use Brotli. Thankfully, with [Let's Encrypt](https://github.com/win-acme/win-acme), HTTPS is now free and easy to set up. Just do it. 86 | 87 | ### Brotli is Low-Priority on Older IIS Versions 88 | 89 | Current browsers advertise Brotli support *after* `gzip` and `deflate` in the `Accept-Encoding` header. Typical headers will look like: `Accept-Encoding: gzip, deflate, br`. This is probably also for reasons related to existing poorly-behaved Internet software. 90 | 91 | The [HTTP RFC](https://tools.ietf.org/html/rfc7231#section-5.3.4) gives no specific guidance on how to choose from many `Accept-Encoding` values with the same priority, so it would be acceptable to return `br` content to those clients, but some versions of IIS choose the first one (left to right) that matches one of the configured compression schemes. This means they won't choose `br` if either `gzip` or `deflate` compression is also enabled. 92 | 93 | This issue has been resolved in IIS 10 version 1803 and newer. On IIS 10, the priority for response encoding is set by the order of the `scheme` elements under `httpCompression`. If you list `br` before `gzip`, it will be given priority regardless of the order in the request header. 94 | 95 | On older versions of IIS, you have two options: 96 | 97 | 1) Disable `gzip` and `deflate` on your server so that `br` is the only possible match. As of early 2024, Brotli is supported by nearly 99% of browsers worldwide, and that number continues to climb. Older clients would continue to work with uncompressed responses. 98 | 2) Take some action to force IIS to choose `br` when acceptable. To accomplish this, you can modify the `Accept-Encoding` header value on requests as they enter your IIS pipeline. The [IIS URL Rewrite Module](https://www.iis.net/downloads/microsoft/url-rewrite) makes this easy. 99 | 100 | The `Accept-Encoding` header is represented by the `HTTP_ACCEPT_ENCODING` Server Variable in the IIS pipeline, and you can modify it before it reaches the Compression Module(s). Here is a sample configuration: 101 | 102 | ``` 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | ``` 120 | 121 | The rule above simply looks for the string `br` (surrounded by word boundaries and not immediately followed by `;q=0`) in the `Accept-Encoding` header and re-writes it to be just plain `br`, giving IIS only one choice. 122 | 123 | Note that the default URL Rewrite configuration does not allow modification of the `HTTP_ACCEPT_ENCODING` variable. The `allowedServerVariables` element overrides that restriction and must be configured in `applicationHost.config`. The rewrite rule can then be defined at any level in the config hierarchy, although it probably makes sense to make it global. 124 | 125 | Note also that URL Rewrite makes requests ineligible for caching in the HTTP.sys kernel-mode cache. If you wish to take full advantage of kernel-mode caching while using Brotli on older IIS versions, you will need to use option 1) above. 126 | 127 | Testing 128 | ------- 129 | 130 | Once you have configured the Compression Scheme and ensured that Brotli will be chosen by the server, all you have to do is fire up a modern browser (no, IE11 is not modern) and request some text content over HTTPS. 131 | 132 | Open your developer tools (F12) network tab and review the request and response headers. You should see something like this: 133 | 134 | ![Brotli on IIS 8.5](img/br.png) 135 | 136 | It's alive! 137 | 138 | Or with HTTP/2 on IIS 10 it should look like this: 139 | 140 | ![Brotli on IIS 10](img/brh2.png) 141 | 142 | Gotta love those lowercase header names! 143 | 144 | License 145 | ------- 146 | 147 | Like [Google's Brotli](https://github.com/google/brotli) software, this Brotli IIS Compression Scheme Plugin is licensed under the [MIT License](https://opensource.org/licenses/MIT). It is free for all uses, including commercial. 148 | 149 | --------------------------------------------------------------------------------