Skip to content

Commit

Permalink
Dynamic Backends (#12)
Browse files Browse the repository at this point in the history
* Start dynamic backend

* Register dynamic backends

* Add stubs

* Add back cast

* Try creating pointer

* Change how we create the pointer

* Logs

* Cleanup

* Try empty options

* Try ssl

* Try ssl versions

* Try ssl versions

* Try ssl versions

* Remove ssl versions

* Try timeouts

* Try host override

* Fix tls version

* Try ssl versions

* Try setting sni hostname

* Try cert hostname

* Copy pointer

* Only set hostname

* Try sni

* Try host override

* Try new bitwise logic

* Dont set mask

* Log

* Fix flags

* Disable

* SSL

* Try new defs

* Dont set anything

* Only set ssl

* Prevent double registering backends

* Enable stubs

* Dont fail when registering backend

* Catch generic error

* Cleanup
  • Loading branch information
AndrewBarba authored Sep 15, 2022
1 parent d256cd3 commit 5d1ee1b
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 10 deletions.
26 changes: 26 additions & 0 deletions Sources/Compute/Fetch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ public func fetch(_ request: FetchRequest) async throws -> FetchResponse {
break
}

// Register the backend
if Environment.Compute.viceroy {
try registerDynamicBackend(request.backend, for: httpRequest, ssl: urlComponents.scheme == "https")
}

// Issue async request
let pendingRequest: PendingRequest
if let streamingBody = streamingBody {
Expand Down Expand Up @@ -146,3 +151,24 @@ public func fetch (
backend: options.backend
))
}

private var dynamicBackends: Set<String> = []

private func registerDynamicBackend(_ backend: String, for request: Request, ssl: Bool) throws {
// Make sure we didn't already register the backend
guard dynamicBackends.contains(backend) == false else {
return
}

// Attempt to register the backend
do {
try request.registerDynamicBackend(name: backend, target: backend, options: .init(ssl: ssl))
} catch WasiStatus.genericError {
// ignore
} catch {
throw error
}

// Mark the backend as registered
dynamicBackends.insert(backend)
}
95 changes: 91 additions & 4 deletions Sources/Compute/Runtime/Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public struct Request: Sendable {
}

public mutating func setCachePolicy(_ policy: CachePolicy, surrogateKey: String? = nil) throws {
let tag: CacheOverrideTag
var tag: CacheOverrideTag
let ttl: UInt32
let swr: UInt32
switch policy {
Expand All @@ -79,14 +79,14 @@ public struct Request: Sendable {
ttl = 0
swr = 0
case .ttl(let seconds, let staleWhileRevalidate, let pciCompliant):
tag = .ttl | (staleWhileRevalidate > 0 ? .swr : 0) | (pciCompliant ? .pci : 0)
tag = .ttl.union(staleWhileRevalidate > 0 ? .swr : .none).union(pciCompliant ? .pci : .none)
ttl = .init(seconds)
swr = .init(staleWhileRevalidate)
}
if let surrogateKey = surrogateKey {
try wasi(fastly_http_req__cache_override_v2_set(handle, tag, ttl, swr, surrogateKey, surrogateKey.utf8.count))
try wasi(fastly_http_req__cache_override_v2_set(handle, tag.rawValue, ttl, swr, surrogateKey, surrogateKey.utf8.count))
} else {
try wasi(fastly_http_req__cache_override_set(handle, tag, ttl, swr))
try wasi(fastly_http_req__cache_override_set(handle, tag.rawValue, ttl, swr))
}
}

Expand Down Expand Up @@ -160,6 +160,93 @@ public struct Request: Sendable {
}
}

