HTTP Content-Encoding multiplexer — decoder (v0.1+) + one-shot encoder (v0.2+) + streaming encoder (v0.5 single-coding, v0.6 multi-coding). Sendable, Foundation-free.
Part of the bare-swift ecosystem.
Add to your Package.swift:
.package(url: "https://github.com/bare-swift/swift-content-encoding.git", from: "0.6.0")Then depend on the ContentEncoding product:
.product(name: "ContentEncoding", package: "swift-content-encoding")import ContentEncoding
import Bytes
let body: Bytes = ... // raw HTTP response body
let header = "gzip" // from response Content-Encoding
let plain = try ContentEncoding.decode(body, contentEncoding: header)import ContentEncoding
import Bytes
let payload: Bytes = ...
let body = try ContentEncoding.encode(payload, contentEncoding: "gzip", level: .default)
// Round-trip: ContentEncoding.decode(body, contentEncoding: "gzip") == payloadMulti-coding works in both directions (encode applies left-to-right; decode reverses):
let body = try ContentEncoding.encode(payload, contentEncoding: "gzip, deflate")
let back = try ContentEncoding.decode(body, contentEncoding: "gzip, deflate")import ContentEncoding
import Bytes
var encoder = try ContentEncoding.Streaming.Encoder(contentEncoding: "gzip", level: .default)
encoder.update(chunk1)
encoder.update(chunk2)
let body = try encoder.finish()
let plain = try ContentEncoding.decode(body, contentEncoding: "gzip")
// plain == chunk1 + chunk2The streaming encoder dispatches to the canonical streaming encoders in
swift-gzip / swift-zlib / swift-brotli per the configured coding.
Empty / whitespace header and identity coding buffer and return at
finish().
Multi-coding headers (v0.6+) stream correctly via cascaded
drain() calls on the underlying v0.4+ codec streaming encoders:
var encoder = try ContentEncoding.Streaming.Encoder(
contentEncoding: "gzip, br", level: .default
)
encoder.update(chunk1)
encoder.update(chunk2)
let compressed = try encoder.finish()
let plain = try ContentEncoding.decode(compressed, contentEncoding: "gzip, br")Per RFC 9110 § 8.4, codings apply left-to-right at encode time — so
"gzip, br" means input → gzip-encode → br-encode → output; the
decoder reverses to br-decode → gzip-decode.
(v0.5 threw ContentEncodingError.multipleCodingsNotStreamable for
multi-coding; v0.6 supports it. The error case is kept in the enum
for backwards-compat but is no longer thrown.)
identity— passthrough.gzip/x-gzip— RFC 1952 via swift-gzip.deflate/x-deflate— zlib-framed DEFLATE per RFC 7230 § 4.2.2 via swift-zlib. Not raw DEFLATE.br— Brotli (RFC 7932) decode-only via swift-brotli. Encodingbrthrows.unsupportedEncoding("br")(the encoder ships with swift-brotli v0.2).
Unsupported codings (zstd, compress) throw ContentEncodingError.unsupportedEncoding.
ContentEncoding.decode(_:contentEncoding:) throws(ContentEncodingError) -> BytesContentEncoding.encode(_:contentEncoding:level:) throws(ContentEncodingError) -> BytesContentEncoding.Level— typealias forDeflate.Encoder.Level(.none/.fast/.default/.best).ContentEncodingErrortyped-throws enum (2 cases).
swift-deflate0.2.0 — for theLeveltypealias.swift-gzip0.2.0 — gzip codec.swift-zlib0.2.0 — zlib codec.swift-brotli0.1.0 — Brotli decoder (v0.3+).swift-bytes0.1.0 — buffer.
- Brotli encoding. Lands when swift-brotli v0.2 ships.
- zstd, compress.
- Streaming encoding.
Accept-Encodingnegotiation.
Full DocC documentation: https://bare-swift.github.io/swift-content-encoding/