Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
86373428b9 | |||
eaca6dde6a | |||
1550ec1298 | |||
a8cf77ec7c | |||
d634e9c6ae |
@@ -1,4 +1,4 @@
|
||||
// swift-tools-version:4.0
|
||||
// swift-tools-version:5.2
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
@@ -8,12 +8,15 @@ let package = Package(
|
||||
],
|
||||
dependencies: [
|
||||
// 💧 A server-side Swift web framework.
|
||||
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
|
||||
.package(url: "https://github.com/vapor/vapor.git", from: "4.3.0"),
|
||||
|
||||
// 🤐 Unzip archives
|
||||
.package(url: "https://github.com/microtherion/ZIPFoundation.git", .upToNextMajor(from: "0.9.8-µ"))
|
||||
.package(url: "https://github.com/microtherion/ZIPFoundation.git", from: "0.9.9-µ5")
|
||||
],
|
||||
targets: [
|
||||
.target(name: "GzipMiddleware", dependencies: ["Vapor", "ZIPFoundation"]),
|
||||
.target(name: "GzipMiddleware", dependencies: [
|
||||
.product(name: "Vapor", package: "vapor"),
|
||||
.product(name: "ZIPFoundation", package: "ZIPFoundation"),
|
||||
])
|
||||
]
|
||||
)
|
||||
|
@@ -3,7 +3,7 @@ import Vapor
|
||||
import Foundation
|
||||
import ZIPFoundation
|
||||
|
||||
/// Server Gzip middlere:
|
||||
/// Server Gzip middleware:
|
||||
/// 1. checks if the "Accept-Encoding" header contains "gzip"
|
||||
/// 2. if so, compresses the body and sets the response header "Content-Encoding" to "gzip",
|
||||
public struct GzipServerMiddleware: Middleware, ServiceType {
|
||||
@@ -11,22 +11,37 @@ public struct GzipServerMiddleware: Middleware, ServiceType {
|
||||
return .init()
|
||||
}
|
||||
|
||||
private let shouldGzip: (_ request: Request) -> Bool
|
||||
private let shouldGzipRequest: (_ request: Request) -> Bool
|
||||
private let shouldGzipResponse: (_ response: Response) -> Bool
|
||||
|
||||
/// The `shouldGzip` closure is asked for every request whether that request
|
||||
/// should allow response gzipping. Returns `true` always by default.
|
||||
public init(shouldGzip: @escaping (_ request: Request) -> Bool = { _ in true }) {
|
||||
self.shouldGzip = shouldGzip
|
||||
self.shouldGzipRequest = shouldGzip
|
||||
self.shouldGzipResponse = { _ in true }
|
||||
}
|
||||
|
||||
/// The `shouldGzipRequest` closure is asked for every request whether that request
|
||||
/// should allow response gzipping. `shouldGzipResponse` asks the same for the response.
|
||||
/// Both return`true` always by default.
|
||||
public init(shouldGzipRequest: @escaping (_ request: Request) -> Bool = { _ in true },
|
||||
shouldGzipResponse: @escaping (_ response: Response) -> Bool = { _ in true }
|
||||
) {
|
||||
self.shouldGzipRequest = shouldGzipRequest
|
||||
self.shouldGzipResponse = shouldGzipResponse
|
||||
}
|
||||
|
||||
public func respond(to request: Request, chainingTo next: Responder) throws -> EventLoopFuture<Response> {
|
||||
let acceptsGzip = request.http.headers[.acceptEncoding].first?.contains("gzip") ?? false
|
||||
guard acceptsGzip && shouldGzip(request) else {
|
||||
return try next.respond(to: request)
|
||||
let response = try next.respond(to: request)
|
||||
guard acceptsGzip && shouldGzipRequest(request) else {
|
||||
return response
|
||||
}
|
||||
|
||||
let response = try next.respond(to: request)
|
||||
return response.flatMap { response in
|
||||
guard self.shouldGzipResponse(response) else {
|
||||
return request.future(response)
|
||||
}
|
||||
var headers = response.http.headers
|
||||
headers.replaceOrAdd(name: .contentEncoding, value: "gzip")
|
||||
return response.http.body.consumeData(on: request).map { data in
|
||||
@@ -51,14 +66,18 @@ public struct GzipServerMiddleware: Middleware, ServiceType {
|
||||
return stream.write(.chunk(buffer))
|
||||
}
|
||||
})
|
||||
write.always {
|
||||
write = write.flatMap {
|
||||
buffer.clear()
|
||||
buffer.write(integer: crc32, endianness: .little)
|
||||
buffer.write(integer: UInt32(data.count), endianness: .little)
|
||||
stream.write(.chunk(buffer)).always {
|
||||
_ = stream.write(.end)
|
||||
}
|
||||
return stream.write(.chunk(buffer))
|
||||
}.flatMap {
|
||||
return stream.write(.end)
|
||||
}
|
||||
DispatchQueue.global().async {
|
||||
_ = try? write.wait()
|
||||
}
|
||||
|
||||
let httpResponse = HTTPResponse(status: response.http.status, headers: headers, body: stream)
|
||||
return request.response(http: httpResponse)
|
||||
}
|
||||
|
Reference in New Issue
Block a user