diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dece3f9a4a..855ab89b0f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,7 @@ defaults: run: shell: bash env: - viceroy_version: 0.4.4 + viceroy_version: 0.5.1 wasm-tools_version: 1.0.28 jobs: @@ -128,7 +128,7 @@ jobs: matrix: include: - crate: viceroy - version: 0.4.4 # Note: workflow-level env vars can't be used in matrix definitions + version: 0.5.1 # Note: workflow-level env vars can't be used in matrix definitions options: "" - crate: wasm-tools version: 1.0.28 # Note: workflow-level env vars can't be used in matrix definitions @@ -383,6 +383,7 @@ jobs: - 'byob' - 'byte-repeater' - 'cache-override' + - 'cache-simple' - 'crypto' - 'edge-dictionary' - 'error' diff --git a/integration-tests/js-compute/assertions.js b/integration-tests/js-compute/assertions.js index 71b7fd27ca..9781d0e475 100644 --- a/integration-tests/js-compute/assertions.js +++ b/integration-tests/js-compute/assertions.js @@ -1,5 +1,33 @@ +/* global ReadableStream */ + // Testing/Assertion functions // +// TODO: Implement ReadableStream getIterator() and [@@asyncIterator]() methods +export async function streamToString(stream) { + const decoder = new TextDecoder(); + let string = ''; + let reader = stream.getReader() + // eslint-disable-next-line no-constant-condition + while (true) { + const { done, value } = await reader.read(); + if (done) { + return string; + } + string += decoder.decode(value) + } +} + +export function iteratableToStream(iterable) { + return new ReadableStream({ + async pull(controller) { + for await (const value of iterable) { + controller.enqueue(value); + } + controller.close(); + } + }); +} + export function pass(message = '') { return new Response(message) } diff --git a/integration-tests/js-compute/fixtures/cache-simple/bin/index.js b/integration-tests/js-compute/fixtures/cache-simple/bin/index.js new file mode 100644 index 0000000000..32ee3c4dd2 --- /dev/null +++ b/integration-tests/js-compute/fixtures/cache-simple/bin/index.js @@ -0,0 +1,1011 @@ +/// +/* eslint-env serviceworker */ +/* global ReadableStream */ +import { pass, assert, assertDoesNotThrow, assertThrows, assertRejects, iteratableToStream, streamToString } from "../../../assertions.js"; +import { routes, FASTLY_SERVICE_VERSION } from "../../../test-harness.js"; +import { SimpleCache, SimpleCacheEntry } from 'fastly:cache'; + +let error; +routes.set("/simple-cache/interface", () => { + let actual = Reflect.ownKeys(SimpleCache) + let expected = ["prototype", "delete", "get", "set", "length", "name"] + error = assert(actual, expected, `Reflect.ownKeys(SimpleCache)`) + if (error) { return error } + + // Check the prototype descriptors are correct + { + actual = Reflect.getOwnPropertyDescriptor(SimpleCache, 'prototype') + expected = { + "value": SimpleCache.prototype, + "writable": false, + "enumerable": false, + "configurable": false + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache, 'prototype')`) + if (error) { return error } + } + + // Check the constructor function's defined parameter length is correct + { + actual = Reflect.getOwnPropertyDescriptor(SimpleCache, 'length') + expected = { + "value": 0, + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache, 'length')`) + if (error) { return error } + } + + // Check the constructor function's name is correct + { + actual = Reflect.getOwnPropertyDescriptor(SimpleCache, 'name') + expected = { + "value": "SimpleCache", + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache, 'name')`) + if (error) { return error } + } + + // Check the prototype has the correct keys + { + actual = Reflect.ownKeys(SimpleCache.prototype) + expected = ["constructor", Symbol.toStringTag] + error = assert(actual, expected, `Reflect.ownKeys(SimpleCache.prototype)`) + if (error) { return error } + } + + // Check the constructor on the prototype is correct + { + actual = Reflect.getOwnPropertyDescriptor(SimpleCache.prototype, 'constructor') + expected = { "writable": true, "enumerable": false, "configurable": true, value: SimpleCache.prototype.constructor } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache.prototype, 'constructor')`) + if (error) { return error } + + error = assert(typeof SimpleCache.prototype.constructor, 'function', `typeof SimpleCache.prototype.constructor`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCache.prototype.constructor, 'length') + expected = { + "value": 0, + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache.prototype.constructor, 'length')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCache.prototype.constructor, 'name') + expected = { + "value": "SimpleCache", + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache.prototype.constructor, 'name')`) + if (error) { return error } + } + + // Check the Symbol.toStringTag on the prototype is correct + { + actual = Reflect.getOwnPropertyDescriptor(SimpleCache.prototype, Symbol.toStringTag) + expected = { "writable": false, "enumerable": false, "configurable": true, value: "SimpleCache" } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache.prototype, [Symbol.toStringTag])`) + if (error) { return error } + + error = assert(typeof SimpleCache.prototype[Symbol.toStringTag], 'string', `typeof SimpleCache.prototype[Symbol.toStringTag]`) + if (error) { return error } + } + + // Check the get static method has correct descriptors, length and name + { + actual = Reflect.getOwnPropertyDescriptor(SimpleCache, 'get') + expected = { "writable": true, "enumerable": true, "configurable": true, value: SimpleCache.get } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache, 'get')`) + if (error) { return error } + + error = assert(typeof SimpleCache.get, 'function', `typeof SimpleCache.get`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCache.get, 'length') + expected = { + "value": 1, + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache.get, 'length')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCache.get, 'name') + expected = { + "value": "get", + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache.get, 'name')`) + if (error) { return error } + } + + // Check the set static method has correct descriptors, length and name + { + actual = Reflect.getOwnPropertyDescriptor(SimpleCache, 'set') + expected = { "writable": true, "enumerable": true, "configurable": true, value: SimpleCache.set } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache, 'set')`) + if (error) { return error } + + error = assert(typeof SimpleCache.set, 'function', `typeof SimpleCache.set`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCache.set, 'length') + expected = { + "value": 3, + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache.set, 'length')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCache.set, 'name') + expected = { + "value": "set", + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache.set, 'name')`) + if (error) { return error } + } + + // Check the delete static method has correct descriptors, length and name + { + actual = Reflect.getOwnPropertyDescriptor(SimpleCache, 'delete') + expected = { "writable": true, "enumerable": true, "configurable": true, value: SimpleCache.delete } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache, 'delete')`) + if (error) { return error } + + error = assert(typeof SimpleCache.delete, 'function', `typeof SimpleCache.delete`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCache.delete, 'length') + expected = { + "value": 1, + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache.delete, 'length')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCache.delete, 'name') + expected = { + "value": "delete", + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCache.delete, 'name')`) + if (error) { return error } + } + + return pass() +}); + +// SimpleCache constructor +{ + + routes.set("/simple-store/constructor/called-as-regular-function", () => { + error = assertThrows(() => { + SimpleCache() + }, TypeError, `Illegal constructor`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/constructor/throws", () => { + error = assertThrows(() => new SimpleCache(), TypeError, `Illegal constructor`) + if (error) { return error } + return pass() + }); +} + +// SimpleCache delete static method +// static delete(key: string): undefined; +{ + routes.set("/simple-cache/delete/called-as-constructor", () => { + error = assertThrows(() => { + new SimpleCache.delete('1') + }, TypeError, `SimpleCache.delete is not a constructor`) + if (error) { return error } + return pass() + }); + // Ensure we correctly coerce the parameter to a string as according to + // https://tc39.es/ecma262/#sec-tostring + routes.set("/simple-cache/delete/key-parameter-calls-7.1.17-ToString", () => { + let sentinel; + const test = () => { + sentinel = Symbol('sentinel'); + const key = { + toString() { + throw sentinel; + } + } + SimpleCache.delete(key) + } + error = assertThrows(test) + if (error) { return error } + try { + test() + } catch (thrownError) { + error = assert(thrownError, sentinel, 'thrownError === sentinel') + if (error) { return error } + } + error = assertThrows(() => { + SimpleCache.delete(Symbol()) + }, TypeError, `can't convert symbol to string`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/delete/key-parameter-not-supplied", () => { + error = assertThrows(() => { + SimpleCache.delete() + }, TypeError, `SimpleCache.delete: At least 1 argument required, but only 0 passed`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/delete/key-parameter-empty-string", () => { + error = assertThrows(() => { + SimpleCache.delete('') + }, Error, `SimpleCache.delete: key can not be an empty string`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/delete/key-parameter-1024-character-string", () => { + error = assertDoesNotThrow(() => { + const key = 'a'.repeat(1024) + SimpleCache.delete(key) + }) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/delete/key-parameter-1025-character-string", () => { + error = assertThrows(() => { + const key = 'a'.repeat(1025) + SimpleCache.delete(key) + }, Error, `SimpleCache.delete: key is too long, the maximum allowed length is 1024.`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/delete/returns-undefined", () => { + error = assert(SimpleCache.delete('meow'), undefined, "SimpleCache.delete('meow') === undefined") + if (error) { return error } + return pass() + }); +} + +// SimpleCache set static method +// static set(key: string, value: BodyInit, ttl: number): undefined; +{ + routes.set("/simple-cache/set/called-as-constructor", () => { + error = assertThrows(() => { + new SimpleCache.set('1', 'meow', 1) + }, TypeError, `SimpleCache.set is not a constructor`) + if (error) { return error } + return pass() + }); + // Ensure we correctly coerce the key parameter to a string as according to + // https://tc39.es/ecma262/#sec-tostring + routes.set("/simple-cache/set/key-parameter-calls-7.1.17-ToString", () => { + let sentinel; + const test = () => { + sentinel = Symbol('sentinel'); + const key = { + toString() { + throw sentinel; + } + } + SimpleCache.set(key, 'meow', 1) + } + error = assertThrows(test) + if (error) { return error } + try { + test() + } catch (thrownError) { + error = assert(thrownError, sentinel, 'thrownError === sentinel') + if (error) { return error } + } + error = assertThrows(() => { + SimpleCache.set(Symbol(), 'meow', 1) + }, TypeError, `can't convert symbol to string`) + if (error) { return error } + return pass() + }); + // Ensure we correctly coerce the tll parameter to a number as according to + // https://tc39.es/ecma262/#sec-tonumber + routes.set("/simple-cache/set/tll-parameter-7.1.4-ToNumber", () => { + let sentinel; + let requestedType; + const test = () => { + sentinel = Symbol('sentinel'); + const ttl = { + [Symbol.toPrimitive](type) { + requestedType = type; + throw sentinel; + } + } + SimpleCache.set('1', 'meow', ttl) + } + let error = assertThrows(test) + if (error) { return error } + try { + test() + } catch (thrownError) { + let error = assert(thrownError, sentinel, 'thrownError === sentinel') + if (error) { return error } + error = assert(requestedType, 'number', 'requestedType === "number"') + if (error) { return error } + } + error = assertThrows(() => SimpleCache.set('1', 'meow', Symbol()), TypeError, `can't convert symbol to number`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/no-parameters-supplied", () => { + error = assertThrows(() => { + SimpleCache.set() + }, TypeError, `SimpleCache.set: At least 3 arguments required, but only 0 passed`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/key-parameter-empty-string", () => { + error = assertThrows(() => { + SimpleCache.set('', 'meow', 1) + }, Error, `SimpleCache.set: key can not be an empty string`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/key-parameter-8135-character-string", () => { + error = assertDoesNotThrow(() => { + const key = 'a'.repeat(8135) + SimpleCache.set(key, 'meow', 1) + }) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/key-parameter-8136-character-string", () => { + error = assertThrows(() => { + const key = 'a'.repeat(8136) + SimpleCache.set(key, 'meow', 1) + }, Error, `SimpleCache.set: key is too long, the maximum allowed length is 8135.`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/ttl-parameter-negative-number", () => { + error = assertThrows(() => { + SimpleCache.set('cat', 'meow', -1) + }, Error, `SimpleCache.set: TTL parameter is an invalid value, only positive numbers can be used for TTL values.`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/ttl-parameter-NaN", () => { + error = assertThrows(() => { + SimpleCache.set('cat', 'meow', NaN) + }, Error, `SimpleCache.set: TTL parameter is an invalid value, only positive numbers can be used for TTL values.`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/ttl-parameter-Infinity", () => { + error = assertThrows(() => { + SimpleCache.set('cat', 'meow', Number.POSITIVE_INFINITY) + }, Error, `SimpleCache.set: TTL parameter is an invalid value, only positive numbers can be used for TTL values.`) + if (error) { return error } + error = assertThrows(() => { + SimpleCache.set('cat', 'meow', Number.NEGATIVE_INFINITY) + }, Error, `SimpleCache.set: TTL parameter is an invalid value, only positive numbers can be used for TTL values.`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/value-parameter-as-undefined", () => { + error = assert(SimpleCache.set("meow", undefined, 1), undefined, 'SimpleCache.set("meow", undefined, 1) === undefined') + if (error) { return error } + return pass() + }); + // - ReadableStream + routes.set("/simple-cache/set/value-parameter-readablestream-missing-length-parameter", () => { + // TODO: remove this when streams are supported + let error = assertThrows(() => { + const stream = iteratableToStream([]) + SimpleCache.set("meow", stream, 1) + }, TypeError, `Content-provided streams are not yet supported for streaming into SimpleCache`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/value-parameter-readablestream-negative-length-parameter", () => { + // TODO: remove this when streams are supported + let error = assertThrows(() => { + const stream = iteratableToStream([]) + SimpleCache.set("meow", stream, 1, -1) + }, TypeError, `Content-provided streams are not yet supported for streaming into SimpleCache`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/value-parameter-readablestream-nan-length-parameter", () => { + // TODO: remove this when streams are supported + let error = assertThrows(() => { + const stream = iteratableToStream([]) + SimpleCache.set("meow", stream, 1, NaN) + }, TypeError, `Content-provided streams are not yet supported for streaming into SimpleCache`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/value-parameter-readablestream-negative-infinity-length-parameter", () => { + // TODO: remove this when streams are supported + let error = assertThrows(() => { + const stream = iteratableToStream([]) + SimpleCache.set("meow", stream, 1, Number.NEGATIVE_INFINITY) + }, TypeError, `Content-provided streams are not yet supported for streaming into SimpleCache`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/value-parameter-readablestream-positive-infinity-length-parameter", () => { + // TODO: remove this when streams are supported + let error = assertThrows(() => { + const stream = iteratableToStream([]) + SimpleCache.set("meow", stream, 1, Number.POSITIVE_INFINITY) + }, TypeError, `Content-provided streams are not yet supported for streaming into SimpleCache`) + if (error) { return error } + return pass() + }); + // Ensure we correctly coerce the tll parameter to a number as according to + // https://tc39.es/ecma262/#sec-tonumber + routes.set("/simple-cache/set/length-parameter-7.1.4-ToNumber", async () => { + const res = await fetch('https://compute-sdk-test-backend.edgecompute.app/', { + backend: "TheOrigin", + }) + let sentinel; + let requestedType; + const test = () => { + sentinel = Symbol('sentinel'); + const length = { + [Symbol.toPrimitive](type) { + requestedType = type; + throw sentinel; + } + } + SimpleCache.set('1', res.body, 1, length) + } + let error = assertThrows(test) + if (error) { return error } + try { + test() + } catch (thrownError) { + let error = assert(thrownError, sentinel, 'thrownError === sentinel') + if (error) { return error } + error = assert(requestedType, 'number', 'requestedType === "number"') + if (error) { return error } + } + error = assertThrows(() => SimpleCache.set('1', res.body, 1, Symbol()), TypeError, `can't convert symbol to number`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/value-parameter-readablestream-empty", () => { + // TODO: remove this when streams are supported + let error = assertThrows(() => { + const stream = iteratableToStream([]) + SimpleCache.set("meow", stream, 1, 0) + }, TypeError, `Content-provided streams are not yet supported for streaming into SimpleCache`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/value-parameter-readablestream-locked", () => { + const stream = iteratableToStream([]) + // getReader() causes the stream to become locked + stream.getReader() + let error = assertThrows(() => { + SimpleCache.set("meow", stream, 1, 0) + }, TypeError, `Can't use a ReadableStream that's locked or has ever been read from or canceled`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/set/value-parameter-readablestream", async () => { + const res = await fetch('https://compute-sdk-test-backend.edgecompute.app/', { + backend: "TheOrigin", + }) + let result = SimpleCache.set('readablestream', res.body, 100, res.headers.get('content-length')) + error = assert(result, undefined, `SimpleCache.set('readablestream', res.body, 100)`) + if (error) { return error } + return pass() + }); + + // - URLSearchParams + routes.set("/simple-cache/set/value-parameter-URLSearchParams", () => { + const items = [ + new URLSearchParams, + new URLSearchParams({ a: 'b', c: 'd' }), + ]; + for (const searchParams of items) { + let result = SimpleCache.set("meow", searchParams,1 ) + error = assert(result, undefined, `SimpleCache.set("meow", searchParams, 1) === undefiend`) + if (error) { return error } + } + return pass() + }); + // - USV strings + routes.set("/simple-cache/set/value-parameter-strings", () => { + const strings = [ + // empty + '', + // lone surrogate + '\uD800', + // surrogate pair + '𠈓', + String('carrot'), + ]; + for (const string of strings) { + let result = SimpleCache.set("meow", string, 1) + error = assert(result, undefined, `SimpleCache.set("meow", string, 1) === undefined`) + if (error) { return error } + } + return pass() + }); + + // https://tc39.es/ecma262/#sec-tostring + routes.set("/simple-cache/set/value-parameter-calls-7.1.17-ToString", () => { + let sentinel; + const test = () => { + sentinel = Symbol('sentinel'); + const value = { + toString() { + throw sentinel; + } + } + SimpleCache.set("meow", value, 1) + } + let error = assertThrows(test) + if (error) { return error } + try { + test() + } catch (thrownError) { + let error = assert(thrownError, sentinel, 'thrownError === sentinel') + if (error) { return error } + } + error = assertThrows(() => { + SimpleCache.set("meow", Symbol(), 1) + }, TypeError, `can't convert symbol to string`) + if (error) { return error } + return pass() + }); + + // - buffer source + routes.set("/simple-cache/set/value-parameter-buffer", () => { + const typedArrayConstructors = [ + Int8Array, + Int16Array, + Int32Array, + Float32Array, + Float64Array, + BigInt64Array, + Uint8Array, + Uint8ClampedArray, + Uint16Array, + Uint32Array, + BigUint64Array, + ]; + for (const constructor of typedArrayConstructors) { + const typedArray = new constructor(8); + let result = SimpleCache.set("meow", typedArray.buffer, 1) + error = assert(result, undefined, `SimpleCache.set("meow", typedArray.buffer, 1) === undefined`) + if (error) { return error } + } + return pass() + }); + routes.set("/simple-cache/set/value-parameter-arraybuffer", () => { + const typedArrayConstructors = [ + Int8Array, + Int16Array, + Int32Array, + Float32Array, + Float64Array, + BigInt64Array, + Uint8Array, + Uint8ClampedArray, + Uint16Array, + Uint32Array, + BigUint64Array, + ]; + for (const constructor of typedArrayConstructors) { + const typedArray = new constructor(8); + let result = SimpleCache.set("meow", typedArray.buffer, 1) + error = assert(result, undefined, `SimpleCache.set("meow", typedArray.buffer, 1) === undefined`) + if (error) { return error } + } + return pass() + }); + routes.set("/simple-cache/set/value-parameter-typed-arrays", () => { + const typedArrayConstructors = [ + Int8Array, + Int16Array, + Int32Array, + Float32Array, + Float64Array, + BigInt64Array, + Uint8Array, + Uint8ClampedArray, + Uint16Array, + Uint32Array, + BigUint64Array, + ]; + for (const constructor of typedArrayConstructors) { + const typedArray = new constructor(8); + let result = SimpleCache.set("meow", typedArray, 1) + error = assert(result, undefined, `SimpleCache.set("meow", typedArray, 1) === undefined`) + if (error) { return error } + } + return pass() + }); + routes.set("/simple-cache/set/value-parameter-dataview", () => { + const typedArrayConstructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, + ]; + for (const constructor of typedArrayConstructors) { + const typedArray = new constructor(8); + const view = new DataView(typedArray.buffer); + let result = SimpleCache.set("meow", view, 1) + error = assert(result, undefined, `SimpleCache.set("meow", view, 1) === undefined`) + if (error) { return error } + } + return pass() + }); + routes.set("/simple-cache/set/returns-undefined", () => { + error = assert(SimpleCache.set('1', 'meow', 1), undefined, "SimpleCache.set('1', 'meow', 1) === undefined") + if (error) { return error } + return pass() + }); +} + +// SimpleCache get static method +// static get(key: string): SimpleCacheEntry | null; +{ + routes.set("/simple-cache/get/called-as-constructor", () => { + let error = assertThrows(() => { + new SimpleCache.get('1') + }, TypeError, `SimpleCache.get is not a constructor`) + if (error) { return error } + return pass() + }); + // https://tc39.es/ecma262/#sec-tostring + routes.set("/simple-cache/get/key-parameter-calls-7.1.17-ToString", () => { + let sentinel; + const test = () => { + sentinel = Symbol('sentinel'); + const key = { + toString() { + throw sentinel; + } + } + SimpleCache.get(key) + } + let error = assertThrows(test) + if (error) { return error } + try { + test() + } catch (thrownError) { + let error = assert(thrownError, sentinel, 'thrownError === sentinel') + if (error) { return error } + } + error = assertThrows(() => { + SimpleCache.get(Symbol()) + }, TypeError, `can't convert symbol to string`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/get/key-parameter-not-supplied", () => { + let error = assertThrows(() => { + SimpleCache.get() + }, TypeError, `SimpleCache.get: At least 1 argument required, but only 0 passed`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/get/key-parameter-empty-string", () => { + let error = assertThrows(() => { + SimpleCache.get('') + }, Error, `SimpleCache.get: key can not be an empty string`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/get/key-parameter-8135-character-string", () => { + let error = assertDoesNotThrow(() => { + const key = 'a'.repeat(8135) + SimpleCache.get(key) + }) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/get/key-parameter-8136-character-string", () => { + let error = assertThrows(() => { + const key = 'a'.repeat(8136) + SimpleCache.get(key) + }, Error, `SimpleCache.get: key is too long, the maximum allowed length is 8135.`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/get/key-does-not-exist-returns-null", () => { + let result = SimpleCache.get(Math.random()) + error = assert(result, null, `SimpleCache.get(Math.random()) === null`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache/get/key-exists", () => { + SimpleCache.set('cat', 'meow', 100); + let result = SimpleCache.get('cat'); + error = assert(result instanceof SimpleCacheEntry, true, `SimpleCache.get('cat') instanceof SimpleCacheEntry`) + if (error) { return error } + return pass() + }); +} + +// SimpleCacheEntry +{ + routes.set("/simple-cache-entry/interface", async () => { + return simpleCacheEntryInterfaceTests() + }); + routes.set("/simple-cache-entry/text/valid", async () => { + let key = `entry-text-valid-${FASTLY_SERVICE_VERSION}`; + SimpleCache.set(key, 'hello', 100) + let entry = SimpleCache.get(key) + let result = entry.text() + let error = assert(result instanceof Promise, true, `entry.text() instanceof Promise`) + if (error) { return error } + result = await result + error = assert(result, 'hello', `await entry.text()`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache-entry/json/valid", async () => { + let key = `entry-json-valid-${FASTLY_SERVICE_VERSION}`; + const obj = { a: 1, b: 2, c: 3 } + SimpleCache.set(key, JSON.stringify(obj), 100) + let entry = SimpleCache.get(key) + let result = entry.json() + let error = assert(result instanceof Promise, true, `entry.json() instanceof Promise`) + if (error) { return error } + result = await result + error = assert(result, obj, `await entry.json()`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache-entry/json/invalid", async () => { + let key = `entry-json-invalid-${FASTLY_SERVICE_VERSION}`; + SimpleCache.set(key, "132abc;['-=9", 100) + let entry = SimpleCache.get(key) + let error = await assertRejects(() => entry.json(), SyntaxError, `JSON.parse: unexpected non-whitespace character after JSON data at line 1 column 4 of the JSON data`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache-entry/arrayBuffer/valid", async () => { + let key = `entry-arraybuffer-valid-${FASTLY_SERVICE_VERSION}`; + SimpleCache.set(key, new Int8Array([0, 1, 2, 3]), 100) + let entry = SimpleCache.get(key) + let result = entry.arrayBuffer() + let error = assert(result instanceof Promise, true, `entry.arrayBuffer() instanceof Promise`) + if (error) { return error } + result = await result + error = assert(result instanceof ArrayBuffer, true, `(await entry.arrayBuffer()) instanceof ArrayBuffer`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache-entry/body", async () => { + let key = `entry-body-${FASTLY_SERVICE_VERSION}`; + SimpleCache.set(key, 'body body body', 100) + let entry = SimpleCache.get(key) + let result = entry.body; + let error = assert(result instanceof ReadableStream, true, `entry.body instanceof ReadableStream`) + if (error) { return error } + let text = await streamToString(result); + error = assert(text, 'body body body', `entry.body contents as string`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache-entry/bodyUsed", async () => { + let key = `entry-bodyUsed-${FASTLY_SERVICE_VERSION}`; + SimpleCache.set(key, 'body body body', 100) + let entry = SimpleCache.get(key) + let error = assert(entry.bodyUsed, false, `entry.bodyUsed`) + if (error) { return error } + await entry.text(); + error = assert(entry.bodyUsed, true, `entry.bodyUsed`) + if (error) { return error } + return pass() + }); + routes.set("/simple-cache-entry/readablestream", async () => { + const res = await fetch('https://compute-sdk-test-backend.edgecompute.app/', { + backend: "TheOrigin", + }) + let key = `readablestream-${FASTLY_SERVICE_VERSION}`; + SimpleCache.set(key, res.body, 100, res.headers.get('content-length')) + let entry = SimpleCache.get(key) + error = assert(await entry.text(), 'Compute SDK Test Backend', `await entry.text()`) + if (error) { return error } + return pass() + }); +} +async function simpleCacheEntryInterfaceTests() { + let actual = Reflect.ownKeys(SimpleCacheEntry) + let expected = ["prototype", "length", "name"] + let error = assert(actual, expected, `Reflect.ownKeys(SimpleCacheEntry)`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry, 'prototype') + expected = { + "value": SimpleCacheEntry.prototype, + "writable": false, + "enumerable": false, + "configurable": false + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry, 'prototype')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry, 'length') + expected = { + "value": 0, + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry, 'length')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry, 'name') + expected = { + "value": "SimpleCacheEntry", + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry, 'name')`) + if (error) { return error } + + actual = Reflect.ownKeys(SimpleCacheEntry.prototype) + expected = ["constructor", "body", "bodyUsed", "arrayBuffer", "json", "text", Symbol.toStringTag] + error = assert(actual, expected, `Reflect.ownKeys(SimpleCacheEntry.prototype)`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'constructor') + expected = { "writable": true, "enumerable": false, "configurable": true, value: SimpleCacheEntry.prototype.constructor } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'constructor')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'text') + expected = { "writable": true, "enumerable": true, "configurable": true, value: SimpleCacheEntry.prototype.text } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'text')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'json') + expected = { "writable": true, "enumerable": true, "configurable": true, value: SimpleCacheEntry.prototype.json } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'json')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'arrayBuffer') + expected = { "writable": true, "enumerable": true, "configurable": true, value: SimpleCacheEntry.prototype.arrayBuffer } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'arrayBuffer')`) + if (error) { return error } + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'body') + error = assert(actual.enumerable, true, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'body').enumerable`) + error = assert(actual.configurable, true, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'body').configurable`) + error = assert('set' in actual, true, `'set' in Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'body')`) + error = assert(actual.set, undefined, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'body').set`) + error = assert(typeof actual.get, 'function', `typeof Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'body').get`) + if (error) { return error } + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'bodyUsed') + error = assert(actual.enumerable, true, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'bodyUsed').enumerable`) + error = assert(actual.configurable, true, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'bodyUsed').configurable`) + error = assert('set' in actual, true, `'set' in Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'bodyUsed')`) + error = assert(actual.set, undefined, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'bodyUsed').set`) + error = assert(typeof actual.get, 'function', `typeof Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype, 'bodyUsed').get`) + if (error) { return error } + + error = assert(typeof SimpleCacheEntry.prototype.constructor, 'function', `typeof SimpleCacheEntry.prototype.constructor`) + if (error) { return error } + error = assert(typeof SimpleCacheEntry.prototype.text, 'function', `typeof SimpleCacheEntry.prototype.text`) + if (error) { return error } + error = assert(typeof SimpleCacheEntry.prototype.json, 'function', `typeof SimpleCacheEntry.prototype.json`) + if (error) { return error } + error = assert(typeof SimpleCacheEntry.prototype.arrayBuffer, 'function', `typeof SimpleCacheEntry.prototype.arrayBuffer`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.constructor, 'length') + expected = { + "value": 0, + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.constructor, 'length')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.constructor, 'name') + expected = { + "value": "SimpleCacheEntry", + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.constructor, 'name')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.text, 'length') + expected = { + "value": 0, + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.text, 'length')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.text, 'name') + expected = { + "value": "text", + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.text, 'name')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.json, 'length') + expected = { + "value": 0, + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.json, 'length')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.json, 'name') + expected = { + "value": "json", + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.json, 'name')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.arrayBuffer, 'length') + expected = { + "value": 0, + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.arrayBuffer, 'length')`) + if (error) { return error } + + actual = Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.arrayBuffer, 'name') + expected = { + "value": "arrayBuffer", + "writable": false, + "enumerable": false, + "configurable": true + } + error = assert(actual, expected, `Reflect.getOwnPropertyDescriptor(SimpleCacheEntry.prototype.arrayBuffer, 'name')`) + if (error) { return error } + + return pass() +} \ No newline at end of file diff --git a/integration-tests/js-compute/fixtures/cache-simple/fastly.toml.in b/integration-tests/js-compute/fixtures/cache-simple/fastly.toml.in new file mode 100644 index 0000000000..6ae31b6749 --- /dev/null +++ b/integration-tests/js-compute/fixtures/cache-simple/fastly.toml.in @@ -0,0 +1,27 @@ +# This file describes a Fastly Compute@Edge package. To learn more visit: +# https://developer.fastly.com/reference/fastly-toml/ + +authors = ["me@jakechampion.name"] +description = "" +language = "other" +manifest_version = 2 +name = "cache-simple" +service_id = "" + +[scripts] + build = "node ../../../../js-compute-runtime-cli.js bin/index.js" + +[local_server] + + [local_server.backends] + + [local_server.backends.TheOrigin] + url = "JS_COMPUTE_TEST_BACKEND/" + +[setup] + + [setup.backends] + + [setup.backends.TheOrigin] + address = "compute-sdk-test-backend.edgecompute.app" + port = 443 \ No newline at end of file diff --git a/integration-tests/js-compute/fixtures/cache-simple/tests.json b/integration-tests/js-compute/fixtures/cache-simple/tests.json new file mode 100644 index 0000000000..f5a7ea6226 --- /dev/null +++ b/integration-tests/js-compute/fixtures/cache-simple/tests.json @@ -0,0 +1,542 @@ +{ + "GET /simple-cache/interface": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/interface" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-store/constructor/called-as-regular-function": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-store/constructor/called-as-regular-function" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/constructor/throws": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/constructor/throws" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/delete/called-as-constructor": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/delete/called-as-constructor" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/delete/key-parameter-calls-7.1.17-ToString": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/delete/key-parameter-calls-7.1.17-ToString" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/delete/key-parameter-not-supplied": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/delete/key-parameter-not-supplied" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/delete/key-parameter-empty-string": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/delete/key-parameter-empty-string" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/delete/key-parameter-1024-character-string": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/delete/key-parameter-1024-character-string" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/delete/key-parameter-1025-character-string": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/delete/key-parameter-1025-character-string" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/delete/returns-undefined": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/delete/returns-undefined" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/called-as-constructor": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/called-as-constructor" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/key-parameter-calls-7.1.17-ToString": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/key-parameter-calls-7.1.17-ToString" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/tll-parameter-7.1.4-ToNumber": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/tll-parameter-7.1.4-ToNumber" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/no-parameters-supplied": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/no-parameters-supplied" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/key-parameter-empty-string": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/key-parameter-empty-string" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/key-parameter-8135-character-string": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/key-parameter-8135-character-string" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/key-parameter-8136-character-string": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/key-parameter-8136-character-string" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/ttl-parameter-negative-number": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/ttl-parameter-negative-number" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/ttl-parameter-NaN": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/ttl-parameter-NaN" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/ttl-parameter-Infinity": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/ttl-parameter-Infinity" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-as-undefined": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-as-undefined" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-readablestream-missing-length-parameter": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-readablestream-missing-length-parameter" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-readablestream-negative-length-parameter": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-readablestream-negative-length-parameter" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-readablestream-nan-length-parameter": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-readablestream-nan-length-parameter" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-readablestream-negative-infinity-length-parameter": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-readablestream-negative-infinity-length-parameter" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-readablestream-positive-infinity-length-parameter": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-readablestream-positive-infinity-length-parameter" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/length-parameter-7.1.4-ToNumber": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/length-parameter-7.1.4-ToNumber" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-readablestream-empty": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-readablestream-empty" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-readablestream-locked": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-readablestream-locked" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-readablestream": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-readablestream" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-URLSearchParams": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-URLSearchParams" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-strings": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-strings" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-calls-7.1.17-ToString": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-calls-7.1.17-ToString" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-buffer": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-buffer" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-arraybuffer": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-arraybuffer" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-typed-arrays": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-typed-arrays" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/value-parameter-dataview": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/value-parameter-dataview" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/set/returns-undefined": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/set/returns-undefined" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/get/called-as-constructor": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/get/called-as-constructor" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/get/key-parameter-calls-7.1.17-ToString": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/get/key-parameter-calls-7.1.17-ToString" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/get/key-parameter-not-supplied": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/get/key-parameter-not-supplied" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/get/key-parameter-empty-string": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/get/key-parameter-empty-string" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/get/key-parameter-8135-character-string": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/get/key-parameter-8135-character-string" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/get/key-parameter-8136-character-string": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/get/key-parameter-8136-character-string" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/get/key-does-not-exist-returns-null": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/get/key-does-not-exist-returns-null" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache/get/key-exists": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache/get/key-exists" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache-entry/interface": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache-entry/interface" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache-entry/text/valid": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache-entry/text/valid" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache-entry/json/valid": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache-entry/json/valid" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache-entry/json/invalid": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache-entry/json/invalid" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache-entry/arrayBuffer/valid": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache-entry/arrayBuffer/valid" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache-entry/body": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache-entry/body" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache-entry/bodyUsed": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache-entry/bodyUsed" + }, + "downstream_response": { + "status": 200 + } + }, + "GET /simple-cache-entry/readablestream": { + "environments": ["c@e"], + "downstream_request": { + "method": "GET", + "pathname": "/simple-cache-entry/readablestream" + }, + "downstream_response": { + "status": 200 + } + } +} diff --git a/integration-tests/js-compute/test-harness.js b/integration-tests/js-compute/test-harness.js index 272a5f1f6f..0dfe7f7dcc 100644 --- a/integration-tests/js-compute/test-harness.js +++ b/integration-tests/js-compute/test-harness.js @@ -5,23 +5,32 @@ import { fail } from "./assertions.js"; addEventListener("fetch", event => { event.respondWith(app(event)) }) + +export let FASTLY_SERVICE_VERSION; + /** * @param {FetchEvent} event * @returns {Response} */ async function app(event) { + let res = new Response('Internal Server Error', { status: 500 }); try { const path = (new URL(event.request.url)).pathname console.log(`path: ${path}`) - console.log(`FASTLY_SERVICE_VERSION: ${env('FASTLY_SERVICE_VERSION')}`) + FASTLY_SERVICE_VERSION = env('FASTLY_SERVICE_VERSION') + console.log(`FASTLY_SERVICE_VERSION: ${FASTLY_SERVICE_VERSION}`) if (routes.has(path)) { const routeHandler = routes.get(path) - return await routeHandler() + res = await routeHandler() + } else { + res = fail(`${path} endpoint does not exist`) } - return fail(`${path} endpoint does not exist`) } catch (error) { - return fail(`The routeHandler threw an error: ${error.message}` + '\n' + error.stack) + res = fail(`The routeHandler threw an error: ${error.message}` + '\n' + error.stack) + } finally { + res.headers.set('FASTLY_SERVICE_VERSION', env('FASTLY_SERVICE_VERSION')); } + return res; } export const routes = new Map() diff --git a/runtime/js-compute-runtime/builtins/cache-simple.cpp b/runtime/js-compute-runtime/builtins/cache-simple.cpp new file mode 100644 index 0000000000..c2ac0327c7 --- /dev/null +++ b/runtime/js-compute-runtime/builtins/cache-simple.cpp @@ -0,0 +1,439 @@ +#include "cache-simple.h" +#include "builtin.h" +#include "builtins/native-stream-source.h" +#include "builtins/shared/url.h" +#include "host_interface/host_api.h" +#include "host_interface/host_call.h" +#include "js-compute-builtins.h" +#include "js/ArrayBuffer.h" +#include "js/Result.h" +#include "js/Stream.h" +#include "openssl/evp.h" +#include + +namespace builtins { + +template +bool SimpleCacheEntry::bodyAll(JSContext *cx, unsigned argc, JS::Value *vp) { + METHOD_HEADER(0); + return RequestOrResponse::bodyAll(cx, args, self); +} + +bool SimpleCacheEntry::body_get(JSContext *cx, unsigned argc, JS::Value *vp) { + METHOD_HEADER(0); + if (!JS::GetReservedSlot(self, static_cast(Slots::HasBody)).isBoolean()) { + JS::SetReservedSlot(self, static_cast(Slots::HasBody), JS::BooleanValue(false)); + } + return RequestOrResponse::body_get(cx, args, self, true); +} + +bool SimpleCacheEntry::bodyUsed_get(JSContext *cx, unsigned argc, JS::Value *vp) { + METHOD_HEADER(0); + if (!JS::GetReservedSlot(self, static_cast(Slots::BodyUsed)).isBoolean()) { + JS::SetReservedSlot(self, static_cast(Slots::BodyUsed), JS::BooleanValue(false)); + } + args.rval().setBoolean(RequestOrResponse::body_used(self)); + return true; +} + +const JSFunctionSpec SimpleCacheEntry::static_methods[] = { + JS_FS_END, +}; + +const JSPropertySpec SimpleCacheEntry::static_properties[] = { + JS_PS_END, +}; + +const JSFunctionSpec SimpleCacheEntry::methods[] = { + JS_FN("arrayBuffer", bodyAll, 0, + JSPROP_ENUMERATE), + JS_FN("json", bodyAll, 0, JSPROP_ENUMERATE), + JS_FN("text", bodyAll, 0, JSPROP_ENUMERATE), + JS_FS_END, +}; + +const JSPropertySpec SimpleCacheEntry::properties[] = { + JS_PSG("body", body_get, JSPROP_ENUMERATE), + JS_PSG("bodyUsed", bodyUsed_get, JSPROP_ENUMERATE), + JS_STRING_SYM_PS(toStringTag, "SimpleCacheEntry", JSPROP_READONLY), + JS_PS_END, +}; + +bool SimpleCacheEntry::constructor(JSContext *cx, unsigned argc, JS::Value *vp) { + JS_ReportErrorUTF8(cx, "SimpleCacheEntry can't be instantiated directly"); + return false; +} + +JSObject *SimpleCacheEntry::create(JSContext *cx, fastly_body_handle_t body_handle) { + JS::RootedObject SimpleCacheEntry(cx, JS_NewObjectWithGivenProto(cx, &class_, proto_obj)); + if (!SimpleCacheEntry) + return nullptr; + + JS::SetReservedSlot(SimpleCacheEntry, static_cast(Slots::Body), + JS::Int32Value(body_handle)); + JS::SetReservedSlot(SimpleCacheEntry, static_cast(Slots::BodyStream), JS::NullValue()); + JS::SetReservedSlot(SimpleCacheEntry, static_cast(Slots::HasBody), + JS::BooleanValue(true)); + JS::SetReservedSlot(SimpleCacheEntry, static_cast(Slots::BodyUsed), JS::FalseValue()); + + return SimpleCacheEntry; +} + +bool SimpleCacheEntry::init_class(JSContext *cx, JS::HandleObject global) { + return init_class_impl(cx, global); +} + +namespace { +// We currently support five types of body inputs: +// - byte sequence +// - buffer source +// - USV strings +// - URLSearchParams +// - ReadableStream (Currently only supports Host-backed ReadableStreams) +// After the other other options are checked explicitly, all other inputs are +// encoded to a UTF8 string to be treated as a USV string. +// TODO: Add support for Blob and FormData when we have implemented those classes. +JS::Result> convertBodyInit(JSContext *cx, + JS::HandleValue bodyInit) { + JS::RootedObject bodyObj(cx, bodyInit.isObject() ? &bodyInit.toObject() : nullptr); + mozilla::Maybe maybeNoGC; + JS::UniqueChars buf; + size_t length; + + if (bodyObj && JS_IsArrayBufferViewObject(bodyObj)) { + // `maybeNoGC` needs to be populated for the lifetime of `buf` because + // short typed arrays have inline data which can move on GC, so assert + // that no GC happens. (Which it doesn't, because we're not allocating + // before `buf` goes out of scope.) + maybeNoGC.emplace(cx); + JS::AutoCheckCannotGC &noGC = maybeNoGC.ref(); + bool is_shared; + length = JS_GetArrayBufferViewByteLength(bodyObj); + buf = JS::UniqueChars( + reinterpret_cast(JS_GetArrayBufferViewData(bodyObj, &is_shared, noGC))); + MOZ_ASSERT(!is_shared); + } else if (bodyObj && JS::IsArrayBufferObject(bodyObj)) { + bool is_shared; + uint8_t *bytes; + JS::GetArrayBufferLengthAndData(bodyObj, &length, &is_shared, &bytes); + MOZ_ASSERT(!is_shared); + buf.reset(reinterpret_cast(bytes)); + } else if (bodyObj && builtins::URLSearchParams::is_instance(bodyObj)) { + jsurl::SpecSlice slice = builtins::URLSearchParams::serialize(cx, bodyObj); + buf = JS::UniqueChars(reinterpret_cast(const_cast(slice.data))); + length = slice.len; + } else { + // Convert into a String following https://tc39.es/ecma262/#sec-tostring + buf = encode(cx, bodyInit, &length); + if (!buf) { + return JS::Result>(JS::Error()); + } + } + return JS::Result>(std::make_tuple(std::move(buf), length)); +} + +// Purging/Deleting a cache item within the Compute SDKs via a hostcall is only +// possible via surrogate-keys. We add a surrogate key to all the cache entries, +// which is the sha-256 digest of the cache entries cache-key, converted to +// uppercase hexadecimal. +// Note: We should keep this consistent across the Compute SDKs, this would allow +// a Compute Service to move from one SDK to another, and have consistent purging +// behavior between the Compute Service Versions which were using a different SDK. +JS::Result createSurrogateKeyFromCacheKey(JSContext *cx, + fastly_world_string_t cache_key) { + const EVP_MD *algorithm = EVP_sha256(); + unsigned int size = EVP_MD_size(algorithm); + std::vector md(size); + + if (!EVP_Digest(cache_key.ptr, cache_key.len, md.data(), &size, algorithm, nullptr)) { + return JS::Result(JS::Error()); + } + JS::UniqueChars data{OPENSSL_buf2hexstr(md.data(), size)}; + std::string surrogate_key{data.get(), std::remove(data.get(), data.get() + size, ':')}; + + return JS::Result(surrogate_key); +} + +} // namespace + +// static set(key: string, value: BodyInit, ttl: number): undefined; +// static set(key: string, value: ReadableStream, ttl: number, length: number): undefined; +bool SimpleCache::set(JSContext *cx, unsigned argc, JS::Value *vp) { + REQUEST_HANDLER_ONLY("The SimpleCache builtin"); + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + if (!args.requireAtLeast(cx, "SimpleCache.set", 3)) { + return false; + } + + fastly_world_string_t key; + // Convert key parameter into a string and check the value adheres to our validation rules. + JS::UniqueChars key_chars = encode(cx, args.get(0), &key.len); + if (!key_chars) { + return false; + } + key.ptr = key_chars.get(); + + if (key.len == 0) { + JS_ReportErrorASCII(cx, "SimpleCache.set: key can not be an empty string"); + return false; + } + if (key.len > 8135) { + JS_ReportErrorASCII(cx, + "SimpleCache.set: key is too long, the maximum allowed length is 8135."); + return false; + } + + fastly_cache_write_options_t options; + // Convert ttl (time-to-live) parameter into a number and check the value adheres to our + // validation rules. + JS::HandleValue ttl_val = args.get(2); + std::memset(&options, 0, sizeof(options)); + double ttl; + if (!JS::ToNumber(cx, ttl_val, &ttl)) { + return false; + } + if (ttl < 0 || std::isnan(ttl) || std::isinf(ttl)) { + JS_ReportErrorASCII( + cx, "SimpleCache.set: TTL parameter is an invalid value, only positive numbers can " + "be used for TTL values."); + return false; + } + options.max_age_ns = JS::ToUint64(ttl) * + 1'000'000'000; // turn second representation into nanosecond representation + + JS::HandleValue body_val = args.get(1); + HttpBody source_body; + JS::UniqueChars buf; + JS::RootedObject body_obj(cx, body_val.isObject() ? &body_val.toObject() : nullptr); + // If the body parameter is a Host-backed ReadableStream we optimise our implementation + // by using the ReadableStream's handle directly. + if (body_obj && JS::IsReadableStream(body_obj)) { + if (RequestOrResponse::body_unusable(cx, body_obj)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLE_STREAM_LOCKED_OR_DISTRUBED); + return false; + } + + // If the stream is backed by a C@E body handle, we can use that handle directly. + if (builtins::NativeStreamSource::stream_is_body(cx, body_obj)) { + JS::RootedObject stream_source(cx, + builtins::NativeStreamSource::get_stream_source(cx, body_obj)); + JS::RootedObject source_owner(cx, builtins::NativeStreamSource::owner(stream_source)); + source_body = RequestOrResponse::body_handle(source_owner); + } else { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_SIMPLE_CACHE_SET_CONTENT_STREAM); + return false; + } + + // The cache APIs require the length to be known upfront, we don't know the length of a + // stream upfront, which means the caller will need to supply the information explicitly for us. + if (!args.hasDefined(3)) { + JS_ReportErrorASCII(cx, "SimpleCache.set: length parameter is required when the value " + "parameter is a ReadableStream. The length of the stream needs to be " + "known before inserting into the cache."); + return false; + } + + JS::HandleValue length_val = args.get(3); + double number; + if (!JS::ToNumber(cx, length_val, &number)) { + return false; + } + if (number < 0 || std::isnan(number) || std::isinf(number)) { + JS_ReportErrorASCII( + cx, "SimpleCache.set: length parameter is an invalid value, only positive numbers can " + "be used for length values."); + return false; + } + options.length = JS::ToInteger(number); + } else { + auto result = convertBodyInit(cx, body_val); + if (result.isErr()) { + return false; + } + std::tie(buf, options.length) = result.unwrap(); + } + + // We create a surrogate-key from the cache-key, as this allows the cached contents to be purgable + // from within the JavaScript application + // This is because the cache API currently only supports purging via surrogate-key + auto key_result = createSurrogateKeyFromCacheKey(cx, key); + if (key_result.isErr()) { + return false; + } + auto surrogate_key = key_result.unwrap(); + options.surrogate_keys.ptr = const_cast(surrogate_key.c_str()); + options.surrogate_keys.len = surrogate_key.length(); + + fastly_error_t err; + fastly_body_handle_t body_handle = INVALID_HANDLE; + if (!fastly_cache_insert(&key, &options, &body_handle, &err)) { + HANDLE_ERROR(cx, err); + return false; + } + + auto body = HttpBody(body_handle); + if (!body.valid()) { + return false; + } + // source_body will only be valid when the body parameter is a Host-backed ReadableStream + if (source_body.valid()) { + auto res = body.append(source_body); + if (auto *err = res.to_err()) { + HANDLE_ERROR(cx, *err); + return false; + } + args.rval().setUndefined(); + return true; + } else { + auto write_res = body.write_all(reinterpret_cast(buf.get()), options.length); + if (auto *err = write_res.to_err()) { + HANDLE_ERROR(cx, *err); + return false; + } + } + auto close_res = body.close(); + if (auto *err = close_res.to_err()) { + HANDLE_ERROR(cx, *err); + return false; + } + + args.rval().setUndefined(); + return true; +} + +// static get(key: string): SimpleCacheEntry | null; +bool SimpleCache::get(JSContext *cx, unsigned argc, JS::Value *vp) { + REQUEST_HANDLER_ONLY("The SimpleCache builtin"); + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + if (!args.requireAtLeast(cx, "SimpleCache.get", 1)) { + return false; + } + + fastly_world_string_t key; + // Convert key parameter into a string and check the value adheres to our validation rules. + JS::UniqueChars key_chars = encode(cx, args[0], &key.len); + if (!key_chars) { + return false; + } + key.ptr = key_chars.get(); + + if (key.len == 0) { + JS_ReportErrorASCII(cx, "SimpleCache.get: key can not be an empty string"); + return false; + } + if (key.len > 8135) { + JS_ReportErrorASCII(cx, + "SimpleCache.get: key is too long, the maximum allowed length is 8135."); + return false; + } + + fastly_error_t err; + fastly_cache_lookup_options_t options; + std::memset(&options, 0, sizeof(options)); + + fastly_cache_handle_t handle = INVALID_HANDLE; + if (!fastly_cache_lookup(&key, &options, &handle, &err)) { + HANDLE_ERROR(cx, err); + return false; + } + + fastly_body_handle_t body = INVALID_HANDLE; + fastly_cache_get_body_options_t opts; + if (!fastly_cache_get_body(handle, &opts, &body, &err)) { + HANDLE_ERROR(cx, err); + return false; + } + + if (body == INVALID_HANDLE) { + args.rval().setNull(); + } else { + JS::RootedObject entry(cx, SimpleCacheEntry::create(cx, body)); + if (!entry) { + return false; + } + args.rval().setObject(*entry); + } + + return true; +} + +// static delete(key: string): undefined; +bool SimpleCache::delete_(JSContext *cx, unsigned argc, JS::Value *vp) { + REQUEST_HANDLER_ONLY("The SimpleCache builtin"); + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + if (!args.requireAtLeast(cx, "SimpleCache.delete", 1)) { + return false; + } + + fastly_world_string_t key; + // Convert key parameter into a string and check the value adheres to our validation rules. + JS::UniqueChars key_chars = encode(cx, args.get(0), &key.len); + if (!key_chars) { + return false; + } + key.ptr = key_chars.get(); + + if (key.len == 0) { + JS_ReportErrorASCII(cx, "SimpleCache.delete: key can not be an empty string"); + return false; + } + if (key.len > 1024) { + JS_ReportErrorASCII(cx, + "SimpleCache.delete: key is too long, the maximum allowed length is 1024."); + return false; + } + + // We create a surrogate-key from the cache-key, as this allows the cached contents to be purgable + // from within the JavaScript application + // This is because the cache API currently only supports purging via surrogate-key + auto surrogate_key_result = createSurrogateKeyFromCacheKey(cx, key); + if (surrogate_key_result.isErr()) { + return false; + } + auto surrogate_key = surrogate_key_result.unwrap(); + + fastly_world_string_t skey; + skey.ptr = const_cast(surrogate_key.c_str()); + skey.len = surrogate_key.length(); + + fastly_error_t err; + fastly_world_option_string_t ret; + fastly_purge_options_mask_t purge_options = 0; + if (!fastly_purge_surrogate_key(&skey, purge_options, &ret, &err)) { + HANDLE_ERROR(cx, err); + return false; + } + MOZ_ASSERT(!ret.is_some); + + args.rval().setUndefined(); + return true; +} + +const JSFunctionSpec SimpleCache::static_methods[] = { + JS_FN("delete", delete_, 1, JSPROP_ENUMERATE), + JS_FN("get", get, 1, JSPROP_ENUMERATE), + JS_FN("set", set, 3, JSPROP_ENUMERATE), + JS_FS_END, +}; + +const JSPropertySpec SimpleCache::static_properties[] = { + JS_PS_END, +}; + +const JSFunctionSpec SimpleCache::methods[] = {JS_FS_END}; + +const JSPropertySpec SimpleCache::properties[] = { + JS_STRING_SYM_PS(toStringTag, "SimpleCache", JSPROP_READONLY), JS_PS_END}; + +bool SimpleCache::constructor(JSContext *cx, unsigned argc, JS::Value *vp) { + JS_ReportErrorNumberASCII(cx, GetErrorMessageBuiltin, nullptr, JSMSG_ILLEGAL_CTOR); + return false; +} + +bool SimpleCache::init_class(JSContext *cx, JS::HandleObject global) { + return BuiltinImpl::init_class_impl(cx, global); +} + +} // namespace builtins diff --git a/runtime/js-compute-runtime/builtins/cache-simple.h b/runtime/js-compute-runtime/builtins/cache-simple.h new file mode 100644 index 0000000000..44ecb84522 --- /dev/null +++ b/runtime/js-compute-runtime/builtins/cache-simple.h @@ -0,0 +1,55 @@ +#ifndef JS_COMPUTE_RUNTIME_CACHE_SIMPLE_H +#define JS_COMPUTE_RUNTIME_CACHE_SIMPLE_H + +#include "builtin.h" +#include "js-compute-builtins.h" +#include "request-response.h" + +namespace builtins { + +class SimpleCacheEntry final : public BuiltinImpl { + template + static bool bodyAll(JSContext *cx, unsigned argc, JS::Value *vp); + static bool body_get(JSContext *cx, unsigned argc, JS::Value *vp); + static bool bodyUsed_get(JSContext *cx, unsigned argc, JS::Value *vp); + +public: + static constexpr const char *class_name = "SimpleCacheEntry"; + + using Slots = RequestOrResponse::Slots; + static const JSFunctionSpec static_methods[]; + static const JSPropertySpec static_properties[]; + static const JSFunctionSpec methods[]; + static const JSPropertySpec properties[]; + + static const unsigned ctor_length = 0; + + static bool init_class(JSContext *cx, JS::HandleObject global); + static bool constructor(JSContext *cx, unsigned argc, JS::Value *vp); + static JSObject *create(JSContext *cx, fastly_body_handle_t body_handle); +}; + +class SimpleCache : public BuiltinImpl { +private: +public: + static constexpr const char *class_name = "SimpleCache"; + static const int ctor_length = 0; + enum Slots { Count }; + + static const JSFunctionSpec static_methods[]; + static const JSPropertySpec static_properties[]; + static const JSFunctionSpec methods[]; + static const JSPropertySpec properties[]; + + static bool delete_(JSContext *cx, unsigned argc, JS::Value *vp); + static bool get(JSContext *cx, unsigned argc, JS::Value *vp); + static bool set(JSContext *cx, unsigned argc, JS::Value *vp); + + static bool constructor(JSContext *cx, unsigned argc, JS::Value *vp); + + static bool init_class(JSContext *cx, JS::HandleObject global); +}; + +} // namespace builtins + +#endif diff --git a/runtime/js-compute-runtime/error-numbers.msg b/runtime/js-compute-runtime/error-numbers.msg index 06113d5966..13633ab2d4 100644 --- a/runtime/js-compute-runtime/error-numbers.msg +++ b/runtime/js-compute-runtime/error-numbers.msg @@ -112,4 +112,5 @@ MSG_DEF(JSMSG_TEXT_DECODER_DECODING_FAILED, 0, JSEXN_TYPEERR, MSG_DEF(JSMSG_TEXT_DECODER_OPTIONS_NOT_DICTIONARY, 0, JSEXN_TYPEERR, "TextDecoder constructor: options argument can't be converted to a dictionary.") MSG_DEF(JSMSG_TEXT_DECODER_DECODE_OPTIONS_NOT_DICTIONARY, 0, JSEXN_TYPEERR, "TextDecoder.decode: options argument can't be converted to a dictionary.") MSG_DEF(JSMSG_TEXT_ENCODER_ENCODEINTO_INVALID_ARRAY, 0, JSEXN_TYPEERR, "TextEncoder.encodeInto: Argument 2 does not implement interface Uint8Array.") +MSG_DEF(JSMSG_SIMPLE_CACHE_SET_CONTENT_STREAM, 0, JSEXN_TYPEERR, "Content-provided streams are not yet supported for streaming into SimpleCache") //clang-format on \ No newline at end of file diff --git a/runtime/js-compute-runtime/fastly-world/fastly_world.c b/runtime/js-compute-runtime/fastly-world/fastly_world.c index aab9c6a7e2..2c803dcfa1 100644 --- a/runtime/js-compute-runtime/fastly-world/fastly_world.c +++ b/runtime/js-compute-runtime/fastly-world/fastly_world.c @@ -212,10 +212,10 @@ typedef struct { typedef struct { bool is_err; union { - fastly_purge_result_t ok; + fastly_cache_handle_t ok; fastly_error_t err; } val; -} fastly_world_result_purge_result_error_t; +} fastly_world_result_cache_handle_error_t; typedef struct { bool is_err; @@ -223,229 +223,238 @@ typedef struct { } val; } fastly_world_result_void_void_t; -__attribute__((__import_module__("fastly"), __import_name__("abi-init"))) +__attribute__((import_module("fastly"), import_name("abi-init"))) void __wasm_import_fastly_abi_init(int64_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("uap-parse"))) +__attribute__((import_module("fastly"), import_name("uap-parse"))) void __wasm_import_fastly_uap_parse(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-body-new"))) +__attribute__((import_module("fastly"), import_name("http-body-new"))) void __wasm_import_fastly_http_body_new(int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-body-append"))) +__attribute__((import_module("fastly"), import_name("http-body-append"))) void __wasm_import_fastly_http_body_append(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-body-read"))) +__attribute__((import_module("fastly"), import_name("http-body-read"))) void __wasm_import_fastly_http_body_read(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-body-write"))) +__attribute__((import_module("fastly"), import_name("http-body-write"))) void __wasm_import_fastly_http_body_write(int32_t, int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-body-close"))) +__attribute__((import_module("fastly"), import_name("http-body-close"))) void __wasm_import_fastly_http_body_close(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("log-endpoint-get"))) +__attribute__((import_module("fastly"), import_name("log-endpoint-get"))) void __wasm_import_fastly_log_endpoint_get(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("log-write"))) +__attribute__((import_module("fastly"), import_name("log-write"))) void __wasm_import_fastly_log_write(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-cache-override-set"))) +__attribute__((import_module("fastly"), import_name("http-req-cache-override-set"))) void __wasm_import_fastly_http_req_cache_override_set(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-downstream-client-ip-addr"))) +__attribute__((import_module("fastly"), import_name("http-req-downstream-client-ip-addr"))) void __wasm_import_fastly_http_req_downstream_client_ip_addr(int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-downstream-client-h2-fingerprint"))) +__attribute__((import_module("fastly"), import_name("http-req-downstream-client-h2-fingerprint"))) void __wasm_import_fastly_http_req_downstream_client_h2_fingerprint(int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-downstream-tls-cipher-openssl-name"))) +__attribute__((import_module("fastly"), import_name("http-req-downstream-tls-cipher-openssl-name"))) void __wasm_import_fastly_http_req_downstream_tls_cipher_openssl_name(int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-downstream-tls-protocol"))) +__attribute__((import_module("fastly"), import_name("http-req-downstream-tls-protocol"))) void __wasm_import_fastly_http_req_downstream_tls_protocol(int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-downstream-tls-client-hello"))) +__attribute__((import_module("fastly"), import_name("http-req-downstream-tls-client-hello"))) void __wasm_import_fastly_http_req_downstream_tls_client_hello(int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-downstream-tls-client-certificate"))) +__attribute__((import_module("fastly"), import_name("http-req-downstream-tls-client-certificate"))) void __wasm_import_fastly_http_req_downstream_tls_client_certificate(int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-downstream-tls-client-cert-verify-result"))) +__attribute__((import_module("fastly"), import_name("http-req-downstream-tls-client-cert-verify-result"))) void __wasm_import_fastly_http_req_downstream_tls_client_cert_verify_result(int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-downstream-tls-ja3-md5"))) +__attribute__((import_module("fastly"), import_name("http-req-downstream-tls-ja3-md5"))) void __wasm_import_fastly_http_req_downstream_tls_ja3_md5(int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-new"))) +__attribute__((import_module("fastly"), import_name("http-req-new"))) void __wasm_import_fastly_http_req_new(int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-header-names-get"))) +__attribute__((import_module("fastly"), import_name("http-req-header-names-get"))) void __wasm_import_fastly_http_req_header_names_get(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-header-value-get"))) +__attribute__((import_module("fastly"), import_name("http-req-header-value-get"))) void __wasm_import_fastly_http_req_header_value_get(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-header-values-get"))) +__attribute__((import_module("fastly"), import_name("http-req-header-values-get"))) void __wasm_import_fastly_http_req_header_values_get(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-header-values-set"))) +__attribute__((import_module("fastly"), import_name("http-req-header-values-set"))) void __wasm_import_fastly_http_req_header_values_set(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-header-insert"))) +__attribute__((import_module("fastly"), import_name("http-req-header-insert"))) void __wasm_import_fastly_http_req_header_insert(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-header-append"))) +__attribute__((import_module("fastly"), import_name("http-req-header-append"))) void __wasm_import_fastly_http_req_header_append(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-header-remove"))) +__attribute__((import_module("fastly"), import_name("http-req-header-remove"))) void __wasm_import_fastly_http_req_header_remove(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-method-get"))) +__attribute__((import_module("fastly"), import_name("http-req-method-get"))) void __wasm_import_fastly_http_req_method_get(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-method-set"))) +__attribute__((import_module("fastly"), import_name("http-req-method-set"))) void __wasm_import_fastly_http_req_method_set(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-uri-get"))) +__attribute__((import_module("fastly"), import_name("http-req-uri-get"))) void __wasm_import_fastly_http_req_uri_get(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-uri-set"))) +__attribute__((import_module("fastly"), import_name("http-req-uri-set"))) void __wasm_import_fastly_http_req_uri_set(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-version-get"))) +__attribute__((import_module("fastly"), import_name("http-req-version-get"))) void __wasm_import_fastly_http_req_version_get(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-version-set"))) +__attribute__((import_module("fastly"), import_name("http-req-version-set"))) void __wasm_import_fastly_http_req_version_set(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-send"))) +__attribute__((import_module("fastly"), import_name("http-req-send"))) void __wasm_import_fastly_http_req_send(int32_t, int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-send-async"))) +__attribute__((import_module("fastly"), import_name("http-req-send-async"))) void __wasm_import_fastly_http_req_send_async(int32_t, int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-send-async-streaming"))) +__attribute__((import_module("fastly"), import_name("http-req-send-async-streaming"))) void __wasm_import_fastly_http_req_send_async_streaming(int32_t, int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-pending-req-poll"))) +__attribute__((import_module("fastly"), import_name("http-req-pending-req-poll"))) void __wasm_import_fastly_http_req_pending_req_poll(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-pending-req-wait"))) +__attribute__((import_module("fastly"), import_name("http-req-pending-req-wait"))) void __wasm_import_fastly_http_req_pending_req_wait(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-pending-req-select"))) +__attribute__((import_module("fastly"), import_name("http-req-pending-req-select"))) void __wasm_import_fastly_http_req_pending_req_select(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-key-is-valid"))) +__attribute__((import_module("fastly"), import_name("http-req-key-is-valid"))) void __wasm_import_fastly_http_req_key_is_valid(int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-close"))) +__attribute__((import_module("fastly"), import_name("http-req-close"))) void __wasm_import_fastly_http_req_close(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-auto-decompress-response-set"))) +__attribute__((import_module("fastly"), import_name("http-req-auto-decompress-response-set"))) void __wasm_import_fastly_http_req_auto_decompress_response_set(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-upgrade-websocket"))) +__attribute__((import_module("fastly"), import_name("http-req-upgrade-websocket"))) void __wasm_import_fastly_http_req_upgrade_websocket(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-redirect-to-websocket-proxy"))) +__attribute__((import_module("fastly"), import_name("http-req-redirect-to-websocket-proxy"))) void __wasm_import_fastly_http_req_redirect_to_websocket_proxy(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-redirect-to-grip-proxy"))) +__attribute__((import_module("fastly"), import_name("http-req-redirect-to-grip-proxy"))) void __wasm_import_fastly_http_req_redirect_to_grip_proxy(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-framing-headers-mode-set"))) +__attribute__((import_module("fastly"), import_name("http-req-framing-headers-mode-set"))) void __wasm_import_fastly_http_req_framing_headers_mode_set(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-req-register-dynamic-backend"))) +__attribute__((import_module("fastly"), import_name("http-req-register-dynamic-backend"))) void __wasm_import_fastly_http_req_register_dynamic_backend(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-new"))) +__attribute__((import_module("fastly"), import_name("http-resp-new"))) void __wasm_import_fastly_http_resp_new(int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-header-names-get"))) +__attribute__((import_module("fastly"), import_name("http-resp-header-names-get"))) void __wasm_import_fastly_http_resp_header_names_get(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-header-value-get"))) +__attribute__((import_module("fastly"), import_name("http-resp-header-value-get"))) void __wasm_import_fastly_http_resp_header_value_get(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-header-values-get"))) +__attribute__((import_module("fastly"), import_name("http-resp-header-values-get"))) void __wasm_import_fastly_http_resp_header_values_get(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-header-values-set"))) +__attribute__((import_module("fastly"), import_name("http-resp-header-values-set"))) void __wasm_import_fastly_http_resp_header_values_set(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-header-insert"))) +__attribute__((import_module("fastly"), import_name("http-resp-header-insert"))) void __wasm_import_fastly_http_resp_header_insert(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-header-append"))) +__attribute__((import_module("fastly"), import_name("http-resp-header-append"))) void __wasm_import_fastly_http_resp_header_append(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-header-remove"))) +__attribute__((import_module("fastly"), import_name("http-resp-header-remove"))) void __wasm_import_fastly_http_resp_header_remove(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-version-get"))) +__attribute__((import_module("fastly"), import_name("http-resp-version-get"))) void __wasm_import_fastly_http_resp_version_get(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-version-set"))) +__attribute__((import_module("fastly"), import_name("http-resp-version-set"))) void __wasm_import_fastly_http_resp_version_set(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-send-downstream"))) +__attribute__((import_module("fastly"), import_name("http-resp-send-downstream"))) void __wasm_import_fastly_http_resp_send_downstream(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-status-get"))) +__attribute__((import_module("fastly"), import_name("http-resp-status-get"))) void __wasm_import_fastly_http_resp_status_get(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-status-set"))) +__attribute__((import_module("fastly"), import_name("http-resp-status-set"))) void __wasm_import_fastly_http_resp_status_set(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-close"))) +__attribute__((import_module("fastly"), import_name("http-resp-close"))) void __wasm_import_fastly_http_resp_close(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("http-resp-framing-headers-mode-set"))) +__attribute__((import_module("fastly"), import_name("http-resp-framing-headers-mode-set"))) void __wasm_import_fastly_http_resp_framing_headers_mode_set(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("dictionary-open"))) +__attribute__((import_module("fastly"), import_name("dictionary-open"))) void __wasm_import_fastly_dictionary_open(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("dictionary-get"))) +__attribute__((import_module("fastly"), import_name("dictionary-get"))) void __wasm_import_fastly_dictionary_get(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("geo-lookup"))) +__attribute__((import_module("fastly"), import_name("geo-lookup"))) void __wasm_import_fastly_geo_lookup(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("object-store-open"))) +__attribute__((import_module("fastly"), import_name("object-store-open"))) void __wasm_import_fastly_object_store_open(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("object-store-lookup"))) +__attribute__((import_module("fastly"), import_name("object-store-lookup"))) void __wasm_import_fastly_object_store_lookup(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("object-store-lookup-as-fd"))) +__attribute__((import_module("fastly"), import_name("object-store-lookup-as-fd"))) void __wasm_import_fastly_object_store_lookup_as_fd(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("object-store-insert"))) +__attribute__((import_module("fastly"), import_name("object-store-insert"))) void __wasm_import_fastly_object_store_insert(int32_t, int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("secret-store-open"))) +__attribute__((import_module("fastly"), import_name("secret-store-open"))) void __wasm_import_fastly_secret_store_open(int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("secret-store-get"))) +__attribute__((import_module("fastly"), import_name("secret-store-get"))) void __wasm_import_fastly_secret_store_get(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("secret-store-plaintext"))) +__attribute__((import_module("fastly"), import_name("secret-store-plaintext"))) void __wasm_import_fastly_secret_store_plaintext(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("async-io-select"))) +__attribute__((import_module("fastly"), import_name("async-io-select"))) void __wasm_import_fastly_async_io_select(int32_t, int32_t, int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("async-io-is-ready"))) +__attribute__((import_module("fastly"), import_name("async-io-is-ready"))) void __wasm_import_fastly_async_io_is_ready(int32_t, int32_t); -__attribute__((__import_module__("fastly"), __import_name__("purge-surrogate-key"))) +__attribute__((import_module("fastly"), import_name("purge-surrogate-key"))) void __wasm_import_fastly_purge_surrogate_key(int32_t, int32_t, int32_t, int32_t); -__attribute__((__weak__, __export_name__("cabi_realloc"))) +__attribute__((import_module("fastly"), import_name("cache-lookup"))) +void __wasm_import_fastly_cache_lookup(int32_t, int32_t, int32_t, int32_t, int32_t); + +__attribute__((import_module("fastly"), import_name("cache-insert"))) +void __wasm_import_fastly_cache_insert(int32_t, int32_t, int64_t, int32_t, int32_t, int32_t, int64_t, int64_t, int32_t, int32_t, int64_t, int32_t, int32_t, int32_t, int32_t); + +__attribute__((import_module("fastly"), import_name("cache-get-body"))) +void __wasm_import_fastly_cache_get_body(int32_t, int64_t, int64_t, int32_t); + +__attribute__((weak, export_name("cabi_realloc"))) void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { if (new_size == 0) return (void*) align; void *ret = realloc(ptr, new_size); @@ -456,7 +465,7 @@ void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { // Component Adapters bool fastly_abi_init(uint64_t abi_version, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_abi_init((int64_t) (abi_version), ptr); @@ -481,7 +490,7 @@ bool fastly_abi_init(uint64_t abi_version, fastly_error_t *err) { } bool fastly_uap_parse(fastly_world_string_t *user_agent, fastly_user_agent_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[36]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_uap_parse((int32_t) (*user_agent).ptr, (int32_t) (*user_agent).len, ptr); @@ -513,7 +522,7 @@ bool fastly_uap_parse(fastly_world_string_t *user_agent, fastly_user_agent_t *re } bool fastly_http_body_new(fastly_body_handle_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[8]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_body_new(ptr); @@ -540,7 +549,7 @@ bool fastly_http_body_new(fastly_body_handle_t *ret, fastly_error_t *err) { } bool fastly_http_body_append(fastly_body_handle_t dest, fastly_body_handle_t src, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_body_append((int32_t) (dest), (int32_t) (src), ptr); @@ -565,7 +574,7 @@ bool fastly_http_body_append(fastly_body_handle_t dest, fastly_body_handle_t src } bool fastly_http_body_read(fastly_body_handle_t h, uint32_t chunk_size, fastly_world_list_u8_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_body_read((int32_t) (h), (int32_t) (chunk_size), ptr); @@ -592,7 +601,7 @@ bool fastly_http_body_read(fastly_body_handle_t h, uint32_t chunk_size, fastly_w } bool fastly_http_body_write(fastly_body_handle_t h, fastly_world_list_u8_t *buf, fastly_body_write_end_t end, uint32_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[8]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_body_write((int32_t) (h), (int32_t) (*buf).ptr, (int32_t) (*buf).len, (int32_t) end, ptr); @@ -619,7 +628,7 @@ bool fastly_http_body_write(fastly_body_handle_t h, fastly_world_list_u8_t *buf, } bool fastly_http_body_close(fastly_body_handle_t h, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_body_close((int32_t) (h), ptr); @@ -644,7 +653,7 @@ bool fastly_http_body_close(fastly_body_handle_t h, fastly_error_t *err) { } bool fastly_log_endpoint_get(fastly_world_string_t *name, fastly_log_endpoint_handle_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[8]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_log_endpoint_get((int32_t) (*name).ptr, (int32_t) (*name).len, ptr); @@ -671,7 +680,7 @@ bool fastly_log_endpoint_get(fastly_world_string_t *name, fastly_log_endpoint_ha } bool fastly_log_write(fastly_log_endpoint_handle_t h, fastly_world_string_t *msg, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_log_write((int32_t) (h), (int32_t) (*msg).ptr, (int32_t) (*msg).len, ptr); @@ -696,7 +705,7 @@ bool fastly_log_write(fastly_log_endpoint_handle_t h, fastly_world_string_t *msg } bool fastly_http_req_cache_override_set(fastly_request_handle_t h, fastly_http_cache_override_tag_t tag, uint32_t *maybe_ttl, uint32_t *maybe_stale_while_revalidate, fastly_world_string_t *maybe_sk, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; fastly_world_option_u32_t ttl; ttl.is_some = maybe_ttl != NULL;if (maybe_ttl) { @@ -766,7 +775,7 @@ bool fastly_http_req_cache_override_set(fastly_request_handle_t h, fastly_http_c } bool fastly_http_req_downstream_client_ip_addr(fastly_world_list_u8_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_downstream_client_ip_addr(ptr); @@ -793,7 +802,7 @@ bool fastly_http_req_downstream_client_ip_addr(fastly_world_list_u8_t *ret, fast } bool fastly_http_req_downstream_client_h2_fingerprint(fastly_world_list_u8_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_downstream_client_h2_fingerprint(ptr); @@ -820,7 +829,7 @@ bool fastly_http_req_downstream_client_h2_fingerprint(fastly_world_list_u8_t *re } bool fastly_http_req_downstream_tls_cipher_openssl_name(fastly_world_string_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_downstream_tls_cipher_openssl_name(ptr); @@ -847,7 +856,7 @@ bool fastly_http_req_downstream_tls_cipher_openssl_name(fastly_world_string_t *r } bool fastly_http_req_downstream_tls_protocol(fastly_world_string_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_downstream_tls_protocol(ptr); @@ -874,7 +883,7 @@ bool fastly_http_req_downstream_tls_protocol(fastly_world_string_t *ret, fastly_ } bool fastly_http_req_downstream_tls_client_hello(fastly_world_list_u8_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_downstream_tls_client_hello(ptr); @@ -901,7 +910,7 @@ bool fastly_http_req_downstream_tls_client_hello(fastly_world_list_u8_t *ret, fa } bool fastly_http_req_downstream_tls_client_certificate(fastly_world_list_u8_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_downstream_tls_client_certificate(ptr); @@ -928,7 +937,7 @@ bool fastly_http_req_downstream_tls_client_certificate(fastly_world_list_u8_t *r } bool fastly_http_req_downstream_tls_client_cert_verify_result(fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_downstream_tls_client_cert_verify_result(ptr); @@ -953,7 +962,7 @@ bool fastly_http_req_downstream_tls_client_cert_verify_result(fastly_error_t *er } bool fastly_http_req_downstream_tls_ja3_md5(fastly_world_list_u8_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_downstream_tls_ja3_md5(ptr); @@ -980,7 +989,7 @@ bool fastly_http_req_downstream_tls_ja3_md5(fastly_world_list_u8_t *ret, fastly_ } bool fastly_http_req_new(fastly_request_handle_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[8]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_new(ptr); @@ -1007,7 +1016,7 @@ bool fastly_http_req_new(fastly_request_handle_t *ret, fastly_error_t *err) { } bool fastly_http_req_header_names_get(fastly_request_handle_t h, fastly_world_list_string_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_header_names_get((int32_t) (h), ptr); @@ -1034,7 +1043,7 @@ bool fastly_http_req_header_names_get(fastly_request_handle_t h, fastly_world_li } bool fastly_http_req_header_value_get(fastly_request_handle_t h, fastly_world_string_t *name, fastly_world_option_string_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[16]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_header_value_get((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, ptr); @@ -1074,7 +1083,7 @@ bool fastly_http_req_header_value_get(fastly_request_handle_t h, fastly_world_st } bool fastly_http_req_header_values_get(fastly_request_handle_t h, fastly_world_string_t *name, fastly_world_option_list_string_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[16]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_header_values_get((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, ptr); @@ -1114,7 +1123,7 @@ bool fastly_http_req_header_values_get(fastly_request_handle_t h, fastly_world_s } bool fastly_http_req_header_values_set(fastly_request_handle_t h, fastly_world_string_t *name, fastly_world_list_string_t *values, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_header_values_set((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, (int32_t) (*values).ptr, (int32_t) (*values).len, ptr); @@ -1139,7 +1148,7 @@ bool fastly_http_req_header_values_set(fastly_request_handle_t h, fastly_world_s } bool fastly_http_req_header_insert(fastly_request_handle_t h, fastly_world_string_t *name, fastly_world_string_t *value, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_header_insert((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, (int32_t) (*value).ptr, (int32_t) (*value).len, ptr); @@ -1164,7 +1173,7 @@ bool fastly_http_req_header_insert(fastly_request_handle_t h, fastly_world_strin } bool fastly_http_req_header_append(fastly_request_handle_t h, fastly_world_string_t *name, fastly_world_string_t *value, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_header_append((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, (int32_t) (*value).ptr, (int32_t) (*value).len, ptr); @@ -1189,7 +1198,7 @@ bool fastly_http_req_header_append(fastly_request_handle_t h, fastly_world_strin } bool fastly_http_req_header_remove(fastly_request_handle_t h, fastly_world_string_t *name, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_header_remove((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, ptr); @@ -1214,7 +1223,7 @@ bool fastly_http_req_header_remove(fastly_request_handle_t h, fastly_world_strin } bool fastly_http_req_method_get(fastly_request_handle_t h, fastly_world_string_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_method_get((int32_t) (h), ptr); @@ -1241,7 +1250,7 @@ bool fastly_http_req_method_get(fastly_request_handle_t h, fastly_world_string_t } bool fastly_http_req_method_set(fastly_request_handle_t h, fastly_world_string_t *method, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_method_set((int32_t) (h), (int32_t) (*method).ptr, (int32_t) (*method).len, ptr); @@ -1266,7 +1275,7 @@ bool fastly_http_req_method_set(fastly_request_handle_t h, fastly_world_string_t } bool fastly_http_req_uri_get(fastly_request_handle_t h, fastly_world_string_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_uri_get((int32_t) (h), ptr); @@ -1293,7 +1302,7 @@ bool fastly_http_req_uri_get(fastly_request_handle_t h, fastly_world_string_t *r } bool fastly_http_req_uri_set(fastly_request_handle_t h, fastly_world_string_t *uri, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_uri_set((int32_t) (h), (int32_t) (*uri).ptr, (int32_t) (*uri).len, ptr); @@ -1318,7 +1327,7 @@ bool fastly_http_req_uri_set(fastly_request_handle_t h, fastly_world_string_t *u } bool fastly_http_req_version_get(fastly_request_handle_t h, fastly_http_version_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_version_get((int32_t) (h), ptr); @@ -1345,7 +1354,7 @@ bool fastly_http_req_version_get(fastly_request_handle_t h, fastly_http_version_ } bool fastly_http_req_version_set(fastly_request_handle_t h, fastly_http_version_t version, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_version_set((int32_t) (h), (int32_t) version, ptr); @@ -1370,7 +1379,7 @@ bool fastly_http_req_version_set(fastly_request_handle_t h, fastly_http_version_ } bool fastly_http_req_send(fastly_request_handle_t h, fastly_body_handle_t b, fastly_world_string_t *backend, fastly_response_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_send((int32_t) (h), (int32_t) (b), (int32_t) (*backend).ptr, (int32_t) (*backend).len, ptr); @@ -1400,7 +1409,7 @@ bool fastly_http_req_send(fastly_request_handle_t h, fastly_body_handle_t b, fas } bool fastly_http_req_send_async(fastly_request_handle_t h, fastly_body_handle_t b, fastly_world_string_t *backend, fastly_pending_request_handle_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[8]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_send_async((int32_t) (h), (int32_t) (b), (int32_t) (*backend).ptr, (int32_t) (*backend).len, ptr); @@ -1427,7 +1436,7 @@ bool fastly_http_req_send_async(fastly_request_handle_t h, fastly_body_handle_t } bool fastly_http_req_send_async_streaming(fastly_request_handle_t h, fastly_body_handle_t b, fastly_world_string_t *backend, fastly_pending_request_handle_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[8]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_send_async_streaming((int32_t) (h), (int32_t) (b), (int32_t) (*backend).ptr, (int32_t) (*backend).len, ptr); @@ -1454,7 +1463,7 @@ bool fastly_http_req_send_async_streaming(fastly_request_handle_t h, fastly_body } bool fastly_http_req_pending_req_poll(fastly_pending_request_handle_t h, fastly_world_option_response_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[16]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_pending_req_poll((int32_t) (h), ptr); @@ -1497,7 +1506,7 @@ bool fastly_http_req_pending_req_poll(fastly_pending_request_handle_t h, fastly_ } bool fastly_http_req_pending_req_wait(fastly_pending_request_handle_t h, fastly_response_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_pending_req_wait((int32_t) (h), ptr); @@ -1527,7 +1536,7 @@ bool fastly_http_req_pending_req_wait(fastly_pending_request_handle_t h, fastly_ } bool fastly_http_req_pending_req_select(fastly_world_list_pending_request_handle_t *h, fastly_world_tuple2_u32_response_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[16]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_pending_req_select((int32_t) (*h).ptr, (int32_t) (*h).len, ptr); @@ -1560,7 +1569,7 @@ bool fastly_http_req_pending_req_select(fastly_world_list_pending_request_handle } bool fastly_http_req_key_is_valid(bool *ret, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_key_is_valid(ptr); @@ -1587,7 +1596,7 @@ bool fastly_http_req_key_is_valid(bool *ret, fastly_error_t *err) { } bool fastly_http_req_close(fastly_request_handle_t h, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_close((int32_t) (h), ptr); @@ -1612,7 +1621,7 @@ bool fastly_http_req_close(fastly_request_handle_t h, fastly_error_t *err) { } bool fastly_http_req_auto_decompress_response_set(fastly_request_handle_t h, fastly_content_encodings_t encodings, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_auto_decompress_response_set((int32_t) (h), encodings, ptr); @@ -1637,7 +1646,7 @@ bool fastly_http_req_auto_decompress_response_set(fastly_request_handle_t h, fas } bool fastly_http_req_upgrade_websocket(fastly_world_string_t *backend, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_upgrade_websocket((int32_t) (*backend).ptr, (int32_t) (*backend).len, ptr); @@ -1662,7 +1671,7 @@ bool fastly_http_req_upgrade_websocket(fastly_world_string_t *backend, fastly_er } bool fastly_http_req_redirect_to_websocket_proxy(fastly_world_string_t *backend, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_redirect_to_websocket_proxy((int32_t) (*backend).ptr, (int32_t) (*backend).len, ptr); @@ -1687,7 +1696,7 @@ bool fastly_http_req_redirect_to_websocket_proxy(fastly_world_string_t *backend, } bool fastly_http_req_redirect_to_grip_proxy(fastly_world_string_t *backend, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_redirect_to_grip_proxy((int32_t) (*backend).ptr, (int32_t) (*backend).len, ptr); @@ -1712,7 +1721,7 @@ bool fastly_http_req_redirect_to_grip_proxy(fastly_world_string_t *backend, fast } bool fastly_http_req_framing_headers_mode_set(fastly_request_handle_t h, fastly_framing_headers_mode_t mode, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_req_framing_headers_mode_set((int32_t) (h), (int32_t) mode, ptr); @@ -1737,7 +1746,7 @@ bool fastly_http_req_framing_headers_mode_set(fastly_request_handle_t h, fastly_ } bool fastly_http_req_register_dynamic_backend(fastly_world_string_t *prefix, fastly_world_string_t *target, fastly_dynamic_backend_config_t *config, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[108]; int32_t ptr = (int32_t) &ret_area; *((int32_t*)(ptr + 4)) = (int32_t) (*prefix).len; @@ -1849,7 +1858,7 @@ bool fastly_http_req_register_dynamic_backend(fastly_world_string_t *prefix, fas } bool fastly_http_resp_new(fastly_response_handle_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[8]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_new(ptr); @@ -1876,7 +1885,7 @@ bool fastly_http_resp_new(fastly_response_handle_t *ret, fastly_error_t *err) { } bool fastly_http_resp_header_names_get(fastly_response_handle_t h, fastly_world_list_string_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_header_names_get((int32_t) (h), ptr); @@ -1903,7 +1912,7 @@ bool fastly_http_resp_header_names_get(fastly_response_handle_t h, fastly_world_ } bool fastly_http_resp_header_value_get(fastly_response_handle_t h, fastly_world_string_t *name, fastly_world_option_string_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[16]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_header_value_get((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, ptr); @@ -1943,7 +1952,7 @@ bool fastly_http_resp_header_value_get(fastly_response_handle_t h, fastly_world_ } bool fastly_http_resp_header_values_get(fastly_response_handle_t h, fastly_world_string_t *name, fastly_world_option_list_string_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[16]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_header_values_get((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, ptr); @@ -1983,7 +1992,7 @@ bool fastly_http_resp_header_values_get(fastly_response_handle_t h, fastly_world } bool fastly_http_resp_header_values_set(fastly_response_handle_t h, fastly_world_string_t *name, fastly_world_list_string_t *values, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_header_values_set((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, (int32_t) (*values).ptr, (int32_t) (*values).len, ptr); @@ -2008,7 +2017,7 @@ bool fastly_http_resp_header_values_set(fastly_response_handle_t h, fastly_world } bool fastly_http_resp_header_insert(fastly_response_handle_t h, fastly_world_string_t *name, fastly_world_string_t *value, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_header_insert((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, (int32_t) (*value).ptr, (int32_t) (*value).len, ptr); @@ -2033,7 +2042,7 @@ bool fastly_http_resp_header_insert(fastly_response_handle_t h, fastly_world_str } bool fastly_http_resp_header_append(fastly_response_handle_t h, fastly_world_string_t *name, fastly_world_string_t *value, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_header_append((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, (int32_t) (*value).ptr, (int32_t) (*value).len, ptr); @@ -2058,7 +2067,7 @@ bool fastly_http_resp_header_append(fastly_response_handle_t h, fastly_world_str } bool fastly_http_resp_header_remove(fastly_response_handle_t h, fastly_world_string_t *name, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_header_remove((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, ptr); @@ -2083,7 +2092,7 @@ bool fastly_http_resp_header_remove(fastly_response_handle_t h, fastly_world_str } bool fastly_http_resp_version_get(fastly_response_handle_t h, fastly_http_version_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_version_get((int32_t) (h), ptr); @@ -2110,7 +2119,7 @@ bool fastly_http_resp_version_get(fastly_response_handle_t h, fastly_http_versio } bool fastly_http_resp_version_set(fastly_response_handle_t h, fastly_http_version_t version, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_version_set((int32_t) (h), (int32_t) version, ptr); @@ -2135,7 +2144,7 @@ bool fastly_http_resp_version_set(fastly_response_handle_t h, fastly_http_versio } bool fastly_http_resp_send_downstream(fastly_response_handle_t h, fastly_body_handle_t b, bool streaming, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_send_downstream((int32_t) (h), (int32_t) (b), streaming, ptr); @@ -2160,7 +2169,7 @@ bool fastly_http_resp_send_downstream(fastly_response_handle_t h, fastly_body_ha } bool fastly_http_resp_status_get(fastly_response_handle_t h, fastly_http_status_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(2))) + __attribute__((aligned(2))) uint8_t ret_area[4]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_status_get((int32_t) (h), ptr); @@ -2187,7 +2196,7 @@ bool fastly_http_resp_status_get(fastly_response_handle_t h, fastly_http_status_ } bool fastly_http_resp_status_set(fastly_response_handle_t h, fastly_http_status_t status, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_status_set((int32_t) (h), (int32_t) (status), ptr); @@ -2212,7 +2221,7 @@ bool fastly_http_resp_status_set(fastly_response_handle_t h, fastly_http_status_ } bool fastly_http_resp_close(fastly_response_handle_t h, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_close((int32_t) (h), ptr); @@ -2237,7 +2246,7 @@ bool fastly_http_resp_close(fastly_response_handle_t h, fastly_error_t *err) { } bool fastly_http_resp_framing_headers_mode_set(fastly_response_handle_t h, fastly_framing_headers_mode_t mode, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_http_resp_framing_headers_mode_set((int32_t) (h), (int32_t) mode, ptr); @@ -2262,7 +2271,7 @@ bool fastly_http_resp_framing_headers_mode_set(fastly_response_handle_t h, fastl } bool fastly_dictionary_open(fastly_world_string_t *name, fastly_dictionary_handle_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[8]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_dictionary_open((int32_t) (*name).ptr, (int32_t) (*name).len, ptr); @@ -2289,7 +2298,7 @@ bool fastly_dictionary_open(fastly_world_string_t *name, fastly_dictionary_handl } bool fastly_dictionary_get(fastly_dictionary_handle_t h, fastly_world_string_t *key, fastly_world_option_string_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[16]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_dictionary_get((int32_t) (h), (int32_t) (*key).ptr, (int32_t) (*key).len, ptr); @@ -2329,7 +2338,7 @@ bool fastly_dictionary_get(fastly_dictionary_handle_t h, fastly_world_string_t * } bool fastly_geo_lookup(fastly_world_list_u8_t *addr_octets, fastly_world_string_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_geo_lookup((int32_t) (*addr_octets).ptr, (int32_t) (*addr_octets).len, ptr); @@ -2356,7 +2365,7 @@ bool fastly_geo_lookup(fastly_world_list_u8_t *addr_octets, fastly_world_string_ } bool fastly_object_store_open(fastly_world_string_t *name, fastly_object_store_handle_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[8]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_object_store_open((int32_t) (*name).ptr, (int32_t) (*name).len, ptr); @@ -2383,7 +2392,7 @@ bool fastly_object_store_open(fastly_world_string_t *name, fastly_object_store_h } bool fastly_object_store_lookup(fastly_object_store_handle_t store, fastly_world_string_t *key, fastly_world_option_body_handle_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_object_store_lookup((int32_t) (store), (int32_t) (*key).ptr, (int32_t) (*key).len, ptr); @@ -2423,7 +2432,7 @@ bool fastly_object_store_lookup(fastly_object_store_handle_t store, fastly_world } bool fastly_object_store_lookup_as_fd(fastly_object_store_handle_t store, fastly_world_string_t *key, fastly_world_option_fd_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_object_store_lookup_as_fd((int32_t) (store), (int32_t) (*key).ptr, (int32_t) (*key).len, ptr); @@ -2463,7 +2472,7 @@ bool fastly_object_store_lookup_as_fd(fastly_object_store_handle_t store, fastly } bool fastly_object_store_insert(fastly_object_store_handle_t store, fastly_world_string_t *key, fastly_body_handle_t body_handle, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_object_store_insert((int32_t) (store), (int32_t) (*key).ptr, (int32_t) (*key).len, (int32_t) (body_handle), ptr); @@ -2488,7 +2497,7 @@ bool fastly_object_store_insert(fastly_object_store_handle_t store, fastly_world } bool fastly_secret_store_open(fastly_world_string_t *name, fastly_secret_store_handle_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[8]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_secret_store_open((int32_t) (*name).ptr, (int32_t) (*name).len, ptr); @@ -2515,7 +2524,7 @@ bool fastly_secret_store_open(fastly_world_string_t *name, fastly_secret_store_h } bool fastly_secret_store_get(fastly_secret_store_handle_t store, fastly_world_string_t *key, fastly_world_option_secret_handle_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_secret_store_get((int32_t) (store), (int32_t) (*key).ptr, (int32_t) (*key).len, ptr); @@ -2555,7 +2564,7 @@ bool fastly_secret_store_get(fastly_secret_store_handle_t store, fastly_world_st } bool fastly_secret_store_plaintext(fastly_secret_handle_t secret, fastly_world_option_string_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[16]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_secret_store_plaintext((int32_t) (secret), ptr); @@ -2595,7 +2604,7 @@ bool fastly_secret_store_plaintext(fastly_secret_handle_t secret, fastly_world_o } bool fastly_async_io_select(fastly_world_list_async_handle_t *hs, uint32_t timeout_ms, fastly_world_option_u32_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) + __attribute__((aligned(4))) uint8_t ret_area[12]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_async_io_select((int32_t) (*hs).ptr, (int32_t) (*hs).len, (int32_t) (timeout_ms), ptr); @@ -2635,7 +2644,7 @@ bool fastly_async_io_select(fastly_world_list_async_handle_t *hs, uint32_t timeo } bool fastly_async_io_is_ready(fastly_async_handle_t handle, bool *ret, fastly_error_t *err) { - __attribute__((__aligned__(1))) + __attribute__((aligned(1))) uint8_t ret_area[2]; int32_t ptr = (int32_t) &ret_area; __wasm_import_fastly_async_io_is_ready((int32_t) (handle), ptr); @@ -2661,18 +2670,120 @@ bool fastly_async_io_is_ready(fastly_async_handle_t handle, bool *ret, fastly_er } } -bool fastly_purge_surrogate_key(fastly_world_string_t *surrogate_key, bool soft_purge, fastly_purge_result_t *ret, fastly_error_t *err) { - __attribute__((__aligned__(4))) - uint8_t ret_area[12]; +bool fastly_purge_surrogate_key(fastly_world_string_t *surrogate_keys, fastly_purge_options_mask_t purge_options, fastly_world_option_string_t *ret, fastly_error_t *err) { + __attribute__((aligned(4))) + uint8_t ret_area[16]; int32_t ptr = (int32_t) &ret_area; - __wasm_import_fastly_purge_surrogate_key((int32_t) (*surrogate_key).ptr, (int32_t) (*surrogate_key).len, soft_purge, ptr); - fastly_world_result_purge_result_error_t result; + __wasm_import_fastly_purge_surrogate_key((int32_t) (*surrogate_keys).ptr, (int32_t) (*surrogate_keys).len, purge_options, ptr); + fastly_world_result_option_string_error_t result; switch ((int32_t) (*((uint8_t*) (ptr + 0)))) { case 0: { result.is_err = false; - result.val.ok = (fastly_purge_result_t) { - (fastly_world_string_t) { (char*)(*((int32_t*) (ptr + 4))), (size_t)(*((int32_t*) (ptr + 8))) }, - }; + fastly_world_option_string_t option; + switch ((int32_t) (*((uint8_t*) (ptr + 4)))) { + case 0: { + option.is_some = false; + break; + } + case 1: { + option.is_some = true; + option.val = (fastly_world_string_t) { (char*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) }; + break; + } + } + + result.val.ok = option; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) (*((uint8_t*) (ptr + 4))); + break; + } + } + if (!result.is_err) { + *ret = result.val.ok; + return 1; + } else { + *err = result.val.err; + return 0; + } +} + +bool fastly_cache_lookup(fastly_world_string_t *cache_key, fastly_cache_lookup_options_t *options, fastly_cache_handle_t *ret, fastly_error_t *err) { + __attribute__((aligned(4))) + uint8_t ret_area[8]; + int32_t option; + int32_t option1; + if (((*options).request_headers).is_some) { + const fastly_request_handle_t *payload0 = &((*options).request_headers).val; + option = 1; + option1 = (int32_t) (*payload0); + } else { + option = 0; + option1 = 0; + } + int32_t ptr = (int32_t) &ret_area; + __wasm_import_fastly_cache_lookup((int32_t) (*cache_key).ptr, (int32_t) (*cache_key).len, option, option1, ptr); + fastly_world_result_cache_handle_error_t result; + switch ((int32_t) (*((uint8_t*) (ptr + 0)))) { + case 0: { + result.is_err = false; + result.val.ok = (uint32_t) (*((int32_t*) (ptr + 4))); + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) (*((uint8_t*) (ptr + 4))); + break; + } + } + if (!result.is_err) { + *ret = result.val.ok; + return 1; + } else { + *err = result.val.err; + return 0; + } +} + +bool fastly_cache_insert(fastly_world_string_t *cache_key, fastly_cache_write_options_t *options, fastly_body_handle_t *ret, fastly_error_t *err) { + __attribute__((aligned(4))) + uint8_t ret_area[8]; + int32_t ptr = (int32_t) &ret_area; + __wasm_import_fastly_cache_insert((int32_t) (*cache_key).ptr, (int32_t) (*cache_key).len, (int64_t) ((*options).max_age_ns), (int32_t) ((*options).request_headers), (int32_t) ((*options).vary_rule).ptr, (int32_t) ((*options).vary_rule).len, (int64_t) ((*options).initial_age_ns), (int64_t) ((*options).stale_while_revalidate_ns), (int32_t) ((*options).surrogate_keys).ptr, (int32_t) ((*options).surrogate_keys).len, (int64_t) ((*options).length), (int32_t) ((*options).user_metadata).ptr, (int32_t) ((*options).user_metadata).len, (*options).sensitive_data, ptr); + fastly_world_result_body_handle_error_t result; + switch ((int32_t) (*((uint8_t*) (ptr + 0)))) { + case 0: { + result.is_err = false; + result.val.ok = (uint32_t) (*((int32_t*) (ptr + 4))); + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) (*((uint8_t*) (ptr + 4))); + break; + } + } + if (!result.is_err) { + *ret = result.val.ok; + return 1; + } else { + *err = result.val.err; + return 0; + } +} + +bool fastly_cache_get_body(fastly_cache_handle_t handle, fastly_cache_get_body_options_t *options, fastly_body_handle_t *ret, fastly_error_t *err) { + __attribute__((aligned(4))) + uint8_t ret_area[8]; + int32_t ptr = (int32_t) &ret_area; + __wasm_import_fastly_cache_get_body((int32_t) (handle), (int64_t) ((*options).start), (int64_t) ((*options).end), ptr); + fastly_world_result_body_handle_error_t result; + switch ((int32_t) (*((uint8_t*) (ptr + 0)))) { + case 0: { + result.is_err = false; + result.val.ok = (uint32_t) (*((int32_t*) (ptr + 4))); break; } case 1: { @@ -2690,7 +2801,7 @@ bool fastly_purge_surrogate_key(fastly_world_string_t *surrogate_key, bool soft_ } } -__attribute__((__export_name__("compute-at-edge#serve"))) +__attribute__((export_name("compute-at-edge#serve"))) int32_t __wasm_export_compute_at_edge_serve(int32_t arg, int32_t arg0) { compute_at_edge_request_t arg1 = (compute_at_edge_request_t) { (uint32_t) (arg), diff --git a/runtime/js-compute-runtime/fastly-world/fastly_world.h b/runtime/js-compute-runtime/fastly-world/fastly_world.h index e6523e5fd4..743d0b1ac0 100644 --- a/runtime/js-compute-runtime/fastly-world/fastly_world.h +++ b/runtime/js-compute-runtime/fastly-world/fastly_world.h @@ -37,9 +37,10 @@ typedef uint32_t fastly_response_handle_t; typedef uint32_t fastly_request_handle_t; -typedef struct { - fastly_world_string_t id; -} fastly_purge_result_t; +typedef uint8_t fastly_purge_options_mask_t; + +#define FASTLY_PURGE_OPTIONS_MASK_SOFT_PURGE (1 << 0) +#define FASTLY_PURGE_OPTIONS_MASK_RET_BUF (1 << 1) typedef uint32_t fastly_pending_request_handle_t; @@ -59,7 +60,6 @@ typedef uint16_t fastly_http_status_t; typedef uint8_t fastly_http_cache_override_tag_t; -// Do not cache the response to this request, regardless of the origin response's headers. #define FASTLY_HTTP_CACHE_OVERRIDE_TAG_PASS (1 << 0) #define FASTLY_HTTP_CACHE_OVERRIDE_TAG_TTL (1 << 1) #define FASTLY_HTTP_CACHE_OVERRIDE_TAG_STALE_WHILE_REVALIDATE (1 << 2) @@ -81,84 +81,24 @@ typedef struct { } fastly_world_option_float32_t; typedef struct { - // * The name of the organization associated with as_number. - // * - // * For example, fastly is the value given for IP addresses under AS-54113. fastly_world_option_string_t as_name; - // * [Autonomous system](https://en.wikipedia.org/wiki/Autonomous_system_(Internet)) (AS) number. fastly_world_option_u32_t as_number; - // * The telephone area code associated with an IP address. - // * - // * These are only available for IP addresses in the United States, its territories, and - // Canada. fastly_world_option_u32_t area_code; - // * City or town name. fastly_world_option_string_t city; - // * Connection speed. fastly_world_option_string_t conn_speed; - // * Connection type. fastly_world_option_string_t conn_type; - // * Continent. fastly_world_option_string_t continent; - // * A two-character [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) country code for the - // country associated with an IP address. - // * - // * The US country code is returned for IP addresses associated with overseas United States - // military bases. - // * - // * These values include subdivisions that are assigned their own country codes in ISO - // 3166-1. For example, subdivisions NO-21 and NO-22 are presented with the country code SJ - // for Svalbard and the Jan Mayen Islands. fastly_world_option_string_t country_code; - // * A three-character [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) - // country code for the country associated with the IP address. - // * - // * The USA country code is returned for IP addresses associated with overseas United - // States military bases. fastly_world_option_string_t country_code3; - // * Country name. - // * - // * This field is the [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) English short - // name for a country. fastly_world_option_string_t country_name; - // * Time zone offset from Greenwich Mean Time (GMT) for `city`. fastly_world_option_string_t gmt_offset; - // * Latitude, in units of degrees from the equator. - // * - // * Values range from -90.0 to +90.0 inclusive, and are based on the [WGS - // 84](https://en.wikipedia.org/wiki/World_Geodetic_System) coordinate reference system. fastly_world_option_float32_t latitude; - // * Longitude, in units of degrees from the [IERS Reference - // Meridian](https://en.wikipedia.org/wiki/IERS_Reference_Meridian). - // * - // * Values range from -180.0 to +180.0 inclusive, and are based on the [WGS - // 84](https://en.wikipedia.org/wiki/World_Geodetic_System) coordinate reference system. fastly_world_option_float32_t longitude; - // * Metro code, representing designated market areas (DMAs) in the United States. fastly_world_option_u32_t metro_code; - // * The postal code associated with the IP address. - // * - // * These are available for some IP addresses in Australia, Canada, France, Germany, Italy, - // Spain, Switzerland, the United Kingdom, and the United States. - // * - // * For Canadian postal codes, this is the first 3 characters. For the United Kingdom, this - // is the first 2-4 characters (outward code). For countries with alphanumeric postal codes, - // this field is a lowercase transliteration. fastly_world_option_string_t postal_code; - // * Client proxy description. fastly_world_option_string_t proxy_description; - // * Client proxy type. fastly_world_option_string_t proxy_type; - // * [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-2) country subdivision code. - // * - // * For countries with multiple levels of subdivision (for example, nations within the - // United Kingdom), this variable gives the more specific subdivision. - // * - // * This field can be None for countries that do not have ISO country subdivision codes. - // For example, None is given for IP addresses assigned to the Åland Islands (country code - // AX, illustrated below). fastly_world_option_string_t region; - // * Time zone offset from coordinated universal time (UTC) for `city`. fastly_world_option_u32_t utc_offset; } fastly_geo_data_t; @@ -172,51 +112,19 @@ typedef uint32_t fastly_fd_t; typedef uint8_t fastly_error_t; -// Unknown error value. -// It should be an internal error if this is returned. #define FASTLY_ERROR_UNKNOWN_ERROR 0 -// Generic error value. -// This means that some unexpected error occurred during a hostcall. #define FASTLY_ERROR_GENERIC_ERROR 1 -// Invalid argument. #define FASTLY_ERROR_INVALID_ARGUMENT 2 -// Invalid handle. -// Thrown when a handle is not valid. E.G. No dictionary exists with the given name. #define FASTLY_ERROR_BAD_HANDLE 3 -// Buffer length error. -// Thrown when a buffer is too long. #define FASTLY_ERROR_BUFFER_LEN 4 -// Unsupported operation error. -// This error is thrown when some operation cannot be performed, because it is not supported. #define FASTLY_ERROR_UNSUPPORTED 5 -// Alignment error. -// This is thrown when a pointer does not point to a properly aligned slice of memory. #define FASTLY_ERROR_BAD_ALIGN 6 -// Invalid HTTP error. -// This can be thrown when a method, URI, header, or status is not valid. This can also -// be thrown if a message head is too large. #define FASTLY_ERROR_HTTP_INVALID 7 -// HTTP user error. -// This is thrown in cases where user code caused an HTTP error. For example, attempt to send -// a 1xx response code, or a request with a non-absolute URI. This can also be caused by -// an unexpected header: both `content-length` and `transfer-encoding`, for example. #define FASTLY_ERROR_HTTP_USER 8 -// HTTP incomplete message error. -// This can be thrown when a stream ended unexpectedly. #define FASTLY_ERROR_HTTP_INCOMPLETE 9 -// A `None` error. -// This status code is used to indicate when an optional value did not exist, as opposed to -// an empty value. -// Note, this value should no longer be used, as we have explicit optional types now. #define FASTLY_ERROR_OPTIONAL_NONE 10 -// Message head too large. #define FASTLY_ERROR_HTTP_HEAD_TOO_LARGE 11 -// Invalid HTTP status. #define FASTLY_ERROR_HTTP_INVALID_STATUS 12 -// Limit exceeded -// -// This is returned when an attempt to allocate a resource has exceeded the maximum number of -// resources permitted. For example, creating too many response handles. #define FASTLY_ERROR_LIMIT_EXCEEDED 13 typedef struct { @@ -250,6 +158,55 @@ typedef uint8_t fastly_content_encodings_t; #define FASTLY_CONTENT_ENCODINGS_GZIP (1 << 0) +typedef uint64_t fastly_cache_object_length_t; + +typedef struct { + bool is_some; + fastly_request_handle_t val; +} fastly_world_option_request_handle_t; + +// Extensible options for cache lookup operations; currently used for both `lookup` and +// `transaction_lookup`. +typedef struct { + fastly_world_option_request_handle_t request_headers; +} fastly_cache_lookup_options_t; + +typedef uint64_t fastly_cache_hit_count_t; + +// The outcome of a cache lookup (either bare or as part of a cache transaction) +typedef uint32_t fastly_cache_handle_t; + +typedef struct { + uint64_t start; + uint64_t end; +} fastly_cache_get_body_options_t; + +typedef uint64_t fastly_cache_duration_ns_t; + +typedef struct { + uint8_t *ptr; + size_t len; +} fastly_world_list_u8_t; + +// Configuration for several hostcalls that write to the cache: +// - `insert` +// - `transaction-insert` +// - `transaction-insert-and-stream-back` +// - `transaction-update` +// +// Some options are only allowed for certain of these hostcalls; see `cache-write-options-mask`. +typedef struct { + fastly_cache_duration_ns_t max_age_ns; + fastly_request_handle_t request_headers; + fastly_world_string_t vary_rule; + fastly_cache_duration_ns_t initial_age_ns; + fastly_cache_duration_ns_t stale_while_revalidate_ns; + fastly_world_string_t surrogate_keys; + fastly_cache_object_length_t length; + fastly_world_list_u8_t user_metadata; + bool sensitive_data; +} fastly_cache_write_options_t; + typedef uint8_t fastly_body_write_end_t; #define FASTLY_BODY_WRITE_END_BACK 0 @@ -280,11 +237,6 @@ typedef struct { // into, even before the origin itself consumes that data. typedef uint32_t fastly_async_handle_t; -typedef struct { - uint8_t *ptr; - size_t len; -} fastly_world_list_u8_t; - typedef struct { fastly_world_string_t *ptr; size_t len; @@ -414,9 +366,6 @@ bool fastly_http_req_pending_req_wait(fastly_pending_request_handle_t h, fastly_ bool fastly_http_req_pending_req_select(fastly_world_list_pending_request_handle_t *h, fastly_world_tuple2_u32_response_t *ret, fastly_error_t *err); -// Returns whether or not the original client request arrived with a -// Fastly-Key belonging to a user with the rights to purge content on this -// service. bool fastly_http_req_key_is_valid(bool *ret, fastly_error_t *err); bool fastly_http_req_close(fastly_request_handle_t h, fastly_error_t *err); bool fastly_http_req_auto_decompress_response_set(fastly_request_handle_t h, @@ -460,7 +409,6 @@ bool fastly_http_resp_status_get(fastly_response_handle_t h, fastly_http_status_ bool fastly_http_resp_status_set(fastly_response_handle_t h, fastly_http_status_t status, fastly_error_t *err); bool fastly_http_resp_close(fastly_response_handle_t h, fastly_error_t *err); -// Adjust how this response's framing headers are determined. bool fastly_http_resp_framing_headers_mode_set(fastly_response_handle_t h, fastly_framing_headers_mode_t mode, fastly_error_t *err); @@ -468,7 +416,6 @@ bool fastly_dictionary_open(fastly_world_string_t *name, fastly_dictionary_handl fastly_error_t *err); bool fastly_dictionary_get(fastly_dictionary_handle_t h, fastly_world_string_t *key, fastly_world_option_string_t *ret, fastly_error_t *err); -// JSON string for now bool fastly_geo_lookup(fastly_world_list_u8_t *addr_octets, fastly_world_string_t *ret, fastly_error_t *err); bool fastly_object_store_open(fastly_world_string_t *name, fastly_object_store_handle_t *ret, @@ -486,28 +433,18 @@ bool fastly_secret_store_get(fastly_secret_store_handle_t store, fastly_world_st fastly_world_option_secret_handle_t *ret, fastly_error_t *err); bool fastly_secret_store_plaintext(fastly_secret_handle_t secret, fastly_world_option_string_t *ret, fastly_error_t *err); -// Blocks until one of the given objects is ready for I/O, or the optional timeout expires. -// -// Valid object handles includes bodies and pending requests. See the `async_item_handle` -// definition for more details, including what I/O actions are associated with each handle -// type. -// -// The timeout is specified in milliseconds, or 0 if no timeout is desired. -// -// Returns the _index_ (not handle!) of the first object that is ready, or u32::MAX if the -// timeout expires before any objects are ready for I/O. bool fastly_async_io_select(fastly_world_list_async_handle_t *hs, uint32_t timeout_ms, fastly_world_option_u32_t *ret, fastly_error_t *err); -// Returns 1 if the given async item is "ready" for its associated I/O action, 0 otherwise. -// -// If an object is ready, the I/O action is guaranteed to complete without blocking. -// -// Valid object handles includes bodies and pending requests. See the `async_item_handle` -// definition for more details, including what I/O actions are associated with each handle -// type. bool fastly_async_io_is_ready(fastly_async_handle_t handle, bool *ret, fastly_error_t *err); -bool fastly_purge_surrogate_key(fastly_world_string_t *surrogate_key, bool soft_purge, - fastly_purge_result_t *ret, fastly_error_t *err); +bool fastly_purge_surrogate_key(fastly_world_string_t *surrogate_keys, + fastly_purge_options_mask_t purge_options, + fastly_world_option_string_t *ret, fastly_error_t *err); +bool fastly_cache_lookup(fastly_world_string_t *cache_key, fastly_cache_lookup_options_t *options, + fastly_cache_handle_t *ret, fastly_error_t *err); +bool fastly_cache_insert(fastly_world_string_t *cache_key, fastly_cache_write_options_t *options, + fastly_body_handle_t *ret, fastly_error_t *err); +bool fastly_cache_get_body(fastly_cache_handle_t handle, fastly_cache_get_body_options_t *options, + fastly_body_handle_t *ret, fastly_error_t *err); // Exported Functions from `compute-at-edge` bool compute_at_edge_serve(compute_at_edge_request_t *req); diff --git a/runtime/js-compute-runtime/fastly-world/fastly_world_adapter.cpp b/runtime/js-compute-runtime/fastly-world/fastly_world_adapter.cpp index d2f4ba7105..43808a96c9 100644 --- a/runtime/js-compute-runtime/fastly-world/fastly_world_adapter.cpp +++ b/runtime/js-compute-runtime/fastly-world/fastly_world_adapter.cpp @@ -701,3 +701,99 @@ bool fastly_async_io_is_ready(fastly_async_handle_t handle, bool *ret, fastly_er *ret = (bool)ret_int; return true; } + +bool fastly_purge_surrogate_key(fastly_world_string_t *surrogate_key, + fastly_purge_options_mask_t options_mask, + fastly_world_option_string_t *ret, fastly_error_t *err) { + fastly::PurgeOptions options{nullptr, 0, nullptr}; + + // Currently this host-call has been implemented to support the `SimpleCache.delete(key)` method, + // which uses hard-purging and not soft-purging. + // TODO: Create a JS API for this hostcall which supports hard-purging and another which supports + // soft-purging. E.G. `fastly.purgeSurrogateKey(key)` and `fastly.softPurgeSurrogateKey(key)` + MOZ_ASSERT(!(options_mask & FASTLY_PURGE_OPTIONS_MASK_SOFT_PURGE)); + MOZ_ASSERT(!(options_mask & FASTLY_PURGE_OPTIONS_MASK_RET_BUF)); + + ret->is_some = false; + + return convert_result( + fastly::purge_surrogate_key(surrogate_key->ptr, surrogate_key->len, options_mask, &options), + err); +} + +#define FASTLY_CACHE_LOOKUP_OPTIONS_MASK_RESERVED (1 << 0) +#define FASTLY_CACHE_LOOKUP_OPTIONS_MASK_REQUEST_HEADERS (1 << 1) + +bool fastly_cache_lookup(fastly_world_string_t *cache_key, fastly_cache_lookup_options_t *options, + fastly_cache_handle_t *ret, fastly_error_t *err) { + // Currently this host-call has been implemented to support the `SimpleCache.get(key)` method, + // which does not use any fields from `fastly_cache_lookup_options_t`. + uint8_t options_mask = 0; + return convert_result( + fastly::cache_lookup(cache_key->ptr, cache_key->len, options_mask, options, ret), err); +} + +#define FASTLY_CACHE_WRITE_OPTIONS_MASK_RESERVED (1 << 0) +#define FASTLY_CACHE_WRITE_OPTIONS_MASK_REQUEST_HEADERS (1 << 1) +#define FASTLY_CACHE_WRITE_OPTIONS_MASK_VARY_RULE (1 << 2) +#define FASTLY_CACHE_WRITE_OPTIONS_MASK_INITIAL_AGE_NS (1 << 3) +#define FASTLY_CACHE_WRITE_OPTIONS_MASK_STALE_WHILE_REVALIDATE_NS (1 << 4) +#define FASTLY_CACHE_WRITE_OPTIONS_MASK_SURROGATE_KEYS (1 << 5) +#define FASTLY_CACHE_WRITE_OPTIONS_MASK_LENGTH (1 << 6) +#define FASTLY_CACHE_WRITE_OPTIONS_MASK_USER_METADATA (1 << 7) +#define FASTLY_CACHE_WRITE_OPTIONS_MASK_SENSITIVE_DATA (1 << 8) + +bool fastly_cache_insert(fastly_world_string_t *cache_key, fastly_cache_write_options_t *options, + fastly_body_handle_t *ret, fastly_error_t *err) { + uint16_t options_mask = 0; + fastly::CacheWriteOptions opts; + std::memset(&opts, 0, sizeof(opts)); + opts.max_age_ns = options->max_age_ns; + + if (options->request_headers != INVALID_HANDLE && options->request_headers != 0) { + options_mask |= FASTLY_CACHE_WRITE_OPTIONS_MASK_REQUEST_HEADERS; + opts.request_headers = options->request_headers; + } + if (options->vary_rule.ptr != nullptr) { + options_mask |= FASTLY_CACHE_WRITE_OPTIONS_MASK_VARY_RULE; + opts.vary_rule_len = options->vary_rule.len; + opts.vary_rule_ptr = reinterpret_cast(options->vary_rule.ptr); + } + if (options->initial_age_ns != 0) { + options_mask |= FASTLY_CACHE_WRITE_OPTIONS_MASK_INITIAL_AGE_NS; + opts.initial_age_ns = options->initial_age_ns; + } + if (options->stale_while_revalidate_ns != 0) { + options_mask |= FASTLY_CACHE_WRITE_OPTIONS_MASK_STALE_WHILE_REVALIDATE_NS; + opts.stale_while_revalidate_ns = options->stale_while_revalidate_ns; + } + if (options->surrogate_keys.ptr != nullptr) { + options_mask |= FASTLY_CACHE_WRITE_OPTIONS_MASK_SURROGATE_KEYS; + opts.surrogate_keys_len = options->surrogate_keys.len; + opts.surrogate_keys_ptr = reinterpret_cast(options->surrogate_keys.ptr); + } + if (options->length != 0) { + options_mask |= FASTLY_CACHE_WRITE_OPTIONS_MASK_LENGTH; + opts.length = options->length; + } + if (options->user_metadata.ptr != nullptr) { + options_mask |= FASTLY_CACHE_WRITE_OPTIONS_MASK_USER_METADATA; + opts.user_metadata_len = options->user_metadata.len; + opts.user_metadata_ptr = options->user_metadata.ptr; + } + if (options->sensitive_data) { + options_mask |= FASTLY_CACHE_WRITE_OPTIONS_MASK_SENSITIVE_DATA; + } + return convert_result( + fastly::cache_insert(cache_key->ptr, cache_key->len, options_mask, &opts, ret), err); +} +bool fastly_cache_get_body(fastly_cache_handle_t handle, fastly_cache_get_body_options_t *options, + fastly_body_handle_t *ret, fastly_error_t *err) { + uint32_t options_mask = 0; + bool ok = convert_result(fastly::cache_get_body(handle, options_mask, options, ret), err); + if (!ok && *err == FASTLY_ERROR_OPTIONAL_NONE) { + *ret = INVALID_HANDLE; + return true; + } + return ok; +} \ No newline at end of file diff --git a/runtime/js-compute-runtime/fastly-world/fastly_world_component_type.o b/runtime/js-compute-runtime/fastly-world/fastly_world_component_type.o index 86f89ba958..c3055cfc22 100644 Binary files a/runtime/js-compute-runtime/fastly-world/fastly_world_component_type.o and b/runtime/js-compute-runtime/fastly-world/fastly_world_component_type.o differ diff --git a/runtime/js-compute-runtime/fastly.wit b/runtime/js-compute-runtime/fastly.wit index f244a3e567..ec73786930 100644 --- a/runtime/js-compute-runtime/fastly.wit +++ b/runtime/js-compute-runtime/fastly.wit @@ -45,7 +45,7 @@ default world fastly-world { http-invalid-status, /// Limit exceeded /// - /// This is returned when an attempt to allocate a resource has exceeded the maximum number of + /// This is returned when an attempt to allocate a resource has exceeded the maximum number of /// resources permitted. For example, creating too many response handles. limit-exceeded } @@ -479,11 +479,91 @@ default world fastly-world { /* * Fastly Purge */ - record purge-result { - id: string + + flags purge-options-mask { + soft-purge, + ret-buf + } + + /* + * A surrogate key can be a max of 1024 characters. + * A surrogate key must contain only printable ASCII characters (those between `0x21` and `0x7E`, inclusive). + */ + purge-surrogate-key: func(surrogate-keys: string, purge-options: purge-options-mask) -> result, error> + + /* + * Fastly Cache + */ + /// The outcome of a cache lookup (either bare or as part of a cache transaction) + type cache-handle = u32 + type cache-object-length = u64 + type cache-duration-ns = u64 + type cache-hit-count = u64 + + /// Extensible options for cache lookup operations; currently used for both `lookup` and `transaction_lookup`. + record cache-lookup-options { + /** + * A full request handle, but used only for its headers + */ + request-headers: option, + } + + /// Configuration for several hostcalls that write to the cache: + /// - `insert` + /// - `transaction-insert` + /// - `transaction-insert-and-stream-back` + /// - `transaction-update` + /// + /// Some options are only allowed for certain of these hostcalls; see `cache-write-options-mask`. + record cache-write-options { + /// this is a required field; there's no flag for it + max-age-ns: cache-duration-ns, + /// a full request handle, but used only for its headers + request-headers: request-handle, + /// a list of header names separated by spaces + vary-rule: string, + /// The initial age of the object in nanoseconds (default: 0). + /// + /// This age is used to determine the freshness lifetime of the object as well as to + /// prioritize which variant to return if a subsequent lookup matches more than one vary rule + initial-age-ns: cache-duration-ns, + stale-while-revalidate-ns: cache-duration-ns, + /// a list of surrogate keys separated by spaces + surrogate-keys: string, + length: cache-object-length, + user-metadata: list, + sensitive-data: bool, + } + + record cache-get-body-options { + start: u64, + end: u64, } - purge-surrogate-key: func(surrogate-key: string, soft-purge: bool) -> result + + /// Performs a non-request-collapsing cache lookup. + /// + /// Returns a result without waiting for any request collapsing that may be ongoing. + cache-lookup: func(cache-key: string, options: cache-lookup-options) -> result + + /// Performs a non-request-collapsing cache insertion (or update). + /// + /// The returned handle is to a streaming body that is used for writing the object into + /// the cache. + cache-insert: func(cache-key: string, options: cache-write-options) -> result + + + /// Gets a range of the found object body, returning the `$none` error if there + /// was no found object. + /// + /// The returned `body_handle` must be closed before calling this function again on the same + /// `cache_handle`. + /// + /// Note: until the CacheD protocol is adjusted to fully support this functionality, + /// the body of objects that are past the stale-while-revalidate period will not + /// be available, even when other metadata is. + cache-get-body: func(handle: cache-handle, options: cache-get-body-options) -> result + } export compute-at-edge: interface { diff --git a/runtime/js-compute-runtime/host_interface/fastly.h b/runtime/js-compute-runtime/host_interface/fastly.h index f8b75ea88e..fcde1f907c 100644 --- a/runtime/js-compute-runtime/host_interface/fastly.h +++ b/runtime/js-compute-runtime/host_interface/fastly.h @@ -358,6 +358,43 @@ int async_select(fastly_async_handle_t handles[], size_t handles_len, uint32_t t // type. WASM_IMPORT("fastly_async_io", "is_ready") int async_is_ready(fastly_async_handle_t handle, uint32_t *is_ready_out); + +struct __attribute__((aligned(4))) PurgeOptions { + uint8_t *ret_buf_ptr; + size_t ret_buf_len; + size_t *ret_buf_nwritten_out; +}; + +WASM_IMPORT("fastly_purge", "purge_surrogate_key") +int purge_surrogate_key(char *surrogate_key, size_t surrogate_key_len, uint32_t options_mask, + PurgeOptions *purge_options); + +WASM_IMPORT("fastly_cache", "lookup") +int cache_lookup(char *cache_key, size_t cache_key_len, uint32_t options_mask, + fastly_cache_lookup_options_t *options, fastly_cache_handle_t *ret); + +typedef __attribute__((aligned(8))) struct { + 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; +} CacheWriteOptions; + +WASM_IMPORT("fastly_cache", "insert") +int cache_insert(char *cache_key, size_t cache_key_len, uint32_t options_mask, + CacheWriteOptions *options, fastly_body_handle_t *ret); + +WASM_IMPORT("fastly_cache", "get_body") +int cache_get_body(fastly_cache_handle_t handle, uint32_t options_mask, + fastly_cache_get_body_options_t *options, fastly_body_handle_t *ret); + } // namespace fastly #ifdef __cplusplus } diff --git a/runtime/js-compute-runtime/js-compute-builtins.cpp b/runtime/js-compute-runtime/js-compute-builtins.cpp index fd4d8335ca..7da671dcd4 100644 --- a/runtime/js-compute-runtime/js-compute-builtins.cpp +++ b/runtime/js-compute-runtime/js-compute-builtins.cpp @@ -39,6 +39,7 @@ #include "builtin.h" #include "builtins/backend.h" #include "builtins/cache-override.h" +#include "builtins/cache-simple.h" #include "builtins/client-info.h" #include "builtins/compression-stream.h" #include "builtins/config-store.h" @@ -1343,6 +1344,12 @@ bool define_fastly_sys(JSContext *cx, HandleObject global, FastlyOptions options return false; if (!builtins::SecretStoreEntry::init_class(cx, global)) return false; + if (!builtins::SimpleCache::init_class(cx, global)) { + return false; + } + if (!builtins::SimpleCacheEntry::init_class(cx, global)) { + return false; + } pending_async_tasks = new JS::PersistentRootedObjectVector(cx); diff --git a/src/bundle.js b/src/bundle.js index ed5857fe5d..8d1f02a0a5 100644 --- a/src/bundle.js +++ b/src/bundle.js @@ -36,6 +36,7 @@ export const allowDynamicBackends = Object.getOwnPropertyDescriptor(globalThis.f case 'logger': { return { contents: `export const Logger = globalThis.Logger;` } } case 'kv-store': { return { contents: `export const KVStore = globalThis.KVStore;` } } case 'secret-store': { return { contents: `export const SecretStore = globalThis.SecretStore;export const SecretStoreEntry = globalThis.SecretStoreEntry;` } } + case 'cache': { return { contents: `export const SimpleCache = globalThis.SimpleCache;export const SimpleCacheEntry = globalThis.SimpleCacheEntry;` } } } }) }, diff --git a/types/fastly:cache.d.ts b/types/fastly:cache.d.ts new file mode 100644 index 0000000000..7b3a41d3ba --- /dev/null +++ b/types/fastly:cache.d.ts @@ -0,0 +1,16 @@ +declare module "fastly:cache" { + export class SimpleCache { + static get(key: string): SimpleCacheEntry | null; + static set(key: string, value: BodyInit, ttl: number): undefined; + static set(key: string, value: ReadableStream, ttl: number, length: number): undefined; + static delete(key: string): undefined; + } + + export interface SimpleCacheEntry { + get body(): ReadableStream; + get bodyUsed(): boolean; + text(): Promise; + json(): Promise; + arrayBuffer(): Promise; + } +} diff --git a/types/index.d.ts b/types/index.d.ts index 5b538783db..2ef76e2c8f 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -8,5 +8,6 @@ /// /// /// +/// /// ///