diff --git a/Sources/GRPC/Compression/Zlib.swift b/Sources/GRPC/Compression/Zlib.swift index 4186f1f9e..0c21d685e 100644 --- a/Sources/GRPC/Compression/Zlib.swift +++ b/Sources/GRPC/Compression/Zlib.swift @@ -265,6 +265,12 @@ enum Zlib { /// - Returns: The number of bytes written into `output`. @discardableResult func inflate(_ input: inout ByteBuffer, into output: inout ByteBuffer) throws -> Int { + if input.readableBytes == 0 { + // Zero length compressed messages are always empty messages. Skip the inflate step + // below and just return the number of bytes we wrote. + return 0 + } + return try input.readWithUnsafeMutableReadableBytes { inputPointer -> (Int, Int) in // Setup the input buffer. self.stream.availableInputBytes = inputPointer.count diff --git a/Tests/GRPCTests/ZlibTests.swift b/Tests/GRPCTests/ZlibTests.swift index 5ee37be52..436f7ebdd 100644 --- a/Tests/GRPCTests/ZlibTests.swift +++ b/Tests/GRPCTests/ZlibTests.swift @@ -207,4 +207,16 @@ class ZlibTests: GRPCTestCase { let ratio: DecompressionLimit = .ratio(2) XCTAssertEqual(ratio.maximumDecompressedSize(compressedSize: 10), 20) } + + func testDecompressEmptyPayload() throws { + for limit in [DecompressionLimit.ratio(1), .absolute(1)] { + for format in [Zlib.CompressionFormat.deflate, .gzip] { + var compressed = self.allocator.buffer(capacity: 0) + let inflate = Zlib.Inflate(format: format, limit: limit) + var output = self.allocator.buffer(capacity: 0) + XCTAssertEqual(try inflate.inflate(&compressed, into: &output), 0) + XCTAssertEqual(output.readableBytes, 0) + } + } + } }