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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,23 @@ The router attempts to identify errors in scripts before applying the changes. I

By [@garypen](https://github.com/garypen) in https://github.com/apollographql/router/pull/2198

### Add support for setting multi-value header keys to rhai ([Issue #2211](https://github.com/apollographql/router/issues/2211))
### Add support for working with multi-value header keys to rhai ([Issue #2211](https://github.com/apollographql/router/issues/2211), [Issue #2255](https://github.com/apollographql/router/issues/2255))

Adds support for setting a header map key with an array. This causes the HeaderMap key/values to be appended() to the map, rather than inserted().

Adds support for a new `values()` fn which retrieves multiple values for a HeaderMap key as an array.

Example use from rhai as:

```
response.headers["set-cookie"] = [
"foo=bar; Domain=localhost; Path=/; Expires=Wed, 04 Jan 2023 17:25:27 GMT; HttpOnly; Secure; SameSite=None",
"foo2=bar2; Domain=localhost; Path=/; Expires=Wed, 04 Jan 2023 17:25:27 GMT; HttpOnly; Secure; SameSite=None",
];
response.headers.values("set-cookie"); // Returns the array of values
```

By [@garypen](https://github.com/garypen) in https://github.com/apollographql/router/pull/2219
By [@garypen](https://github.com/garypen) in https://github.com/apollographql/router/pull/2219, https://github.com/apollographql/router/pull/2258

## 🐛 Fixes

Expand Down
26 changes: 24 additions & 2 deletions apollo-router/src/plugins/rhai.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use rhai::module_resolvers::FileModuleResolver;
use rhai::plugin::*;
use rhai::serde::from_dynamic;
use rhai::serde::to_dynamic;
use rhai::Array;
use rhai::Dynamic;
use rhai::Engine;
use rhai::EvalAltResult;
Expand Down Expand Up @@ -1304,9 +1305,30 @@ impl Rhai {
);
Ok(())
})
// Register an additional getter which allows us to get multiple values for the same
// key.
// Note: We can't register this as an indexer, because that would simply override the
// existing one, which would break code. When router 2.0 is released, we should replace
// the existing indexer_get for HeaderMap with this function and mark it as an
// incompatible change.
.register_fn("values",
|x: &mut HeaderMap, key: &str| -> Result<Array, Box<EvalAltResult>> {
let search_name =
HeaderName::from_str(key).map_err(|e: InvalidHeaderName| e.to_string())?;
let mut response = Array::new();
for value in x.get_all(search_name).iter() {
response.push(value
.to_str()
.map_err(|e| e.to_string())?
.to_string()
.into())
}
Ok(response)
}
)
// Register an additional setter which allows us to set multiple values for the same
// key
.register_indexer_set(|x: &mut HeaderMap, key: &str, value: rhai::Array| {
// key.
.register_indexer_set(|x: &mut HeaderMap, key: &str, value: Array| {
let h_key = HeaderName::from_str(key).map_err(|e| e.to_string())?;
for v in value {
x.append(
Expand Down
15 changes: 13 additions & 2 deletions docs/source/customizations/rhai-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,14 @@ fn supergraph_service(service) {
}
```

### Headers with multiple values

The simple get/set api for dealing with single value headers is sufficient for most use cases. If you wish to set multiple values on a key then you should do this by supplying an array of values.

If you wish to get multiple values for a header key, then you must use the `values()` fn, NOT the indexed accessor. If you do use the indexed accessor, it will only return the first value (as a string) associated with the key.

Look at the examples to see how this works in practice.

## `Request` interface

All callback functions registered via `map_request` are passed a `request` object that represents the request sent by the client. This object provides the following fields, any of which a callback can modify in-place (read-write):
Expand Down Expand Up @@ -248,14 +256,17 @@ The headers of a request are accessible as a read/write indexed variable. The ke
// You can interact with request.headers as an indexed variable
request.headers["x-my-new-header"] = 42.to_string(); // Inserts a new header "x-my-new-header" with value "42"
print(`${request.headers["x-my-new-header"]}`); // Writes "42" into the router log at info level
// Rhai also supports extended dot notation for indexed variables, so this is equivalent
request.headers.x-my-new-header = 42.to_string();
// You can also set an header value from an array. Useful with the "set-cookie" header,
// Note: It's probably more useful to do this on response headers. Simply illustrating the syntax here.
request.headers["set-cookie"] = [
"foo=bar; Domain=localhost; Path=/; Expires=Wed, 04 Jan 2023 17:25:27 GMT; HttpOnly; Secure; SameSite=None",
"foo2=bar2; Domain=localhost; Path=/; Expires=Wed, 04 Jan 2023 17:25:27 GMT; HttpOnly; Secure; SameSite=None",
];
// Rhai also supports extended dot notation for indexed variables, so this is equivalent
request.headers.x-my-new-header = 42.to_string();
// You can also get multiple header values for a header using the values() fn
// Note: It's probably more useful to do this on response headers. Simply illustrating the syntax here.
print(`${request.headers.values("set-cookie")}`);
```

### `request.body.query`
Expand Down