proxy_sdk/
http.rs

1use std::ops::RangeBounds;
2
3use crate::{
4    calculate_range,
5    context::BaseContext,
6    hostcalls::{self, BufferType, MapType},
7    log_concern,
8    property::envoy::Attributes,
9    Status,
10};
11
12/// Defines control functions for http data
13pub trait HttpControl {
14    /// Request or Response
15    const TYPE: HttpType;
16
17    /// Retrieve attributes for the http data
18    fn attributes(&self) -> &Attributes;
19
20    /// If `true`, this is the last block
21    fn end_of_stream(&self) -> bool {
22        true
23    }
24
25    /// Resume a paused HTTP request/response
26    fn resume(&self) {
27        log_concern(Self::TYPE.resume(), Self::TYPE.call_resume())
28    }
29
30    /// Reset the HTTP request/response
31    fn reset(&self) {
32        log_concern(Self::TYPE.reset(), Self::TYPE.call_reset())
33    }
34
35    /// Send an early HTTP response, terminating the current request/response
36    fn send_http_response(
37        &self,
38        status_code: u32,
39        headers: &[(&str, &[u8])],
40        body: Option<&[u8]>,
41    ) -> Result<(), Status> {
42        hostcalls::send_http_response(status_code, headers, body)
43    }
44
45    /// Mark this transaction as complete
46    fn done(&self) {
47        log_concern("trigger-done", hostcalls::done());
48    }
49}
50
51/// Defines functions to interact with header data
52pub trait HttpHeaderControl: HttpControl {
53    /// The header type
54    const HEADER_TYPE: HeaderType;
55
56    /// Number of headers contained in block
57    fn header_count(&self) -> usize;
58
59    /// Get all headers in this block
60    fn all(&self) -> Vec<(String, Vec<u8>)> {
61        log_concern(
62            Self::HEADER_TYPE.all(),
63            hostcalls::get_map(Self::HEADER_TYPE.map()),
64        )
65        .unwrap_or_default()
66    }
67
68    /// Check for a specific header value
69    fn get(&self, name: impl AsRef<str>) -> Option<Vec<u8>> {
70        log_concern(
71            Self::HEADER_TYPE.get(),
72            hostcalls::get_map_value(Self::HEADER_TYPE.map(), name.as_ref()),
73        )
74    }
75
76    /// Set a specific header
77    fn set(&self, name: impl AsRef<str>, value: impl AsRef<[u8]>) {
78        log_concern(
79            Self::HEADER_TYPE.set(),
80            hostcalls::set_map_value(Self::HEADER_TYPE.map(), name.as_ref(), Some(value.as_ref())),
81        );
82    }
83
84    /// Replace all headers in this block
85    fn set_all(&self, values: &[(&str, &[u8])]) {
86        log_concern(
87            Self::HEADER_TYPE.set_all(),
88            hostcalls::set_map(Self::HEADER_TYPE.map(), values),
89        );
90    }
91
92    /// Add a header to this block (append to existing if present)
93    fn add(&self, name: impl AsRef<str>, value: impl AsRef<[u8]>) {
94        log_concern(
95            Self::HEADER_TYPE.add(),
96            hostcalls::add_map_value(Self::HEADER_TYPE.map(), name.as_ref(), value.as_ref()),
97        );
98    }
99
100    /// Remove a header from this block
101    fn remove(&self, name: impl AsRef<str>) {
102        log_concern(
103            Self::HEADER_TYPE.remove(),
104            hostcalls::set_map_value(Self::HEADER_TYPE.map(), name.as_ref(), None),
105        );
106    }
107}
108
109/// Defines functions to interact with body data
110pub trait HttpBodyControl: HttpControl {
111    /// Length of this body fragment
112    fn body_size(&self) -> usize;
113
114    /// Get a range of the body block content
115    fn get(&self, range: impl RangeBounds<usize>) -> Option<Vec<u8>> {
116        let (start, size) = calculate_range(range, self.body_size());
117        log_concern(
118            Self::TYPE.get(),
119            hostcalls::get_buffer(Self::TYPE.buffer(), start, size),
120        )
121    }
122
123    /// Set a range of the body block content
124    fn set(&self, range: impl RangeBounds<usize>, value: &[u8]) {
125        let (start, size) = calculate_range(range, self.body_size());
126        log_concern(
127            Self::TYPE.set(),
128            hostcalls::set_buffer(Self::TYPE.buffer(), start, size, value),
129        );
130    }
131
132    /// Get the entire body block content
133    fn all(&self) -> Option<Vec<u8>> {
134        self.get(..)
135    }
136
137    /// Replace the entire body block with `value`
138    fn replace(&self, value: &[u8]) {
139        self.set(.., value);
140    }
141
142    /// Clear the entire body block
143    fn clear(&self) {
144        self.replace(&[]);
145    }
146}
147
148/// Defines which section the header data belongs too
149pub enum HeaderType {
150    RequestHeaders,
151    RequestTrailers,
152    ResponseHeaders,
153    ResponseTrailers,
154}
155
156impl HeaderType {
157    const fn all(&self) -> &'static str {
158        match self {
159            Self::RequestHeaders => "get-all-request-header",
160            Self::RequestTrailers => "get-all-request-trailer",
161            Self::ResponseHeaders => "get-all-response-header",
162            Self::ResponseTrailers => "get-all-response-trailer",
163        }
164    }
165
166    const fn get(&self) -> &'static str {
167        match self {
168            Self::RequestHeaders => "get-request-header",
169            Self::RequestTrailers => "get-request-trailer",
170            Self::ResponseHeaders => "get-response-header",
171            Self::ResponseTrailers => "get-response-trailer",
172        }
173    }
174
175    const fn set(&self) -> &'static str {
176        match self {
177            Self::RequestHeaders => "set-request-header",
178            Self::RequestTrailers => "set-request-trailer",
179            Self::ResponseHeaders => "set-response-header",
180            Self::ResponseTrailers => "set-response-trailer",
181        }
182    }
183
184    const fn set_all(&self) -> &'static str {
185        match self {
186            Self::RequestHeaders => "set-all-request-headers",
187            Self::RequestTrailers => "set-all-request-trailers",
188            Self::ResponseHeaders => "set-all-response-headers",
189            Self::ResponseTrailers => "set-all-response-trailers",
190        }
191    }
192
193    const fn add(&self) -> &'static str {
194        match self {
195            Self::RequestHeaders => "add-request-headers",
196            Self::RequestTrailers => "add-request-trailers",
197            Self::ResponseHeaders => "add-response-headers",
198            Self::ResponseTrailers => "add-response-trailers",
199        }
200    }
201
202    const fn remove(&self) -> &'static str {
203        match self {
204            Self::RequestHeaders => "remove-request-headers",
205            Self::RequestTrailers => "remove-request-trailers",
206            Self::ResponseHeaders => "remove-response-headers",
207            Self::ResponseTrailers => "remove-response-trailers",
208        }
209    }
210
211    const fn map(&self) -> MapType {
212        match self {
213            HeaderType::RequestHeaders => MapType::HttpRequestHeaders,
214            HeaderType::RequestTrailers => MapType::HttpRequestTrailers,
215            HeaderType::ResponseHeaders => MapType::HttpResponseHeaders,
216            HeaderType::ResponseTrailers => MapType::HttpResponseTrailers,
217        }
218    }
219}
220
221/// Defines if data belongs to a request or response
222pub enum HttpType {
223    Request,
224    Response,
225}
226
227impl HttpType {
228    const fn resume(&self) -> &'static str {
229        match self {
230            HttpType::Request => "resume-http-request",
231            HttpType::Response => "resume-http-response",
232        }
233    }
234
235    fn call_resume(&self) -> Result<(), Status> {
236        match self {
237            HttpType::Request => hostcalls::resume_http_request(),
238            HttpType::Response => hostcalls::resume_http_response(),
239        }
240    }
241
242    const fn reset(&self) -> &'static str {
243        match self {
244            HttpType::Request => "reset-http-request",
245            HttpType::Response => "reset-http-response",
246        }
247    }
248
249    fn call_reset(&self) -> Result<(), Status> {
250        match self {
251            HttpType::Request => hostcalls::reset_http_request(),
252            HttpType::Response => hostcalls::reset_http_response(),
253        }
254    }
255
256    const fn get(&self) -> &'static str {
257        match self {
258            HttpType::Request => "get-request-body",
259            HttpType::Response => "get-response-body",
260        }
261    }
262
263    const fn set(&self) -> &'static str {
264        match self {
265            HttpType::Request => "set-request-body",
266            HttpType::Response => "set-response-body",
267        }
268    }
269
270    const fn buffer(&self) -> BufferType {
271        match self {
272            HttpType::Request => BufferType::HttpRequestBody,
273            HttpType::Response => BufferType::HttpResponseBody,
274        }
275    }
276}
277
278/// Return status for header callbacks
279#[repr(usize)]
280#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
281#[non_exhaustive]
282pub enum FilterHeadersStatus {
283    Continue = 0,
284    StopIteration = 1,
285    ContinueAndEndStream = 2,
286    StopAllIterationAndBuffer = 3,
287    StopAllIterationAndWatermark = 4,
288}
289
290/// Return status for trailer callbacks
291#[repr(usize)]
292#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
293#[non_exhaustive]
294pub enum FilterTrailersStatus {
295    Continue = 0,
296    StopIteration = 1,
297}
298
299/// Return status for body callbacks
300#[repr(usize)]
301#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
302#[non_exhaustive]
303pub enum FilterDataStatus {
304    Continue = 0,
305    StopAllIterationAndBuffer = 1,
306    StopAllIterationAndWatermark = 2,
307    StopIterationNoBuffer = 3,
308}
309
310/// Request header context
311pub struct RequestHeaders {
312    pub(crate) header_count: usize,
313    pub(crate) end_of_stream: bool,
314    pub(crate) attributes: Attributes,
315}
316
317impl HttpControl for RequestHeaders {
318    const TYPE: HttpType = HttpType::Request;
319
320    fn attributes(&self) -> &Attributes {
321        &self.attributes
322    }
323
324    fn end_of_stream(&self) -> bool {
325        self.end_of_stream
326    }
327}
328
329impl HttpHeaderControl for RequestHeaders {
330    const HEADER_TYPE: HeaderType = HeaderType::RequestHeaders;
331
332    fn header_count(&self) -> usize {
333        self.header_count
334    }
335}
336
337pub struct RequestBody {
338    pub(crate) body_size: usize,
339    pub(crate) end_of_stream: bool,
340    pub(crate) attributes: Attributes,
341}
342
343impl HttpControl for RequestBody {
344    const TYPE: HttpType = HttpType::Request;
345
346    fn attributes(&self) -> &Attributes {
347        &self.attributes
348    }
349
350    fn end_of_stream(&self) -> bool {
351        self.end_of_stream
352    }
353}
354
355impl HttpBodyControl for RequestBody {
356    fn body_size(&self) -> usize {
357        self.body_size
358    }
359}
360
361pub struct RequestTrailers {
362    pub(crate) trailer_count: usize,
363    pub(crate) attributes: Attributes,
364}
365
366impl HttpControl for RequestTrailers {
367    const TYPE: HttpType = HttpType::Request;
368
369    fn attributes(&self) -> &Attributes {
370        &self.attributes
371    }
372}
373
374impl HttpHeaderControl for RequestTrailers {
375    const HEADER_TYPE: HeaderType = HeaderType::RequestTrailers;
376
377    fn header_count(&self) -> usize {
378        self.trailer_count
379    }
380}
381
382pub struct ResponseHeaders {
383    pub(crate) header_count: usize,
384    pub(crate) end_of_stream: bool,
385    pub(crate) attributes: Attributes,
386}
387
388impl HttpControl for ResponseHeaders {
389    const TYPE: HttpType = HttpType::Response;
390
391    fn attributes(&self) -> &Attributes {
392        &self.attributes
393    }
394
395    fn end_of_stream(&self) -> bool {
396        self.end_of_stream
397    }
398}
399
400impl HttpHeaderControl for ResponseHeaders {
401    const HEADER_TYPE: HeaderType = HeaderType::ResponseHeaders;
402
403    fn header_count(&self) -> usize {
404        self.header_count
405    }
406}
407
408pub struct ResponseBody {
409    pub(crate) body_size: usize,
410    pub(crate) end_of_stream: bool,
411    pub(crate) attributes: Attributes,
412}
413
414impl HttpControl for ResponseBody {
415    const TYPE: HttpType = HttpType::Response;
416
417    fn attributes(&self) -> &Attributes {
418        &self.attributes
419    }
420
421    fn end_of_stream(&self) -> bool {
422        self.end_of_stream
423    }
424}
425
426impl HttpBodyControl for ResponseBody {
427    fn body_size(&self) -> usize {
428        self.body_size
429    }
430}
431
432pub struct ResponseTrailers {
433    pub(crate) trailer_count: usize,
434    pub(crate) attributes: Attributes,
435}
436
437impl HttpControl for ResponseTrailers {
438    const TYPE: HttpType = HttpType::Response;
439
440    fn attributes(&self) -> &Attributes {
441        &self.attributes
442    }
443}
444
445impl HttpHeaderControl for ResponseTrailers {
446    const HEADER_TYPE: HeaderType = HeaderType::ResponseTrailers;
447
448    fn header_count(&self) -> usize {
449        self.trailer_count
450    }
451}
452
453/// Context for a HTTP filter plugin.
454#[allow(unused_variables)]
455pub trait HttpContext: BaseContext {
456    /// Called one or more times as the proxy receives request headers. If `headers.end_of_stream()` is true, then they are the last request headers.
457    fn on_http_request_headers(&mut self, headers: &RequestHeaders) -> FilterHeadersStatus {
458        FilterHeadersStatus::Continue
459    }
460
461    /// Called only if and only if there is a request body. Called one or more times as the proxy receives blocks of request body data. If `body.end_of_stream()` is true, it is the last block.
462    fn on_http_request_body(&mut self, body: &RequestBody) -> FilterDataStatus {
463        FilterDataStatus::Continue
464    }
465
466    /// Called once if and only if any trailers are sent at the end of the request. Not called multiple times.
467    fn on_http_request_trailers(&mut self, trailers: &RequestTrailers) -> FilterTrailersStatus {
468        FilterTrailersStatus::Continue
469    }
470
471    /// Called one or more times as the proxy receives response headers. If `headers.end_of_stream()` is true, then they are the last response headers.
472    fn on_http_response_headers(&mut self, headers: &ResponseHeaders) -> FilterHeadersStatus {
473        FilterHeadersStatus::Continue
474    }
475
476    /// Called only if and only if there is a response body. Called one or more times as the proxy receives blocks of response body data. If `body.end_of_stream()` is true, it is the last block.
477    fn on_http_response_body(&mut self, body: &ResponseBody) -> FilterDataStatus {
478        FilterDataStatus::Continue
479    }
480
481    /// Called once if and only if any trailers are sent at the end of the response. Not called multiple times.
482    fn on_http_response_trailers(&mut self, trailers: &ResponseTrailers) -> FilterTrailersStatus {
483        FilterTrailersStatus::Continue
484    }
485}