Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ node_modules/
.vscode/

*.wasm

package-lock.json
1 change: 1 addition & 0 deletions assemblyscript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [Geo Block](./assembly/geoBlock/README.md)
- [Headers](./assembly/headers/README.md)
- [JWT](./assembly/jwt/README.md)
- [KV Store](./assembly/kvStore/README.md)
- [Log time](./assembly/logTime/README.md)
- [Properties](./assembly/properties/README.md)

Expand Down
4 changes: 2 additions & 2 deletions assemblyscript/assembly/body/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ This application modifies the body in `onRequestBody` and `onResponseBody` funct

2. In `onRequestBody` it reads in the body from `get_buffer_bytes` and checks if it contains the term `Client`, if so it alters the body using `set_buffer_bytes`.

3. In `onResponseHeaders` it resets the `content-length` header, sets the `transfer-encoding` header and ensures that the runtime property `response.content_type` is set according to what it has recieved in the headers.
3. In `onResponseHeaders` it resets the `content-length` header, sets the `transfer-encoding` header and ensures that the runtime property `response.content_type` is set according to what it has received in the headers.

4. In `onResponseBody` it finally reads and logs the known properties and the returned body content.
4. In `onResponseBody` it finally reads and logs the known properties and the returns the body content.

This demonstrates the basic flow of how to manipulate the body in each life cycle hook, be mindful that altering the body requires you to manipulate the required headers before doing anything.

Expand Down
6 changes: 3 additions & 3 deletions assemblyscript/assembly/body/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import {
RootContext,
set_buffer_bytes,
set_property,
setLogLevel,
stream_context,
} from "@gcoredev/proxy-wasm-sdk-as/assembly";
import { setLogLevel } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge";