extension Request {

public struct DynamicBackendOptions {
public var connectTimeoutMs: Int
public var firstByteTimeoutMs: Int
public var betweenBytesTimeoutMs: Int
public var ssl: Bool
public var sslMinVersion: TLSVersion
public var sslMaxVersion: TLSVersion

public init(
connectTimeoutMs: Int = 1_000,
firstByteTimeoutMs: Int = 15_000,
betweenBytesTimeoutMs: Int = 10_000,
ssl: Bool = true,
sslMinVersion: TLSVersion = .v1_1,
sslMaxVersion: TLSVersion = .v1_3
) {
self.connectTimeoutMs = connectTimeoutMs
self.firstByteTimeoutMs = firstByteTimeoutMs
self.betweenBytesTimeoutMs = betweenBytesTimeoutMs
self.ssl = ssl
self.sslMinVersion = sslMinVersion
self.sslMaxVersion = sslMaxVersion
}
}

public func registerDynamicBackend(name: String, target: String, options: DynamicBackendOptions = .init()) throws {
var mask: BackendConfigOptions = []

var config = DynamicBackendConfig()

// create target pointer used later
try target.withCString { targetPointer in

// host override
mask.insert(.hostOverride)
config.host_override = targetPointer
config.host_override_len = target.utf8.count

// connect timeout
mask.insert(.connectTimeout)
config.connect_timeout_ms = options.connectTimeoutMs

// first byte timeout
mask.insert(.firstByteTimeout)
config.first_byte_timeout_ms = options.firstByteTimeoutMs

// between bytes timeout
mask.insert(.betweenBytesTimeout)
config.between_bytes_timeout_ms = options.betweenBytesTimeoutMs

// ssl
if options.ssl {
mask.insert(.useSSL)

// ssl min version
mask.insert(.sslMinVersion)
config.ssl_min_version = options.sslMinVersion.rawValue

// ssl max version
mask.insert(.sslMaxVersion)
config.ssl_max_version = options.sslMaxVersion.rawValue

// cert hostname
mask.insert(.certHostname)
config.cert_hostname = targetPointer
config.cert_hostname_len = target.utf8.count

// sni hostname
mask.insert(.sniHostname)
config.sni_hostname = targetPointer
config.sni_hostname_len = target.utf8.count
}

try wasi(fastly_http_req__register_dynamic_backend(
name,
name.utf8.count,
target,
target.utf8.count,
mask.rawValue,
&config
))
}
}
}

