Compress to single data buffer instead of chunked stream

This commit is contained in:
Matthias Neeracher 2018-10-07 01:32:19 +02:00
parent 6b2983cbbb
commit 3a6a02deb5
2 changed files with 19 additions and 23 deletions

View File

@ -11,7 +11,7 @@ let package = Package(
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"), .package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
// 🤐 Unzip archives // 🤐 Unzip archives
.package(url: "https://github.com/microtherion/ZIPFoundation.git", .upToNextMajor(from: "0.9.7")) .package(url: "https://github.com/microtherion/ZIPFoundation.git", .upToNextMajor(from: "0.9.8"))
], ],
targets: [ targets: [
.target(name: "GzipMiddleware", dependencies: ["Vapor", "ZIPFoundation"]), .target(name: "GzipMiddleware", dependencies: ["Vapor", "ZIPFoundation"]),

View File

@ -3,6 +3,15 @@ import Vapor
import Foundation import Foundation
import ZIPFoundation import ZIPFoundation
private extension Data {
mutating func appendLittleEndian(_ word: UInt32) {
self.append(UInt8(word & 0xFF))
self.append(UInt8((word >> 8) & 0xFF))
self.append(UInt8((word >> 16) & 0xFF))
self.append(UInt8((word >> 24) & 0xFF))
}
}
/// Server Gzip middlere: /// Server Gzip middlere:
/// 1. checks if the "Accept-Encoding" header contains "gzip" /// 1. checks if the "Accept-Encoding" header contains "gzip"
/// 2. if so, compresses the body and sets the response header "Content-Encoding" to "gzip", /// 2. if so, compresses the body and sets the response header "Content-Encoding" to "gzip",
@ -30,35 +39,22 @@ public struct GzipServerMiddleware: Middleware, ServiceType {
var headers = response.http.headers var headers = response.http.headers
headers.replaceOrAdd(name: .contentEncoding, value: "gzip") headers.replaceOrAdd(name: .contentEncoding, value: "gzip")
return response.http.body.consumeData(on: request).map { data in return response.http.body.consumeData(on: request).map { data in
let stream = HTTPChunkedStream(on: request) var buffer = Data()
let bufSize = 4096
var buffer = ByteBufferAllocator().buffer(capacity: bufSize)
let header : [UInt8] = [0x1f, 0x8b, 0x08, 0x00] let header : [UInt8] = [0x1f, 0x8b, 0x08, 0x00]
let header2 : [UInt8] = [0x00, 0x03] let header2 : [UInt8] = [0x00, 0x03]
buffer.write(bytes: header) buffer.append(contentsOf: header)
buffer.write(integer: UInt32(Date().timeIntervalSince1970), endianness: .little) buffer.appendLittleEndian(UInt32(Date().timeIntervalSince1970))
buffer.write(bytes: header2) buffer.append(contentsOf: header2)
var write = stream.write(.chunk(buffer)) let crc32 = try Data.compress(size: data.count, bufferSize: 4096,
let crc32 = try Data.compress(size: data.count, bufferSize: bufSize,
provider: {(offset, readSize) -> Data in provider: {(offset, readSize) -> Data in
return data.subdata(in: offset..<offset+readSize) return data.subdata(in: offset..<offset+readSize)
}, },
consumer: { data -> Void in consumer: { data -> Void in
write = write.flatMap { buffer.append(data)
buffer.clear()
buffer.write(bytes: data)
return stream.write(.chunk(buffer))
}
}) })
write.always { buffer.appendLittleEndian(crc32)
buffer.clear() buffer.appendLittleEndian(UInt32(data.count))
buffer.write(integer: crc32, endianness: .little) let httpResponse = HTTPResponse(status: response.http.status, headers: headers, body: buffer)
buffer.write(integer: UInt32(data.count), endianness: .little)
stream.write(.chunk(buffer)).always {
_ = stream.write(.end)
}
}
let httpResponse = HTTPResponse(status: response.http.status, headers: headers, body: stream)
return request.response(http: httpResponse) return request.response(http: httpResponse)
} }
} }