├── .gitignore ├── LICENSE ├── README.md ├── build.zig ├── build.zig.zon ├── example ├── build.zig ├── build.zig.zon └── src │ └── main.zig ├── logo.png └── src ├── tests.zig ├── zigly.zig └── zigly ├── dictionary.zig ├── errors.zig ├── geo.zig ├── http.zig ├── kv.zig ├── lib.zig ├── logger.zig ├── useragent.zig └── wasm.zig /.gitignore: -------------------------------------------------------------------------------- 1 | zig-cache 2 | zig-out 3 | *~ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Zigly](logo.png) 2 | ======== 3 | 4 | The easiest way to write Fastly Compute services in Zig. 5 | 6 | - [](#) 7 | - [What is Fastly Compute?](#what-is-fastly-compute) 8 | - [What is Zigly?](#what-is-zigly) 9 | - [Usage](#usage) 10 | - [Example application](#example-application) 11 | - [Adding Zigly as a dependency](#adding-zigly-as-a-dependency) 12 | - [A minimal WebAssembly program](#a-minimal-webassembly-program) 13 | - [Testing Fastly Compute modules](#testing-fastly-compute-modules) 14 | - [Using Zigly](#using-zigly) 15 | - [Hello world!](#hello-world) 16 | - [Inspecting incoming requests](#inspecting-incoming-requests) 17 | - [Making HTTP queries](#making-http-queries) 18 | - [Cache override](#cache-override) 19 | - [Pipes](#pipes) 20 | - [Proxying](#proxying) 21 | - [Redirects](#redirects) 22 | - [Response decompression](#response-decompression) 23 | - [Dictionaries](#dictionaries) 24 | - [Logging](#logging) 25 | - [Deployment to Fastly's platform](#deployment-to-fastlys-platform) 26 | 27 | ## What is Fastly Compute? 28 | 29 | [Fastly Compute](https://www.fastly.com/products/compute) is [Fastly](https://fastly.com)'s service to run custom code directly on CDN nodes. 30 | 31 | The service runs anything that can be compiled to WebAssembly, and exports a convenient set of functions to interact with the platform. 32 | 33 | ## What is Zigly? 34 | 35 | Zigly is a library that makes it easy to write Fastly Compute modules in [Zig](https://ziglang.org). 36 | 37 | Beyond the functions exported by the Fastly platform, Zigly will eventually include additional utility functions (cookie manipulation, JWT tokens, tracing...) to make application development as simple as possible. 38 | 39 | Zigly is written for Zig 0.12.x. 40 | 41 | ## Usage 42 | 43 | ### Example application 44 | 45 | Check out the `example` directory. 46 | 47 | This contains an example Fastly application that relays all incoming traffic to a backend server, with transparent caching. 48 | 49 | If you just want to use Fastly as a CDN, this is all you need! 50 | 51 | ### Adding Zigly as a dependency 52 | 53 | Add the dependency to your project: 54 | 55 | ```sh 56 | zig fetch --save=zigly https://github.com/jedisct1/zigly/archive/refs/tags/0.1.9.tar.gz 57 | ``` 58 | 59 | And the following to your `build.zig` file: 60 | 61 | ```zig 62 | const zigly = b.dependency("zigly", .{ 63 | .target = target, 64 | .optimize = optimize, 65 | }); 66 | exe.root_module.addImport("zigly", zigly.module("zigly")); 67 | exe.linkLibrary(zigly.artifact("zigly")); 68 | ``` 69 | 70 | The `zigly` structure can be imported in your application with: 71 | 72 | ```zig 73 | const zigly = @import("zigly"); 74 | ``` 75 | 76 | ### A minimal WebAssembly program 77 | 78 | ```zig 79 | const std = @import("std"); 80 | 81 | pub fn main() !void 82 | std.debug.print("Hello from WebAssembly and Zig!\n", .{}); 83 | } 84 | ``` 85 | 86 | The program can be compiled with (replace `example.zig` with the source file name): 87 | 88 | ```sh 89 | zig build-exe -target wasm32-wasi example.zig 90 | ``` 91 | 92 | Happy with the result? Add `-Doptimize=ReleaseSmall` or `-Doptimize=ReleaseFast` to get very small or very fast module: 93 | 94 | ```sh 95 | zig build-exe -target wasm32-wasi -Doptimize=ReleaseSmall example.zig 96 | ``` 97 | 98 | The example above should not compile to more than 411 bytes. 99 | 100 | If you are using a build file instead, define the target as `wasm32-wasi` in the `build.zig` file: 101 | 102 | ```zig 103 | const target = b.standardTargetOptions(.{ .default_target = .{ .cpu_arch = .wasm32, .os_tag = .wasi } }); 104 | ``` 105 | 106 | ...and build with `zig build -Doptimize=ReleaseSmall` or `-Doptimize=ReleaseFast` to get optimized modules. 107 | 108 | ### Testing Fastly Compute modules 109 | 110 | The easiest way to test the resulting modules is to use [Viceroy](https://github.com/fastly/Viceroy), a reimplementation of the Fastly API that runs locally. 111 | 112 | ### Using Zigly 113 | 114 | #### Hello world! 115 | 116 | ```zig 117 | const downstream = try zigly.downstream(); 118 | var response = downstream.response; 119 | try response.body.writeAll("Hello world!"); 120 | try response.finish(); 121 | ``` 122 | 123 | `downstream()` returns a type representing the initial connection, from a client to the proxy. 124 | 125 | That type includes `response`, that can be used to send a response, as well as `request`, that can be used to inspect the incoming request. 126 | 127 | Every function call may fail with an error from the `FastlyError` set. 128 | 129 | Slightly more complicated example: 130 | 131 | ```zig 132 | const downstream = try zigly.downstream(); 133 | var response = downstream.response; 134 | 135 | response.setStatus(201); 136 | response.headers.set("X-Example", "Header"); 137 | 138 | try response.body.writeAll("Partial"); 139 | try response.flush(); 140 | try response.body.writeAll("Response"); 141 | try response.finish(); 142 | 143 | var logger = Logger.open("logging_endpoint"); 144 | logger.write("Operation sucessful!"); 145 | ``` 146 | 147 | Note that calling `finish()` is always required in order to actually send a response to the client. 148 | 149 | But realistically, most responses will either be simple redirects: 150 | 151 | ```zig 152 | var downstream = try zigly.downstream(); 153 | try downstream.redirect(302, "https://www.perdu.com"); 154 | ``` 155 | 156 | or responding directly from the cache, proxying to the origin if the cached entry is nonexistent or expired: 157 | 158 | ```zig 159 | var downstream = try zigly.downstream(); 160 | try downstream.proxy("google", "www.google.com"); 161 | ``` 162 | 163 | #### Inspecting incoming requests 164 | 165 | Applications can read the body of an incoming requests as well as other informations such as the headers: 166 | 167 | ```zig 168 | const request = downstream.request; 169 | const user_agent = try request.headers.get(allocator, "user-agent"); 170 | if (request.isPost()) { 171 | // method is POST, read the body until the end, up to 1000000 bytes 172 | const body = try request.body.readAll(allocator, 1000000); 173 | } 174 | ``` 175 | 176 | As usual in Zig, memory allocations are never hidden, and applications can choose the allocator they want to use for individual function calls. 177 | 178 | #### Making HTTP queries 179 | 180 | Making HTTP queries is easy: 181 | 182 | ```zig 183 | var query = try zigly.Request.new("GET", "https://example.com"); 184 | var response = try query.send("backend"); 185 | const body = try response.body.readAll(allocator, 0); 186 | ``` 187 | 188 | Arbitrary headers can be added the the outgoing `query`: 189 | 190 | ```zig 191 | try query.headers.set("X-Custom-Header", "Custom value"); 192 | ``` 193 | 194 | Body content can also be pushed, even as chunks: 195 | 196 | ```zig 197 | try query.body.write("X"); 198 | try query.body.write("Y"); 199 | try query.body.close(); 200 | ``` 201 | 202 | And the resulting `response` contains `headers` and `body` properties, that can be inspected the same way as a downstream query. 203 | 204 | #### Cache override 205 | 206 | Caching can be disabled or configured on a per-query basis with `setCachingPolicy()`: 207 | 208 | ```zig 209 | try query.setCachingPolicy(.{ .serve_stale = 600, .pci = true }); 210 | ``` 211 | 212 | Attributes include: 213 | 214 | - `no_cache` 215 | - `ttl` 216 | - `serve_stale` 217 | - `pci` 218 | - `surrogate_key` 219 | 220 | #### Pipes 221 | 222 | With `pipe()`, the response sent to a client can be a direct copy of another response. The application will then act as a proxy, optionally also copying the original status and headers. 223 | 224 | ```zig 225 | var query = try zigly.Request.new("GET", "https://google.com"); 226 | var upstream_response = try query.send("google"); 227 | const downstream = try zigly.downstream(); 228 | try downstream.response.pipe(&upstream_response, true, true); 229 | ``` 230 | 231 | ### Proxying 232 | 233 | Proxying is even easier to use than pipes when a query should be sent unmodified (with the exception of the `Host` header) to the origin: 234 | 235 | ```zig 236 | var downstream = try zigly.downstream(); 237 | try downstream.proxy("google", "www.google.com"); 238 | ``` 239 | 240 | The second parameter is optional. If `null`, the original `Host` header will not be modified. 241 | 242 | ### Redirects 243 | 244 | Redirecting the client to another address can be done with a single function call on the downstream object: 245 | 246 | ```zig 247 | const downstream = try zigly.downstream(); 248 | try downstream.redirect(302, "https://www.perdu.com"); 249 | ``` 250 | 251 | ### Response decompression 252 | 253 | By default, responses are left as-is. Which means that if compression (`Content-Encoding`) was accepted by the client, the response can be compressed. 254 | 255 | Calling `setAutoDecompressResponse(true)` on a `Request` object configures the Fastly Compute runtime to decompress gzip-encoded responses before streaming them to the application. 256 | 257 | #### Dictionaries 258 | 259 | ```zig 260 | const dict = try zigly.Dictionary.open("name"); 261 | const value = try dict.get(allocator, "key"); 262 | ``` 263 | 264 | #### Logging 265 | 266 | ```zig 267 | const logger = try zigly.Logger.open("endpoint); 268 | try logger.write("Log entry"); 269 | ``` 270 | 271 | ## Deployment to Fastly's platform 272 | 273 | The `fastly` command-line tool only supports compilation of Rust and AssemblyScript at the moment. 274 | However, it can still be used to upload pre-compiled code written in other languages, including Zig. 275 | 276 | 1. Create a new project: 277 | 278 | ```sh 279 | fastly compute init 280 | ``` 281 | 282 | For the language, select `Other (pre-compiled WASM binary)`. 283 | 284 | 2. Add a build script: 285 | 286 | Add the following lines to the fastly.toml file: 287 | 288 | ```toml 289 | [scripts] 290 | build = "zig build -Doptimize=ReleaseSmall -Dtarget=wasm32-wasi && mkdir -p bin && cp zig-out/bin/*.wasm bin/main.wasm" 291 | ``` 292 | 293 | 3. Compile and package the Fastly Compute module: 294 | 295 | ```sh 296 | fastly compute build 297 | ``` 298 | 299 | 4. Test locally 300 | 301 | ```sh 302 | fastly compute serve 303 | ``` 304 | 305 | 5. Deploy! 306 | 307 | ```sh 308 | fastly compute deploy 309 | ``` 310 | 311 | In order to deploy new versions, repeat steps 3 and 5. 312 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build) !void { 4 | const target = b.standardTargetOptions(.{ 5 | .default_target = .{ 6 | .cpu_arch = .wasm32, 7 | .os_tag = .wasi, 8 | }, 9 | }); 10 | const optimize = b.standardOptimizeOption(.{}); 11 | 12 | _ = b.addModule("zigly", .{ 13 | .root_source_file = b.path("src/zigly.zig"), 14 | }); 15 | 16 | const lib = b.addStaticLibrary(.{ 17 | .name = "zigly", 18 | .root_source_file = b.path("src/zigly.zig"), 19 | .target = target, 20 | .optimize = optimize, 21 | }); 22 | b.installArtifact(lib); 23 | 24 | const exe = b.addExecutable(.{ 25 | .name = "zig-tests", 26 | .root_source_file = b.path("src/tests.zig"), 27 | .target = target, 28 | .optimize = optimize, 29 | .strip = true, 30 | }); 31 | b.installArtifact(exe); 32 | } 33 | -------------------------------------------------------------------------------- /build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = .zigly, 3 | .version = "0.1.10", 4 | .fingerprint = 0x9e67876c39819840, 5 | 6 | .paths = .{ 7 | "build.zig", 8 | "build.zig.zon", 9 | "src", 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /example/build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build) void { 4 | const target = b.standardTargetOptions(.{ 5 | .default_target = .{ 6 | .cpu_arch = .wasm32, 7 | .os_tag = .wasi, 8 | }, 9 | }); 10 | const optimize = b.standardOptimizeOption(.{}); 11 | 12 | const exe = b.addExecutable(.{ 13 | .name = "package", 14 | .root_source_file = b.path("src/main.zig"), 15 | .target = target, 16 | .optimize = optimize, 17 | }); 18 | 19 | const zigly = b.dependency("zigly", .{ 20 | .target = target, 21 | .optimize = optimize, 22 | }); 23 | exe.root_module.addImport("zigly", zigly.module("zigly")); 24 | exe.linkLibrary(zigly.artifact("zigly")); 25 | 26 | b.installArtifact(exe); 27 | } 28 | -------------------------------------------------------------------------------- /example/build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = "package", 3 | .version = "0.0.0", 4 | 5 | .minimum_zig_version = "0.12.0", 6 | 7 | .dependencies = .{ 8 | .zigly = .{ 9 | .url = "https://github.com/jedisct1/zigly/archive/refs/tags/0.1.9.tar.gz", 10 | .hash = "1220ce4a09b85beb42d0df825d3043d3e41f1caa185969ca53fa42e5ec731f1e39db", 11 | }, 12 | }, 13 | .paths = .{""}, 14 | } 15 | -------------------------------------------------------------------------------- /example/src/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const zigly = @import("zigly"); 3 | 4 | // The backend name (registered as a "host" for that service in the Fastly UI) 5 | const backend_name = "backend"; 6 | // The hostname to use in the Host header when making requests to the backend 7 | // It can be set to `null` to use the original Host header 8 | const host_header = "example.com"; 9 | 10 | // Proxy all incoming requests to the backend, with transparent caching. 11 | pub fn main() !void { 12 | var downstream = try zigly.downstream(); 13 | try downstream.proxy(backend_name, host_header); 14 | } 15 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jedisct1/zigly/31bd08274828c1dad59916bc95dc2035e734d76f/logo.png -------------------------------------------------------------------------------- /src/tests.zig: -------------------------------------------------------------------------------- 1 | // These are just examples to exercise the bindings 2 | // Only `zigly.zig` and the `zigly` directory need to be included in your actual applications. 3 | 4 | const std = @import("std"); 5 | const ArenaAllocator = std.heap.ArenaAllocator; 6 | 7 | const zigly = @import("zigly.zig"); 8 | const Dictionary = zigly.Dictionary; 9 | const UserAgent = zigly.UserAgent; 10 | const Request = zigly.http.Request; 11 | const Logger = zigly.Logger; 12 | 13 | fn start() !void { 14 | var gpa = std.heap.GeneralPurposeAllocator(.{ .safety = true }){}; 15 | defer _ = gpa.deinit(); 16 | const allocator = gpa.allocator(); 17 | 18 | try zigly.compatibilityCheck(); 19 | 20 | var downstream = try zigly.downstream(); 21 | var request = downstream.request; 22 | 23 | { 24 | var arena = ArenaAllocator.init(allocator); 25 | defer arena.deinit(); 26 | try request.setAutoDecompressResponse(true); 27 | const body = try request.body.readAll(arena.allocator(), 0); 28 | std.debug.print("[{s}]\n", .{body}); 29 | } 30 | 31 | { 32 | var arena = ArenaAllocator.init(allocator); 33 | defer arena.deinit(); 34 | const names = try request.headers.names(arena.allocator()); 35 | for (names) |name| { 36 | std.debug.print("[{s}]\n", .{name}); 37 | } 38 | } 39 | 40 | { 41 | var arena = ArenaAllocator.init(allocator); 42 | defer arena.deinit(); 43 | try request.headers.set("x-test", "test"); 44 | try request.headers.remove("x-test"); 45 | } 46 | 47 | { 48 | var arena = ArenaAllocator.init(allocator); 49 | defer arena.deinit(); 50 | const ua = try request.headers.get(arena.allocator(), "user-agent"); 51 | std.debug.print("UA: [{s}]\n", .{ua}); 52 | } 53 | 54 | { 55 | var method_buf: [16]u8 = undefined; 56 | const method = try request.getMethod(&method_buf); 57 | std.debug.print("[{s}]\n", .{method}); 58 | _ = try request.isPost(); 59 | } 60 | 61 | { 62 | var arena = ArenaAllocator.init(allocator); 63 | defer arena.deinit(); 64 | var query = try Request.new("GET", "https://www.google.com"); 65 | try query.setCachingPolicy(.{ .no_cache = true }); 66 | var response = try query.send("google"); 67 | const body = try response.body.readAll(arena.allocator(), 0); 68 | std.debug.print("{s}\n", .{body}); 69 | } 70 | 71 | { 72 | var arena = ArenaAllocator.init(allocator); 73 | defer arena.deinit(); 74 | 75 | var response = downstream.response; 76 | try response.headers.set("X-MyHeader", "XYZ"); 77 | 78 | try response.setStatus(205); 79 | try response.body.writeAll("OK!\n"); 80 | try response.finish(); 81 | } 82 | 83 | { 84 | var arena = ArenaAllocator.init(allocator); 85 | defer arena.deinit(); 86 | var query = try Request.new("GET", "https://www.google.com"); 87 | var upstream_response = try query.send("google"); 88 | try downstream.response.pipe(&upstream_response, false, false); 89 | } 90 | } 91 | 92 | pub export fn _start() callconv(.C) void { 93 | start() catch unreachable; 94 | } 95 | -------------------------------------------------------------------------------- /src/zigly.zig: -------------------------------------------------------------------------------- 1 | pub usingnamespace @import("zigly/lib.zig"); 2 | -------------------------------------------------------------------------------- /src/zigly/dictionary.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mem = std.mem; 3 | const Allocator = mem.Allocator; 4 | 5 | const wasm = @import("wasm.zig"); 6 | const fastly = @import("errors.zig").fastly; 7 | const errors = fastly.errors; 8 | const FastlyError = errors.FastlyError; 9 | 10 | pub const Dictionary = struct { 11 | handle: wasm.DictionaryHandle, 12 | 13 | /// Access a dictionary given its name. 14 | pub fn open(name: []const u8) !Dictionary { 15 | var handle: wasm.DictionaryHandle = undefined; 16 | try fastly(wasm.FastlyDictionary.open(name.ptr, name.len, &handle)); 17 | return Dictionary{ .handle = handle }; 18 | } 19 | 20 | /// Get the value associated to a key. 21 | pub fn get(self: Dictionary, allocator: Allocator, name: []const u8) ![]const u8 { 22 | var value_len_max: usize = 64; 23 | var value_buf = try allocator.alloc(u8, value_len_max); 24 | var value_len: usize = undefined; 25 | while (true) { 26 | const ret = wasm.FastlyDictionary.get(self.handle, name.ptr, name.len, value_buf.ptr, value_len_max, &value_len); 27 | if (ret) break else |err| { 28 | if (err != FastlyError.FastlyBufferTooSmall) { 29 | return err; 30 | } 31 | value_len_max *= 2; 32 | value_buf = try allocator.realloc(value_buf, value_len_max); 33 | } 34 | } 35 | return value_buf[0..value_len]; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /src/zigly/errors.zig: -------------------------------------------------------------------------------- 1 | const wasm = @import("wasm.zig"); 2 | 3 | pub const FastlyError = error{ 4 | FastlyGenericError, 5 | FastlyInvalidValue, 6 | FastlyBadDescriptor, 7 | FastlyBufferTooSmall, 8 | FastlyUnsupported, 9 | FastlyWrongAlignment, 10 | FastlyHttpParserError, 11 | FastlyHttpUserError, 12 | FastlyHttpIncomplete, 13 | FastlyNone, 14 | FastlyHttpHeaderTooLarge, 15 | FastlyHttpInvalidStatus, 16 | FastlyLimitExceeded, 17 | FastlyAgain, 18 | }; 19 | 20 | pub fn fastly(fastly_status: wasm.FastlyStatus) FastlyError!void { 21 | switch (fastly_status) { 22 | wasm.FastlyStatus.OK => return, 23 | wasm.FastlyStatus.ERROR => return FastlyError.FastlyGenericError, 24 | wasm.FastlyStatus.INVAL => return FastlyError.FastlyInvalidValue, 25 | wasm.FastlyStatus.BADF => return FastlyError.FastlyBadDescriptor, 26 | wasm.FastlyStatus.BUFLEN => return FastlyError.FastlyBufferTooSmall, 27 | wasm.FastlyStatus.UNSUPPORTED => return FastlyError.FastlyUnsupported, 28 | wasm.FastlyStatus.BADALIGN => return FastlyError.FastlyWrongAlignment, 29 | wasm.FastlyStatus.HTTPINVALID => return FastlyError.FastlyHttpParserError, 30 | wasm.FastlyStatus.HTTPUSER => return FastlyError.FastlyHttpUserError, 31 | wasm.FastlyStatus.HTTPINCOMPLETE => return FastlyError.FastlyHttpIncomplete, 32 | wasm.FastlyStatus.NONE => return FastlyError.FastlyNone, 33 | wasm.FastlyStatus.HTTPHEADTOOLARGE => return FastlyError.FastlyHttpHeaderTooLarge, 34 | wasm.FastlyStatus.HTTPINVALIDSTATUS => return FastlyError.FastlyHttpInvalidStatus, 35 | wasm.FastlyStatus.LIMITEXCEEDED => return FastlyError.FastlyLimitExceeded, 36 | wasm.FastlyStatus.AGAIN => return FastlyError.FastlyAgain, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/zigly/geo.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mem = std.mem; 3 | const Allocator = mem.Allocator; 4 | 5 | const wasm = @import("wasm.zig"); 6 | const fastly = @import("errors.zig").fastly; 7 | 8 | pub const Ip = union(enum) { 9 | ip4: [4]u8, 10 | ip16: [16]u8, 11 | 12 | pub fn print(self: Ip, alloc: std.mem.Allocator) ![]const u8 { 13 | if (self == .ip4) { 14 | return try std.fmt.allocPrint( 15 | alloc, 16 | "{}.{}.{}.{}", 17 | .{ 18 | self.ip4[0], 19 | self.ip4[1], 20 | self.ip4[2], 21 | self.ip4[3], 22 | }, 23 | ); 24 | } 25 | 26 | return try std.fmt.allocPrint( 27 | alloc, 28 | "{x:0>2}{x:0>2}:{x:0>2}{x:0>2}:{x:0>2}{x:0>2}:{x:0>2}{x:0>2}:{x:0>2}{x:0>2}:{x:0>2}{x:0>2}:{x:0>2}{x:0>2}:{x:0>2}{x:0>2}", 29 | .{ 30 | self.ip16[0], 31 | self.ip16[1], 32 | self.ip16[2], 33 | self.ip16[3], 34 | self.ip16[4], 35 | self.ip16[5], 36 | self.ip16[6], 37 | self.ip16[7], 38 | self.ip16[8], 39 | self.ip16[9], 40 | self.ip16[10], 41 | self.ip16[11], 42 | self.ip16[12], 43 | self.ip16[13], 44 | self.ip16[14], 45 | self.ip16[15], 46 | }, 47 | ); 48 | } 49 | }; 50 | 51 | test "IPv4 print" { 52 | const v4 = Ip{ .ip4 = .{ 127, 0, 0, 1 } }; 53 | try std.testing.expectEqualStrings(try v4.print(std.heap.page_allocator), "127.0.0.1"); 54 | } 55 | 56 | test "IPv6 print" { 57 | var v6 = Ip{ .ip16 = .{0xff} ** 16 }; 58 | try std.testing.expectEqualStrings( 59 | try v6.print(std.heap.page_allocator), 60 | "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 61 | ); 62 | 63 | v6.ip16[8] = 0x00; 64 | v6.ip16[13] = 0xaa; 65 | try std.testing.expectEqualStrings( 66 | try v6.print(std.heap.page_allocator), 67 | "ffff:ffff:ffff:ffff:00ff:ffff:ffaa:ffff", 68 | ); 69 | } 70 | 71 | /// Response from the call to `lookup`. 72 | /// 73 | /// { 74 | /// "area_code": 415, 75 | /// "as_name": "Fastly, Inc", 76 | /// "as_number": 54113, 77 | /// "city": "San Francisco", 78 | /// "conn_speed": "broadband", 79 | /// "conn_type": "wired", 80 | /// "continent": "NA", 81 | /// "country_code": "US", 82 | /// "country_code3": "USA", 83 | /// "country_name": "United States of America", 84 | /// "gmt_offset": -700, 85 | /// "latitude": 37.77869, 86 | /// "longitude": -122.39557, 87 | /// "metro_code": 0, 88 | /// "postal_code": "94107", 89 | /// "proxy_description": "?", 90 | /// "proxy_type": "?", 91 | /// "region": "CA", 92 | /// "utc_offset": -700 93 | /// } 94 | const Location = struct { 95 | area_code: usize, 96 | as_name: []const u8, 97 | as_number: usize, 98 | city: []const u8, 99 | conn_speed: []const u8, 100 | conn_type: []const u8, 101 | continent: []const u8, 102 | country_code: []const u8, 103 | country_code3: []const u8, 104 | country_name: []const u8, 105 | // Not available in Viceroy: https://github.com/fastly/Viceroy/issues/343 106 | // gmt_offset: isize, 107 | latitude: f32, 108 | longitude: f32, 109 | metro_code: isize, 110 | postal_code: []const u8, 111 | proxy_description: []const u8, 112 | proxy_type: []const u8, 113 | region: []const u8, 114 | utc_offset: isize, 115 | }; 116 | 117 | /// Get location information about an IP address. 118 | /// The function returns a `buf` slice with the location filled with a json 119 | /// response. 120 | /// 121 | /// If `buf` is too small, `FastlyBufferTooSmall` will be returned. 122 | /// 4096 should be a safe size to use. 123 | pub fn lookup(allocator: std.mem.Allocator, ip: Ip, buf: []u8) !std.json.Parsed(Location) { 124 | const ip_bin = if (ip == .ip4) ip.ip4[0..] else ip.ip16[0..]; 125 | var len: usize = undefined; 126 | 127 | try fastly(wasm.FastlyGeo.lookup(ip_bin.ptr, ip_bin.len, buf.ptr, buf.len, &len)); 128 | 129 | return try std.json.parseFromSlice( 130 | Location, 131 | allocator, 132 | buf[0..len], 133 | .{ 134 | .ignore_unknown_fields = true, 135 | }, 136 | ); 137 | } 138 | -------------------------------------------------------------------------------- /src/zigly/http.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mem = std.mem; 3 | const Allocator = mem.Allocator; 4 | const ArrayList = std.ArrayList; 5 | 6 | const wasm = @import("wasm.zig"); 7 | const errors = @import("errors.zig"); 8 | const fastly = errors.fastly; 9 | const FastlyError = errors.FastlyError; 10 | const geo = @import("geo.zig"); 11 | 12 | const RequestHeaders = struct { 13 | handle: wasm.RequestHandle, 14 | 15 | /// Return the full list of header names. 16 | pub fn names(self: RequestHeaders, allocator: Allocator) ![][]const u8 { 17 | var names_list = ArrayList([]const u8).init(allocator); 18 | var cursor: u32 = 0; 19 | var cursor_next: i64 = 0; 20 | while (true) { 21 | var name_len_max: usize = 64; 22 | var name_buf = try allocator.alloc(u8, name_len_max); 23 | var name_len: usize = undefined; 24 | while (true) { 25 | name_len = ~@as(usize, 0); 26 | const ret = fastly(wasm.FastlyHttpReq.header_names_get(self.handle, name_buf.ptr, name_len_max, cursor, &cursor_next, &name_len)); 27 | var retry = name_len == ~@as(usize, 0); 28 | ret catch |err| { 29 | if (err != FastlyError.FastlyBufferTooSmall) { 30 | return err; 31 | } 32 | retry = true; 33 | }; 34 | if (!retry) break; 35 | name_len_max *= 2; 36 | name_buf = try allocator.realloc(name_buf, name_len_max); 37 | } 38 | if (name_len == 0) { 39 | break; 40 | } 41 | if (name_buf[name_len - 1] != 0) { 42 | return FastlyError.FastlyGenericError; 43 | } 44 | const name = name_buf[0 .. name_len - 1]; 45 | try names_list.append(name); 46 | if (cursor_next < 0) { 47 | break; 48 | } 49 | cursor = @as(u32, @intCast(cursor_next)); 50 | } 51 | return names_list.items; 52 | } 53 | 54 | /// Return the value for a header. 55 | pub fn get(self: RequestHeaders, allocator: Allocator, name: []const u8) ![]const u8 { 56 | var value_len_max: usize = 64; 57 | var value_buf = try allocator.alloc(u8, value_len_max); 58 | var value_len: usize = undefined; 59 | while (true) { 60 | const ret = fastly(wasm.FastlyHttpReq.header_value_get( 61 | self.handle, 62 | name.ptr, 63 | name.len, 64 | value_buf.ptr, 65 | value_len_max, 66 | &value_len, 67 | )); 68 | if (ret) break else |err| { 69 | if (err != FastlyError.FastlyBufferTooSmall) { 70 | return err; 71 | } 72 | value_len_max *= 2; 73 | value_buf = try allocator.realloc(value_buf, value_len_max); 74 | } 75 | } 76 | return value_buf[0..value_len]; 77 | } 78 | 79 | /// Return all the values for a header. 80 | pub fn getAll(self: RequestHeaders, allocator: Allocator, name: []const u8) ![][]const u8 { 81 | var values_list = ArrayList([]const u8).init(allocator); 82 | var cursor: u32 = 0; 83 | var cursor_next: i64 = 0; 84 | while (true) { 85 | var value_len_max: usize = 64; 86 | var value_buf = try allocator.alloc(u8, value_len_max); 87 | var value_len: usize = undefined; 88 | while (true) { 89 | value_len = ~@as(usize, 0); 90 | const ret = fastly(wasm.FastlyHttpReq.header_values_get(self.handle, name.ptr, name.len, value_buf.ptr, value_len_max, cursor, &cursor_next, &value_len)); 91 | var retry = value_len == ~@as(usize, 0); 92 | ret catch |err| { 93 | if (err != FastlyError.FastlyBufferTooSmall) { 94 | return err; 95 | } 96 | retry = true; 97 | }; 98 | if (!retry) break; 99 | value_len_max *= 2; 100 | value_buf = try allocator.realloc(value_buf, value_len_max); 101 | } 102 | if (value_len == 0) { 103 | break; 104 | } 105 | if (value_buf[value_len - 1] != 0) { 106 | return FastlyError.FastlyGenericError; 107 | } 108 | const value = value_buf[0 .. value_len - 1]; 109 | try values_list.append(value); 110 | if (cursor_next < 0) { 111 | break; 112 | } 113 | cursor = @as(u32, @intCast(cursor_next)); 114 | } 115 | return values_list.items; 116 | } 117 | 118 | /// Set the value for a header. 119 | pub fn set(self: *RequestHeaders, name: []const u8, value: []const u8) !void { 120 | try fastly(wasm.FastlyHttpReq.header_insert(self.handle, name.ptr, name.len, value.ptr, value.len)); 121 | } 122 | 123 | /// Append a value to a header. 124 | pub fn append(self: *RequestHeaders, allocator: Allocator, name: []const u8, value: []const u8) !void { 125 | var value0 = try allocator.alloc(u8, value.len + 1); 126 | mem.copy(u8, value0[0..value.len], value); 127 | value0[value.len] = 0; 128 | try fastly(wasm.FastlyHttpReq.header_append(self.handle, name.ptr, name.len, value0.ptr, value0.len)); 129 | } 130 | 131 | /// Remove a header. 132 | pub fn remove(self: *RequestHeaders, name: []const u8) !void { 133 | try fastly(wasm.FastlyHttpReq.header_remove(self.handle, name.ptr, name.len)); 134 | } 135 | }; 136 | 137 | const Body = struct { 138 | handle: wasm.BodyHandle, 139 | 140 | /// Possibly partial read of the body content. 141 | /// An empty slice is returned when no data has to be read any more. 142 | pub fn read(self: *Body, buf: []u8) ![]u8 { 143 | var buf_len: usize = undefined; 144 | try fastly(wasm.FastlyHttpBody.read(self.handle, buf.ptr, buf.len, &buf_len)); 145 | return buf[0..buf_len]; 146 | } 147 | 148 | /// Read all the body content. This requires an allocator. 149 | pub fn readAll(self: *Body, allocator: Allocator, max_length: usize) ![]u8 { 150 | const chunk_size: usize = std.heap.page_size_max; 151 | var buf_len = chunk_size; 152 | var pos: usize = 0; 153 | var buf = try allocator.alloc(u8, buf_len); 154 | while (true) { 155 | const chunk = try self.read(buf[pos..]); 156 | if (chunk.len == 0) { 157 | return buf[0..pos]; 158 | } 159 | pos += chunk.len; 160 | if (max_length > 0 and pos >= max_length) { 161 | return buf[0..max_length]; 162 | } 163 | if (buf_len - pos <= chunk_size) { 164 | buf_len += chunk_size; 165 | buf = try allocator.realloc(buf, buf_len); 166 | } 167 | } 168 | } 169 | 170 | /// Add body content. The number of bytes that could be written is returned. 171 | pub fn write(self: *Body, buf: []const u8) !usize { 172 | var written: usize = undefined; 173 | try fastly(wasm.FastlyHttpBody.write(self.handle, buf.ptr, buf.len, wasm.BodyWriteEnd.BACK, &written)); 174 | return written; 175 | } 176 | 177 | /// Add body content. The entire buffer is written. 178 | pub fn writeAll(self: *Body, buf: []const u8) !void { 179 | var pos: usize = 0; 180 | while (pos < buf.len) { 181 | const written = try self.write(buf[pos..]); 182 | pos += written; 183 | } 184 | } 185 | 186 | /// Close the body. 187 | pub fn close(self: *Body) !void { 188 | try fastly(wasm.FastlyHttpBody.close(self.handle)); 189 | } 190 | }; 191 | 192 | /// An HTTP request. 193 | pub const Request = struct { 194 | /// The request headers. 195 | headers: RequestHeaders, 196 | /// The request body. 197 | body: Body, 198 | 199 | /// Return the initial request made to the proxy. 200 | pub fn downstream() !Request { 201 | var req_handle: wasm.RequestHandle = undefined; 202 | var body_handle: wasm.BodyHandle = undefined; 203 | try fastly(wasm.FastlyHttpReq.body_downstream_get(&req_handle, &body_handle)); 204 | return Request{ 205 | .headers = RequestHeaders{ .handle = req_handle }, 206 | .body = Body{ .handle = body_handle }, 207 | }; 208 | } 209 | 210 | /// Copy the HTTP method used by this request. 211 | pub fn getMethod(self: Request, method: []u8) ![]u8 { 212 | var method_len: usize = undefined; 213 | try fastly(wasm.FastlyHttpReq.method_get(self.headers.handle, method.ptr, method.len, &method_len)); 214 | return method[0..method_len]; 215 | } 216 | 217 | /// Return `true` if the request uses the `GET` method. 218 | pub fn isGet(self: Request) !bool { 219 | var method_buf: [64]u8 = undefined; 220 | const method = try self.getMethod(&method_buf); 221 | return mem.eql(u8, method, "GET"); 222 | } 223 | 224 | /// Return `true` if the request uses the `POST` method. 225 | pub fn isPost(self: Request) !bool { 226 | var method_buf: [64]u8 = undefined; 227 | const method = try self.getMethod(&method_buf); 228 | return mem.eql(u8, method, "POST"); 229 | } 230 | 231 | /// Set the method of a request. 232 | pub fn setMethod(self: Request, method: []const u8) !void { 233 | try fastly(wasm.FastlyHttpReq.method_set(self.headers.handle, method.ptr, method.len)); 234 | } 235 | 236 | /// Get the request URI. 237 | /// `uri` is a buffer that should be large enough to store the URI. 238 | /// The function returns the slice containing the actual string. 239 | /// Individual components can be extracted with `Uri.parse()`. 240 | pub fn getUriString(self: Request, uri: []u8) ![]u8 { 241 | var uri_len: usize = undefined; 242 | try fastly(wasm.FastlyHttpReq.uri_get(self.headers.handle, uri.ptr, uri.len, &uri_len)); 243 | return uri[0..uri_len]; 244 | } 245 | 246 | /// Set the request URI. 247 | pub fn setUriString(self: Request, uri: []const u8) !void { 248 | try fastly(wasm.FastlyHttpReq.uri_set(self.headers.handle, uri.ptr, uri.len)); 249 | } 250 | 251 | /// Create a new request. 252 | pub fn new(method: []const u8, uri: []const u8) !Request { 253 | var req_handle: wasm.RequestHandle = undefined; 254 | var body_handle: wasm.BodyHandle = undefined; 255 | try fastly(wasm.FastlyHttpReq.new(&req_handle)); 256 | try fastly(wasm.FastlyHttpBody.new(&body_handle)); 257 | 258 | var request = Request{ 259 | .headers = RequestHeaders{ .handle = req_handle }, 260 | .body = Body{ .handle = body_handle }, 261 | }; 262 | try request.setMethod(method); 263 | try request.setUriString(uri); 264 | return request; 265 | } 266 | 267 | /// Send a request. 268 | pub fn send(self: *Request, backend: []const u8) !IncomingResponse { 269 | var resp_handle: wasm.ResponseHandle = undefined; 270 | var resp_body_handle: wasm.BodyHandle = undefined; 271 | try fastly(wasm.FastlyHttpReq.send(self.headers.handle, self.body.handle, backend.ptr, backend.len, &resp_handle, &resp_body_handle)); 272 | return IncomingResponse{ 273 | .handle = resp_handle, 274 | .headers = ResponseHeaders{ .handle = resp_handle }, 275 | .body = Body{ .handle = resp_body_handle }, 276 | }; 277 | } 278 | 279 | /// Caching policy 280 | pub const CachingPolicy = struct { 281 | /// Bypass the cache 282 | no_cache: bool = false, 283 | /// Enforce a sepcific TTL 284 | ttl: ?u32 = null, 285 | /// Return stale content up to this TTL if the origin is unreachable 286 | serve_stale: ?u32 = null, 287 | /// Activate PCI restrictions 288 | pci: bool = false, 289 | /// Cache with a surrogate key 290 | surrogate_key: []const u8 = "", 291 | }; 292 | 293 | /// Force a caching policy for this request 294 | pub fn setCachingPolicy(self: *Request, policy: CachingPolicy) !void { 295 | var wasm_policy: wasm.CacheOverrideTag = 0; 296 | if (policy.no_cache) { 297 | wasm_policy |= wasm.CACHE_OVERRIDE_TAG_PASS; 298 | } 299 | if (policy.ttl) |_| { 300 | wasm_policy |= wasm.CACHE_OVERRIDE_TAG_TTL; 301 | } 302 | if (policy.serve_stale) |_| { 303 | wasm_policy |= wasm.CACHE_OVERRIDE_TAG_STALE_WHILE_REVALIDATE; 304 | } 305 | if (policy.pci) { 306 | wasm_policy |= wasm.CACHE_OVERRIDE_TAG_PCI; 307 | } 308 | try fastly(wasm.FastlyHttpReq.cache_override_v2_set(self.headers.handle, wasm_policy, policy.ttl orelse 0, policy.serve_stale orelse 0, policy.surrogate_key.ptr, policy.surrogate_key.len)); 309 | } 310 | 311 | /// Automatically decompress the body of the request. 312 | pub fn setAutoDecompressResponse(self: *Request, enable: bool) !void { 313 | const encodings = if (enable) wasm.CONTENT_ENCODINGS_GZIP else 0; 314 | try fastly(wasm.FastlyHttpReq.auto_decompress_response_set(self.headers.handle, encodings)); 315 | } 316 | 317 | /// Close the request prematurely. 318 | pub fn close(self: *Request) !void { 319 | try fastly(wasm.FastlyHttpReq.close(self.headers.handle)); 320 | } 321 | }; 322 | 323 | const ResponseHeaders = struct { 324 | handle: wasm.ResponseHandle, 325 | 326 | /// Return the full list of header names. 327 | pub fn names(self: ResponseHeaders, allocator: Allocator) ![][]const u8 { 328 | var names_list = ArrayList([]const u8).init(allocator); 329 | var cursor: u32 = 0; 330 | var cursor_next: i64 = 0; 331 | while (true) { 332 | var name_len_max: usize = 64; 333 | var name_buf = try allocator.alloc(u8, name_len_max); 334 | var name_len: usize = undefined; 335 | while (true) { 336 | name_len = ~@as(usize, 0); 337 | const ret = fastly(wasm.FastlyHttpResp.header_names_get(self.handle, name_buf.ptr, name_len_max, cursor, &cursor_next, &name_len)); 338 | var retry = name_len == ~@as(usize, 0); 339 | ret catch |err| { 340 | if (err != FastlyError.FastlyBufferTooSmall) { 341 | return err; 342 | } 343 | retry = true; 344 | }; 345 | if (!retry) break; 346 | name_len_max *= 2; 347 | name_buf = try allocator.realloc(name_buf, name_len_max); 348 | } 349 | if (name_len == 0) { 350 | break; 351 | } 352 | if (name_buf[name_len - 1] != 0) { 353 | return FastlyError.FastlyGenericError; 354 | } 355 | const name = name_buf[0 .. name_len - 1]; 356 | try names_list.append(name); 357 | if (cursor_next < 0) { 358 | break; 359 | } 360 | cursor = @as(u32, @intCast(cursor_next)); 361 | } 362 | return names_list.items; 363 | } 364 | 365 | /// Return the value for a header. 366 | pub fn get(self: ResponseHeaders, allocator: Allocator, name: []const u8) ![]const u8 { 367 | var value_len_max: usize = 64; 368 | var value_buf = try allocator.alloc(u8, value_len_max); 369 | var value_len: usize = undefined; 370 | while (true) { 371 | const ret = wasm.FastlyHttpResp.header_value_get(self.handle, name.ptr, name.len, value_buf.ptr, value_len_max, &value_len); 372 | if (ret) break else |err| { 373 | if (err != FastlyError.FastlyBufferTooSmall) { 374 | return err; 375 | } 376 | value_len_max *= 2; 377 | value_buf = try allocator.realloc(value_buf, value_len_max); 378 | } 379 | } 380 | return value_buf[0..value_len]; 381 | } 382 | 383 | /// Return all the values for a header. 384 | pub fn getAll(self: RequestHeaders, allocator: Allocator, name: []const u8) ![][]const u8 { 385 | var values_list = ArrayList([]const u8).init(allocator); 386 | var cursor: u32 = 0; 387 | var cursor_next: i64 = 0; 388 | while (true) { 389 | var value_len_max: usize = 64; 390 | var value_buf = try allocator.alloc(u8, value_len_max); 391 | var value_len: usize = undefined; 392 | while (true) { 393 | value_len = ~@as(usize, 0); 394 | const ret = fastly(wasm.FastlyHttpResp.header_values_get(self.handle, name.ptr, name.len, value_buf.ptr, value_len_max, cursor, &cursor_next, &value_len)); 395 | var retry = value_len == ~@as(usize, 0); 396 | ret catch |err| { 397 | if (err != FastlyError.FastlyBufferTooSmall) { 398 | return err; 399 | } 400 | retry = true; 401 | }; 402 | if (!retry) break; 403 | value_len_max *= 2; 404 | value_buf = try allocator.realloc(value_buf, value_len_max); 405 | } 406 | if (value_len == 0) { 407 | break; 408 | } 409 | if (value_buf[value_len - 1] != 0) { 410 | return FastlyError.FastlyGenericError; 411 | } 412 | const value = value_buf[0 .. value_len - 1]; 413 | try values_list.append(value); 414 | if (cursor_next < 0) { 415 | break; 416 | } 417 | cursor = @as(u32, @intCast(cursor_next)); 418 | } 419 | return values_list.items; 420 | } 421 | 422 | /// Set a header to a value. 423 | pub fn set(self: *ResponseHeaders, name: []const u8, value: []const u8) !void { 424 | try fastly(wasm.FastlyHttpResp.header_insert(self.handle, name.ptr, name.len, value.ptr, value.len)); 425 | } 426 | 427 | /// Append a value to a header. 428 | pub fn append(self: *ResponseHeaders, allocator: Allocator, name: []const u8, value: []const u8) !void { 429 | var value0 = try allocator.alloc(u8, value.len + 1); 430 | mem.copy(u8, value0[0..value.len], value); 431 | value0[value.len] = 0; 432 | try fastly(wasm.FastlyHttpResp.header_append(self.handle, name.ptr, name.len, value0.ptr, value0.len)); 433 | } 434 | 435 | /// Remove a header. 436 | pub fn remove(self: *ResponseHeaders, name: []const u8) !void { 437 | try fastly(wasm.FastlyHttpResp.header_remove(self.handle, name.ptr, name.len)); 438 | } 439 | }; 440 | 441 | const OutgoingResponse = struct { 442 | handle: wasm.ResponseHandle, 443 | headers: ResponseHeaders, 444 | body: Body, 445 | 446 | /// The response to the initial query sent to the proxy. 447 | pub fn downstream() !OutgoingResponse { 448 | var resp_handle: wasm.ResponseHandle = undefined; 449 | var body_handle: wasm.BodyHandle = undefined; 450 | try fastly(wasm.FastlyHttpResp.new(&resp_handle)); 451 | try fastly(wasm.FastlyHttpBody.new(&body_handle)); 452 | return OutgoingResponse{ 453 | .handle = resp_handle, 454 | .headers = ResponseHeaders{ .handle = resp_handle }, 455 | .body = Body{ .handle = body_handle }, 456 | }; 457 | } 458 | 459 | /// Send a buffered response, but don't close the stream. 460 | /// Either call `finish` or `body.close` at the end of the response. 461 | pub fn flush(self: *OutgoingResponse) !void { 462 | try fastly(wasm.FastlyHttpResp.send_downstream(self.handle, self.body.handle, 1)); 463 | } 464 | 465 | /// Send an unbuffered response and close the stream. 466 | pub fn finish(self: *OutgoingResponse) !void { 467 | try fastly(wasm.FastlyHttpResp.send_downstream(self.handle, self.body.handle, 0)); 468 | } 469 | 470 | /// Get a the status code of a response. 471 | pub fn getStatus(self: OutgoingResponse) !u16 { 472 | var status: wasm.HttpStatus = undefined; 473 | try fastly(wasm.FastlyHttpResp.status_get(self.handle, &status)); 474 | return @as(u16, @intCast(status)); 475 | } 476 | 477 | /// Change the status code of a response. 478 | pub fn setStatus(self: *OutgoingResponse, status: u16) !void { 479 | try fastly(wasm.FastlyHttpResp.status_set(self.handle, @as(wasm.HttpStatus, @intCast(status)))); 480 | } 481 | 482 | /// Zero-copy the content of an incoming response. 483 | /// The status from the incoming response is copied if `copy_status` is set to `true`, 484 | /// and the headers are copied if `copy_headers` is set to `true`. 485 | pub fn pipe(self: *OutgoingResponse, incoming: *IncomingResponse, copy_status: bool, copy_headers: bool) !void { 486 | if (copy_status) { 487 | try self.setStatus(try incoming.getStatus()); 488 | } 489 | try fastly(wasm.FastlyHttpResp.send_downstream(if (copy_headers) incoming.handle else self.handle, incoming.body.handle, 0)); 490 | } 491 | 492 | /// Prematurely close the response without sending potentially buffered data. 493 | pub fn cancel(self: *Request) !void { 494 | try fastly(wasm.FastlyHttpResp.close(self.handle)); 495 | } 496 | }; 497 | 498 | pub const IncomingResponse = struct { 499 | handle: wasm.ResponseHandle, 500 | headers: ResponseHeaders, 501 | body: Body, 502 | 503 | /// Get the status code of a response. 504 | pub fn getStatus(self: IncomingResponse) !u16 { 505 | var status: wasm.HttpStatus = undefined; 506 | try fastly(wasm.FastlyHttpResp.status_get(self.handle, &status)); 507 | return @as(u16, @intCast(status)); 508 | } 509 | 510 | /// Close the response after use. 511 | pub fn close(self: *IncomingResponse) !void { 512 | try fastly(wasm.FastlyHttpResp.close(self.handle)); 513 | } 514 | }; 515 | 516 | pub const Downstream = struct { 517 | /// Initial request sent to the proxy. 518 | request: Request, 519 | /// Response to the initial request sent to the proxy. 520 | response: OutgoingResponse, 521 | 522 | /// Redirect to a different URI, with the given status code (usually 301 or 302) 523 | pub fn redirect(self: *Downstream, status: u16, uri: []const u8) !void { 524 | var response = self.response; 525 | try response.setStatus(status); 526 | try response.headers.set("Location", uri); 527 | try response.finish(); 528 | } 529 | 530 | /// Proxy the request and its response to the origin, optionally changing the Host header field 531 | pub fn proxy(self: *Downstream, backend: []const u8, host_header: ?[]const u8) !void { 532 | if (host_header) |host| { 533 | try self.request.headers.set("Host", host); 534 | } 535 | try fastly(wasm.FastlyHttpReq.send(self.request.headers.handle, self.request.body.handle, backend.ptr, backend.len, &self.response.handle, &self.response.body.handle)); 536 | try self.response.finish(); 537 | } 538 | 539 | /// Get the downstream client IP address 540 | pub fn getClientIpAddr() !geo.Ip { 541 | var ip = [_]u8{0} ** 16; 542 | var count: usize = 0; 543 | 544 | try fastly(wasm.FastlyHttpReq.downstream_client_ip_addr(&ip, &count)); 545 | 546 | if (count == 16) { 547 | return geo.Ip{ .ip16 = ip }; 548 | } 549 | 550 | var ipv4 = [_]u8{0} ** 4; 551 | std.mem.copyForwards(u8, ipv4[0..], ip[0..4]); 552 | 553 | return geo.Ip{ .ip4 = ipv4 }; 554 | } 555 | }; 556 | 557 | /// The initial connection to the proxy. 558 | pub fn downstream() !Downstream { 559 | return Downstream{ 560 | .request = try Request.downstream(), 561 | .response = try OutgoingResponse.downstream(), 562 | }; 563 | } 564 | -------------------------------------------------------------------------------- /src/zigly/kv.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mem = std.mem; 3 | const Allocator = mem.Allocator; 4 | 5 | const Body = @import("http").Body; 6 | 7 | const wasm = @import("wasm.zig"); 8 | const errors = @import("errors.zig"); 9 | const fastly = errors.fastly; 10 | 11 | pub const Store = struct { 12 | handle: wasm.KvStoreHandle, 13 | 14 | pub fn open(name: []const u8) !Store { 15 | var handle: wasm.KvStoreHandle = -1; 16 | try fastly(wasm.FastlyKv.open(name.ptr, name.len, &handle)); 17 | if (handle == -1) { 18 | return errors.FastlyError.FastlyInvalidValue; 19 | } 20 | return Store{ .handle = handle }; 21 | } 22 | 23 | pub fn close(_: *Store) !void { 24 | // No-op; there is no close() hostcall. 25 | } 26 | 27 | pub fn getAsHttpBody(store: *Store, key: []const u8) !Body { 28 | var body_handle: wasm.KvStoreBodyHandle = -1; 29 | try fastly(wasm.FastlyKv.lookup(store.handle, key.ptr, key.len, &body_handle)); 30 | if (body_handle == -1) { 31 | return fastly(wasm.FastlyStatus.INVAL); 32 | } 33 | return Body{ .handle = body_handle }; 34 | } 35 | 36 | pub fn getAll(store: *Store, key: []const u8, allocator: Allocator, max_length: usize) !void { 37 | var body = try getAsHttpBody(store, key); 38 | try body.readAll(allocator, max_length); 39 | } 40 | 41 | pub fn replace(store: *Store, key: []const u8, value: []const u8, ttl: u32) !void { 42 | var body_handle: wasm.KvStoreBodyHandle = undefined; 43 | try fastly(wasm.FastlyHttpBody.new(&body_handle)); 44 | 45 | var body: Body = Body{ .handle = body_handle }; 46 | try body.writeAll(value); 47 | 48 | const inserted: wasm.Inserted = undefined; 49 | try fastly(wasm.FastlyKv.insert(store.handle, key.ptr, key.len, body_handle, ttl, inserted)); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /src/zigly/lib.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const wasm = @import("wasm.zig"); 3 | const errors = @import("errors.zig"); 4 | const fastly = errors.fastly; 5 | 6 | pub const Uri = std.Uri; 7 | 8 | pub const FastlyError = errors.FastlyError; 9 | pub const UserAgent = @import("useragent.zig").UserAgent; 10 | pub const Dictionary = @import("dictionary.zig").Dictionary; 11 | pub const Logger = @import("logger.zig").Logger; 12 | pub const http = @import("http.zig"); 13 | pub const downstream = http.downstream; 14 | pub const geo = @import("geo.zig"); 15 | pub const kv = @import("kv.zig"); 16 | 17 | /// Check that the module is compatible with the current version of the API. 18 | pub fn compatibilityCheck() !void { 19 | try fastly(wasm.FastlyAbi.init(1)); 20 | } 21 | -------------------------------------------------------------------------------- /src/zigly/logger.zig: -------------------------------------------------------------------------------- 1 | const wasm = @import("wasm.zig"); 2 | const fastly = @import("errors.zig").fastly; 3 | 4 | pub const Logger = struct { 5 | handle: wasm.EndpointHandle, 6 | 7 | /// Create a logger for a given endpoint. 8 | pub fn open(name: []const u8) !Logger { 9 | var handle: wasm.EndpointHandle = undefined; 10 | try fastly(wasm.FastlyLog.endpoint_get(name.ptr, name.len, &handle)); 11 | return Logger{ .handle = handle }; 12 | } 13 | 14 | /// Send a message to a logging endpoint. 15 | pub fn write(self: *Logger, msg: []const u8) !void { 16 | var written: usize = undefined; 17 | try fastly(wasm.FastlyLog.write(self.handle, msg.ptr, msg.len, &written)); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/zigly/useragent.zig: -------------------------------------------------------------------------------- 1 | const wasm = @import("wasm.zig"); 2 | const fastly = @import("errors.zig").fastly; 3 | 4 | /// Parse user agent information. 5 | pub const UserAgent = struct { 6 | pub fn parse(user_agent: []const u8, family: []u8, major: []u8, minor: []u8, patch: []u8) !struct { family: []u8, major: []u8, minor: []u8, patch: []u8 } { 7 | var family_len: usize = undefined; 8 | var major_len: usize = undefined; 9 | var minor_len: usize = undefined; 10 | var patch_len: usize = undefined; 11 | try fastly(wasm.FastlyUap.parse(user_agent.ptr, user_agent.len, &family, family.len, &family_len, &major, major.len, &major_len, &minor, minor.len, &minor_len, &patch, patch.len, &patch_len)); 12 | const ret = .{ 13 | .family = family[0..family_len], 14 | .major = major[0..major_len], 15 | .minor = minor[0..minor_len], 16 | .patch = patch[0..patch_len], 17 | }; 18 | return ret; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/zigly/wasm.zig: -------------------------------------------------------------------------------- 1 | // 2 | // This file was automatically generated by witx-codegen - Do not edit manually. 3 | // 4 | 5 | pub const WasiHandle = i32; 6 | pub const Char8 = u8; 7 | pub const Char32 = u32; 8 | pub fn WasiPtr(comptime T: type) type { 9 | return [*c]const T; 10 | } 11 | pub fn WasiMutPtr(comptime T: type) type { 12 | return [*c]T; 13 | } 14 | pub const WasiStringBytesPtr = WasiPtr(Char8); 15 | 16 | pub const WasiString = extern struct { 17 | ptr: WasiStringBytesPtr, 18 | len: usize, 19 | 20 | fn from_slice(slice: []const u8) WasiString { 21 | return WasiString{ .ptr = slice.ptr, .len = slice.len }; 22 | } 23 | 24 | fn as_slice(wasi_string: WasiString) []const u8 { 25 | return wasi_string.ptr[wasi_string.len]; 26 | } 27 | }; 28 | 29 | pub fn WasiSlice(comptime T: type) type { 30 | return extern struct { 31 | ptr: WasiPtr(T), 32 | len: usize, 33 | 34 | fn from_slice(slice: []const u8) WasiSlice { 35 | return WasiSlice{ .ptr = slice.ptr, .len = slice.len }; 36 | } 37 | 38 | fn as_slice(wasi_slice: WasiSlice) []const u8 { 39 | return wasi_slice.ptr[wasi_slice.len]; 40 | } 41 | }; 42 | } 43 | 44 | pub fn WasiMutSlice(comptime T: type) type { 45 | return extern struct { 46 | ptr: WasiMutPtr(T), 47 | len: usize, 48 | 49 | fn from_slice(slice: []u8) WasiMutSlice { 50 | return WasiMutSlice{ .ptr = slice.ptr, .len = slice.len }; 51 | } 52 | 53 | fn as_slice(wasi_slice: WasiMutSlice) []u8 { 54 | return wasi_slice.ptr[wasi_slice.len]; 55 | } 56 | }; 57 | } 58 | 59 | // ---------------------- Module: [typenames] ---------------------- 60 | 61 | /// Status codes returned from hostcalls. 62 | pub const FastlyStatus = enum(u32) { 63 | OK = 0, 64 | ERROR = 1, 65 | INVAL = 2, 66 | BADF = 3, 67 | BUFLEN = 4, 68 | UNSUPPORTED = 5, 69 | BADALIGN = 6, 70 | HTTPINVALID = 7, 71 | HTTPUSER = 8, 72 | HTTPINCOMPLETE = 9, 73 | NONE = 10, 74 | HTTPHEADTOOLARGE = 11, 75 | HTTPINVALIDSTATUS = 12, 76 | LIMITEXCEEDED = 13, 77 | AGAIN = 14, 78 | }; 79 | 80 | /// A tag indicating HTTP protocol versions. 81 | pub const HttpVersion = enum(u32) { 82 | HTTP_09 = 0, 83 | HTTP_10 = 1, 84 | HTTP_11 = 2, 85 | H_2 = 3, 86 | H_3 = 4, 87 | }; 88 | 89 | /// HTTP status codes. 90 | pub const HttpStatus = u16; 91 | 92 | pub const BodyWriteEnd = enum(u32) { 93 | BACK = 0, 94 | FRONT = 1, 95 | }; 96 | 97 | /// A handle to an HTTP request or response body. 98 | pub const BodyHandle = WasiHandle; 99 | 100 | /// A handle to an HTTP request. 101 | pub const RequestHandle = WasiHandle; 102 | 103 | /// A handle to an HTTP response. 104 | pub const ResponseHandle = WasiHandle; 105 | 106 | /// A handle to a currently-pending asynchronous HTTP request. 107 | pub const PendingRequestHandle = WasiHandle; 108 | 109 | /// A handle to a logging endpoint. 110 | pub const EndpointHandle = WasiHandle; 111 | 112 | /// A handle to an Edge Dictionary. 113 | pub const DictionaryHandle = WasiHandle; 114 | 115 | /// A handle to an Object Store. 116 | pub const ObjectStoreHandle = WasiHandle; 117 | 118 | /// A handle to a pending Object Store lookup. 119 | pub const PendingObjectStoreLookupHandle = WasiHandle; 120 | 121 | /// A handle to a pending Object Store insert. 122 | pub const PendingObjectStoreInsertHandle = WasiHandle; 123 | 124 | /// A handle to a pending Object Store delete. 125 | pub const PendingObjectStoreDeleteHandle = WasiHandle; 126 | 127 | /// A handle to a Secret Store. 128 | pub const SecretStoreHandle = WasiHandle; 129 | 130 | /// A handle to an individual secret. 131 | pub const SecretHandle = WasiHandle; 132 | 133 | /// A handle to an object supporting generic async operations. 134 | /// Can be either a `body_handle` or a `pending_request_handle`. 135 | /// 136 | /// Each async item has an associated I/O action: 137 | /// 138 | /// * Pending requests: awaiting the response headers / `Response` object 139 | /// * Normal bodies: reading bytes from the body 140 | /// * Streaming bodies: writing bytes to the body 141 | /// 142 | /// For writing bytes, note that there is a large host-side buffer that bytes can eagerly be written 143 | /// into, even before the origin itself consumes that data. 144 | pub const AsyncItemHandle = WasiHandle; 145 | 146 | /// A "multi-value" cursor. 147 | pub const MultiValueCursor = u32; 148 | 149 | /// -1 represents "finished", non-negative represents a $multi_value_cursor: 150 | pub const MultiValueCursorResult = i64; 151 | 152 | /// An override for response caching behavior. 153 | /// A zero value indicates that the origin response's cache control headers should be used. 154 | pub const CacheOverrideTag = u32; 155 | pub const CACHE_OVERRIDE_TAG_PASS: CacheOverrideTag = 0x1; 156 | pub const CACHE_OVERRIDE_TAG_TTL: CacheOverrideTag = 0x2; 157 | pub const CACHE_OVERRIDE_TAG_STALE_WHILE_REVALIDATE: CacheOverrideTag = 0x4; 158 | pub const CACHE_OVERRIDE_TAG_PCI: CacheOverrideTag = 0x8; 159 | 160 | pub const NumBytes = usize; 161 | 162 | pub const HeaderCount = u32; 163 | 164 | pub const IsDone = u32; 165 | 166 | pub const DoneIdx = u32; 167 | 168 | pub const IsValid = u32; 169 | 170 | pub const Inserted = u32; 171 | 172 | pub const ReadyIdx = u32; 173 | 174 | pub const Port = u16; 175 | 176 | pub const TimeoutMs = u32; 177 | 178 | pub const BackendExists = u32; 179 | 180 | pub const IsDynamic = u32; 181 | 182 | pub const IsSsl = u32; 183 | 184 | pub const BackendHealth = enum(u32) { 185 | UNKNOWN = 0, 186 | HEALTHY = 1, 187 | UNHEALTHY = 2, 188 | }; 189 | 190 | pub const ContentEncodings = u32; 191 | pub const CONTENT_ENCODINGS_GZIP: ContentEncodings = 1; 192 | 193 | pub const FramingHeadersMode = enum(u32) { 194 | AUTOMATIC = 0, 195 | MANUALLY_FROM_HEADERS = 1, 196 | }; 197 | 198 | pub const HttpKeepaliveMode = enum(u32) { 199 | AUTOMATIC = 0, 200 | NO_KEEPALIVE = 1, 201 | }; 202 | 203 | pub const TlsVersion = enum(u32) { 204 | TLS_1 = 0, 205 | TLS_1_1 = 1, 206 | TLS_1_2 = 2, 207 | TLS_1_3 = 3, 208 | }; 209 | 210 | pub const BackendConfigOptions = u32; 211 | pub const BACKEND_CONFIG_OPTIONS_RESERVED: BackendConfigOptions = 0x1; 212 | pub const BACKEND_CONFIG_OPTIONS_HOST_OVERRIDE: BackendConfigOptions = 0x2; 213 | pub const BACKEND_CONFIG_OPTIONS_CONNECT_TIMEOUT: BackendConfigOptions = 0x4; 214 | pub const BACKEND_CONFIG_OPTIONS_FIRST_BYTE_TIMEOUT: BackendConfigOptions = 0x8; 215 | pub const BACKEND_CONFIG_OPTIONS_BETWEEN_BYTES_TIMEOUT: BackendConfigOptions = 0x10; 216 | pub const BACKEND_CONFIG_OPTIONS_USE_SSL: BackendConfigOptions = 0x20; 217 | pub const BACKEND_CONFIG_OPTIONS_SSL_MIN_VERSION: BackendConfigOptions = 0x40; 218 | pub const BACKEND_CONFIG_OPTIONS_SSL_MAX_VERSION: BackendConfigOptions = 0x80; 219 | pub const BACKEND_CONFIG_OPTIONS_CERT_HOSTNAME: BackendConfigOptions = 0x100; 220 | pub const BACKEND_CONFIG_OPTIONS_CA_CERT: BackendConfigOptions = 0x200; 221 | pub const BACKEND_CONFIG_OPTIONS_CIPHERS: BackendConfigOptions = 0x400; 222 | pub const BACKEND_CONFIG_OPTIONS_SNI_HOSTNAME: BackendConfigOptions = 0x800; 223 | pub const BACKEND_CONFIG_OPTIONS_DONT_POOL: BackendConfigOptions = 0x1000; 224 | pub const BACKEND_CONFIG_OPTIONS_CLIENT_CERT: BackendConfigOptions = 0x2000; 225 | pub const BACKEND_CONFIG_OPTIONS_GRPC: BackendConfigOptions = 0x4000; 226 | 227 | pub const DynamicBackendConfig = extern struct { 228 | host_override: WasiMutPtr(Char8), 229 | host_override_len: u32, 230 | connect_timeout_ms: u32, 231 | first_byte_timeout_ms: u32, 232 | between_bytes_timeout_ms: u32, 233 | ssl_min_version: TlsVersion, 234 | ssl_max_version: TlsVersion, 235 | cert_hostname: WasiMutPtr(Char8), 236 | cert_hostname_len: u32, 237 | ca_cert: WasiMutPtr(Char8), 238 | ca_cert_len: u32, 239 | ciphers: WasiMutPtr(Char8), 240 | ciphers_len: u32, 241 | sni_hostname: WasiMutPtr(Char8), 242 | sni_hostname_len: u32, 243 | client_certificate: WasiMutPtr(Char8), 244 | client_certificate_len: u32, 245 | client_key: SecretHandle, 246 | }; 247 | 248 | /// TLS client certificate verified result from downstream. 249 | pub const ClientCertVerifyResult = enum(u32) { 250 | OK = 0, 251 | BAD_CERTIFICATE = 1, 252 | CERTIFICATE_REVOKED = 2, 253 | CERTIFICATE_EXPIRED = 3, 254 | UNKNOWN_CA = 4, 255 | CERTIFICATE_MISSING = 5, 256 | CERTIFICATE_UNKNOWN = 6, 257 | }; 258 | 259 | pub const PurgeOptionsMask = u32; 260 | pub const PURGE_OPTIONS_MASK_SOFT_PURGE: PurgeOptionsMask = 1; 261 | pub const PURGE_OPTIONS_MASK_RET_BUF: PurgeOptionsMask = 2; 262 | 263 | pub const PurgeOptions = extern struct { 264 | ret_buf_ptr: WasiMutPtr(u8), 265 | ret_buf_len: usize, 266 | ret_buf_nwritten_out: WasiMutPtr(usize), 267 | }; 268 | 269 | pub const SendErrorDetailTag = enum(u32) { 270 | UNINITIALIZED = 0, 271 | OK = 1, 272 | DNS_TIMEOUT = 2, 273 | DNS_ERROR = 3, 274 | DESTINATION_NOT_FOUND = 4, 275 | DESTINATION_UNAVAILABLE = 5, 276 | DESTINATION_IP_UNROUTABLE = 6, 277 | CONNECTION_REFUSED = 7, 278 | CONNECTION_TERMINATED = 8, 279 | CONNECTION_TIMEOUT = 9, 280 | CONNECTION_LIMIT_REACHED = 10, 281 | TLS_CERTIFICATE_ERROR = 11, 282 | TLS_CONFIGURATION_ERROR = 12, 283 | HTTP_INCOMPLETE_RESPONSE = 13, 284 | HTTP_RESPONSE_HEADER_SECTION_TOO_LARGE = 14, 285 | HTTP_RESPONSE_BODY_TOO_LARGE = 15, 286 | HTTP_RESPONSE_TIMEOUT = 16, 287 | HTTP_RESPONSE_STATUS_INVALID = 17, 288 | HTTP_UPGRADE_FAILED = 18, 289 | HTTP_PROTOCOL_ERROR = 19, 290 | HTTP_REQUEST_CACHE_KEY_INVALID = 20, 291 | HTTP_REQUEST_URI_INVALID = 21, 292 | INTERNAL_ERROR = 22, 293 | TLS_ALERT_RECEIVED = 23, 294 | TLS_PROTOCOL_ERROR = 24, 295 | }; 296 | 297 | /// Mask representing which fields are understood by the guest, and which have been set by the host. 298 | /// 299 | /// When the guest calls hostcalls with a mask, it should set every bit in the mask that corresponds 300 | /// to a defined flag. This signals the host to write only to fields with a set bit, allowing 301 | /// forward compatibility for existing guest programs even after new fields are added to the struct. 302 | pub const SendErrorDetailMask = u32; 303 | pub const SEND_ERROR_DETAIL_MASK_RESERVED: SendErrorDetailMask = 0x1; 304 | pub const SEND_ERROR_DETAIL_MASK_DNS_ERROR_RCODE: SendErrorDetailMask = 0x2; 305 | pub const SEND_ERROR_DETAIL_MASK_DNS_ERROR_INFO_CODE: SendErrorDetailMask = 0x4; 306 | pub const SEND_ERROR_DETAIL_MASK_TLS_ALERT_ID: SendErrorDetailMask = 0x8; 307 | 308 | pub const SendErrorDetail = extern struct { 309 | tag: SendErrorDetailTag, 310 | mask: SendErrorDetailMask, 311 | dns_error_rcode: u16, 312 | dns_error_info_code: u16, 313 | tls_alert_id: u8, 314 | }; 315 | 316 | pub const Blocked = u32; 317 | 318 | pub const Rate = u32; 319 | 320 | pub const Count = u32; 321 | 322 | pub const Has = u32; 323 | 324 | pub const BodyLength = u64; 325 | 326 | pub const Typenames = struct {}; 327 | 328 | // ---------------------- Module: [fastly_abi] ---------------------- 329 | 330 | pub const FastlyAbi = struct { 331 | pub extern "fastly_abi" fn init( 332 | abi_version: u64, 333 | ) callconv(.C) FastlyStatus; 334 | }; 335 | 336 | // ---------------------- Module: [fastly_async_io] ---------------------- 337 | 338 | pub const FastlyAsyncIo = struct { 339 | /// Blocks until one of the given objects is ready for I/O, or the optional timeout expires. 340 | /// 341 | /// Valid object handles includes bodies and pending requests. See the `async_item_handle` 342 | /// definition for more details, including what I/O actions are associated with each handle 343 | /// type. 344 | /// 345 | /// The timeout is specified in milliseconds, or 0 if no timeout is desired. 346 | /// 347 | /// Returns the _index_ (not handle!) of the first object that is ready, or u32::MAX if the 348 | /// timeout expires before any objects are ready for I/O. 349 | pub extern "fastly_async_io" fn select( 350 | hs_ptr: WasiPtr(AsyncItemHandle), 351 | hs_len: usize, 352 | timeout_ms: u32, 353 | result_ptr: WasiMutPtr(ReadyIdx), 354 | ) callconv(.C) FastlyStatus; 355 | 356 | /// Returns 1 if the given async item is "ready" for its associated I/O action, 0 otherwise. 357 | /// 358 | /// If an object is ready, the I/O action is guaranteed to complete without blocking. 359 | /// 360 | /// Valid object handles includes bodies and pending requests. See the `async_item_handle` 361 | /// definition for more details, including what I/O actions are associated with each handle 362 | /// type. 363 | pub extern "fastly_async_io" fn is_ready( 364 | handle: AsyncItemHandle, 365 | result_ptr: WasiMutPtr(IsDone), 366 | ) callconv(.C) FastlyStatus; 367 | }; 368 | 369 | // ---------------------- Module: [fastly_backend] ---------------------- 370 | 371 | pub const FastlyBackend = struct { 372 | pub extern "fastly_backend" fn exists( 373 | backend_ptr: WasiPtr(Char8), 374 | backend_len: usize, 375 | result_ptr: WasiMutPtr(BackendExists), 376 | ) callconv(.C) FastlyStatus; 377 | 378 | pub extern "fastly_backend" fn is_healthy( 379 | backend_ptr: WasiPtr(Char8), 380 | backend_len: usize, 381 | result_ptr: WasiMutPtr(BackendHealth), 382 | ) callconv(.C) FastlyStatus; 383 | 384 | pub extern "fastly_backend" fn is_dynamic( 385 | backend_ptr: WasiPtr(Char8), 386 | backend_len: usize, 387 | result_ptr: WasiMutPtr(IsDynamic), 388 | ) callconv(.C) FastlyStatus; 389 | 390 | pub extern "fastly_backend" fn get_host( 391 | backend_ptr: WasiPtr(Char8), 392 | backend_len: usize, 393 | value: WasiMutPtr(Char8), 394 | value_max_len: usize, 395 | nwritten_out: WasiMutPtr(usize), 396 | ) callconv(.C) FastlyStatus; 397 | 398 | pub extern "fastly_backend" fn get_override_host( 399 | backend_ptr: WasiPtr(Char8), 400 | backend_len: usize, 401 | value: WasiMutPtr(Char8), 402 | value_max_len: usize, 403 | nwritten_out: WasiMutPtr(usize), 404 | ) callconv(.C) FastlyStatus; 405 | 406 | pub extern "fastly_backend" fn get_port( 407 | backend_ptr: WasiPtr(Char8), 408 | backend_len: usize, 409 | result_ptr: WasiMutPtr(Port), 410 | ) callconv(.C) FastlyStatus; 411 | 412 | pub extern "fastly_backend" fn get_connect_timeout_ms( 413 | backend_ptr: WasiPtr(Char8), 414 | backend_len: usize, 415 | result_ptr: WasiMutPtr(TimeoutMs), 416 | ) callconv(.C) FastlyStatus; 417 | 418 | pub extern "fastly_backend" fn get_first_byte_timeout_ms( 419 | backend_ptr: WasiPtr(Char8), 420 | backend_len: usize, 421 | result_ptr: WasiMutPtr(TimeoutMs), 422 | ) callconv(.C) FastlyStatus; 423 | 424 | pub extern "fastly_backend" fn get_between_bytes_timeout_ms( 425 | backend_ptr: WasiPtr(Char8), 426 | backend_len: usize, 427 | result_ptr: WasiMutPtr(TimeoutMs), 428 | ) callconv(.C) FastlyStatus; 429 | 430 | pub extern "fastly_backend" fn is_ssl( 431 | backend_ptr: WasiPtr(Char8), 432 | backend_len: usize, 433 | result_ptr: WasiMutPtr(IsSsl), 434 | ) callconv(.C) FastlyStatus; 435 | 436 | pub extern "fastly_backend" fn get_ssl_min_version( 437 | backend_ptr: WasiPtr(Char8), 438 | backend_len: usize, 439 | result_ptr: WasiMutPtr(TlsVersion), 440 | ) callconv(.C) FastlyStatus; 441 | 442 | pub extern "fastly_backend" fn get_ssl_max_version( 443 | backend_ptr: WasiPtr(Char8), 444 | backend_len: usize, 445 | result_ptr: WasiMutPtr(TlsVersion), 446 | ) callconv(.C) FastlyStatus; 447 | }; 448 | 449 | // ---------------------- Module: [fastly_cache] ---------------------- 450 | 451 | /// The outcome of a cache lookup (either bare or as part of a cache transaction) 452 | pub const CacheHandle = WasiHandle; 453 | 454 | pub const CacheObjectLength = u64; 455 | 456 | pub const CacheDurationNs = u64; 457 | 458 | pub const CacheHitCount = u64; 459 | 460 | /// Extensible options for cache lookup operations; currently used for both `lookup` and `transaction_lookup`. 461 | pub const CacheLookupOptions = extern struct { 462 | request_headers: RequestHandle, 463 | }; 464 | 465 | pub const CacheLookupOptionsMask = u32; 466 | pub const CACHE_LOOKUP_OPTIONS_MASK_RESERVED: CacheLookupOptionsMask = 1; 467 | pub const CACHE_LOOKUP_OPTIONS_MASK_REQUEST_HEADERS: CacheLookupOptionsMask = 2; 468 | 469 | /// Configuration for several hostcalls that write to the cache: 470 | /// - `insert` 471 | /// - `transaction_insert` 472 | /// - `transaction_insert_and_stream_back` 473 | /// - `transaction_update` 474 | /// 475 | /// Some options are only allowed for certain of these hostcalls; see `cache_write_options_mask`. 476 | pub const CacheWriteOptions = extern struct { 477 | max_age_ns: CacheDurationNs, 478 | request_headers: RequestHandle, 479 | vary_rule_ptr: WasiMutPtr(Char8), 480 | vary_rule_len: usize, 481 | __pad32_0: u32 = undefined, 482 | initial_age_ns: CacheDurationNs, 483 | stale_while_revalidate_ns: CacheDurationNs, 484 | surrogate_keys_ptr: WasiMutPtr(Char8), 485 | surrogate_keys_len: usize, 486 | length: CacheObjectLength, 487 | user_metadata_ptr: WasiMutPtr(u8), 488 | user_metadata_len: usize, 489 | }; 490 | 491 | pub const CacheWriteOptionsMask = u32; 492 | pub const CACHE_WRITE_OPTIONS_MASK_RESERVED: CacheWriteOptionsMask = 0x1; 493 | pub const CACHE_WRITE_OPTIONS_MASK_REQUEST_HEADERS: CacheWriteOptionsMask = 0x2; 494 | pub const CACHE_WRITE_OPTIONS_MASK_VARY_RULE: CacheWriteOptionsMask = 0x4; 495 | pub const CACHE_WRITE_OPTIONS_MASK_INITIAL_AGE_NS: CacheWriteOptionsMask = 0x8; 496 | pub const CACHE_WRITE_OPTIONS_MASK_STALE_WHILE_REVALIDATE_NS: CacheWriteOptionsMask = 0x10; 497 | pub const CACHE_WRITE_OPTIONS_MASK_SURROGATE_KEYS: CacheWriteOptionsMask = 0x20; 498 | pub const CACHE_WRITE_OPTIONS_MASK_LENGTH: CacheWriteOptionsMask = 0x40; 499 | pub const CACHE_WRITE_OPTIONS_MASK_USER_METADATA: CacheWriteOptionsMask = 0x80; 500 | pub const CACHE_WRITE_OPTIONS_MASK_SENSITIVE_DATA: CacheWriteOptionsMask = 0x100; 501 | 502 | pub const CacheGetBodyOptions = extern struct { 503 | from: u64, 504 | to: u64, 505 | }; 506 | 507 | pub const CacheGetBodyOptionsMask = u32; 508 | pub const CACHE_GET_BODY_OPTIONS_MASK_RESERVED: CacheGetBodyOptionsMask = 0x1; 509 | pub const CACHE_GET_BODY_OPTIONS_MASK_FROM: CacheGetBodyOptionsMask = 0x2; 510 | pub const CACHE_GET_BODY_OPTIONS_MASK_TO: CacheGetBodyOptionsMask = 0x4; 511 | 512 | /// The status of this lookup (and potential transaction) 513 | pub const CacheLookupState = u32; 514 | pub const CACHE_LOOKUP_STATE_FOUND: CacheLookupState = 0x1; 515 | pub const CACHE_LOOKUP_STATE_USABLE: CacheLookupState = 0x2; 516 | pub const CACHE_LOOKUP_STATE_STALE: CacheLookupState = 0x4; 517 | pub const CACHE_LOOKUP_STATE_MUST_INSERT_OR_UPDATE: CacheLookupState = 0x8; 518 | 519 | pub const FastlyCache = struct { 520 | /// Performs a non-request-collapsing cache lookup. 521 | /// 522 | /// Returns a result without waiting for any request collapsing that may be ongoing. 523 | pub extern "fastly_cache" fn lookup( 524 | cache_key_ptr: WasiPtr(u8), 525 | cache_key_len: usize, 526 | options_mask: CacheLookupOptionsMask, 527 | options: WasiMutPtr(CacheLookupOptions), 528 | result_ptr: WasiMutPtr(CacheHandle), 529 | ) callconv(.C) FastlyStatus; 530 | 531 | /// Performs a non-request-collapsing cache insertion (or update). 532 | /// 533 | /// The returned handle is to a streaming body that is used for writing the object into 534 | /// the cache. 535 | pub extern "fastly_cache" fn insert( 536 | cache_key_ptr: WasiPtr(u8), 537 | cache_key_len: usize, 538 | options_mask: CacheWriteOptionsMask, 539 | options: WasiMutPtr(CacheWriteOptions), 540 | result_ptr: WasiMutPtr(BodyHandle), 541 | ) callconv(.C) FastlyStatus; 542 | 543 | /// The entrypoint to the request-collapsing cache transaction API. 544 | /// 545 | /// This operation always participates in request collapsing and may return stale objects. To bypass 546 | /// request collapsing, use `lookup` and `insert` instead. 547 | pub extern "fastly_cache" fn transaction_lookup( 548 | cache_key_ptr: WasiPtr(u8), 549 | cache_key_len: usize, 550 | options_mask: CacheLookupOptionsMask, 551 | options: WasiMutPtr(CacheLookupOptions), 552 | result_ptr: WasiMutPtr(CacheHandle), 553 | ) callconv(.C) FastlyStatus; 554 | 555 | /// Insert an object into the cache with the given metadata. 556 | /// 557 | /// Can only be used in if the cache handle state includes the `$must_insert_or_update` flag. 558 | /// 559 | /// The returned handle is to a streaming body that is used for writing the object into 560 | /// the cache. 561 | pub extern "fastly_cache" fn transaction_insert( 562 | handle: CacheHandle, 563 | options_mask: CacheWriteOptionsMask, 564 | options: WasiMutPtr(CacheWriteOptions), 565 | result_ptr: WasiMutPtr(BodyHandle), 566 | ) callconv(.C) FastlyStatus; 567 | 568 | /// Insert an object into the cache with the given metadata, and return a readable stream of the 569 | /// bytes as they are stored. 570 | /// 571 | /// This helps avoid the "slow reader" problem on a teed stream, for example when a program wishes 572 | /// to store a backend request in the cache while simultaneously streaming to a client in an HTTP 573 | /// response. 574 | /// 575 | /// The returned body handle is to a streaming body that is used for writing the object _into_ 576 | /// the cache. The returned cache handle provides a separate transaction for reading out the 577 | /// newly cached object to send elsewhere. 578 | pub extern "fastly_cache" fn transaction_insert_and_stream_back( 579 | handle: CacheHandle, 580 | options_mask: CacheWriteOptionsMask, 581 | options: WasiMutPtr(CacheWriteOptions), 582 | result_0_ptr: WasiMutPtr(BodyHandle), 583 | result_1_ptr: WasiMutPtr(CacheHandle), 584 | ) callconv(.C) FastlyStatus; 585 | 586 | /// Update the metadata of an object in the cache without changing its data. 587 | /// 588 | /// Can only be used in if the cache handle state includes both of the flags: 589 | /// - `$found` 590 | /// - `$must_insert_or_update` 591 | pub extern "fastly_cache" fn transaction_update( 592 | handle: CacheHandle, 593 | options_mask: CacheWriteOptionsMask, 594 | options: WasiMutPtr(CacheWriteOptions), 595 | ) callconv(.C) FastlyStatus; 596 | 597 | /// Cancel an obligation to provide an object to the cache. 598 | /// 599 | /// Useful if there is an error before streaming is possible, e.g. if a backend is unreachable. 600 | pub extern "fastly_cache" fn transaction_cancel( 601 | handle: CacheHandle, 602 | ) callconv(.C) FastlyStatus; 603 | 604 | /// Close an ongoing interaction with the cache. 605 | /// 606 | /// If the cache handle state includes the `$must_insert_or_update` (and hence no insert or 607 | /// update has been performed), closing the handle cancels any request collapsing, potentially 608 | /// choosing a new waiter to perform the insertion/update. 609 | pub extern "fastly_cache" fn close( 610 | handle: CacheHandle, 611 | ) callconv(.C) FastlyStatus; 612 | 613 | pub extern "fastly_cache" fn get_state( 614 | handle: CacheHandle, 615 | result_ptr: WasiMutPtr(CacheLookupState), 616 | ) callconv(.C) FastlyStatus; 617 | 618 | /// Gets the user metadata of the found object, returning the `$none` error if there 619 | /// was no found object. 620 | pub extern "fastly_cache" fn get_user_metadata( 621 | handle: CacheHandle, 622 | user_metadata_out_ptr: WasiMutPtr(u8), 623 | user_metadata_out_len: usize, 624 | nwritten_out: WasiMutPtr(usize), 625 | ) callconv(.C) FastlyStatus; 626 | 627 | /// Gets a range of the found object body, returning the `$none` error if there 628 | /// was no found object. 629 | /// 630 | /// The returned `body_handle` must be closed before calling this function again on the same 631 | /// `cache_handle`. 632 | /// 633 | /// Note: until the CacheD protocol is adjusted to fully support this functionality, 634 | /// the body of objects that are past the stale-while-revalidate period will not 635 | /// be available, even when other metadata is. 636 | pub extern "fastly_cache" fn get_body( 637 | handle: CacheHandle, 638 | options_mask: CacheGetBodyOptionsMask, 639 | options: CacheGetBodyOptions, 640 | result_ptr: WasiMutPtr(BodyHandle), 641 | ) callconv(.C) FastlyStatus; 642 | 643 | /// Gets the content length of the found object, returning the `$none` error if there 644 | /// was no found object, or no content length was provided. 645 | pub extern "fastly_cache" fn get_length( 646 | handle: CacheHandle, 647 | result_ptr: WasiMutPtr(CacheObjectLength), 648 | ) callconv(.C) FastlyStatus; 649 | 650 | /// Gets the configured max age of the found object, returning the `$none` error if there 651 | /// was no found object. 652 | pub extern "fastly_cache" fn get_max_age_ns( 653 | handle: CacheHandle, 654 | result_ptr: WasiMutPtr(CacheDurationNs), 655 | ) callconv(.C) FastlyStatus; 656 | 657 | /// Gets the configured stale-while-revalidate period of the found object, returning the 658 | /// `$none` error if there was no found object. 659 | pub extern "fastly_cache" fn get_stale_while_revalidate_ns( 660 | handle: CacheHandle, 661 | result_ptr: WasiMutPtr(CacheDurationNs), 662 | ) callconv(.C) FastlyStatus; 663 | 664 | /// Gets the age of the found object, returning the `$none` error if there 665 | /// was no found object. 666 | pub extern "fastly_cache" fn get_age_ns( 667 | handle: CacheHandle, 668 | result_ptr: WasiMutPtr(CacheDurationNs), 669 | ) callconv(.C) FastlyStatus; 670 | 671 | /// Gets the number of cache hits for the found object, returning the `$none` error if there 672 | /// was no found object. 673 | pub extern "fastly_cache" fn get_hits( 674 | handle: CacheHandle, 675 | result_ptr: WasiMutPtr(CacheHitCount), 676 | ) callconv(.C) FastlyStatus; 677 | }; 678 | 679 | // ---------------------- Module: [fastly_config_store] ---------------------- 680 | 681 | /// A handle to an Config Store. 682 | pub const ConfigStoreHandle = WasiHandle; 683 | 684 | pub const FastlyConfigStore = struct { 685 | pub extern "fastly_config_store" fn open( 686 | name_ptr: WasiPtr(Char8), 687 | name_len: usize, 688 | result_ptr: WasiMutPtr(ConfigStoreHandle), 689 | ) callconv(.C) FastlyStatus; 690 | 691 | pub extern "fastly_config_store" fn get( 692 | h: ConfigStoreHandle, 693 | key_ptr: WasiPtr(Char8), 694 | key_len: usize, 695 | value: WasiMutPtr(Char8), 696 | value_max_len: usize, 697 | result_ptr: WasiMutPtr(NumBytes), 698 | ) callconv(.C) FastlyStatus; 699 | }; 700 | 701 | // ---------------------- Module: [fastly_device_detection] ---------------------- 702 | 703 | pub const FastlyDeviceDetection = struct { 704 | pub extern "fastly_device_detection" fn lookup( 705 | user_agent_ptr: WasiPtr(Char8), 706 | user_agent_len: usize, 707 | buf: WasiMutPtr(Char8), 708 | buf_len: usize, 709 | nwritten_out: WasiMutPtr(usize), 710 | ) callconv(.C) FastlyStatus; 711 | }; 712 | 713 | // ---------------------- Module: [fastly_dictionary] ---------------------- 714 | 715 | pub const FastlyDictionary = struct { 716 | pub extern "fastly_dictionary" fn open( 717 | name_ptr: WasiPtr(Char8), 718 | name_len: usize, 719 | result_ptr: WasiMutPtr(DictionaryHandle), 720 | ) callconv(.C) FastlyStatus; 721 | 722 | pub extern "fastly_dictionary" fn get( 723 | h: DictionaryHandle, 724 | key_ptr: WasiPtr(Char8), 725 | key_len: usize, 726 | value: WasiMutPtr(Char8), 727 | value_max_len: usize, 728 | result_ptr: WasiMutPtr(NumBytes), 729 | ) callconv(.C) FastlyStatus; 730 | }; 731 | 732 | // ---------------------- Module: [fastly_dns] ---------------------- 733 | 734 | pub const DnsLookupHandle = WasiHandle; 735 | 736 | pub const FastlyDns = struct { 737 | /// Lookup the IP addresses (IPv4 + IPv6) associated with a name. 738 | /// Returns a handle to be consumed by lookup_wait(). 739 | pub extern "fastly_dns" fn lookup_addr( 740 | name_ptr: WasiPtr(Char8), 741 | name_len: usize, 742 | result_ptr: WasiMutPtr(DnsLookupHandle), 743 | ) callconv(.C) FastlyStatus; 744 | 745 | /// Lookup the names associated with an IP address. 746 | /// Returns a handle to be consumed by lookup_wait(). 747 | pub extern "fastly_dns" fn lookup_reverse( 748 | ip_ptr: WasiPtr(Char8), 749 | ip_len: usize, 750 | result_ptr: WasiMutPtr(DnsLookupHandle), 751 | ) callconv(.C) FastlyStatus; 752 | 753 | /// Lookup the TXT records associated with a name. 754 | /// Returns a handle to be consumed by lookup_wait(). 755 | pub extern "fastly_dns" fn lookup_txt( 756 | name_ptr: WasiPtr(Char8), 757 | name_len: usize, 758 | result_ptr: WasiMutPtr(DnsLookupHandle), 759 | ) callconv(.C) FastlyStatus; 760 | 761 | /// Wait for a DNS lookup to complete. 762 | /// Returns an array of byte strings. 763 | pub extern "fastly_dns" fn lookup_wait( 764 | handle: DnsLookupHandle, 765 | buf: WasiMutPtr(Char8), 766 | buf_len: usize, 767 | cursor: MultiValueCursor, 768 | ending_cursor_out: WasiMutPtr(MultiValueCursorResult), 769 | nwritten_out: WasiMutPtr(usize), 770 | ) callconv(.C) FastlyStatus; 771 | 772 | /// Send a raw DNS query. 773 | /// Returns a handle to be consumed by lookup_wait_raw(). 774 | pub extern "fastly_dns" fn lookup_raw( 775 | query: WasiPtr(Char8), 776 | query_len: usize, 777 | result_ptr: WasiMutPtr(DnsLookupHandle), 778 | ) callconv(.C) FastlyStatus; 779 | 780 | /// Wait for a raw DNS response. 781 | /// Returns a byte string. 782 | pub extern "fastly_dns" fn lookup_wait_raw( 783 | handle: DnsLookupHandle, 784 | response: WasiMutPtr(Char8), 785 | response_len: usize, 786 | nwritten_out: WasiMutPtr(usize), 787 | ) callconv(.C) FastlyStatus; 788 | }; 789 | 790 | // ---------------------- Module: [fastly_erl] ---------------------- 791 | 792 | pub const FastlyErl = struct { 793 | pub extern "fastly_erl" fn check_rate( 794 | rc_ptr: WasiPtr(Char8), 795 | rc_len: usize, 796 | entry_ptr: WasiPtr(Char8), 797 | entry_len: usize, 798 | delta: u32, 799 | window: u32, 800 | limit: u32, 801 | pb_ptr: WasiPtr(Char8), 802 | pb_len: usize, 803 | ttl: u32, 804 | result_ptr: WasiMutPtr(Blocked), 805 | ) callconv(.C) FastlyStatus; 806 | 807 | pub extern "fastly_erl" fn ratecounter_increment( 808 | rc_ptr: WasiPtr(Char8), 809 | rc_len: usize, 810 | entry_ptr: WasiPtr(Char8), 811 | entry_len: usize, 812 | delta: u32, 813 | ) callconv(.C) FastlyStatus; 814 | 815 | pub extern "fastly_erl" fn ratecounter_lookup_rate( 816 | rc_ptr: WasiPtr(Char8), 817 | rc_len: usize, 818 | entry_ptr: WasiPtr(Char8), 819 | entry_len: usize, 820 | window: u32, 821 | result_ptr: WasiMutPtr(Rate), 822 | ) callconv(.C) FastlyStatus; 823 | 824 | pub extern "fastly_erl" fn ratecounter_lookup_count( 825 | rc_ptr: WasiPtr(Char8), 826 | rc_len: usize, 827 | entry_ptr: WasiPtr(Char8), 828 | entry_len: usize, 829 | duration: u32, 830 | result_ptr: WasiMutPtr(Count), 831 | ) callconv(.C) FastlyStatus; 832 | 833 | pub extern "fastly_erl" fn penaltybox_add( 834 | pb_ptr: WasiPtr(Char8), 835 | pb_len: usize, 836 | entry_ptr: WasiPtr(Char8), 837 | entry_len: usize, 838 | ttl: u32, 839 | ) callconv(.C) FastlyStatus; 840 | 841 | pub extern "fastly_erl" fn penaltybox_has( 842 | pb_ptr: WasiPtr(Char8), 843 | pb_len: usize, 844 | entry_ptr: WasiPtr(Char8), 845 | entry_len: usize, 846 | result_ptr: WasiMutPtr(Has), 847 | ) callconv(.C) FastlyStatus; 848 | }; 849 | 850 | // ---------------------- Module: [fastly_geo] ---------------------- 851 | 852 | pub const FastlyGeo = struct { 853 | pub extern "fastly_geo" fn lookup( 854 | addr_octets: WasiPtr(Char8), 855 | addr_len: usize, 856 | buf: WasiMutPtr(Char8), 857 | buf_len: usize, 858 | nwritten_out: WasiMutPtr(usize), 859 | ) callconv(.C) FastlyStatus; 860 | }; 861 | 862 | // ---------------------- Module: [fastly_http_body] ---------------------- 863 | 864 | pub const FastlyHttpBody = struct { 865 | pub extern "fastly_http_body" fn append( 866 | dest: BodyHandle, 867 | src: BodyHandle, 868 | ) callconv(.C) FastlyStatus; 869 | 870 | pub extern "fastly_http_body" fn new( 871 | result_ptr: WasiMutPtr(BodyHandle), 872 | ) callconv(.C) FastlyStatus; 873 | 874 | pub extern "fastly_http_body" fn read( 875 | h: BodyHandle, 876 | buf: WasiMutPtr(u8), 877 | buf_len: usize, 878 | result_ptr: WasiMutPtr(NumBytes), 879 | ) callconv(.C) FastlyStatus; 880 | 881 | pub extern "fastly_http_body" fn write( 882 | h: BodyHandle, 883 | buf_ptr: WasiPtr(u8), 884 | buf_len: usize, 885 | end: BodyWriteEnd, 886 | result_ptr: WasiMutPtr(NumBytes), 887 | ) callconv(.C) FastlyStatus; 888 | 889 | /// Frees the body on the host. 890 | /// 891 | /// For streaming bodies, this is a _successful_ stream termination, which will signal 892 | /// via framing that the body transfer is complete. 893 | pub extern "fastly_http_body" fn close( 894 | h: BodyHandle, 895 | ) callconv(.C) FastlyStatus; 896 | 897 | /// Frees a streaming body on the host _unsuccessfully_, so that framing makes clear that 898 | /// the body is incomplete. 899 | pub extern "fastly_http_body" fn abandon( 900 | h: BodyHandle, 901 | ) callconv(.C) FastlyStatus; 902 | 903 | pub extern "fastly_http_body" fn trailer_append( 904 | h: BodyHandle, 905 | name_ptr: WasiPtr(u8), 906 | name_len: usize, 907 | value_ptr: WasiPtr(u8), 908 | value_len: usize, 909 | ) callconv(.C) FastlyStatus; 910 | 911 | pub extern "fastly_http_body" fn trailer_names_get( 912 | h: BodyHandle, 913 | buf: WasiMutPtr(Char8), 914 | buf_len: usize, 915 | cursor: MultiValueCursor, 916 | ending_cursor_out: WasiMutPtr(MultiValueCursorResult), 917 | nwritten_out: WasiMutPtr(usize), 918 | ) callconv(.C) FastlyStatus; 919 | 920 | pub extern "fastly_http_body" fn trailer_value_get( 921 | h: BodyHandle, 922 | name_ptr: WasiPtr(u8), 923 | name_len: usize, 924 | value: WasiMutPtr(Char8), 925 | value_max_len: usize, 926 | nwritten_out: WasiMutPtr(usize), 927 | ) callconv(.C) FastlyStatus; 928 | 929 | pub extern "fastly_http_body" fn trailer_values_get( 930 | h: BodyHandle, 931 | name_ptr: WasiPtr(u8), 932 | name_len: usize, 933 | buf: WasiMutPtr(Char8), 934 | buf_len: usize, 935 | cursor: MultiValueCursor, 936 | ending_cursor_out: WasiMutPtr(MultiValueCursorResult), 937 | nwritten_out: WasiMutPtr(usize), 938 | ) callconv(.C) FastlyStatus; 939 | 940 | /// Returns a u64 body length if the length of a body is known, or `FastlyStatus::None` 941 | /// otherwise. 942 | /// 943 | /// If the length is unknown, it is likely due to the body arising from an HTTP/1.1 message with 944 | /// chunked encoding, an HTTP/2 or later message with no `content-length`, or being a streaming 945 | /// body. 946 | /// 947 | /// Note that receiving a length from this function does not guarantee that the full number of 948 | /// bytes can actually be read from the body. For example, when proxying a response from a 949 | /// backend, this length may reflect the `content-length` promised in the response, but if the 950 | /// backend connection is closed prematurely, fewer bytes may be delivered before this body 951 | /// handle can no longer be read. 952 | pub extern "fastly_http_body" fn known_length( 953 | h: BodyHandle, 954 | result_ptr: WasiMutPtr(BodyLength), 955 | ) callconv(.C) FastlyStatus; 956 | }; 957 | 958 | // ---------------------- Module: [fastly_http_req] ---------------------- 959 | 960 | pub const FastlyHttpReq = struct { 961 | pub extern "fastly_http_req" fn body_downstream_get( 962 | result_0_ptr: WasiMutPtr(RequestHandle), 963 | result_1_ptr: WasiMutPtr(BodyHandle), 964 | ) callconv(.C) FastlyStatus; 965 | 966 | pub extern "fastly_http_req" fn cache_override_set( 967 | h: RequestHandle, 968 | tag: CacheOverrideTag, 969 | ttl: u32, 970 | stale_while_revalidate: u32, 971 | ) callconv(.C) FastlyStatus; 972 | 973 | pub extern "fastly_http_req" fn cache_override_v2_set( 974 | h: RequestHandle, 975 | tag: CacheOverrideTag, 976 | ttl: u32, 977 | stale_while_revalidate: u32, 978 | sk_ptr: WasiPtr(u8), 979 | sk_len: usize, 980 | ) callconv(.C) FastlyStatus; 981 | 982 | pub extern "fastly_http_req" fn downstream_client_ip_addr( 983 | addr_octets_out: WasiMutPtr(Char8), 984 | result_ptr: WasiMutPtr(NumBytes), 985 | ) callconv(.C) FastlyStatus; 986 | 987 | pub extern "fastly_http_req" fn downstream_client_h2_fingerprint( 988 | h_2_fp_out: WasiMutPtr(Char8), 989 | h_2_fp_max_len: usize, 990 | nwritten_out: WasiMutPtr(usize), 991 | ) callconv(.C) FastlyStatus; 992 | 993 | pub extern "fastly_http_req" fn downstream_client_request_id( 994 | reqid_out: WasiMutPtr(Char8), 995 | reqid_max_len: usize, 996 | nwritten_out: WasiMutPtr(usize), 997 | ) callconv(.C) FastlyStatus; 998 | 999 | pub extern "fastly_http_req" fn downstream_client_oh_fingerprint( 1000 | ohfp_out: WasiMutPtr(Char8), 1001 | ohfp_max_len: usize, 1002 | nwritten_out: WasiMutPtr(usize), 1003 | ) callconv(.C) FastlyStatus; 1004 | 1005 | pub extern "fastly_http_req" fn downstream_tls_cipher_openssl_name( 1006 | cipher_out: WasiMutPtr(Char8), 1007 | cipher_max_len: usize, 1008 | nwritten_out: WasiMutPtr(usize), 1009 | ) callconv(.C) FastlyStatus; 1010 | 1011 | pub extern "fastly_http_req" fn downstream_tls_protocol( 1012 | protocol_out: WasiMutPtr(Char8), 1013 | protocol_max_len: usize, 1014 | nwritten_out: WasiMutPtr(usize), 1015 | ) callconv(.C) FastlyStatus; 1016 | 1017 | pub extern "fastly_http_req" fn downstream_tls_client_hello( 1018 | chello_out: WasiMutPtr(Char8), 1019 | chello_max_len: usize, 1020 | nwritten_out: WasiMutPtr(usize), 1021 | ) callconv(.C) FastlyStatus; 1022 | 1023 | pub extern "fastly_http_req" fn downstream_tls_raw_client_certificate( 1024 | raw_client_cert_out: WasiMutPtr(Char8), 1025 | raw_client_cert_max_len: usize, 1026 | nwritten_out: WasiMutPtr(usize), 1027 | ) callconv(.C) FastlyStatus; 1028 | 1029 | pub extern "fastly_http_req" fn downstream_tls_client_cert_verify_result( 1030 | result_ptr: WasiMutPtr(ClientCertVerifyResult), 1031 | ) callconv(.C) FastlyStatus; 1032 | 1033 | pub extern "fastly_http_req" fn downstream_tls_ja3_md5( 1034 | cja_3_md_5_out: WasiMutPtr(Char8), 1035 | result_ptr: WasiMutPtr(NumBytes), 1036 | ) callconv(.C) FastlyStatus; 1037 | 1038 | pub extern "fastly_http_req" fn downstream_tls_ja4( 1039 | ja_4_out: WasiMutPtr(Char8), 1040 | ja_4_max_len: usize, 1041 | nwritten_out: WasiMutPtr(usize), 1042 | ) callconv(.C) FastlyStatus; 1043 | 1044 | pub extern "fastly_http_req" fn new( 1045 | result_ptr: WasiMutPtr(RequestHandle), 1046 | ) callconv(.C) FastlyStatus; 1047 | 1048 | pub extern "fastly_http_req" fn header_names_get( 1049 | h: RequestHandle, 1050 | buf: WasiMutPtr(Char8), 1051 | buf_len: usize, 1052 | cursor: MultiValueCursor, 1053 | ending_cursor_out: WasiMutPtr(MultiValueCursorResult), 1054 | nwritten_out: WasiMutPtr(usize), 1055 | ) callconv(.C) FastlyStatus; 1056 | 1057 | pub extern "fastly_http_req" fn original_header_names_get( 1058 | buf: WasiMutPtr(Char8), 1059 | buf_len: usize, 1060 | cursor: MultiValueCursor, 1061 | ending_cursor_out: WasiMutPtr(MultiValueCursorResult), 1062 | nwritten_out: WasiMutPtr(usize), 1063 | ) callconv(.C) FastlyStatus; 1064 | 1065 | pub extern "fastly_http_req" fn original_header_count( 1066 | result_ptr: WasiMutPtr(HeaderCount), 1067 | ) callconv(.C) FastlyStatus; 1068 | 1069 | pub extern "fastly_http_req" fn header_value_get( 1070 | h: RequestHandle, 1071 | name_ptr: WasiPtr(u8), 1072 | name_len: usize, 1073 | value: WasiMutPtr(Char8), 1074 | value_max_len: usize, 1075 | nwritten_out: WasiMutPtr(usize), 1076 | ) callconv(.C) FastlyStatus; 1077 | 1078 | pub extern "fastly_http_req" fn header_values_get( 1079 | h: RequestHandle, 1080 | name_ptr: WasiPtr(u8), 1081 | name_len: usize, 1082 | buf: WasiMutPtr(Char8), 1083 | buf_len: usize, 1084 | cursor: MultiValueCursor, 1085 | ending_cursor_out: WasiMutPtr(MultiValueCursorResult), 1086 | nwritten_out: WasiMutPtr(usize), 1087 | ) callconv(.C) FastlyStatus; 1088 | 1089 | pub extern "fastly_http_req" fn header_values_set( 1090 | h: RequestHandle, 1091 | name_ptr: WasiPtr(u8), 1092 | name_len: usize, 1093 | values_ptr: WasiPtr(Char8), 1094 | values_len: usize, 1095 | ) callconv(.C) FastlyStatus; 1096 | 1097 | pub extern "fastly_http_req" fn header_insert( 1098 | h: RequestHandle, 1099 | name_ptr: WasiPtr(u8), 1100 | name_len: usize, 1101 | value_ptr: WasiPtr(u8), 1102 | value_len: usize, 1103 | ) callconv(.C) FastlyStatus; 1104 | 1105 | pub extern "fastly_http_req" fn header_append( 1106 | h: RequestHandle, 1107 | name_ptr: WasiPtr(u8), 1108 | name_len: usize, 1109 | value_ptr: WasiPtr(u8), 1110 | value_len: usize, 1111 | ) callconv(.C) FastlyStatus; 1112 | 1113 | pub extern "fastly_http_req" fn header_remove( 1114 | h: RequestHandle, 1115 | name_ptr: WasiPtr(u8), 1116 | name_len: usize, 1117 | ) callconv(.C) FastlyStatus; 1118 | 1119 | pub extern "fastly_http_req" fn method_get( 1120 | h: RequestHandle, 1121 | buf: WasiMutPtr(Char8), 1122 | buf_len: usize, 1123 | nwritten_out: WasiMutPtr(usize), 1124 | ) callconv(.C) FastlyStatus; 1125 | 1126 | pub extern "fastly_http_req" fn method_set( 1127 | h: RequestHandle, 1128 | method_ptr: WasiPtr(Char8), 1129 | method_len: usize, 1130 | ) callconv(.C) FastlyStatus; 1131 | 1132 | pub extern "fastly_http_req" fn uri_get( 1133 | h: RequestHandle, 1134 | buf: WasiMutPtr(Char8), 1135 | buf_len: usize, 1136 | nwritten_out: WasiMutPtr(usize), 1137 | ) callconv(.C) FastlyStatus; 1138 | 1139 | pub extern "fastly_http_req" fn uri_set( 1140 | h: RequestHandle, 1141 | uri_ptr: WasiPtr(Char8), 1142 | uri_len: usize, 1143 | ) callconv(.C) FastlyStatus; 1144 | 1145 | pub extern "fastly_http_req" fn version_get( 1146 | h: RequestHandle, 1147 | result_ptr: WasiMutPtr(HttpVersion), 1148 | ) callconv(.C) FastlyStatus; 1149 | 1150 | pub extern "fastly_http_req" fn version_set( 1151 | h: RequestHandle, 1152 | version: HttpVersion, 1153 | ) callconv(.C) FastlyStatus; 1154 | 1155 | pub extern "fastly_http_req" fn send( 1156 | h: RequestHandle, 1157 | b: BodyHandle, 1158 | backend_ptr: WasiPtr(Char8), 1159 | backend_len: usize, 1160 | result_0_ptr: WasiMutPtr(ResponseHandle), 1161 | result_1_ptr: WasiMutPtr(BodyHandle), 1162 | ) callconv(.C) FastlyStatus; 1163 | 1164 | pub extern "fastly_http_req" fn send_v2( 1165 | h: RequestHandle, 1166 | b: BodyHandle, 1167 | backend_ptr: WasiPtr(Char8), 1168 | backend_len: usize, 1169 | error_detail: WasiMutPtr(SendErrorDetail), 1170 | result_0_ptr: WasiMutPtr(ResponseHandle), 1171 | result_1_ptr: WasiMutPtr(BodyHandle), 1172 | ) callconv(.C) FastlyStatus; 1173 | 1174 | pub extern "fastly_http_req" fn send_async( 1175 | h: RequestHandle, 1176 | b: BodyHandle, 1177 | backend_ptr: WasiPtr(Char8), 1178 | backend_len: usize, 1179 | result_ptr: WasiMutPtr(PendingRequestHandle), 1180 | ) callconv(.C) FastlyStatus; 1181 | 1182 | pub extern "fastly_http_req" fn send_async_streaming( 1183 | h: RequestHandle, 1184 | b: BodyHandle, 1185 | backend_ptr: WasiPtr(Char8), 1186 | backend_len: usize, 1187 | result_ptr: WasiMutPtr(PendingRequestHandle), 1188 | ) callconv(.C) FastlyStatus; 1189 | 1190 | pub extern "fastly_http_req" fn pending_req_poll( 1191 | h: PendingRequestHandle, 1192 | result_0_ptr: WasiMutPtr(IsDone), 1193 | result_1_ptr: WasiMutPtr(ResponseHandle), 1194 | result_2_ptr: WasiMutPtr(BodyHandle), 1195 | ) callconv(.C) FastlyStatus; 1196 | 1197 | pub extern "fastly_http_req" fn pending_req_poll_v2( 1198 | h: PendingRequestHandle, 1199 | error_detail: WasiMutPtr(SendErrorDetail), 1200 | result_0_ptr: WasiMutPtr(IsDone), 1201 | result_1_ptr: WasiMutPtr(ResponseHandle), 1202 | result_2_ptr: WasiMutPtr(BodyHandle), 1203 | ) callconv(.C) FastlyStatus; 1204 | 1205 | pub extern "fastly_http_req" fn pending_req_wait( 1206 | h: PendingRequestHandle, 1207 | result_0_ptr: WasiMutPtr(ResponseHandle), 1208 | result_1_ptr: WasiMutPtr(BodyHandle), 1209 | ) callconv(.C) FastlyStatus; 1210 | 1211 | pub extern "fastly_http_req" fn pending_req_wait_v2( 1212 | h: PendingRequestHandle, 1213 | error_detail: WasiMutPtr(SendErrorDetail), 1214 | result_0_ptr: WasiMutPtr(ResponseHandle), 1215 | result_1_ptr: WasiMutPtr(BodyHandle), 1216 | ) callconv(.C) FastlyStatus; 1217 | 1218 | pub extern "fastly_http_req" fn pending_req_select( 1219 | hs_ptr: WasiPtr(PendingRequestHandle), 1220 | hs_len: usize, 1221 | result_0_ptr: WasiMutPtr(DoneIdx), 1222 | result_1_ptr: WasiMutPtr(ResponseHandle), 1223 | result_2_ptr: WasiMutPtr(BodyHandle), 1224 | ) callconv(.C) FastlyStatus; 1225 | 1226 | pub extern "fastly_http_req" fn pending_req_select_v2( 1227 | hs_ptr: WasiPtr(PendingRequestHandle), 1228 | hs_len: usize, 1229 | error_detail: WasiMutPtr(SendErrorDetail), 1230 | result_0_ptr: WasiMutPtr(DoneIdx), 1231 | result_1_ptr: WasiMutPtr(ResponseHandle), 1232 | result_2_ptr: WasiMutPtr(BodyHandle), 1233 | ) callconv(.C) FastlyStatus; 1234 | 1235 | /// Returns whether or not the original client request arrived with a 1236 | /// Fastly-Key belonging to a user with the rights to purge content on this 1237 | /// service. 1238 | pub extern "fastly_http_req" fn fastly_key_is_valid( 1239 | result_ptr: WasiMutPtr(IsValid), 1240 | ) callconv(.C) FastlyStatus; 1241 | 1242 | pub extern "fastly_http_req" fn close( 1243 | h: RequestHandle, 1244 | ) callconv(.C) FastlyStatus; 1245 | 1246 | pub extern "fastly_http_req" fn auto_decompress_response_set( 1247 | h: RequestHandle, 1248 | encodings: ContentEncodings, 1249 | ) callconv(.C) FastlyStatus; 1250 | 1251 | pub extern "fastly_http_req" fn upgrade_websocket( 1252 | backend_name_ptr: WasiPtr(Char8), 1253 | backend_name_len: usize, 1254 | ) callconv(.C) FastlyStatus; 1255 | 1256 | pub extern "fastly_http_req" fn redirect_to_websocket_proxy( 1257 | backend_name_ptr: WasiPtr(Char8), 1258 | backend_name_len: usize, 1259 | ) callconv(.C) FastlyStatus; 1260 | 1261 | pub extern "fastly_http_req" fn redirect_to_grip_proxy( 1262 | backend_name_ptr: WasiPtr(Char8), 1263 | backend_name_len: usize, 1264 | ) callconv(.C) FastlyStatus; 1265 | 1266 | pub extern "fastly_http_req" fn redirect_to_websocket_proxy_v2( 1267 | h: RequestHandle, 1268 | backend_name_ptr: WasiPtr(Char8), 1269 | backend_name_len: usize, 1270 | ) callconv(.C) FastlyStatus; 1271 | 1272 | pub extern "fastly_http_req" fn redirect_to_grip_proxy_v2( 1273 | h: RequestHandle, 1274 | backend_name_ptr: WasiPtr(Char8), 1275 | backend_name_len: usize, 1276 | ) callconv(.C) FastlyStatus; 1277 | 1278 | /// Adjust how this requests's framing headers are determined. 1279 | pub extern "fastly_http_req" fn framing_headers_mode_set( 1280 | h: RequestHandle, 1281 | mode: FramingHeadersMode, 1282 | ) callconv(.C) FastlyStatus; 1283 | 1284 | /// Create a backend for later use 1285 | pub extern "fastly_http_req" fn register_dynamic_backend( 1286 | name_prefix_ptr: WasiPtr(Char8), 1287 | name_prefix_len: usize, 1288 | target_ptr: WasiPtr(Char8), 1289 | target_len: usize, 1290 | backend_config_mask: BackendConfigOptions, 1291 | backend_configuration: WasiMutPtr(DynamicBackendConfig), 1292 | ) callconv(.C) FastlyStatus; 1293 | }; 1294 | 1295 | // ---------------------- Module: [fastly_http_resp] ---------------------- 1296 | 1297 | pub const FastlyHttpResp = struct { 1298 | pub extern "fastly_http_resp" fn new( 1299 | result_ptr: WasiMutPtr(ResponseHandle), 1300 | ) callconv(.C) FastlyStatus; 1301 | 1302 | pub extern "fastly_http_resp" fn header_names_get( 1303 | h: ResponseHandle, 1304 | buf: WasiMutPtr(Char8), 1305 | buf_len: usize, 1306 | cursor: MultiValueCursor, 1307 | ending_cursor_out: WasiMutPtr(MultiValueCursorResult), 1308 | nwritten_out: WasiMutPtr(usize), 1309 | ) callconv(.C) FastlyStatus; 1310 | 1311 | pub extern "fastly_http_resp" fn header_value_get( 1312 | h: ResponseHandle, 1313 | name_ptr: WasiPtr(u8), 1314 | name_len: usize, 1315 | value: WasiMutPtr(Char8), 1316 | value_max_len: usize, 1317 | nwritten_out: WasiMutPtr(usize), 1318 | ) callconv(.C) FastlyStatus; 1319 | 1320 | pub extern "fastly_http_resp" fn header_values_get( 1321 | h: ResponseHandle, 1322 | name_ptr: WasiPtr(u8), 1323 | name_len: usize, 1324 | buf: WasiMutPtr(Char8), 1325 | buf_len: usize, 1326 | cursor: MultiValueCursor, 1327 | ending_cursor_out: WasiMutPtr(MultiValueCursorResult), 1328 | nwritten_out: WasiMutPtr(usize), 1329 | ) callconv(.C) FastlyStatus; 1330 | 1331 | pub extern "fastly_http_resp" fn header_values_set( 1332 | h: ResponseHandle, 1333 | name_ptr: WasiPtr(u8), 1334 | name_len: usize, 1335 | values_ptr: WasiPtr(Char8), 1336 | values_len: usize, 1337 | ) callconv(.C) FastlyStatus; 1338 | 1339 | pub extern "fastly_http_resp" fn header_insert( 1340 | h: ResponseHandle, 1341 | name_ptr: WasiPtr(u8), 1342 | name_len: usize, 1343 | value_ptr: WasiPtr(u8), 1344 | value_len: usize, 1345 | ) callconv(.C) FastlyStatus; 1346 | 1347 | pub extern "fastly_http_resp" fn header_append( 1348 | h: ResponseHandle, 1349 | name_ptr: WasiPtr(u8), 1350 | name_len: usize, 1351 | value_ptr: WasiPtr(u8), 1352 | value_len: usize, 1353 | ) callconv(.C) FastlyStatus; 1354 | 1355 | pub extern "fastly_http_resp" fn header_remove( 1356 | h: ResponseHandle, 1357 | name_ptr: WasiPtr(u8), 1358 | name_len: usize, 1359 | ) callconv(.C) FastlyStatus; 1360 | 1361 | pub extern "fastly_http_resp" fn version_get( 1362 | h: ResponseHandle, 1363 | result_ptr: WasiMutPtr(HttpVersion), 1364 | ) callconv(.C) FastlyStatus; 1365 | 1366 | pub extern "fastly_http_resp" fn version_set( 1367 | h: ResponseHandle, 1368 | version: HttpVersion, 1369 | ) callconv(.C) FastlyStatus; 1370 | 1371 | pub extern "fastly_http_resp" fn send_downstream( 1372 | h: ResponseHandle, 1373 | b: BodyHandle, 1374 | streaming: u32, 1375 | ) callconv(.C) FastlyStatus; 1376 | 1377 | pub extern "fastly_http_resp" fn status_get( 1378 | h: ResponseHandle, 1379 | result_ptr: WasiMutPtr(HttpStatus), 1380 | ) callconv(.C) FastlyStatus; 1381 | 1382 | pub extern "fastly_http_resp" fn status_set( 1383 | h: ResponseHandle, 1384 | status: HttpStatus, 1385 | ) callconv(.C) FastlyStatus; 1386 | 1387 | pub extern "fastly_http_resp" fn close( 1388 | h: ResponseHandle, 1389 | ) callconv(.C) FastlyStatus; 1390 | 1391 | /// Adjust how this response's framing headers are determined. 1392 | pub extern "fastly_http_resp" fn framing_headers_mode_set( 1393 | h: ResponseHandle, 1394 | mode: FramingHeadersMode, 1395 | ) callconv(.C) FastlyStatus; 1396 | 1397 | /// Adjust the response's connection reuse mode. 1398 | pub extern "fastly_http_resp" fn http_keepalive_mode_set( 1399 | h: ResponseHandle, 1400 | mode: HttpKeepaliveMode, 1401 | ) callconv(.C) FastlyStatus; 1402 | }; 1403 | 1404 | // ---------------------- Module: [fastly_kv] ---------------------- 1405 | 1406 | pub const KvStoreHandle = WasiHandle; 1407 | 1408 | pub const FastlyKv = struct { 1409 | pub extern "fastly_kv" fn open( 1410 | name_ptr: WasiPtr(Char8), 1411 | name_len: usize, 1412 | result_ptr: WasiMutPtr(KvStoreHandle), 1413 | ) callconv(.C) FastlyStatus; 1414 | 1415 | pub extern "fastly_kv" fn lookup( 1416 | store: KvStoreHandle, 1417 | key_ptr: WasiPtr(u8), 1418 | key_len: usize, 1419 | opt_body_handle_out: WasiMutPtr(BodyHandle), 1420 | ) callconv(.C) FastlyStatus; 1421 | 1422 | pub extern "fastly_kv" fn insert( 1423 | store: KvStoreHandle, 1424 | key_ptr: WasiPtr(u8), 1425 | key_len: usize, 1426 | body_handle: BodyHandle, 1427 | max_age: u32, 1428 | result_ptr: WasiMutPtr(Inserted), 1429 | ) callconv(.C) FastlyStatus; 1430 | }; 1431 | 1432 | // ---------------------- Module: [fastly_log] ---------------------- 1433 | 1434 | pub const FastlyLog = struct { 1435 | pub extern "fastly_log" fn endpoint_get( 1436 | name_ptr: WasiPtr(u8), 1437 | name_len: usize, 1438 | result_ptr: WasiMutPtr(EndpointHandle), 1439 | ) callconv(.C) FastlyStatus; 1440 | 1441 | pub extern "fastly_log" fn write( 1442 | h: EndpointHandle, 1443 | msg_ptr: WasiPtr(u8), 1444 | msg_len: usize, 1445 | result_ptr: WasiMutPtr(NumBytes), 1446 | ) callconv(.C) FastlyStatus; 1447 | }; 1448 | 1449 | // ---------------------- Module: [fastly_object_store] ---------------------- 1450 | 1451 | pub const FastlyObjectStore = struct { 1452 | pub extern "fastly_object_store" fn open( 1453 | name_ptr: WasiPtr(Char8), 1454 | name_len: usize, 1455 | result_ptr: WasiMutPtr(ObjectStoreHandle), 1456 | ) callconv(.C) FastlyStatus; 1457 | 1458 | pub extern "fastly_object_store" fn lookup( 1459 | store: ObjectStoreHandle, 1460 | key_ptr: WasiPtr(Char8), 1461 | key_len: usize, 1462 | body_handle_out: WasiMutPtr(BodyHandle), 1463 | ) callconv(.C) FastlyStatus; 1464 | 1465 | pub extern "fastly_object_store" fn lookup_async( 1466 | store: ObjectStoreHandle, 1467 | key_ptr: WasiPtr(Char8), 1468 | key_len: usize, 1469 | pending_handle_out: WasiMutPtr(PendingObjectStoreLookupHandle), 1470 | ) callconv(.C) FastlyStatus; 1471 | 1472 | pub extern "fastly_object_store" fn pending_lookup_wait( 1473 | pending_objstr_handle: PendingObjectStoreLookupHandle, 1474 | body_handle_out: WasiMutPtr(BodyHandle), 1475 | ) callconv(.C) FastlyStatus; 1476 | 1477 | pub extern "fastly_object_store" fn lookup_as_fd( 1478 | store: ObjectStoreHandle, 1479 | key_ptr: WasiPtr(Char8), 1480 | key_len: usize, 1481 | fd_out: WasiMutPtr(u32), 1482 | ) callconv(.C) FastlyStatus; 1483 | 1484 | pub extern "fastly_object_store" fn insert( 1485 | store: ObjectStoreHandle, 1486 | key_ptr: WasiPtr(Char8), 1487 | key_len: usize, 1488 | body_handle: BodyHandle, 1489 | ) callconv(.C) FastlyStatus; 1490 | 1491 | pub extern "fastly_object_store" fn insert_async( 1492 | store: ObjectStoreHandle, 1493 | key_ptr: WasiPtr(Char8), 1494 | key_len: usize, 1495 | body_handle: BodyHandle, 1496 | pending_handle_out: WasiMutPtr(PendingObjectStoreInsertHandle), 1497 | ) callconv(.C) FastlyStatus; 1498 | 1499 | pub extern "fastly_object_store" fn pending_insert_wait( 1500 | pending_objstr_handle: PendingObjectStoreInsertHandle, 1501 | ) callconv(.C) FastlyStatus; 1502 | 1503 | pub extern "fastly_object_store" fn delete_async( 1504 | store: ObjectStoreHandle, 1505 | key_ptr: WasiPtr(Char8), 1506 | key_len: usize, 1507 | pending_handle_out: WasiMutPtr(PendingObjectStoreDeleteHandle), 1508 | ) callconv(.C) FastlyStatus; 1509 | 1510 | pub extern "fastly_object_store" fn pending_delete_wait( 1511 | pending_objstr_handle: PendingObjectStoreDeleteHandle, 1512 | ) callconv(.C) FastlyStatus; 1513 | }; 1514 | 1515 | // ---------------------- Module: [fastly_purge] ---------------------- 1516 | 1517 | pub const FastlyPurge = struct { 1518 | pub extern "fastly_purge" fn purge_surrogate_key( 1519 | surrogate_key_ptr: WasiPtr(Char8), 1520 | surrogate_key_len: usize, 1521 | options_mask: PurgeOptionsMask, 1522 | options: WasiMutPtr(PurgeOptions), 1523 | ) callconv(.C) FastlyStatus; 1524 | }; 1525 | 1526 | // ---------------------- Module: [fastly_secret_store] ---------------------- 1527 | 1528 | pub const FastlySecretStore = struct { 1529 | pub extern "fastly_secret_store" fn open( 1530 | name_ptr: WasiPtr(Char8), 1531 | name_len: usize, 1532 | result_ptr: WasiMutPtr(SecretStoreHandle), 1533 | ) callconv(.C) FastlyStatus; 1534 | 1535 | pub extern "fastly_secret_store" fn get( 1536 | store: SecretStoreHandle, 1537 | key_ptr: WasiPtr(Char8), 1538 | key_len: usize, 1539 | result_ptr: WasiMutPtr(SecretHandle), 1540 | ) callconv(.C) FastlyStatus; 1541 | 1542 | pub extern "fastly_secret_store" fn plaintext( 1543 | secret: SecretHandle, 1544 | buf: WasiMutPtr(Char8), 1545 | buf_len: usize, 1546 | nwritten_out: WasiMutPtr(usize), 1547 | ) callconv(.C) FastlyStatus; 1548 | 1549 | pub extern "fastly_secret_store" fn from_bytes( 1550 | buf: WasiMutPtr(Char8), 1551 | buf_len: usize, 1552 | result_ptr: WasiMutPtr(SecretHandle), 1553 | ) callconv(.C) FastlyStatus; 1554 | }; 1555 | 1556 | // ---------------------- Module: [fastly_uap] ---------------------- 1557 | 1558 | pub const FastlyUap = struct { 1559 | pub extern "fastly_uap" fn parse( 1560 | user_agent_ptr: WasiPtr(Char8), 1561 | user_agent_len: usize, 1562 | family: WasiMutPtr(Char8), 1563 | family_len: usize, 1564 | family_nwritten_out: WasiMutPtr(usize), 1565 | major: WasiMutPtr(Char8), 1566 | major_len: usize, 1567 | major_nwritten_out: WasiMutPtr(usize), 1568 | minor: WasiMutPtr(Char8), 1569 | minor_len: usize, 1570 | minor_nwritten_out: WasiMutPtr(usize), 1571 | patch: WasiMutPtr(Char8), 1572 | patch_len: usize, 1573 | patch_nwritten_out: WasiMutPtr(usize), 1574 | ) callconv(.C) FastlyStatus; 1575 | }; 1576 | --------------------------------------------------------------------------------