Skip to content

Commit

Permalink
Start cache api
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewBarba committed Sep 13, 2023
1 parent dcc7014 commit 6ab3d15
Show file tree
Hide file tree
Showing 3 changed files with 292 additions and 50 deletions.
143 changes: 143 additions & 0 deletions Sources/Compute/Fastly/FastlyCache.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
//
// FastlyCache.swift
//
//
// Created by Andrew Barba on 9/13/23.
//

import ComputeRuntime

extension Fastly {

public struct Cache: Sendable {

public static func getOrSet(_ key: String, _ handler: () async throws -> FetchResponse) async throws -> ReadableBody {
return try await getOrSet(key) {
let body = try await handler().body.body
return HandlerData.body(body)
}
}

public static func getOrSet(_ key: String, _ handler: () async throws -> [UInt8]) async throws -> ReadableBody {
return try await getOrSet(key) {
let bytes = try await handler()
return HandlerData.bytes(bytes)
}
}

public static func getOrSet(_ key: String, _ handler: () async throws -> String) async throws -> ReadableBody {
return try await getOrSet(key) {
let text = try await handler()
return HandlerData.bytes(.init(text.utf8))
}
}

public static func getOrSet(_ key: String, _ handler: () async throws -> Data) async throws -> ReadableBody {
return try await getOrSet(key) {
let data = try await handler()
return HandlerData.bytes(data.bytes)
}
}

public static func getOrSet(_ key: String, _ handler: () async throws -> [String: Sendable]) async throws -> ReadableBody {
return try await getOrSet(key) {
let json = try await handler()
let data = try JSONSerialization.data(withJSONObject: json)
return HandlerData.bytes(data.bytes)
}
}

public static func getOrSet(_ key: String, _ handler: () async throws -> [Sendable]) async throws -> ReadableBody {
return try await getOrSet(key) {
let json = try await handler()
let data = try JSONSerialization.data(withJSONObject: json)
return HandlerData.bytes(data.bytes)
}
}
}
}

extension Fastly.Cache {

internal enum HandlerData {
case body(_ body: Fastly.Body)
case bytes(_ bytes: [UInt8])
}

internal static func getOrSet(_ key: String, _ handler: () async throws -> HandlerData) async throws -> ReadableBody {
// Open the transaction
let trx = try await Transaction.lookup(key)

// Get current transaction state
let state = try await trx.getState()

// If state is usable then return the body from cache
if state == .found || state == .usable {
let body = try await trx.getBody()
return ReadableWasiBody(body)
}

// If its not usable then begin executing handler with new value
let data = try await handler()

// Get an instance to the insert handle
var writer = try await trx.insertAndStreamBack()

// Append bytes from handler to writeable body
switch data {
case .body(let body):
try writer.body.append(body)
case .bytes(let bytes):
try writer.body.write(bytes)
}

// Get handle to reabable body out from cache
return try await ReadableWasiBody(writer.transaction.getBody())
}
}

extension Fastly.Cache {
public struct Transaction: Sendable {

internal let handle: WasiHandle

internal init(_ handle: WasiHandle) {
self.handle = handle
}

internal static func lookup(_ key: String) async throws -> Transaction {
var handle: WasiHandle = 0
let options = CacheLookupOptions.reserved
var config = CacheLookupConfig()
try wasi(fastly_cache__cache_transaction_lookup(key, key.utf8.count, options.rawValue, &config, &handle))
return Transaction(handle)
}

internal func insertAndStreamBack() async throws -> (body: Fastly.Body, transaction: Transaction) {
var bodyHandle: WasiHandle = 0
var cacheHandle: WasiHandle = 0
let options = CacheWriteOptions.reserved
var config = CacheWriteConfig()
try wasi(fastly_cache__cache_transaction_insert_and_stream_back(handle, options.rawValue, &config, &bodyHandle, &cacheHandle))
return (.init(bodyHandle), .init(cacheHandle))
}

internal func getState() async throws -> CacheState {
var value: UInt8 = 0
try wasi(fastly_cache__cache_get_state(handle, &value))
return CacheState(rawValue: value)
}

internal func getBody() async throws -> Fastly.Body {
var bodyHandle: WasiHandle = 0
let options = CacheGetBodyOptions.reserved
var config = CacheGetBodyConfig()
try wasi(fastly_cache__cache_get_body(handle, options.rawValue, &config, &bodyHandle))
return .init(bodyHandle)
}

internal func cancel() async throws {
try wasi(fastly_cache__cache_transaction_cancel(handle))
}
}
}
107 changes: 75 additions & 32 deletions Sources/Compute/Fastly/FastlyTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -318,29 +318,6 @@ public typealias MultiValueCursor = Int32

public typealias MultiValueCursorResult = Int64

