/** Experimental Traefik plugin using WASM and Grain. This is the "main" code for the plugin, and where any operations can be preformed on HTTP requests or responses. Examples here include using Grain's [Pattern Matching](https://grain-lang.org/docs/guide/pattern_matching) on requests to take some actions. > Traefik invokes a WASM plugin by call `handle_request` and `handle_response` exports. > Since these require unsafe pointers, `HttpWasm` module wraps them. So Grain-based "callbacks" can use `registerRequestHandler` or `registerResponseHandler` to avoid needing unsafe code. > But for Traefik's [http-wasm](https://http-wasm.io/) host to find them, the `plugin.gr` must expose these, which just uses the implementation in `WasmHttp` to call any registered handlers here. */ module TraefikGrainPlugin // include grain-lang http-wasm implementation/wrappers from "./lib/http-wasm.gr" include HttpWasm use HttpWasm.* // required: low-level handler must be "provide" from "main" WASM module (`plugin.wasm`) to be found provide { handle_request, handle_response } from "result" include Result from "option" include Option from "list" include List from "map" include Map from "string" include String from "number" include Number from "int64" include Int64 from "json" include Json from "wasi/time" include Time /** * Add new header with start time from WASI */ let addRequestTimestamp = (resp: Request) => { enableFeatures([BufferResponse]) addHeaderValue( ResponseHeader, "X-Grain-Timestamp", toString(Result.unwrap(Time.realTime())) ) true } registerRequestHandler(addRequestTimestamp) /** * Example using middleware config & and to do something to time */ registerRequestHandler((req: Request) => { match (getConfigItem("Headers.Foo")) { Some(val) => addHeaderValue(ResponseHeader, "X-Foo", val), None => void, } true }) /** * Add another header, in a response handler, to record the "time taken" * in both logs and in new header */ let addResponseTimestamp = (resp: Response) => { use Int64.{ (-) } // get the timestamp add in above response handler as "start time" let startTimeString = getHeaderValues(ResponseHeader, "X-Grain-Timestamp") // convert to a Number type, since http-wasm only deals in strings match (Number.parse(stripNulls(startTimeString))) { Ok(startTime) => { let diff = Result.unwrap(Time.realTime()) - Int64.fromNumber(startTime) log(Info, getHeaderValues(ResponseHeader, "Grain Execution Time: " ++ toString(diff) ++ " ns")) addHeaderValue(ResponseHeader, "X-Grain-Timing", toString(diff)) void }, Err(err) => log(Warn, "issue get timing data using" ++ startTimeString ++ " got " ++ toString(err) ++ " from " ++ dumpCodePoints(startTimeString)) } void } registerResponseHandler(addResponseTimestamp) /** * To use another module with handler logic... * The following one of some example code with a `provide` that * classifies HTTP method as a homepage, REST, or CORS-related. * * > Commented out to avoid cluttering logs by default */ from "./examples/patterns/patterns.gr" include PatternClassifier //registerRequestHandler(PatternClassifier.methods)