extension Request {

public static func getDownstream() throws -> (request: Request, body: Body) {
Expand Down
18 changes: 18 additions & 0 deletions Sources/Compute/Runtime/Stubs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@
/// When running with Compute runtime library, they are ignored completely.
#if !arch(wasm32)

/* TYPES */

struct DynamicBackendConfig {
var host_override: UnsafePointer<CChar>! = nil
var host_override_len: Int = 0
var connect_timeout_ms: Int = 0
var first_byte_timeout_ms: Int = 0
var between_bytes_timeout_ms: Int = 0
var ssl_min_version: Int = 0
var ssl_max_version: Int = 0
var cert_hostname: UnsafePointer<CChar>! = nil
var cert_hostname_len: Int = 0
var sni_hostname: UnsafePointer<CChar>! = nil
var sni_hostname_len: Int = 0
}

/* FASTLY_ABI */

func fastly_abi__init(_ abi_version: UInt64) -> Int32 { fatalError() }
Expand Down Expand Up @@ -110,6 +126,8 @@ func fastly_http_req__framing_headers_mode_set(_ req_handle: WasiHandle, _ mode:

func fastly_http_req__upgrade_websocket(_ backend: UnsafePointer<CChar>!, _ backend_len: Int) -> Int32 { fatalError() }

func fastly_http_req__register_dynamic_backend(_ name: UnsafePointer<CChar>!, _ name_len: Int, _ target: UnsafePointer<CChar>!, _ target_len: Int, _ backend_config_mask: UInt32, _ backend_configuration: UnsafeMutablePointer<DynamicBackendConfig>!) -> Int32 { fatalError() }

/* FASTLY_HTTP_RESP */

func fastly_http_resp__new(_ handle: UnsafeMutablePointer<WasiHandle>!) -> Int32 { fatalError() }
Expand Down
50 changes: 44 additions & 6 deletions Sources/Compute/Runtime/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -248,14 +248,21 @@ public typealias MultiValueCursor = Int32

public typealias MultiValueCursorResult = Int64

public typealias CacheOverrideTag = UInt32
public struct CacheOverrideTag: OptionSet {

public let rawValue: UInt32

public init(rawValue: UInt32) {
self.rawValue = rawValue
}
}

extension CacheOverrideTag {
public static let none: Self = 0
public static let pass: Self = 1 << 0
public static let ttl: Self = 1 << 1
public static let swr: Self = 1 << 2
public static let pci: Self = 1 << 3
public static let none: CacheOverrideTag = []
public static let pass = CacheOverrideTag(rawValue: 1 << 0)
public static let ttl = CacheOverrideTag(rawValue: 1 << 1)
public static let swr = CacheOverrideTag(rawValue: 1 << 2)
public static let pci = CacheOverrideTag(rawValue: 1 << 3)
}

public enum CachePolicy: Sendable {
Expand Down Expand Up @@ -286,11 +293,42 @@ public enum FramingHeadersMode: UInt32, Sendable {
case manuallyFromHeaders = 1
}

public enum TLSVersion: Int, Sendable {
case v1 = 0
case v1_1 = 1
case v1_2 = 2
case v1_3 = 3
}

public enum BodyScanContinuation: Sendable {
case `continue`
case `break`
}

public struct BackendConfigOptions: OptionSet, Sendable {

public let rawValue: UInt32

public init(rawValue: UInt32) {
self.rawValue = rawValue
}
}

extension BackendConfigOptions {
public static let reserved = BackendConfigOptions(rawValue: 1 << 0)
public static let hostOverride = BackendConfigOptions(rawValue: 1 << 1)
public static let connectTimeout = BackendConfigOptions(rawValue: 1 << 2)
public static let firstByteTimeout = BackendConfigOptions(rawValue: 1 << 3)
public static let betweenBytesTimeout = BackendConfigOptions(rawValue: 1 << 4)
public static let useSSL = BackendConfigOptions(rawValue: 1 << 5)
public static let sslMinVersion = BackendConfigOptions(rawValue: 1 << 6)
public static let sslMaxVersion = BackendConfigOptions(rawValue: 1 << 7)
public static let certHostname = BackendConfigOptions(rawValue: 1 << 8)
public static let caCert = BackendConfigOptions(rawValue: 1 << 9)
public static let ciphers = BackendConfigOptions(rawValue: 1 << 10)
public static let sniHostname = BackendConfigOptions(rawValue: 1 << 11)
}

public let maxHeaderLength = 69000

public let maxMethodLength = 1024
Expand Down
26 changes: 26 additions & 0 deletions Sources/ComputeRuntime/include/ComputeRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,24 @@

typedef uint32_t WasiHandle;

typedef struct DynamicBackendConfig {
const char* host_override;
size_t host_override_len;
size_t connect_timeout_ms;
size_t first_byte_timeout_ms;
size_t between_bytes_timeout_ms;
size_t ssl_min_version;
size_t ssl_max_version;
const char* cert_hostname;
size_t cert_hostname_len;
const char* ca_cert;
size_t ca_cert_len;
const char* ciphers;
size_t ciphers_len;
const char* sni_hostname;
size_t sni_hostname_len;
} DynamicBackendConfig;

/* FASTLY_ABI */

WASM_IMPORT("fastly_abi", "init")
Expand Down Expand Up @@ -176,6 +194,14 @@ int fastly_http_req__framing_headers_mode_set(WasiHandle req_handle, uint32_t mo
WASM_IMPORT("fastly_http_req", "upgrade_websocket")
int fastly_http_req__upgrade_websocket(const char *backend, size_t backend_len);

WASM_IMPORT("fastly_http_req", "register_dynamic_backend")
int fastly_http_req__register_dynamic_backend(const char *name,
size_t name_len,
const char *target,
size_t target_len,
uint32_t backend_config_mask,
DynamicBackendConfig *backend_configuration);

/* FASTLY_HTTP_RESP */

WASM_IMPORT("fastly_http_resp", "new")
Expand Down

0 comments on commit 5d1ee1b

Please sign in to comment.