ic_http_certification/lib.rs
1/*!
2# HTTP Certification
3
4HTTP certification is a sub-protocol of the [ICP](https://internetcomputer.org/) [HTTP gateway protocol](https://internetcomputer.org/docs/current/references/http-gateway-protocol-spec). It is used to verify HTTP responses received by an HTTP gateway from a [canister](https://internetcomputer.org/how-it-works/canister-lifecycle/), with respect to the corresponding HTTP request. This allows HTTP gateways to verify that the responses they receive from canisters are authentic and have not been tampered with.
5
6The `ic-http-certification` crate provides the foundation for implementing the HTTP certification protocol in Rust canisters. Certification is implemented in a number of steps:
7
81. [Defining CEL expressions](#defining-cel-expressions).
92. [Creating certifications](#creating-certifications).
103. [Creating an HTTP certification tree](#creating-an-http-certification-tree).
11
12## Defining CEL expressions
13
14[CEL](https://github.com/google/cel-spec) (Common Expression Language) is a portable expression language that can be used for different applications to easily interoperate. It can be seen as the computation or expression counterpart to [protocol buffers](https://github.com/protocolbuffers/protobuf).
15
16CEL expressions are at the core of ICP's HTTP certification protocol. They are used to define the conditions under which a request and response pair should be certified and also what should be included from the corresponding request and response objects in the certification.
17
18CEL expressions can be created in two ways:
19- Using the [CEL builder](#using-the-cel-builder)
20- Directly creating a [CEL expression](#directly-creating-a-cel-expression).
21
22### Converting CEL expressions into their `String` representation
23
24Note that the [CelExpression] enum is not a CEL expression itself, but rather a Rust representation of a CEL expression. To convert a [CelExpression] into its [String] representation, use [CelExpression::to_string](ToString::to_string()) or [create_cel_expr](cel::create_cel_expr()). This applies to CEL expressions created both by the [CEL builder](#using-the-cel-builder) and [directly](#directly-creating-a-cel-expression).
25
26```rust
27use ic_http_certification::cel::{CelExpression, DefaultCelExpression};
28
29let cel_expr = CelExpression::Default(DefaultCelExpression::Skip).to_string();
30```
31
32Alternatively:
33
34```rust
35use ic_http_certification::cel::{CelExpression, DefaultCelExpression, create_cel_expr};
36
37let certification = CelExpression::Default(DefaultCelExpression::Skip);
38let cel_expr = create_cel_expr(&certification);
39```
40
41### Using the CEL builder
42
43The CEL builder interface is provided to ease the creation of CEL expressions through an ergonomic interface. It is also possible to [create CEL expressions directly](#directly-creating-a-cel-expression). To define a CEL expression, start with [DefaultCelBuilder]. This struct provides a set of associated functions that can be used to define how a request and response pair should be certified.
44
45It's possible to
46- [Fully certify requests and responses](#fully-certified-request--response-pair).
47- [Partially certify requests](#partially-certified-request).
48- [Skip request certification](#skipping-request-certification).
49- [Partially certify responses](#partially-certified-response).
50- [Skip certification entirely](#skipping-certification).
51
52Note that if the request is certified, the response must also be certified. It is not possible to certify a request without also certifying a response. Any combination of fully or partially certified requests and responses can be used.
53
54When a request is certified:
55
56- The request body and method are always certified.
57- The request headers and query parameters are optionally certified using the `with_request_headers` and `with_request_query_parameters` associated functions, respectively. Both associated functions take a `str` slice as an argument.
58
59When a response is certified:
60
61- The response body and status code are always certified.
62- The response headers are optionally certified using the `with_response_certification` associated function. This function takes the `DefaultResponseCertification` enum as an argument.
63 - To specify header inclusions, use the `certified_response_headers` associated function of the `DefaultResponseCertification` enum.
64 - To certify all response headers (with some optional exclusions), use the `response_header_exclusions` associated function of the `DefaultResponseCertification` enum. Both functions take a `str` slice as an argument.
65
66Regardless of what is included in certification, the request path is always used to determine if that certification should be used. It's also possible to set a certification for a "scope" or "directory" of paths; see [Defining tree paths](#defining-tree-paths) for more information on this.
67
68When defining CEL expressions, it's important to determine what should be certified and what can be safely excluded from certification. For example, if a response header is not certified, it will not be included in the certification and will not be verified by the HTTP gateway, meaning that the value of this header cannot be trusted by clients. As a general rule of thumb, starting with a fully certified request and response pair is a good idea and then removing parts of the certification as needed.
69
70It should be considered unsafe to exclude anything from request certification that can change the expected response. The request method, for example, can drastically affect what action is taken by the canister, and so excluding it from certification would allow a malicious replica to respond with the expected responses for a `'GET'` request, even though a `'POST'` request was made.
71
72For responses, it should be considered unsafe to exclude anything from response certification that will be used by clients in a meaningful way. For example, excluding the `Content-Type` header from certification would allow a malicious replica to respond with a different content type than expected, which could cause clients to misinterpret the response.
73
74#### Fully certified request / response pair
75
76To define a fully certified request and response pair, including request headers, query parameters, and response headers use [DefaultCelBuilder::full_certification](DefaultCelBuilder::full_certification()).
77
78For example:
79
80```rust
81use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};
82
83let cel_expr = DefaultCelBuilder::full_certification()
84 .with_request_headers(vec!["Accept", "Accept-Encoding", "If-None-Match"])
85 .with_request_query_parameters(vec!["foo", "bar", "baz"])
86 .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
87 "Cache-Control",
88 "ETag",
89 ]))
90 .build();
91```
92
93#### Partially certified request
94
95Any number of request headers or request query parameters can be certified via `with_request_headers` and `with_request_query_parameters` respectively. Both methods will accept empty arrays, which is the same as not calling them at all. Likewise for `with_request_query_parameters`, if it is called with an empty array or not called at all, then no request query parameters will be certified. If both are called with an empty array, or neither is called, then only the request body and method will be certified, in addition to the response. As a reminder here, the response is always at least partially certified if the request is certified.
96
97For example, to certify only the request body and method, in addition to the response:
98
99```rust
100use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};
101
102let cel_expr = DefaultCelBuilder::full_certification()
103 .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
104 "Cache-Control",
105 "ETag",
106 ]))
107 .build();
108```
109
110Alternatively, this can be done more explicitly:
111
112```rust
113use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};
114
115let cel_expr = DefaultCelBuilder::full_certification()
116 .with_request_headers(vec![])
117 .with_request_query_parameters(vec![])
118 .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
119 "Cache-Control",
120 "ETag",
121 ]))
122 .build();
123```
124
125#### Skipping request certification
126
127Request certification can be skipped entirely by using `DefaultCelBuilder::response_only_certification` instead of `DefaultCelBuilder::full_certification`. Request certification should only be skipped if the response is determined solely by the request path. If any other part of the request can affect the response in a meaningful way, then request certification should not be skipped.
128
129For example:
130
131```rust
132use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};
133
134let cel_expr = DefaultCelBuilder::response_only_certification()
135 .with_response_certification(DefaultResponseCertification::response_header_exclusions(vec![
136 "Date",
137 "Cookie",
138 "Set-Cookie",
139 ]))
140 .build();
141```
142
143#### Partially certified response
144
145Any number of response headers can be provided via the `certified_response_headers` associated function of the `DefaultResponseCertification` enum when calling `with_response_certification`. The provided array can also be empty. If the array is empty, or the associated function is not called, no response headers will be certified. If all response headers are to be certified, with some exclusions, use the `response_header_exclusions` associated function of the `DefaultResponseCertification` enum. Care should be taken when choosing what headers to exclude from certification, as they will not be verified by the HTTP gateway. Any headers that hold meaningful information for clients should not be excluded.
146
147For example, to certify only the response body and status code:
148
149```rust
150use ic_http_certification::DefaultCelBuilder;
151
152let cel_expr = DefaultCelBuilder::response_only_certification().build();
153```
154
155
156This can also be done more explicitly:
157
158```rust
159use ic_http_certification::{DefaultCelBuilder, DefaultResponseCertification};
160
161let cel_expr = DefaultCelBuilder::response_only_certification()
162 .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![]))
163 .build();
164```
165
166The same applies when using [DefaultCelBuilder::response_only_certification](DefaultCelBuilder::response_only_certification()) and [DefaultCelBuilder::full_certification](DefaultCelBuilder::full_certification()).
167
168```rust
169use ic_http_certification::DefaultCelBuilder;
170
171let cel_expr = DefaultCelBuilder::full_certification()
172 .with_request_headers(vec!["Accept", "Accept-Encoding", "If-None-Match"])
173 .with_request_query_parameters(vec!["foo", "bar", "baz"])
174 .build();
175```
176
177To skip response certification completely, certification overall must be skipped completely. It wouldn't be useful to certify a request without certifying a response.
178
179#### Skipping certification
180
181To skip certification entirely, use [DefaultCelBuilder::skip_certification](DefaultCelBuilder::skip_certification()), for example:
182
183```rust
184use ic_http_certification::DefaultCelBuilder;
185
186let cel_expr = DefaultCelBuilder::skip_certification();
187```
188
189Skipping certification may seem counterintuitive at first, but it is not always possible to certify a request and response pair. For example, a canister method that will return different data for every user cannot be easily certified.
190
191Typically, these requests have been routed through `raw` ICP URLs in the past, but this is dangerous because `raw` URLs allow any responding replica to decide whether or not certification is required. In contrast, by skipping certification using the above method with a non-`raw` URL, a replica will no longer be able to decide whether or not certification is required and instead this decision will be made by the canister itself and the result will go through consensus.
192
193Extreme caution should be taken when deciding to skip certification entirely. It should only be done when it is not possible to certify a request and response pair, and a modification of the response's content would not pose a security risk for the application.
194
195## Creating certifications
196
197Once a CEL expression has been defined, it can be used in conjunction with an [HttpRequest] and [HttpResponse] to create an instance of the [HttpCertification] struct. The [HttpCertification] struct has three associated functions:
198
199- The [full](HttpCertification::full) associated function is used to include both the [HttpRequest] and the corresponding [HttpResponse] in certification.
200- The [response_only](HttpCertification::response_only) associated function is used to include only the [HttpResponse] in certification and exclude the corresponding [HttpRequest] from certification.
201- The [skip](HttpCertification::skip) associated function is used to skip certification entirely.
202
203### Full certification
204
205To perform a full certification, a CEL expression created from [DefaultCelBuilder::full_certification] is required, along with an [HttpRequest] and [HttpResponse], and optionally, a pre-calculated response body hash.
206
207For example:
208
209```rust
210use ic_http_certification::{HttpCertification, HttpRequest, HttpResponse, DefaultCelBuilder, DefaultResponseCertification, CERTIFICATE_EXPRESSION_HEADER_NAME, StatusCode};
211
212let cel_expr = DefaultCelBuilder::full_certification()
213 .with_request_headers(vec!["Accept", "Accept-Encoding", "If-None-Match"])
214 .with_request_query_parameters(vec!["foo", "bar", "baz"])
215 .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
216 "Cache-Control",
217 "ETag",
218 ]))
219 .build();
220
221let request = HttpRequest::get("/index.html?foo=a&bar=b&baz=c")
222 .with_headers(vec![
223 ("Accept".to_string(), "application/json".to_string()),
224 ("Accept-Encoding".to_string(), "gzip".to_string()),
225 ("If-None-Match".to_string(), "987654321".to_string()),
226 ])
227 .build();
228
229let response = HttpResponse::ok(
230 vec![1, 2, 3, 4, 5, 6],
231 vec![
232 ("Cache-Control".to_string(), "no-cache".to_string()),
233 ("ETag".to_string(), "123456789".to_string()),
234 (CERTIFICATE_EXPRESSION_HEADER_NAME.to_string(), cel_expr.to_string()),
235 ]
236)
237.build();
238
239let certification = HttpCertification::full(&cel_expr, &request, &response, None);
240```
241
242### Response-only certification
243
244To perform a response-only certification, a CEL expression created from [DefaultCelBuilder::response_only_certification] is required, along with an [HttpResponse] and, optionally, a pre-calculated response body hash.
245
246For example:
247
248```rust
249use ic_http_certification::{HttpCertification, HttpResponse, DefaultCelBuilder, DefaultResponseCertification, CERTIFICATE_EXPRESSION_HEADER_NAME, StatusCode};
250
251let cel_expr = DefaultCelBuilder::response_only_certification()
252 .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
253 "Cache-Control",
254 "ETag",
255 ]))
256 .build();
257
258let response = HttpResponse::ok(
259 vec![1, 2, 3, 4, 5, 6],
260 vec![
261 ("Cache-Control".to_string(), "no-cache".to_string()),
262 ("ETag".to_string(), "123456789".to_string()),
263 (CERTIFICATE_EXPRESSION_HEADER_NAME.to_string(), cel_expr.to_string()),
264 ]
265)
266.build();
267
268let certification = HttpCertification::response_only(&cel_expr, &response, None).unwrap();
269```
270
271### Skipping certification
272
273Skipping certification does not need an explicit CEL expression to be defined since it's always the same.
274
275For example:
276
277```rust
278use ic_http_certification::HttpCertification;
279
280let certification = HttpCertification::skip();
281```
282
283## Creating an HTTP certification tree
284
285### Defining tree paths
286
287Paths for the tree can be defined using the [HttpCertificationPath] struct and come in two types - [Wildcard](HttpCertificationPath::wildcard()) and [Exact](HttpCertificationPath::exact()). Both types of paths may end with or without a trailing slash but note that a path ending in a trailing slash is a distinct path from one that does not end with a trailing slash and they will be treated as such by the tree.
288
289Wildcard paths can be used to match a sub-path of a request URL. This can be useful for 404 responses, fallbacks, or rewrites. They are defined using the [Wildcard](HttpCertificationPath::wildcard()) associated function.
290
291In this example, the certification entered into the tree with this path will be valid for any request URL that begins with `/js`, unless there is a more specific path in the tree (e.g., `/js/example.js` or `/js/example`).
292
293```rust
294use ic_http_certification::HttpCertificationPath;
295
296let path = HttpCertificationPath::wildcard("/js");
297```
298
299Exact paths are used to match an entire request URL. An exact path ending with a trailing slash refers to a file system directory, whereas one without a trailing slash refers to an individual file. Both are separate paths within the certification tree and will be treated completely independently.
300
301In this example, the certification entered into the tree with this path will only be valid for a request URL that is exactly `/js/example.js`.
302
303```rust
304use ic_http_certification::HttpCertificationPath;
305
306let path = HttpCertificationPath::exact("/js/example.js");
307```
308
309### Using the HTTP certification tree
310
311The [HttpCertificationTree] can be easily initialized with the [Default] trait, and entries can be added to, removed from, or have witnesses generated by the tree using the [HttpCertificationTreeEntry] struct. The [HttpCertificationTreeEntry] requires an [HttpCertification] and an [HttpCertificationPath].
312
313For example:
314
315```rust
316use ic_http_certification::{HttpCertification, HttpRequest, HttpResponse, DefaultCelBuilder, DefaultResponseCertification, HttpCertificationTree, HttpCertificationTreeEntry, HttpCertificationPath, CERTIFICATE_EXPRESSION_HEADER_NAME, StatusCode};
317
318let cel_expr = DefaultCelBuilder::full_certification()
319 .with_request_headers(vec!["Accept", "Accept-Encoding", "If-None-Match"])
320 .with_request_query_parameters(vec!["foo", "bar", "baz"])
321 .with_response_certification(DefaultResponseCertification::certified_response_headers(vec![
322 "Cache-Control",
323 "ETag",
324 ]))
325 .build();
326
327let request = HttpRequest::get("/index.html?foo=a&bar=b&baz=c")
328 .with_headers(vec![
329 ("Accept".to_string(), "application/json".to_string()),
330 ("Accept-Encoding".to_string(), "gzip".to_string()),
331 ("If-None-Match".to_string(), "987654321".to_string()),
332 ])
333 .build();
334
335let response = HttpResponse::ok(
336 vec![1, 2, 3, 4, 5, 6],
337 vec![
338 ("Cache-Control".to_string(), "no-cache".to_string()),
339 ("ETag".to_string(), "123456789".to_string()),
340 (CERTIFICATE_EXPRESSION_HEADER_NAME.to_string(), cel_expr.to_string()),
341 ]
342)
343.build();
344
345let request_url = "/example.json";
346let path = HttpCertificationPath::exact(request_url);
347let certification = HttpCertification::full(&cel_expr, &request, &response, None).unwrap();
348
349let mut http_certification_tree = HttpCertificationTree::default();
350
351let entry = HttpCertificationTreeEntry::new(&path, &certification);
352
353// insert the entry into the tree
354http_certification_tree.insert(&entry);
355
356// generate a witness for this entry in the tree
357let witness = http_certification_tree.witness(&entry, request_url);
358
359// delete the entry from the tree
360http_certification_tree.delete(&entry);
361```
362
363### Handling upgrades
364
365CEL expressions, certifications, the certification tree, and the corresponding requests and responses are not persisted across upgrades, by default. This means that if a canister is upgraded, all of this information will be lost. To handle upgrades effectively, all initialization logic run in the canister's `init` hook should also be run in the `post_upgrade` hook. This will ensure that the certification tree is correctly re-initialized after an upgrade. Most data structures, aside from the certification tree, can be persisted using stable memory, and the certification tree can be re-initialized using this persisted data. Care should be taken to not exceed the canister's instruction limit when re-initializing the certification tree, which can easily occur if the number of responses being certified grows very large. This case could potentially be addressed in the future by developing a stable memory-compatible certification tree.
366
367### Changing data
368
369In addition to initializing certifications in the `init` and `post_upgrade` hooks, if a response is changed during the canister's lifetime in response to an `update` call, the certification tree should be updated to reflect this change. This can be done by deleting the old certification from the tree and inserting the new certification. This should be done in the same `update` call as the response is changed to ensure that the certification tree is always up-to-date; otherwise, `query` calls returning that response will fail verification.
370
371## Directly creating a CEL expression
372
373To define a CEL expression, start with the [CelExpression] enum. This enum provides a set of variants that can be used to define different types of CEL expressions supported by ICP HTTP gateways. Currently only one variant is supported, known as the "default" certification expression, but more may be added in the future as the HTTP certification protocol evolves over time.
374
375When certifying requests:
376
377- The request body and method are always certified.
378- To certify request headers and query parameters, use the [headers](cel::DefaultRequestCertification::headers) and [query_parameters](cel::DefaultRequestCertification::query_parameters) fields of the [DefaultRequestCertification](cel::DefaultRequestCertification) struct. Both fields take a [str] slice as an argument.
379
380When certifying responses:
381
382- The response body and status code are always certified.
383- To certify response headers, use the [certified_response_headers](DefaultResponseCertification::certified_response_headers) associated function of the [DefaultResponseCertification] enum. Or to certify all response headers, with some exclusions, use the [response_header_exclusions](DefaultResponseCertification::response_header_exclusions) associated function of the [DefaultResponseCertification] enum. Both associated functions take a [str] slice as an argument.
384
385Note that the example CEL expressions provided below are formatted for readability. The actual CEL expressions produced by [CelExpression::to_string](ToString::to_string()) and [create_cel_expr](cel::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.
386
387### Fully certified request / response pair
388
389To define a fully certified request and response pair, including request headers, query parameters, and response headers:
390
391```rust
392use std::borrow::Cow;
393use ic_http_certification::cel::{CelExpression, DefaultCelExpression, DefaultFullCelExpression, DefaultRequestCertification, DefaultResponseCertification};
394
395let cel_expr = CelExpression::Default(DefaultCelExpression::Full(
396 DefaultFullCelExpression {
397 request: DefaultRequestCertification::new(
398 vec!["Accept", "Accept-Encoding", "If-None-Match"],
399 vec!["foo", "bar", "baz"],
400 ),
401 response: DefaultResponseCertification::certified_response_headers(vec![
402 "ETag",
403 "Cache-Control",
404 ]),
405 }));
406```
407
408This will produce the following CEL expression:
409
410```protobuf
411default_certification (
412 ValidationArgs {
413 request_certification: RequestCertification {
414 certified_request_headers: ["Accept", "Accept-Encoding", "If-None-Match"],
415 certified_query_parameters: ["foo", "bar", "baz"]
416 },
417 response_certification: ResponseCertification {
418 certified_response_headers: ResponseHeaderList {
419 headers: [
420 "ETag",
421 "Cache-Control"
422 ]
423 }
424 }
425 }
426)
427```
428
429### Partially certified request
430
431Any number of request headers or query parameters can be provided via the [headers](cel::DefaultRequestCertification::headers) and [query_parameters](cel::DefaultRequestCertification::query_parameters) fields of the [DefaultRequestCertification](cel::DefaultRequestCertification) struct, and both can be an empty array. If the [headers](cel::DefaultRequestCertification::headers) field is empty, no request headers will be certified. Likewise for the [query_parameters](cel::DefaultRequestCertification::query_parameters) field, if it is empty, then no query parameters will be certified. If both are empty, only the request body and method will be certified.
432
433For example, to certify only the request body and method:
434
435```rust
436use std::borrow::Cow;
437use ic_http_certification::cel::{CelExpression, DefaultCelExpression, DefaultFullCelExpression, DefaultRequestCertification, DefaultResponseCertification};
438
439let cel_expr = CelExpression::Default(DefaultCelExpression::Full(
440 DefaultFullCelExpression {
441 request: DefaultRequestCertification::new(
442 vec![],
443 vec![],
444 ),
445 response: DefaultResponseCertification::certified_response_headers(vec![
446 "ETag",
447 "Cache-Control",
448 ]),
449 }));
450```
451
452This will produce the following CEL expression:
453
454```protobuf
455default_certification (
456 ValidationArgs {
457 request_certification: RequestCertification {
458 certified_request_headers: [],
459 certified_query_parameters: []
460 },
461 response_certification: ResponseCertification {
462 certified_response_headers: ResponseHeaderList {
463 headers: [
464 "ETag",
465 "Cache-Control"
466 ]
467 }
468 }
469 }
470)
471```
472
473### Skipping request certification
474
475Request certification can be skipped entirely by using the [ResponseOnly](DefaultCelExpression::ResponseOnly) variant of the [DefaultCelExpression].
476
477For example:
478
479```rust
480use std::borrow::Cow;
481use ic_http_certification::cel::{CelExpression, DefaultCelExpression, DefaultResponseOnlyCelExpression, DefaultResponseCertification};
482
483let cel_expr = CelExpression::Default(DefaultCelExpression::ResponseOnly(
484 DefaultResponseOnlyCelExpression {
485 response: DefaultResponseCertification::certified_response_headers(vec![
486 "ETag",
487 "Cache-Control",
488 ]),
489 }));
490```
491
492This will produce the following CEL expression:
493
494```protobuf
495default_certification (
496 ValidationArgs {
497 no_request_certification: Empty {},
498 response_certification: ResponseCertification {
499 certified_response_headers: ResponseHeaderList {
500 headers: [
501 "ETag",
502 "Cache-Control"
503 ]
504 }
505 }
506 }
507)
508```
509
510### Partially certified response
511
512Similarly to request certification, any number of response headers can be provided via the [certified_response_headers](DefaultResponseCertification::certified_response_headers) associated function of the [DefaultResponseCertification] enum, and it can also be an empty array. If the array is empty, no response headers will be certified.
513
514For example:
515
516```rust
517use std::borrow::Cow;
518use ic_http_certification::cel::{CelExpression, DefaultCelExpression, DefaultFullCelExpression, DefaultRequestCertification, DefaultResponseCertification};
519
520
521let cel_expr = CelExpression::Default(DefaultCelExpression::Full(
522 DefaultFullCelExpression {
523 request: DefaultRequestCertification::new(
524 vec!["Accept", "Accept-Encoding", "If-None-Match"],
525 vec!["foo", "bar", "baz"],
526 ),
527 response: DefaultResponseCertification::certified_response_headers(vec![]),
528 }));
529```
530
531This will produce the following CEL expression:
532
533```protobuf
534default_certification (
535 ValidationArgs {
536 request_certification: RequestCertification {
537 certified_request_headers: ["Accept", "Accept-Encoding", "If-None-Match"],
538 certified_query_parameters: ["foo", "bar", "baz"]
539 },
540 response_certification: ResponseCertification {
541 certified_response_headers: ResponseHeaderList {
542 headers: []
543 }
544 }
545 }
546)
547```
548
549If the [response_header_exclusions](DefaultResponseCertification::response_header_exclusions) associated function is used, an empty array will certify _all_ response headers. For example:
550
551```rust
552use std::borrow::Cow;
553use ic_http_certification::cel::{CelExpression, DefaultCelExpression, DefaultFullCelExpression, DefaultRequestCertification, DefaultResponseCertification};
554
555let cel_expr = CelExpression::Default(DefaultCelExpression::Full(
556 DefaultFullCelExpression {
557 request: DefaultRequestCertification::new(
558 vec!["Accept", "Accept-Encoding", "If-None-Match"],
559 vec!["foo", "bar", "baz"],
560 ),
561 response: DefaultResponseCertification::response_header_exclusions(vec![]),
562 }));
563```
564
565This will produce the following CEL expression:
566
567```protobuf
568default_certification (
569 ValidationArgs {
570 request_certification: RequestCertification {
571 certified_request_headers: ["Accept", "Accept-Encoding", "If-None-Match"],
572 certified_query_parameters: ["foo", "bar", "baz"]
573 },
574 response_certification: ResponseCertification {
575 response_header_exclusions: ResponseHeaderList {
576 headers: []
577 }
578 }
579 }
580)
581```
582
583To skip response certification completely, then certification overall must be skipped completely. It wouldn't be useful to certify a request without certifying a response.
584
585### Skipping certification
586
587To skip certification entirely:
588
589```rust
590use ic_http_certification::cel::{CelExpression, DefaultCelExpression};
591
592let cel_expr = CelExpression::Default(DefaultCelExpression::Skip);
593```
594
595This will produce the following CEL expression:
596
597```protobuf
598default_certification (
599 ValidationArgs {
600 no_certification: Empty {}
601 }
602)
603```
604*/
605
606#![deny(missing_docs, missing_debug_implementations, rustdoc::all, clippy::all)]
607
608pub mod cel;
609pub use cel::{
610 CelExpression, DefaultCelBuilder, DefaultCelExpression, DefaultFullCelExpression,
611 DefaultResponseCertification, DefaultResponseOnlyCelExpression,
612};
613pub mod hash;
614pub use hash::*;
615pub mod error;
616pub use error::*;
617pub mod http;
618pub use http::*;
619pub mod tree;
620pub use tree::*;
621pub mod utils;
622
623// https://github.com/la10736/rstest/tree/master/rstest_reuse#cavelets
624#[cfg(test)]
625#[allow(clippy::single_component_path_imports)]
626use rstest_reuse;