diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index f1d0ed4f90..93d8e72234 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -115,10 +115,12 @@ 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: ``` @@ -126,9 +128,10 @@ Example use from rhai as: "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 diff --git a/apollo-router/src/plugins/rhai.rs b/apollo-router/src/plugins/rhai.rs index 92367024a5..340a4d26db 100644 --- a/apollo-router/src/plugins/rhai.rs +++ b/apollo-router/src/plugins/rhai.rs @@ -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; @@ -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> { + 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( diff --git a/docs/source/customizations/rhai-api.mdx b/docs/source/customizations/rhai-api.mdx index 57f06f3d6e..e2f264e9bf 100644 --- a/docs/source/customizations/rhai-api.mdx +++ b/docs/source/customizations/rhai-api.mdx @@ -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): @@ -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`