public struct CacheOverrideTag: OptionSet {

public let rawValue: UInt32

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

extension CacheOverrideTag {
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 {
case origin
case pass
case ttl(_ seconds: Int, staleWhileRevalidate: Int = 0, pciCompliant: Bool = false)
}

public typealias HeaderCount = Int32

public typealias IsDone = Int32
Expand Down Expand Up @@ -376,15 +353,6 @@ public enum BodyScanContinuation: Sendable {
}

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)
Expand All @@ -397,6 +365,81 @@ extension BackendConfigOptions {
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 rawValue: UInt32
public init(rawValue: UInt32) {
self.rawValue = rawValue
}
}

public struct CacheOverrideTag: OptionSet {
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 static let none: CacheOverrideTag = []

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

public struct CacheState: OptionSet {
public static let found = CacheState(rawValue: 1 << 0)
public static let usable = CacheState(rawValue: 1 << 1)
public static let stale = CacheState(rawValue: 1 << 2)
public static let mustInsertOrUpdate = CacheState(rawValue: 1 << 3)

public let rawValue: UInt8
public init(rawValue: UInt8) {
self.rawValue = rawValue
}
}

public enum CachePolicy: Sendable {
case origin
case pass
case ttl(_ seconds: Int, staleWhileRevalidate: Int = 0, pciCompliant: Bool = false)
}

public struct CacheWriteOptions: OptionSet, Sendable {
public static let reserved = CacheWriteOptions(rawValue: 1 << 0)
public static let requestHeaders = CacheWriteOptions(rawValue: 1 << 1)
public static let varyRule = CacheWriteOptions(rawValue: 1 << 2)
public static let initialAgeNs = CacheWriteOptions(rawValue: 1 << 3)
public static let staleWhileRevalidateNs = CacheWriteOptions(rawValue: 1 << 4)
public static let surrogateKeys = CacheWriteOptions(rawValue: 1 << 5)
public static let length = CacheWriteOptions(rawValue: 1 << 6)
public static let userMetadata = CacheWriteOptions(rawValue: 1 << 7)
public static let sensitiveData = CacheWriteOptions(rawValue: 1 << 8)

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

public struct CacheLookupOptions: OptionSet, Sendable {
public static let reserved = CacheLookupOptions(rawValue: 1 << 0)
public static let requestHeaders = CacheLookupOptions(rawValue: 1 << 1)

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

public struct CacheGetBodyOptions: OptionSet, Sendable {
public static let reserved = CacheLookupOptions(rawValue: 1 << 0)
public static let from = CacheLookupOptions(rawValue: 1 << 1)
public static let to = CacheLookupOptions(rawValue: 1 << 2)

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

public let maxHeaderLength = 69000
Expand Down
92 changes: 74 additions & 18 deletions Sources/ComputeRuntime/include/ComputeRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,6 @@

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 @@ -211,6 +193,24 @@ int fastly_http_req__redirect_to_grip_proxy(const char *backend, size_t backend_
WASM_IMPORT("fastly_http_req", "downstream_tls_ja3_md5")
int fastly_http_req__downstream_tls_ja3_md5(uint8_t *value, size_t *nwritten);

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;

WASM_IMPORT("fastly_http_req", "register_dynamic_backend")
int fastly_http_req__register_dynamic_backend(const char *name,
size_t name_len,
Expand Down Expand Up @@ -267,5 +267,61 @@ int fastly_http_resp__framing_headers_mode_set(WasiHandle resp_handle, uint32_t
WASM_IMPORT("fastly_http_resp", "http_keepalive_mode_set")
int fastly_http_resp__http_keepalive_mode_set(WasiHandle resp_handle, uint32_t mode);

/* FASTLY_CACHE */

typedef struct CacheLookupConfig {
// * A full request handle, but used only for its headers
WasiHandle request_headers;
} CacheLookupConfig;

typedef struct CacheGetBodyConfig {
uint64_t start;
uint64_t end;
} CacheGetBodyConfig;

typedef struct CacheWriteConfig {
uint64_t max_age_ns;
uint32_t request_headers;
const uint8_t *vary_rule_ptr;
size_t vary_rule_len;
uint64_t initial_age_ns;
uint64_t stale_while_revalidate_ns;
const uint8_t *surrogate_keys_ptr;
size_t surrogate_keys_len;
uint64_t length;
const uint8_t *user_metadata_ptr;
size_t user_metadata_len;
} CacheWriteConfig;

WASM_IMPORT("fastly_cache", "lookup")
int cache_lookup(const char *cache_key, size_t cache_key_len, uint32_t options_mask,
CacheLookupConfig *config,
WasiHandle *ret);

WASM_IMPORT("fastly_cache", "insert")
int fastly_cache__cache_insert(const char *cache_key, size_t cache_key_len, uint32_t options_mask,
CacheWriteConfig *config, WasiHandle *ret);

WASM_IMPORT("fastly_cache", "transaction_lookup")
int fastly_cache__cache_transaction_lookup(const char *cache_key, size_t cache_key_len, uint32_t options_mask,
CacheLookupConfig *config,
WasiHandle *ret);

WASM_IMPORT("fastly_cache", "transaction_insert_and_stream_back")
int fastly_cache__cache_transaction_insert_and_stream_back(WasiHandle handle, uint32_t options_mask, CacheWriteConfig *config,
WasiHandle *ret_body,
WasiHandle *ret_cache);

WASM_IMPORT("fastly_cache", "transaction_cancel")
int fastly_cache__cache_transaction_cancel(WasiHandle handle);

WASM_IMPORT("fastly_cache", "get_state")
int fastly_cache__cache_get_state(WasiHandle handle, uint8_t *ret);

WASM_IMPORT("fastly_cache", "get_body")
int fastly_cache__cache_get_body(WasiHandle handle, uint32_t options_mask,
CacheGetBodyConfig *config,
WasiHandle *ret);

#pragma GCC diagnostic pop
#endif /* ComputeRuntime_h */

0 comments on commit 6ab3d15

Please sign in to comment.