class HttpBodyRoot extends RootContext {
createContext(context_id: u32): Context {
Expand Down Expand Up @@ -42,7 +42,7 @@ class HttpBody extends Context {
body_buffer_length: usize,
end_of_stream: bool
): FilterDataStatusValues {
log(LogLevelValues.debug, "onHttpRequestBody >>");
log(LogLevelValues.debug, "onRequestBody >>");
if (!end_of_stream) {
// Wait until the complete body is buffered
return FilterDataStatusValues.StopIterationAndBuffer;
Expand All @@ -57,7 +57,7 @@ class HttpBody extends Context {

if (bodyBytes.byteLength > 0) {
const bodyStr = String.UTF8.decode(bodyBytes);
log(LogLevelValues.debug, "onHttpRequestBody >> bodyStr: " + bodyStr);
log(LogLevelValues.debug, "onRequestBody >> bodyStr: " + bodyStr);
if (bodyStr.includes("Client")) {
const newBody = `Original message body (${body_buffer_length.toString()} bytes) redacted.\n`;
set_buffer_bytes(
Expand Down
2 changes: 1 addition & 1 deletion assemblyscript/assembly/geoBlock/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import {
Context,
FilterHeadersStatusValues,
get_property,
getEnvVar,
registerRootContext,
RootContext,
send_http_response,
} from "@gcoredev/proxy-wasm-sdk-as/assembly";
import { getEnvVar } from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge";

const BAD_GATEWAY: u32 = 502;
const FORBIDDEN: u32 = 403;
Expand Down
6 changes: 4 additions & 2 deletions assemblyscript/assembly/geoRedirect/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ import {
Context,
FilterHeadersStatusValues,
get_property,
getEnvVar,
log,
LogLevelValues,
registerRootContext,
RootContext,
send_http_response,
set_property,
setLogLevel,
stream_context,
} from "@gcoredev/proxy-wasm-sdk-as/assembly";
import {
getEnvVar,
setLogLevel,
} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge";

const BAD_GATEWAY: u32 = 502;
const INTERNAL_SERVER_ERROR: u32 = 500;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import {
registerRootContext,
RootContext,
send_http_response,
setLogLevel,
stream_context,
} from "@gcoredev/proxy-wasm-sdk-as/assembly";
import {
setLogLevel,
} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge";

function collectHeaders(
headers: Headers,
Expand Down
6 changes: 4 additions & 2 deletions assemblyscript/assembly/jwt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the
import {
Context,
FilterHeadersStatusValues,
getSecretVar,
log,
LogLevelValues,
registerRootContext,
RootContext,
send_http_response,
setLogLevel,
stream_context,
} from "@gcoredev/proxy-wasm-sdk-as/assembly";
import {
getSecretVar,
setLogLevel,
} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge";

import { jwtVerify, JwtValidation } from "@gcoredev/as-jwt/assembly";

Expand Down
31 changes: 31 additions & 0 deletions assemblyscript/assembly/kvStore/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
⏮️ Back to AssemblyScript [README.md](../../README.md)

# KV Store

This application modifies the body in the `onResponseBody` function.

It ignores the body received from the origin and instead just replaces it with results from interacting with a KV Store.

**Note** This is purely to demonstate how to interact with a KV Store. It has no real world use case.

## Application Flow

1. In `onResponseHeaders` it resets the `content-length` header, sets the `transfer-encoding` header and ensures that the runtime property `response.content_type` is set to `application/json`.

2. In `onResponseBody` it demonstrates how to interact with a KV Store.

This interaction with the KV Store is powered using query parameters.

### Query Parameters

`store` - the name of the store you wish to open. This is the name given to a store on the application.

`action` - What you wish to perform. Options are "get", "scan", "zscan", "zrange", "bfExists". ( If no action is provided it will default to "get" )

`key` - The key you wish to access in the KV Store.

`match` - A prefix match pattern, used by "scan" and "zscan". Must include a wildcard. e.g. `foo*`

`min` / `max` - Used by zrange for defining the range of scores you wish to receive results for.

`item` - Used by Bloom Filter exists function.
185 changes: 185 additions & 0 deletions assemblyscript/assembly/kvStore/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
export * from "@gcoredev/proxy-wasm-sdk-as/assembly/proxy"; // this exports the required functions for the proxy to interact with us.
import {
Context,
FilterDataStatusValues,
FilterHeadersStatusValues,
log,
LogLevelValues,
registerRootContext,
RootContext,
stream_context,
set_property,
set_buffer_bytes,
BufferTypeValues,
get_property,
} from "@gcoredev/proxy-wasm-sdk-as/assembly";
import {
KvStore,
setLogLevel,
} from "@gcoredev/proxy-wasm-sdk-as/assembly/fastedge";

import {
stringifyMap,
stringifyValueScoreTuples,
validateQueryParams,
} from "./utils";

const INTERNAL_SERVER_ERROR: u32 = 545;
const REQUEST_QUERY = "request.query";

class HttpBodyRoot extends RootContext {
createContext(context_id: u32): Context {
setLogLevel(LogLevelValues.info); // Set the log level to info - for more logging reduce this to LogLevelValues.trace
return new HttpBody(context_id, this);
}
}

class HttpBody extends Context {
constructor(context_id: u32, root_context: HttpBodyRoot) {
super(context_id, root_context);
}

onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues {
log(LogLevelValues.debug, "onResponseHeaders >>");

// Remove "content-length" header as the body size will change in onResponseBody()
stream_context.headers.response.remove("content-length");

// Remove any redirect headers
stream_context.headers.response.remove("refresh");
stream_context.headers.response.remove("location");

// Set "transfer-encoding" to "chunked"
stream_context.headers.response.replace("transfer-encoding", "Chunked");

// Set content-type to application/json
stream_context.headers.response.replace("content-type", "application/json");

return FilterHeadersStatusValues.Continue;
}

onResponseBody(
body_buffer_length: usize,
end_of_stream: bool
): FilterDataStatusValues {
if (!end_of_stream) {
// Wait until the complete body is buffered
return FilterDataStatusValues.StopIterationAndBuffer;
}
log(LogLevelValues.debug, "onResponseBody >>");

const responseBodyMap = new Map<string, string>();

// Retrieve the request query parameters
const queryBytes = get_property(REQUEST_QUERY);
const query =
queryBytes.byteLength === 0 ? "" : String.UTF8.decode(queryBytes);
if (query === "") {
this.sendErrorResponse(
"App must be called with query parameters",
body_buffer_length
);
return FilterDataStatusValues.Continue;
}

const params = validateQueryParams(query);
if (params.has("error")) {
this.sendErrorResponse(params.get("error"), body_buffer_length);
return FilterDataStatusValues.Continue;
}

const store = params.get("store");
responseBodyMap.set("Store", store);
const myStore = KvStore.open(store);
if (myStore == null) {
this.sendErrorResponse(
`Failed to open KvStore: '${store}'`,
body_buffer_length
);
return FilterDataStatusValues.Continue;
}
const action = params.get("action");
responseBodyMap.set("Action", action);
switch (action) {
case "get": {
const key = params.get("key");
responseBodyMap.set("Key", key);
const storeArrBuff = myStore.get(key);
if (storeArrBuff == null) {
responseBodyMap.set("Response", "null (Not found)");
break;
}
responseBodyMap.set("Response", String.UTF8.decode(storeArrBuff));
break;
}
case "scan": {
const match = params.get("match");
const keys = myStore.scan(match);
responseBodyMap.set("Match", match);
responseBodyMap.set("Response", keys.join(", "));
break;
}
case "zrange": {
const key = params.get("key");
const min = params.get("min");
const max = params.get("max");
const tuples = myStore.zrangeByScore(
key,
parseFloat(min),
parseFloat(max)
);
responseBodyMap.set("Key", key);
responseBodyMap.set("Min", min);
responseBodyMap.set("Max", max);
responseBodyMap.set("Response", stringifyValueScoreTuples(tuples));
break;
}
case "zscan": {
const key = params.get("key");
const match = params.get("match");
const tuples = myStore.zscan(key, match);
responseBodyMap.set("Key", key);
responseBodyMap.set("Match", match);
responseBodyMap.set("Response", stringifyValueScoreTuples(tuples));
break;
}
case "bfExists": {
const key = params.get("key");
const item = params.get("item");
const exists = myStore.bfExists(key, item);
responseBodyMap.set("Key", key);
responseBodyMap.set("Item", item);
responseBodyMap.set("Response", exists ? "true" : "false");
break;
}
}

const responseBody = stringifyMap(responseBodyMap);

set_buffer_bytes(
BufferTypeValues.HttpResponseBody,
0,
<u32>body_buffer_length,
String.UTF8.encode(responseBody)
);
return FilterDataStatusValues.Continue;
}

private sendErrorResponse(errorMsg: string, body_buffer_length: usize): void {
set_property(
"response.status",
String.UTF8.encode(INTERNAL_SERVER_ERROR.toString())
);
log(LogLevelValues.debug, errorMsg);
set_buffer_bytes(
BufferTypeValues.HttpResponseBody,
0,
<u32>body_buffer_length,
String.UTF8.encode('{ "error": "' + errorMsg + '" }')
);
}
}

registerRootContext((context_id: u32) => {
return new HttpBodyRoot(context_id);
}, "httpBody");
Loading