Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add copy on write support to CEL definition types #239

Merged
merged 1 commit into from
Nov 30, 2023
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
54 changes: 29 additions & 25 deletions packages/ic-http-certification/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ let cel_expr = create_cel_expr(&certification);

### Using the CEL builder

The CEL builder interface is provided to ease the creation of CEL expressions through an ergonmic interface. If this interface does not meet your needs, you can also [create CEL expressions directly](#directly-creating-a-cel-expression). To define a CEL expression, start with [DefaultCelBuilder]. This struct provides a set of methods that can be used to define how your request and response pair should be certified.
The CEL builder interface is provided to ease the creation of CEL expressions through an ergonmic interface. If this interface does not meet your needs, you can also [create CEL expressions directly](#directly-creating-a-cel-expression). To define a CEL expression, start with `DefaultCelBuilder`. This struct provides a set of methods that can be used to define how your request and response pair should be certified.

When certifying requests, the request body and method are always certified. To additionally certify request headers and query parameters, use `with_request_headers` and `with_request_query_parameters` respectively. Both methods take a [str] slice as an argument.
When certifying requests, the request body and method are always certified. To additionally certify request headers and query parameters, use `with_request_headers` and `with_request_query_parameters` respectively. Both methods take a `str` slice as an argument.

When certifying a response, the response body and status code are always certified. To additionally certify response headers, use `with_response_certification`. This method takes the `DefaultResponseCertification` enum as an argument. To specify header inclusions, use the `CertifiedResponseHeaders` variant of the `DefaultResponseCertification` enum. Or to certify all response headers, with some exclusions, use the `ResponseHeaderExclusions` variant of the `DefaultResponseCertification` enum. Both variants take a [str] slice as an argument.
When certifying a response, the response body and status code are always certified. To additionally certify response headers, use `with_response_certification`. This method takes the `DefaultResponseCertification` enum as an argument. To specify header inclusions, use the `certified_response_headers` function of the `DefaultResponseCertification` enum. Or to certify all response headers, with some exclusions, use the `response_header_exclusions` function of the `DefaultResponseCertification` enum. Both functions take a `str` slice as an argument.

#### Fully certified request / response pair

Expand All @@ -45,7 +45,7 @@ use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};
let cel_expr = DefaultCelBuilder::full_certification()
.with_request_headers(&["Accept", "Accept-Encoding", "If-Match"])
.with_request_query_parameters(&["foo", "bar", "baz"])
.with_response_certification(DefaultResponseCertification::CertifiedResponseHeaders(&[
.with_response_certification(DefaultResponseCertification::certified_response_headers(&[
"Cache-Control",
"ETag",
]))
Expand All @@ -62,7 +62,7 @@ For example, to certify only the request body and method:
use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};

let cel_expr = DefaultCelBuilder::full_certification()
.with_response_certification(DefaultResponseCertification::CertifiedResponseHeaders(&[
.with_response_certification(DefaultResponseCertification::certified_response_headers(&[
"Cache-Control",
"ETag",
]))
Expand All @@ -77,7 +77,7 @@ use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};
let cel_expr = DefaultCelBuilder::full_certification()
.with_request_headers(&[])
.with_request_query_parameters(&[])
.with_response_certification(DefaultResponseCertification::CertifiedResponseHeaders(&[
.with_response_certification(DefaultResponseCertification::certified_response_headers(&[
"Cache-Control",
"ETag",
]))
Expand All @@ -92,7 +92,7 @@ Request certification can be skipped entirely by using `DefaultCelBuilder::respo
use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};

let cel_expr = DefaultCelBuilder::response_certification()
.with_response_certification(DefaultResponseCertification::ResponseHeaderExclusions(&[
.with_response_certification(DefaultResponseCertification::response_header_exclusions(&[
"Date",
"Cookie",
"Set-Cookie",
Expand All @@ -102,7 +102,7 @@ let cel_expr = DefaultCelBuilder::response_certification()

#### Partially certified response

Similiarly to request certification, any number of response headers can be provided via the `CertifiedResponseHeaders` variant of the `DefaultResponseCertification` enum when calling `with_response_certification`. The provided array can also be an empty. If the array is empty, or the method is not called, then no response headers will be certified.
Similiarly to request certification, any number of response headers can be provided via the `certified_response_headers` function of the `DefaultResponseCertification` enum when calling `with_response_certification`. The provided array can also be an empty. If the array is empty, or the method is not called, then no response headers will be certified.

For example, to certify only the response body and status code:

Expand All @@ -118,7 +118,7 @@ This can also be done more explicitly:
use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};

let cel_expr = DefaultCelBuilder::response_certification()
.with_response_certification(DefaultResponseCertification::CertifiedResponseHeaders(&[]))
.with_response_certification(DefaultResponseCertification::certified_response_headers(&[]))
.build();
```

Expand Down Expand Up @@ -155,7 +155,7 @@ To define a CEL expression, start with the `CelExpression` enum. This enum provi

When certifying requests, the request body and method are always certified. To additionally certify request headers and query parameters, use the `headers` and `query_paramters` of `DefaultRequestCertification` struct. Both properties take a `str` slice as an argument.

When certifying a response, the response body and status code are always certified. To additionally certify response headers, use the `CertifiedResponseHeaders` variant of the `DefaultResponseCertification` enum. Or to certify all response headers, with some exclusions, use the `ResponseHeaderExclusions` variant of the `DefaultResponseCertification` enum. Both variants take a `str` slice as an argument.
When certifying a response, the response body and status code are always certified. To additionally certify response headers, use the `certified_response_headers` function of the `DefaultResponseCertification` enum. Or to certify all response headers, with some exclusions, use the `response_header_exclusions` function of the `DefaultResponseCertification` enum. Both functions take a `str` slice as an argument.

Note that the example CEL expressions provided below are formatted for readability. The actual CEL expressions produced by `CelExpression::to_string` and `create_cel_expr` are minified. The minified CEL expression is preferred because it is more compact, resulting in a smaller payload and a faster evaluation time for the HTTP Gateway that is verifying the certification, but the formatted versions are also accepted.

Expand All @@ -164,14 +164,15 @@ Note that the example CEL expressions provided below are formatted for readabili
To define a fully certified request and response pair, including request headers, query parameters, and response headers:

```rust
use std::borrow::Cow;
use ic_http_certification::cel::{CelExpression, DefaultCertification, DefaultRequestCertification, DefaultResponseCertification};

let cel_expr = CelExpression::DefaultCertification(Some(DefaultCertification {
request_certification: Some(DefaultRequestCertification {
headers: &["Accept", "Accept-Encoding", "If-Match"],
query_parameters: &["foo", "bar", "baz"],
headers: Cow::Borrowed(&["Accept", "Accept-Encoding", "If-Match"]),
query_parameters: Cow::Borrowed(&["foo", "bar", "baz"]),
}),
response_certification: DefaultResponseCertification::CertifiedResponseHeaders(&[
response_certification: DefaultResponseCertification::certified_response_headers(&[
"ETag",
"Cache-Control",
]),
Expand Down Expand Up @@ -206,14 +207,15 @@ Any number of request headers or query parameters can be provided via the `heade
For example, to certify only the request body and method:

```rust
use std::borrow::Cow;
use ic_http_certification::cel::{CelExpression, DefaultCertification, DefaultRequestCertification, DefaultResponseCertification};

let cel_expr = CelExpression::DefaultCertification(Some(DefaultCertification {
request_certification: Some(DefaultRequestCertification {
headers: &[],
query_parameters: &[],
headers: Cow::Borrowed(&["Accept", "Accept-Encoding", "If-Match"]),
query_parameters: Cow::Borrowed(&["foo", "bar", "baz"]),
}),
response_certification: DefaultResponseCertification::CertifiedResponseHeaders(&[
response_certification: DefaultResponseCertification::certified_response_headers(&[
"ETag",
"Cache-Control",
]),
Expand Down Expand Up @@ -250,7 +252,7 @@ use ic_http_certification::cel::{CelExpression, DefaultCertification, DefaultRes

let cel_expr = CelExpression::DefaultCertification(Some(DefaultCertification {
request_certification: None,
response_certification: DefaultResponseCertification::CertifiedResponseHeaders(&[
response_certification: DefaultResponseCertification::certified_response_headers(&[
"ETag",
"Cache-Control",
]),
Expand All @@ -277,17 +279,18 @@ default_certification (

#### Partially certified response

Similiarly to request certification, any number of response headers can be provided via the `CertifiedResponseHeaders` variant of the `DefaultResponseCertification` enum, and it can also be an empty array. If the array is empty, no response headers will be certified. For example:
Similiarly to request certification, any number of response headers can be provided via the `certified_response_headers` variant of the `DefaultResponseCertification` enum, and it can also be an empty array. If the array is empty, no response headers will be certified. For example:

```rust
use std::borrow::Cow;
use ic_http_certification::cel::{CelExpression, DefaultCertification, DefaultRequestCertification, DefaultResponseCertification};

let cel_expr = CelExpression::DefaultCertification(Some(DefaultCertification {
request_certification: Some(DefaultRequestCertification {
headers: &["Accept", "Accept-Encoding", "If-Match"],
query_parameters: &["foo", "bar", "baz"],
headers: Cow::Borrowed(&["Accept", "Accept-Encoding", "If-Match"]),
query_parameters: Cow::Borrowed(&["foo", "bar", "baz"]),
}),
response_certification: DefaultResponseCertification::CertifiedResponseHeaders(&[]),
response_certification: DefaultResponseCertification::certified_response_headers(&[]),
}));
```

Expand All @@ -309,17 +312,18 @@ default_certification (
)
```

If the `ResponseHeaderExclusions` variant is used, an empty array will certify _all_ response headers. For example:
If the `response_header_exclusions` function is used, an empty array will certify _all_ response headers. For example:

```rust
use std::borrow::Cow;
use ic_http_certification::cel::{CelExpression, DefaultCertification, DefaultRequestCertification, DefaultResponseCertification};

let cel_expr = CelExpression::DefaultCertification(Some(DefaultCertification {
request_certification: Some(DefaultRequestCertification {
headers: &["Accept", "Accept-Encoding", "If-Match"],
query_parameters: &["foo", "bar", "baz"],
headers: Cow::Borrowed(&["Accept", "Accept-Encoding", "If-Match"]),
query_parameters: Cow::Borrowed(&["foo", "bar", "baz"]),
}),
response_certification: DefaultResponseCertification::ResponseHeaderExclusions(&[]),
response_certification: DefaultResponseCertification::response_header_exclusions(&[]),
}));
```

Expand Down
95 changes: 48 additions & 47 deletions packages/ic-http-certification/src/cel/cel_builder.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::{
CelExpression, DefaultCertification, DefaultRequestCertification, DefaultResponseCertification,
};
use std::borrow::Cow;

/// A CEL expression builder for creating a default certification expression.
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -104,8 +105,8 @@ impl<'a> DefaultFullCelExpressionBuilder<'a> {
/// Build the CEL expression, consuming the builder.
pub fn build(self) -> CelExpression<'a> {
let request_certification = Some(DefaultRequestCertification {
headers: self.request_headers,
query_parameters: self.request_query_parameters,
headers: Cow::Borrowed(self.request_headers),
query_parameters: Cow::Borrowed(self.request_query_parameters),
});

CelExpression::DefaultCertification(Some(DefaultCertification {
Expand All @@ -131,13 +132,15 @@ mod tests {
#[rstest]
fn no_request_response_inclusions(no_request_response_inclusions_cel: String) {
let cel_expr = DefaultCelBuilder::response_certification()
.with_response_certification(DefaultResponseCertification::CertifiedResponseHeaders(&[
"Cache-Control",
"ETag",
"Content-Length",
"Content-Type",
"Content-Encoding",
]))
.with_response_certification(DefaultResponseCertification::certified_response_headers(
&[
"Cache-Control",
"ETag",
"Content-Length",
"Content-Type",
"Content-Encoding",
],
))
.build()
.to_string();

Expand All @@ -147,11 +150,9 @@ mod tests {
#[rstest]
fn no_request_response_exclusions(no_request_response_exclusions_cel: String) {
let cel_expr = DefaultCelBuilder::response_certification()
.with_response_certification(DefaultResponseCertification::ResponseHeaderExclusions(&[
"Date",
"Cookie",
"Set-Cookie",
]))
.with_response_certification(DefaultResponseCertification::response_header_exclusions(
&["Date", "Cookie", "Set-Cookie"],
))
.build()
.to_string();

Expand All @@ -164,9 +165,9 @@ mod tests {
.build()
.to_string();
let explicit_cel_expr = DefaultCelBuilder::response_certification()
.with_response_certification(
DefaultResponseCertification::CertifiedResponseHeaders(&[]),
)
.with_response_certification(DefaultResponseCertification::certified_response_headers(
&[],
))
.build()
.to_string();
let default_cel_expr = DefaultCelBuilder::response_certification()
Expand All @@ -182,9 +183,9 @@ mod tests {
#[rstest]
fn no_request_empty_response_exclusions(no_request_empty_response_exclusions_cel: String) {
let cel_expr = DefaultCelBuilder::response_certification()
.with_response_certification(
DefaultResponseCertification::ResponseHeaderExclusions(&[]),
)
.with_response_certification(DefaultResponseCertification::response_header_exclusions(
&[],
))
.build()
.to_string();

Expand All @@ -198,13 +199,15 @@ mod tests {
let cel_expr = DefaultCelBuilder::full_certification()
.with_request_headers(&["Accept", "Accept-Encoding", "If-Match"])
.with_request_query_parameters(&["foo", "bar", "baz"])
.with_response_certification(DefaultResponseCertification::CertifiedResponseHeaders(&[
"Cache-Control",
"ETag",
"Content-Length",
"Content-Type",
"Content-Encoding",
]))
.with_response_certification(DefaultResponseCertification::certified_response_headers(
&[
"Cache-Control",
"ETag",
"Content-Length",
"Content-Type",
"Content-Encoding",
],
))
.build()
.to_string();

Expand All @@ -218,11 +221,9 @@ mod tests {
let cel_expr = DefaultCelBuilder::full_certification()
.with_request_headers(&["Accept", "Accept-Encoding", "If-Match"])
.with_request_query_parameters(&["foo", "bar", "baz"])
.with_response_certification(DefaultResponseCertification::ResponseHeaderExclusions(&[
"Date",
"Cookie",
"Set-Cookie",
]))
.with_response_certification(DefaultResponseCertification::response_header_exclusions(
&["Date", "Cookie", "Set-Cookie"],
))
.build()
.to_string();

Expand All @@ -241,9 +242,9 @@ mod tests {
let explicit_cel_expr = DefaultCelBuilder::full_certification()
.with_request_headers(&["Accept", "Accept-Encoding", "If-Match"])
.with_request_query_parameters(&["foo", "bar", "baz"])
.with_response_certification(
DefaultResponseCertification::CertifiedResponseHeaders(&[]),
)
.with_response_certification(DefaultResponseCertification::certified_response_headers(
&[],
))
.build()
.to_string();
let default_cel_expr = DefaultCelBuilder::full_certification()
Expand Down Expand Up @@ -274,9 +275,9 @@ mod tests {
let cel_expr = DefaultCelBuilder::full_certification()
.with_request_headers(&["Accept", "Accept-Encoding", "If-Match"])
.with_request_query_parameters(&["foo", "bar", "baz"])
.with_response_certification(
DefaultResponseCertification::ResponseHeaderExclusions(&[]),
)
.with_response_certification(DefaultResponseCertification::response_header_exclusions(
&[],
))
.build()
.to_string();

Expand All @@ -289,9 +290,9 @@ mod tests {
let explicit_cel_expr = DefaultCelBuilder::full_certification()
.with_request_headers(&[])
.with_request_query_parameters(&[])
.with_response_certification(
DefaultResponseCertification::CertifiedResponseHeaders(&[]),
)
.with_response_certification(DefaultResponseCertification::certified_response_headers(
&[],
))
.build()
.to_string();
let default_cel_expr = DefaultCelBuilder::full_certification()
Expand All @@ -309,17 +310,17 @@ mod tests {
#[rstest]
fn empty_request_response_exclusions(empty_request_response_exclusions_cel: String) {
let implicit_cel_expr = DefaultCelBuilder::full_certification()
.with_response_certification(
DefaultResponseCertification::ResponseHeaderExclusions(&[]),
)
.with_response_certification(DefaultResponseCertification::response_header_exclusions(
&[],
))
.build()
.to_string();
let explicit_cel_expr = DefaultCelBuilder::full_certification()
.with_request_headers(&[])
.with_request_query_parameters(&[])
.with_response_certification(
DefaultResponseCertification::ResponseHeaderExclusions(&[]),
)
.with_response_certification(DefaultResponseCertification::response_header_exclusions(
&[],
))
.build()
.to_string();

Expand Down
Loading