Skip to content

Add a decompression limit #729

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"repositoryURL": "https://github.com/apple/swift-nio.git",
"state": {
"branch": null,
"revision": "4409b57d4c0c40d41ac2b320fccf02e4d451e3db",
"version": "2.13.0"
"revision": "16ab4d657e1ad4e77bd5f8b94af8538561643053",
"version": "2.14.0"
}
},
{
Expand All @@ -33,8 +33,8 @@
"repositoryURL": "https://github.com/apple/swift-nio-ssl.git",
"state": {
"branch": null,
"revision": "cf54f5c1db1c3740a6c7d662dc8569c150c3846c",
"version": "2.6.0"
"revision": "af46d9b58fafbb76f9b01177568d435a1b024f99",
"version": "2.6.2"
}
},
{
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ let package = Package(
dependencies: [
// GRPC dependencies:
// Main SwiftNIO package
.package(url: "https://github.com/apple/swift-nio.git", from: "2.13.0"),
.package(url: "https://github.com/apple/swift-nio.git", from: "2.14.0"),
// HTTP2 via SwiftNIO
.package(url: "https://github.com/apple/swift-nio-http2.git", from: "1.8.0"),
// TLS via SwiftNIO
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.6.0"),
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.6.2"),
// Support for Network.framework where possible.
.package(url: "https://github.com/apple/swift-nio-transport-services.git", from: "1.3.0"),

Expand Down
4 changes: 2 additions & 2 deletions Sources/GRPC/ClientOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public struct CallOptions {
///
/// Note that enabling `compression` via the `sendMessage` or `sendMessages` methods only applies
/// if encoding has been specified in these options.
public var messageEncoding: MessageEncoding
public var messageEncoding: ClientMessageEncoding

/// Whether the call is cacheable.
public var cacheable: Bool
Expand All @@ -61,7 +61,7 @@ public struct CallOptions {
public init(
customMetadata: HPACKHeaders = HPACKHeaders(),
timeout: GRPCTimeout = GRPCTimeout.infinite,
messageEncoding: MessageEncoding = .none,
messageEncoding: ClientMessageEncoding = .disabled,
requestIDProvider: RequestIDProvider = .autogenerated,
requestIDHeader: String? = nil,
cacheable: Bool = false
Expand Down
54 changes: 54 additions & 0 deletions Sources/GRPC/Compression/DecompressionLimit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2020, gRPC Authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

public struct DecompressionLimit: Equatable {
private enum Limit: Equatable {
case ratio(Int)
case absolute(Int)
}
private let limit: Limit

/// Limits decompressed payloads to be no larger than the product of the compressed size
/// and `ratio`.
///
/// - Parameter ratio: The decompression ratio.
/// - Precondition: `ratio` must be greater than zero.
public static func ratio(_ ratio: Int) -> DecompressionLimit {
precondition(ratio > 0, "ratio must be greater than zero")
return DecompressionLimit(limit: .ratio(ratio))
}

/// Limits decompressed payloads to be no larger than the given `size`.
///
/// - Parameter size: The absolute size limit of decompressed payloads.
/// - Precondition: `size` must not be negative.
public static func absolute(_ size: Int) -> DecompressionLimit {
precondition(size >= 0, "absolute size must be non-negative")
return DecompressionLimit(limit: .absolute(size))
}
}

extension DecompressionLimit {
/// The largest allowed decompressed size for this limit.
func maximumDecompressedSize(compressedSize: Int) -> Int {
switch self.limit {
case .ratio(let ratio):
return ratio * compressedSize
case .absolute(let size):
return size
}
}
}
101 changes: 64 additions & 37 deletions Sources/GRPC/Compression/MessageEncoding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/


/// Whether compression should be enabled for the message.
public enum Compression {
/// Enable compression. Note that this will be ignored if compression has not been enabled or is
Expand Down Expand Up @@ -42,14 +41,32 @@ extension Compression {
}
}

extension CallOptions {
public struct MessageEncoding {
public enum ClientMessageEncoding {
case enabled(Configuration)
case disabled
}

extension ClientMessageEncoding {
var enabledForRequests: Bool {
switch self {
case .enabled(let configuration):
return configuration.outbound != nil
case .disabled:
return false
}
}
}

extension ClientMessageEncoding {
public struct Configuration {
public init(
forRequests outbound: CompressionAlgorithm?,
acceptableForResponses inbound: [CompressionAlgorithm] = CompressionAlgorithm.all
acceptableForResponses inbound: [CompressionAlgorithm] = CompressionAlgorithm.all,
decompressionLimit: DecompressionLimit
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be provide a "reasonable" default here?

) {
self.outbound = outbound
self.inbound = inbound
self.decompressionLimit = decompressionLimit
}

/// The compression algorithm used for outbound messages.
Expand All @@ -58,47 +75,57 @@ extension CallOptions {
/// The set of compression algorithms advertised to the remote peer that they may use.
public var inbound: [CompressionAlgorithm]

/// No compression.
public static let none = MessageEncoding(
forRequests: nil,
acceptableForResponses: []
)
/// The decompression limit acceptable for responses. RPCs which receive a message whose
/// decompressed size exceeds the limit will be cancelled.
public var decompressionLimit: DecompressionLimit

/// Accept all supported compression on responses, do not compress requests.
public static let responsesOnly = MessageEncoding(
forRequests: .identity,
acceptableForResponses: CompressionAlgorithm.all
)

/// Whether compression is enabled for requests.
internal var enabledForRequests: Bool {
return self.outbound != nil
public static func responsesOnly(
acceptable: [CompressionAlgorithm] = CompressionAlgorithm.all,
decompressionLimit: DecompressionLimit
) -> Configuration {
return Configuration(
forRequests: .identity,
acceptableForResponses: acceptable,
decompressionLimit: decompressionLimit
)
}
}
}

extension CallOptions.MessageEncoding {
var acceptEncodingHeader: String {
return self.inbound.map { $0.name }.joined(separator: ",")
internal var acceptEncodingHeader: String {
return self.inbound.map { $0.name }.joined(separator: ",")
}
}
}

extension Server.Configuration {
public struct MessageEncoding {
/// The set of compression algorithms advertised that we will accept from clients. Note that
/// clients may send us messages compressed with algorithms not included in this list; if we
/// support it then we still accept the message.
public var enabled: [CompressionAlgorithm]
public enum ServerMessageEncoding {
case enabled(Configuration)
case disabled
}

public init(enabled: [CompressionAlgorithm]) {
self.enabled = enabled
extension ServerMessageEncoding {
public struct Configuration {
/// The set of compression algorithms advertised that we will accept from clients for requests.
/// Note that clients may send us messages compressed with algorithms not included in this list;
/// if we support it then we still accept the message.
///
/// All cases of `CompressionAlgorithm` are supported.
public var enabledAlgorithms: [CompressionAlgorithm]

/// The decompression limit acceptable for requests. RPCs which receive a message whose
/// decompressed size exceeds the limit will be cancelled.
public var decompressionLimit: DecompressionLimit

/// Create a configuration for server message encoding.
///
/// - Parameters:
/// - enabledAlgorithms: The list of algorithms which are enabled.
/// - decompressionLimit: Decompression limit acceptable for requests.
public init(
enabledAlgorithms: [CompressionAlgorithm] = CompressionAlgorithm.all,
decompressionLimit: DecompressionLimit
) {
self.enabledAlgorithms = enabledAlgorithms
self.decompressionLimit = decompressionLimit
}

// All supported algorithms are enabled.
public static let enabled = MessageEncoding(enabled: CompressionAlgorithm.all)

/// No compression.
public static let none = MessageEncoding(enabled: [.identity])
}

}
Loading