You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: context/reference/PROPERTIES_REFERENCE.md
+4-2Lines changed: 4 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,8 @@
2
2
3
3
Properties are read-only metadata about the current request and client, available via `proxy_get_property()` in ProxyWasm apps. They provide context that isn't in the HTTP headers themselves.
4
4
5
+
**Path format:** Always pass the property identifier as a single dotted string in a one-element vec — e.g., `vec!["request.path"]`, `vec!["request.geo.long"]`. Do **not** split on dots (e.g., `vec!["request", "country"]` is incorrect).
6
+
5
7
---
6
8
7
9
## Available Properties
@@ -40,13 +42,13 @@ Properties are read-only metadata about the current request and client, availabl
Copy file name to clipboardExpand all lines: docs/CDN_APPS.md
+86-36Lines changed: 86 additions & 36 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -89,7 +89,7 @@ proxy_wasm::main! {{
89
89
90
90
### Root Context
91
91
92
-
The root context is a singleton created once when the filter loads. Its primary role is to create a new HTTP context for each incoming request.
92
+
The root context is a singleton created once when the filter loads. Its primary role is to create a new HTTP context for each lifecycle callback invocation.
93
93
94
94
```rust,no_run
95
95
# use proxy_wasm::traits::*;
@@ -109,11 +109,11 @@ impl RootContext for MyAppRoot {
109
109
}
110
110
```
111
111
112
-
`get_type()` must return `Some(ContextType::HttpContext)` for HTTP traffic interception. `create_http_context` is called once per request and receives a unique `context_id`.
112
+
`get_type()` must return `Some(ContextType::HttpContext)` for HTTP traffic interception. `create_http_context` is called once per lifecycle callback invocation and receives a unique `context_id`.
113
113
114
114
### HTTP Context
115
115
116
-
The HTTP context is where request and response processing happens. A new instance is created for each request by `create_http_context`.
116
+
The HTTP context is where request and response processing happens. A new instance is created for each lifecycle callback invocation — not once per request. See [Hook State Isolation](#hook-state-isolation) for the consequences this has on state management.
117
117
118
118
```rust,no_run
119
119
# use proxy_wasm::traits::*;
@@ -151,11 +151,11 @@ All callbacks have default no-op implementations. Override only the phases your
151
151
152
152
Every lifecycle callback returns an `Action` that controls what happens next.
|`Action::Continue`| Pass the request or response through to the next stage |
157
+
|`Action::Pause`| Stop processing; used after `send_http_response` to short-circuit origin |
158
+
|`Action::StopIterationAndBuffer`| Buffer the current body chunk; continue accumulating until `end_of_stream`|
159
159
160
160
For body callbacks, return `Action::StopIterationAndBuffer` until `end_of_stream` is `true`, then process the full body and return `Action::Continue`.
161
161
@@ -175,6 +175,40 @@ impl HttpContext for MyApp {
175
175
}
176
176
```
177
177
178
+
### Hook State Isolation
179
+
180
+
On the FastEdge CDN platform, an HTTP context instance exists only for the duration of a single lifecycle callback invocation. It does **not** persist across the request. Different hooks may run on entirely different servers: `on_http_request_headers` runs in nginx, while `on_http_request_body`, `on_http_response_headers`, and `on_http_response_body` run in core-proxy.
181
+
182
+
This has critical consequences for application design:
183
+
184
+
- Struct fields on the HTTP context do **not** persist between callbacks.
185
+
- A fresh context instance is created for each callback invocation.
186
+
- Storing data as a struct field in one callback and reading it in another callback does **not** work.
187
+
188
+
To pass data between callbacks, use `self.set_property` and `self.get_property` with a custom property path. The host preserves these values across callback invocations for the same logical request:
**Known limitation**: On the FastEdge CDN platform, passing `None` to `set_http_request_header` or `set_http_response_header` sets the header value to an empty string rather than removing the header entirely. When checking for header absence, test for an empty string as well as a missing value.
269
+
236
270
### Generating Responses
237
271
238
272
To short-circuit the request and respond directly to the client without forwarding to origin, call `send_http_response` and return `Action::Pause`.
@@ -264,14 +298,30 @@ impl HttpContext for MyApp {
264
298
265
299
CDN apps access request metadata through `self.get_property(vec![...])`. The return type is `Option<Vec<u8>>`.
Most properties are UTF-8 strings that can be decoded with `std::str::from_utf8()`. The `response.status` property is a binary-encoded integer, not a string — it must be decoded as a big-endian `u16`:
301
+
**Path format:** Always pass the property identifier as a single dotted string in a one-element vec — e.g., `vec!["request.path"]`, `vec!["response.status"]`, `vec!["request.geo.long"]`. Do **not** split on dots (e.g., `vec!["response", "status"]` is incorrect).
|`request.x_real_ip`| UTF-8 string | Client IP address |
312
+
|`request.country`| UTF-8 string | 2-letter ISO country code (geo-IP) |
313
+
|`request.country.name`| UTF-8 string | Full country name |
314
+
|`request.city`| UTF-8 string | City name |
315
+
|`request.region`| UTF-8 string | Region/state |
316
+
|`request.continent`| UTF-8 string | Continent |
317
+
|`request.asn`| UTF-8 string | Autonomous System Number |
318
+
|`request.geo.lat`| UTF-8 string | Latitude |
319
+
|`request.geo.long`| UTF-8 string | Longitude |
320
+
|`response.status`| 2-byte big-endian u16 | Response status code (**binary, NOT a string** — decode with `u16::from_be_bytes`) |
321
+
322
+
Most properties are UTF-8 strings decoded with `std::str::from_utf8()`. The `response.status` property is binary-encoded and must be decoded as a big-endian `u16`. Do not use `String::from_utf8` for this property.
323
+
324
+
Geo-IP properties (`request.country`, `request.country.name`, `request.city`, `request.region`, `request.continent`, `request.geo.lat`, `request.geo.long`) are derived from the client IP address.
275
325
276
326
```rust,no_run
277
327
# use proxy_wasm::traits::*;
@@ -281,7 +331,7 @@ Most properties are UTF-8 strings that can be decoded with `std::str::from_utf8(
|`Store::new()`|`Result<Self, Error>`| Open the default store |
333
-
|`Store::open(name: &str)`|`Result<Self, Error>`| Open a named store |
334
-
|`Store::get(key: &str)`|`Result<Option<Vec<u8>>, Error>`| Get the value for a key; `None` if key does not exist |
335
-
|`Store::scan(pattern: &str)`|`Result<Vec<String>, Error>`| List keys matching a glob-style pattern |
336
-
|`Store::zrange_by_score(key: &str, min: f64, max: f64)`|`Result<Vec<(Vec<u8>, f64)>, Error>`| Get sorted-set members with scores between min and max |
337
-
|`Store::zscan(key: &str, pattern: &str)`|`Result<Vec<(Vec<u8>, f64)>, Error>`| Scan sorted-set members matching a pattern |
338
-
|`Store::bf_exists(key: &str, item: &str)`|`Result<bool, Error>`| Test whether an item is in a Bloom filter |
|`Store::new()`|`Result<Self, Error>`| Open the default store |
383
+
|`Store::open(name: &str)`|`Result<Self, Error>`| Open a named store |
384
+
|`Store::get(key: &str)`|`Result<Option<Vec<u8>>, Error>`| Get the value for a key; `None` if key does not exist |
385
+
|`Store::scan(pattern: &str)`|`Result<Vec<String>, Error>`| List keys matching a glob-style pattern |
386
+
|`Store::zrange_by_score(key: &str, min: f64, max: f64)`|`Result<Vec<(Vec<u8>, f64)>, Error>`| Get sorted-set members with scores between min and max |
387
+
|`Store::zscan(key: &str, pattern: &str)`|`Result<Vec<(Vec<u8>, f64)>, Error>`| Scan sorted-set members matching a pattern |
388
+
|`Store::bf_exists(key: &str, item: &str)`|`Result<bool, Error>`| Test whether an item is in a Bloom filter |
|**Advanced data structures**| No | Sorted sets, bloom filters, glob scan | No |
353
+
|**Confidentiality**| Not encrypted; visible in config | Not encrypted at the application layer | Encrypted at rest; access-controlled |
354
+
|**Typical use cases**| Feature flags, routing config, tuning | Caching, counters, state, rate-limit data | API keys, tokens, certificates, credentials |
355
+
|**Versioning / rotation**| No | No | Yes, via `get_effective_at`|
356
356
357
357
Use `dictionary` for simple, non-sensitive string configuration that is known at deployment time. Use `key_value` for larger datasets, binary values, or data that requires advanced query patterns. Use `secret` for any value that must be kept confidential.
0 commit comments