ResponseHeaders

Struct ResponseHeaders 

Source
pub struct ResponseHeaders { /* private fields */ }
Expand description

An opaque object that represents the underlying Envoy Http response headers map. This is used to interact with it from the module code.

This is a shallow wrapper around the raw pointer to the Envoy response headers map.

Implementations§

Source§

impl ResponseHeaders

Source

pub fn get(&self, key: &[u8]) -> Option<&[u8]>

Returns the first header value for the given key. To handle multiple values, use the ResponseHeaders::values method.

Examples found in repository?
example/example.rs (line 149)
144    fn response_headers(
145        &mut self,
146        response_headers: &ResponseHeaders,
147        _end_of_stream: bool,
148    ) -> ResponseHeadersStatus {
149        if let Some(value) = response_headers.get(b"this-is") {
150            if value != b"response-header" {
151                panic!(
152                    "expected this-is to be \"response-header\", got {:?}",
153                    value
154                );
155            } else {
156                println!("this-is: {}", std::str::from_utf8(value).unwrap());
157            }
158        }
159
160        response_headers
161            .values(b"this-is-2")
162            .iter()
163            .for_each(|value| {
164                println!("this-is-2: {}", std::str::from_utf8(value).unwrap());
165            });
166
167        response_headers.remove(b"this-is-2");
168        response_headers.set(b"this-is", b"response-header");
169        response_headers.set(b"multiple-values-res-to-be-single", b"single");
170
171        ResponseHeadersStatus::Continue
172    }
173}
174
175/// DelayFilter is a filter that delays the request.
176///
177/// This implements the [`HttpFilter`] trait, and will be created per each filter chain.
178struct DelayFilter {
179    atomic: std::sync::atomic::AtomicUsize,
180}
181
182impl HttpFilter for DelayFilter {
183    fn new_instance(
184        &mut self,
185        envoy_filter_instance: EnvoyFilterInstance,
186    ) -> Box<dyn HttpFilterInstance> {
187        let req_no = self
188            .atomic
189            .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
190
191        let envoy_filter_instance = Arc::new(Mutex::new(Some(envoy_filter_instance)));
192        Box::new(DelayFilterInstance {
193            req_no,
194            envoy_filter_instance,
195        })
196    }
197}
198
199/// DelayFilterInstance is a filter instance that delays the request.
200///
201/// This implements the [`HttpFilterInstance`] trait, and will be created per each request.
202struct DelayFilterInstance {
203    req_no: usize,
204    envoy_filter_instance: Arc<Mutex<Option<EnvoyFilterInstance>>>,
205}
206
207impl HttpFilterInstance for DelayFilterInstance {
208    fn request_headers(
209        &mut self,
210        _request_headers: &RequestHeaders,
211        _end_of_stream: bool,
212    ) -> RequestHeadersStatus {
213        if self.req_no == 1 {
214            let envoy_filter_instance = self.envoy_filter_instance.clone();
215            let req_no = self.req_no;
216            std::thread::spawn(move || {
217                println!("blocking for 1 second at RequestHeaders with id {}", req_no);
218                std::thread::sleep(std::time::Duration::from_secs(1));
219                println!("calling ContinueRequest with id {}", req_no);
220                if let Some(envoy_filter_instance) = envoy_filter_instance.lock().unwrap().as_ref()
221                {
222                    envoy_filter_instance.continue_request();
223                }
224            });
225            println!(
226                "RequestHeaders returning StopAllIterationAndBuffer with id {}",
227                self.req_no
228            );
229            RequestHeadersStatus::StopAllIterationAndBuffer
230        } else {
231            println!("RequestHeaders called with id {}", self.req_no);
232            RequestHeadersStatus::Continue
233        }
234    }
235
236    fn request_body(
237        &mut self,
238        _request_body_frame: &RequestBodyBuffer,
239        _end_of_stream: bool,
240    ) -> RequestBodyStatus {
241        if self.req_no == 2 {
242            let envoy_filter_instance = self.envoy_filter_instance.clone();
243            let req_no = self.req_no;
244            std::thread::spawn(move || {
245                println!("blocking for 1 second at RequestBody with id {}", req_no);
246                std::thread::sleep(std::time::Duration::from_secs(1));
247                println!("calling ContinueRequest with id {}", req_no);
248                if let Some(envoy_filter_instance) = envoy_filter_instance.lock().unwrap().as_ref()
249                {
250                    envoy_filter_instance.continue_request();
251                }
252            });
253            println!(
254                "RequestBody returning StopIterationAndBuffer with id {}",
255                self.req_no
256            );
257            RequestBodyStatus::StopIterationAndBuffer
258        } else {
259            println!("RequestBody called with id {}", self.req_no);
260            RequestBodyStatus::Continue
261        }
262    }
263
264    fn response_headers(
265        &mut self,
266        _response_headers: &ResponseHeaders,
267        _end_of_stream: bool,
268    ) -> ResponseHeadersStatus {
269        if self.req_no == 3 {
270            let envoy_filter_instance = self.envoy_filter_instance.clone();
271            let req_no = self.req_no;
272            std::thread::spawn(move || {
273                println!(
274                    "blocking for 1 second at ResponseHeaders with id {}",
275                    req_no
276                );
277                std::thread::sleep(std::time::Duration::from_secs(1));
278                println!("calling ContinueResponse with id {}", req_no);
279                if let Some(envoy_filter_instance) = envoy_filter_instance.lock().unwrap().as_ref()
280                {
281                    envoy_filter_instance.continue_response();
282                }
283            });
284            println!(
285                "ResponseHeaders returning StopAllIterationAndBuffer with id {}",
286                self.req_no
287            );
288
289            ResponseHeadersStatus::StopAllIterationAndBuffer
290        } else {
291            println!("ResponseHeaders called with id {}", self.req_no);
292            ResponseHeadersStatus::Continue
293        }
294    }
295
296    fn response_body(
297        &mut self,
298        _response_body_frame: &ResponseBodyBuffer,
299        _end_of_stream: bool,
300    ) -> ResponseBodyStatus {
301        if self.req_no == 4 {
302            let envoy_filter_instance = self.envoy_filter_instance.clone();
303            let req_no = self.req_no;
304            std::thread::spawn(move || {
305                println!("blocking for 1 second at ResponseBody with id {}", req_no);
306                std::thread::sleep(std::time::Duration::from_secs(1));
307                println!("calling ContinueResponse with id {}", req_no);
308                if let Some(envoy_filter_instance) = envoy_filter_instance.lock().unwrap().as_ref()
309                {
310                    envoy_filter_instance.continue_response();
311                }
312            });
313            println!(
314                "ResponseBody returning StopIterationAndBuffer with id {}",
315                self.req_no
316            );
317
318            ResponseBodyStatus::StopIterationAndBuffer
319        } else {
320            println!("ResponseBody called with id {}", self.req_no);
321            ResponseBodyStatus::Continue
322        }
323    }
324
325    fn destroy(&mut self) {
326        *self.envoy_filter_instance.lock().unwrap() = None;
327    }
328}
329
330/// BodyFilter is a filter that manipulates request/response bodies.
331///
332/// This implements the [`HttpFilter`] trait, and will be created per each filter chain.
333struct BodiesFilter {}
334
335impl HttpFilter for BodiesFilter {
336    fn new_instance(
337        &mut self,
338        envoy_filter_instance: EnvoyFilterInstance,
339    ) -> Box<dyn HttpFilterInstance> {
340        Box::new(BodiesFilterInstance {
341            envoy_filter_instance,
342        })
343    }
344}
345
346/// BodiesFilterInstance is a filter instance that manipulates request/response bodies.
347///
348/// This implements the [`HttpFilterInstance`] trait, and will be created per each request.
349struct BodiesFilterInstance {
350    envoy_filter_instance: EnvoyFilterInstance,
351}
352
353impl HttpFilterInstance for BodiesFilterInstance {
354    fn request_body(
355        &mut self,
356        request_body_frame: &RequestBodyBuffer,
357        end_of_stream: bool,
358    ) -> RequestBodyStatus {
359        println!(
360            "new request body frame: {}",
361            String::from_utf8(request_body_frame.copy()).unwrap()
362        );
363        if !end_of_stream {
364            // Wait for the end of the stream to see the full body.
365            return RequestBodyStatus::StopIterationAndBuffer;
366        }
367
368        // Get the entire request body reference - this does not copy the body.
369        let entire_body = self.envoy_filter_instance.get_request_body_buffer();
370        println!(
371            "entire request body: {}",
372            String::from_utf8(entire_body.copy()).unwrap()
373        );
374
375        // This demonstrates how to use the reader to read the body.
376        let mut reader = entire_body.reader();
377        let mut buf = vec![0; 2];
378        let mut offset = 0;
379        loop {
380            let n = reader.read(&mut buf).unwrap();
381            if n == 0 {
382                break;
383            }
384            println!(
385                "request body read 2 bytes offset at {}: \"{}\"",
386                offset,
387                std::str::from_utf8(&buf[..n]).unwrap()
388            );
389            offset += 2;
390        }
391
392        // Replace the entire body with 'Y' without copying.
393        for i in entire_body.slices() {
394            for j in i {
395                *j = b'X';
396            }
397        }
398        RequestBodyStatus::Continue
399    }
400
401    fn response_body(
402        &mut self,
403        response_body_frame: &ResponseBodyBuffer,
404        end_of_stream: bool,
405    ) -> ResponseBodyStatus {
406        println!(
407            "new response body frame: {}",
408            String::from_utf8(response_body_frame.copy()).unwrap()
409        );
410        if !end_of_stream {
411            // Wait for the end of the stream to see the full body.
412            return ResponseBodyStatus::StopIterationAndBuffer;
413        }
414
415        // Get the entire response body reference - this does not copy the body.
416        let entire_body = self.envoy_filter_instance.get_response_body_buffer();
417        println!(
418            "entire response body: {}",
419            String::from_utf8(entire_body.copy()).unwrap()
420        );
421
422        // This demonstrates how to use the reader to read the body.
423        let mut reader = entire_body.reader();
424        let mut buf = vec![0; 2];
425        let mut offset = 0;
426        loop {
427            let n = reader.read(&mut buf).unwrap();
428            if n == 0 {
429                break;
430            }
431            println!(
432                "response body read 2 bytes offset at {}: \"{}\"",
433                offset,
434                std::str::from_utf8(&buf[..n]).unwrap()
435            );
436            offset += 2;
437        }
438
439        // Replace the entire body with 'Y' without copying.
440        for i in entire_body.slices() {
441            for j in i {
442                *j = b'Y';
443            }
444        }
445
446        ResponseBodyStatus::Continue
447    }
448}
449
450/// BodiesReplaceFilter is a filter that replaces request/response bodies.
451///
452/// This implements the [`HttpFilter`] trait, and will be created per each filter chain.
453struct BodiesReplace {}
454
455impl HttpFilter for BodiesReplace {
456    fn new_instance(
457        &mut self,
458        envoy_filter_instance: EnvoyFilterInstance,
459    ) -> Box<dyn HttpFilterInstance> {
460        Box::new(BodiesReplaceInstance {
461            envoy_filter_instance,
462            request_append: String::new(),
463            request_prepend: String::new(),
464            request_replace: String::new(),
465            response_append: String::new(),
466            response_prepend: String::new(),
467            response_replace: String::new(),
468        })
469    }
470}
471
472/// BodiesReplaceInstance is a filter instance that replaces request/response bodies.
473///
474/// This implements the [`HttpFilterInstance`] trait, and will be created per each request.
475struct BodiesReplaceInstance {
476    envoy_filter_instance: EnvoyFilterInstance,
477    request_append: String,
478    request_prepend: String,
479    request_replace: String,
480    response_append: String,
481    response_prepend: String,
482    response_replace: String,
483}
484
485impl HttpFilterInstance for BodiesReplaceInstance {
486    fn request_headers(
487        &mut self,
488        request_headers: &RequestHeaders,
489        _end_of_stream: bool,
490    ) -> RequestHeadersStatus {
491        if let Some(value) = request_headers.get(b"append") {
492            self.request_append = std::str::from_utf8(value).unwrap().to_string();
493        }
494        if let Some(value) = request_headers.get(b"prepend") {
495            self.request_prepend = std::str::from_utf8(value).unwrap().to_string();
496        }
497        if let Some(value) = request_headers.get(b"replace") {
498            self.request_replace = std::str::from_utf8(value).unwrap().to_string();
499        }
500        request_headers.remove(b"content-length"); // Remove content-length header to avoid mismatch.
501        RequestHeadersStatus::Continue
502    }
503
504    fn request_body(
505        &mut self,
506        _request_body_frame: &RequestBodyBuffer,
507        end_of_stream: bool,
508    ) -> RequestBodyStatus {
509        if !end_of_stream {
510            // Wait for the end of the stream to see the full body.
511            return RequestBodyStatus::StopIterationAndBuffer;
512        }
513
514        let entire_body = self.envoy_filter_instance.get_request_body_buffer();
515        if !self.request_append.is_empty() {
516            entire_body.append(self.request_append.as_bytes());
517        }
518        if !self.request_prepend.is_empty() {
519            entire_body.prepend(self.request_prepend.as_bytes());
520        }
521        if !self.request_replace.is_empty() {
522            entire_body.replace(self.request_replace.as_bytes());
523        }
524        RequestBodyStatus::Continue
525    }
526
527    fn response_headers(
528        &mut self,
529        response_headers: &ResponseHeaders,
530        _end_of_stream: bool,
531    ) -> ResponseHeadersStatus {
532        if let Some(value) = response_headers.get(b"append") {
533            self.response_append = std::str::from_utf8(value).unwrap().to_string();
534        }
535        if let Some(value) = response_headers.get(b"prepend") {
536            self.response_prepend = std::str::from_utf8(value).unwrap().to_string();
537        }
538        if let Some(value) = response_headers.get(b"replace") {
539            self.response_replace = std::str::from_utf8(value).unwrap().to_string();
540        }
541        response_headers.remove(b"content-length"); // Remove content-length header to avoid mismatch.
542        ResponseHeadersStatus::Continue
543    }
Source

pub fn values(&self, key: &[u8]) -> Vec<&[u8]>

Returns all the header values for the given key.

Examples found in repository?
example/example.rs (line 161)
144    fn response_headers(
145        &mut self,
146        response_headers: &ResponseHeaders,
147        _end_of_stream: bool,
148    ) -> ResponseHeadersStatus {
149        if let Some(value) = response_headers.get(b"this-is") {
150            if value != b"response-header" {
151                panic!(
152                    "expected this-is to be \"response-header\", got {:?}",
153                    value
154                );
155            } else {
156                println!("this-is: {}", std::str::from_utf8(value).unwrap());
157            }
158        }
159
160        response_headers
161            .values(b"this-is-2")
162            .iter()
163            .for_each(|value| {
164                println!("this-is-2: {}", std::str::from_utf8(value).unwrap());
165            });
166
167        response_headers.remove(b"this-is-2");
168        response_headers.set(b"this-is", b"response-header");
169        response_headers.set(b"multiple-values-res-to-be-single", b"single");
170
171        ResponseHeadersStatus::Continue
172    }
Source

pub fn set(&self, key: &[u8], value: &[u8])

Sets the value for the given key. If multiple values are set for the same key,

Examples found in repository?
example/example.rs (line 168)
144    fn response_headers(
145        &mut self,
146        response_headers: &ResponseHeaders,
147        _end_of_stream: bool,
148    ) -> ResponseHeadersStatus {
149        if let Some(value) = response_headers.get(b"this-is") {
150            if value != b"response-header" {
151                panic!(
152                    "expected this-is to be \"response-header\", got {:?}",
153                    value
154                );
155            } else {
156                println!("this-is: {}", std::str::from_utf8(value).unwrap());
157            }
158        }
159
160        response_headers
161            .values(b"this-is-2")
162            .iter()
163            .for_each(|value| {
164                println!("this-is-2: {}", std::str::from_utf8(value).unwrap());
165            });
166
167        response_headers.remove(b"this-is-2");
168        response_headers.set(b"this-is", b"response-header");
169        response_headers.set(b"multiple-values-res-to-be-single", b"single");
170
171        ResponseHeadersStatus::Continue
172    }
Source

pub fn remove(&self, key: &[u8])

Removes the value for the given key. If multiple values are set for the same key,

Examples found in repository?
example/example.rs (line 167)
144    fn response_headers(
145        &mut self,
146        response_headers: &ResponseHeaders,
147        _end_of_stream: bool,
148    ) -> ResponseHeadersStatus {
149        if let Some(value) = response_headers.get(b"this-is") {
150            if value != b"response-header" {
151                panic!(
152                    "expected this-is to be \"response-header\", got {:?}",
153                    value
154                );
155            } else {
156                println!("this-is: {}", std::str::from_utf8(value).unwrap());
157            }
158        }
159
160        response_headers
161            .values(b"this-is-2")
162            .iter()
163            .for_each(|value| {
164                println!("this-is-2: {}", std::str::from_utf8(value).unwrap());
165            });
166
167        response_headers.remove(b"this-is-2");
168        response_headers.set(b"this-is", b"response-header");
169        response_headers.set(b"multiple-values-res-to-be-single", b"single");
170
171        ResponseHeadersStatus::Continue
172    }
173}
174
175/// DelayFilter is a filter that delays the request.
176///
177/// This implements the [`HttpFilter`] trait, and will be created per each filter chain.
178struct DelayFilter {
179    atomic: std::sync::atomic::AtomicUsize,
180}
181
182impl HttpFilter for DelayFilter {
183    fn new_instance(
184        &mut self,
185        envoy_filter_instance: EnvoyFilterInstance,
186    ) -> Box<dyn HttpFilterInstance> {
187        let req_no = self
188            .atomic
189            .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
190
191        let envoy_filter_instance = Arc::new(Mutex::new(Some(envoy_filter_instance)));
192        Box::new(DelayFilterInstance {
193            req_no,
194            envoy_filter_instance,
195        })
196    }
197}
198
199/// DelayFilterInstance is a filter instance that delays the request.
200///
201/// This implements the [`HttpFilterInstance`] trait, and will be created per each request.
202struct DelayFilterInstance {
203    req_no: usize,
204    envoy_filter_instance: Arc<Mutex<Option<EnvoyFilterInstance>>>,
205}
206
207impl HttpFilterInstance for DelayFilterInstance {
208    fn request_headers(
209        &mut self,
210        _request_headers: &RequestHeaders,
211        _end_of_stream: bool,
212    ) -> RequestHeadersStatus {
213        if self.req_no == 1 {
214            let envoy_filter_instance = self.envoy_filter_instance.clone();
215            let req_no = self.req_no;
216            std::thread::spawn(move || {
217                println!("blocking for 1 second at RequestHeaders with id {}", req_no);
218                std::thread::sleep(std::time::Duration::from_secs(1));
219                println!("calling ContinueRequest with id {}", req_no);
220                if let Some(envoy_filter_instance) = envoy_filter_instance.lock().unwrap().as_ref()
221                {
222                    envoy_filter_instance.continue_request();
223                }
224            });
225            println!(
226                "RequestHeaders returning StopAllIterationAndBuffer with id {}",
227                self.req_no
228            );
229            RequestHeadersStatus::StopAllIterationAndBuffer
230        } else {
231            println!("RequestHeaders called with id {}", self.req_no);
232            RequestHeadersStatus::Continue
233        }
234    }
235
236    fn request_body(
237        &mut self,
238        _request_body_frame: &RequestBodyBuffer,
239        _end_of_stream: bool,
240    ) -> RequestBodyStatus {
241        if self.req_no == 2 {
242            let envoy_filter_instance = self.envoy_filter_instance.clone();
243            let req_no = self.req_no;
244            std::thread::spawn(move || {
245                println!("blocking for 1 second at RequestBody with id {}", req_no);
246                std::thread::sleep(std::time::Duration::from_secs(1));
247                println!("calling ContinueRequest with id {}", req_no);
248                if let Some(envoy_filter_instance) = envoy_filter_instance.lock().unwrap().as_ref()
249                {
250                    envoy_filter_instance.continue_request();
251                }
252            });
253            println!(
254                "RequestBody returning StopIterationAndBuffer with id {}",
255                self.req_no
256            );
257            RequestBodyStatus::StopIterationAndBuffer
258        } else {
259            println!("RequestBody called with id {}", self.req_no);
260            RequestBodyStatus::Continue
261        }
262    }
263
264    fn response_headers(
265        &mut self,
266        _response_headers: &ResponseHeaders,
267        _end_of_stream: bool,
268    ) -> ResponseHeadersStatus {
269        if self.req_no == 3 {
270            let envoy_filter_instance = self.envoy_filter_instance.clone();
271            let req_no = self.req_no;
272            std::thread::spawn(move || {
273                println!(
274                    "blocking for 1 second at ResponseHeaders with id {}",
275                    req_no
276                );
277                std::thread::sleep(std::time::Duration::from_secs(1));
278                println!("calling ContinueResponse with id {}", req_no);
279                if let Some(envoy_filter_instance) = envoy_filter_instance.lock().unwrap().as_ref()
280                {
281                    envoy_filter_instance.continue_response();
282                }
283            });
284            println!(
285                "ResponseHeaders returning StopAllIterationAndBuffer with id {}",
286                self.req_no
287            );
288
289            ResponseHeadersStatus::StopAllIterationAndBuffer
290        } else {
291            println!("ResponseHeaders called with id {}", self.req_no);
292            ResponseHeadersStatus::Continue
293        }
294    }
295
296    fn response_body(
297        &mut self,
298        _response_body_frame: &ResponseBodyBuffer,
299        _end_of_stream: bool,
300    ) -> ResponseBodyStatus {
301        if self.req_no == 4 {
302            let envoy_filter_instance = self.envoy_filter_instance.clone();
303            let req_no = self.req_no;
304            std::thread::spawn(move || {
305                println!("blocking for 1 second at ResponseBody with id {}", req_no);
306                std::thread::sleep(std::time::Duration::from_secs(1));
307                println!("calling ContinueResponse with id {}", req_no);
308                if let Some(envoy_filter_instance) = envoy_filter_instance.lock().unwrap().as_ref()
309                {
310                    envoy_filter_instance.continue_response();
311                }
312            });
313            println!(
314                "ResponseBody returning StopIterationAndBuffer with id {}",
315                self.req_no
316            );
317
318            ResponseBodyStatus::StopIterationAndBuffer
319        } else {
320            println!("ResponseBody called with id {}", self.req_no);
321            ResponseBodyStatus::Continue
322        }
323    }
324
325    fn destroy(&mut self) {
326        *self.envoy_filter_instance.lock().unwrap() = None;
327    }
328}
329
330/// BodyFilter is a filter that manipulates request/response bodies.
331///
332/// This implements the [`HttpFilter`] trait, and will be created per each filter chain.
333struct BodiesFilter {}
334
335impl HttpFilter for BodiesFilter {
336    fn new_instance(
337        &mut self,
338        envoy_filter_instance: EnvoyFilterInstance,
339    ) -> Box<dyn HttpFilterInstance> {
340        Box::new(BodiesFilterInstance {
341            envoy_filter_instance,
342        })
343    }
344}
345
346/// BodiesFilterInstance is a filter instance that manipulates request/response bodies.
347///
348/// This implements the [`HttpFilterInstance`] trait, and will be created per each request.
349struct BodiesFilterInstance {
350    envoy_filter_instance: EnvoyFilterInstance,
351}
352
353impl HttpFilterInstance for BodiesFilterInstance {
354    fn request_body(
355        &mut self,
356        request_body_frame: &RequestBodyBuffer,
357        end_of_stream: bool,
358    ) -> RequestBodyStatus {
359        println!(
360            "new request body frame: {}",
361            String::from_utf8(request_body_frame.copy()).unwrap()
362        );
363        if !end_of_stream {
364            // Wait for the end of the stream to see the full body.
365            return RequestBodyStatus::StopIterationAndBuffer;
366        }
367
368        // Get the entire request body reference - this does not copy the body.
369        let entire_body = self.envoy_filter_instance.get_request_body_buffer();
370        println!(
371            "entire request body: {}",
372            String::from_utf8(entire_body.copy()).unwrap()
373        );
374
375        // This demonstrates how to use the reader to read the body.
376        let mut reader = entire_body.reader();
377        let mut buf = vec![0; 2];
378        let mut offset = 0;
379        loop {
380            let n = reader.read(&mut buf).unwrap();
381            if n == 0 {
382                break;
383            }
384            println!(
385                "request body read 2 bytes offset at {}: \"{}\"",
386                offset,
387                std::str::from_utf8(&buf[..n]).unwrap()
388            );
389            offset += 2;
390        }
391
392        // Replace the entire body with 'Y' without copying.
393        for i in entire_body.slices() {
394            for j in i {
395                *j = b'X';
396            }
397        }
398        RequestBodyStatus::Continue
399    }
400
401    fn response_body(
402        &mut self,
403        response_body_frame: &ResponseBodyBuffer,
404        end_of_stream: bool,
405    ) -> ResponseBodyStatus {
406        println!(
407            "new response body frame: {}",
408            String::from_utf8(response_body_frame.copy()).unwrap()
409        );
410        if !end_of_stream {
411            // Wait for the end of the stream to see the full body.
412            return ResponseBodyStatus::StopIterationAndBuffer;
413        }
414
415        // Get the entire response body reference - this does not copy the body.
416        let entire_body = self.envoy_filter_instance.get_response_body_buffer();
417        println!(
418            "entire response body: {}",
419            String::from_utf8(entire_body.copy()).unwrap()
420        );
421
422        // This demonstrates how to use the reader to read the body.
423        let mut reader = entire_body.reader();
424        let mut buf = vec![0; 2];
425        let mut offset = 0;
426        loop {
427            let n = reader.read(&mut buf).unwrap();
428            if n == 0 {
429                break;
430            }
431            println!(
432                "response body read 2 bytes offset at {}: \"{}\"",
433                offset,
434                std::str::from_utf8(&buf[..n]).unwrap()
435            );
436            offset += 2;
437        }
438
439        // Replace the entire body with 'Y' without copying.
440        for i in entire_body.slices() {
441            for j in i {
442                *j = b'Y';
443            }
444        }
445
446        ResponseBodyStatus::Continue
447    }
448}
449
450/// BodiesReplaceFilter is a filter that replaces request/response bodies.
451///
452/// This implements the [`HttpFilter`] trait, and will be created per each filter chain.
453struct BodiesReplace {}
454
455impl HttpFilter for BodiesReplace {
456    fn new_instance(
457        &mut self,
458        envoy_filter_instance: EnvoyFilterInstance,
459    ) -> Box<dyn HttpFilterInstance> {
460        Box::new(BodiesReplaceInstance {
461            envoy_filter_instance,
462            request_append: String::new(),
463            request_prepend: String::new(),
464            request_replace: String::new(),
465            response_append: String::new(),
466            response_prepend: String::new(),
467            response_replace: String::new(),
468        })
469    }
470}
471
472/// BodiesReplaceInstance is a filter instance that replaces request/response bodies.
473///
474/// This implements the [`HttpFilterInstance`] trait, and will be created per each request.
475struct BodiesReplaceInstance {
476    envoy_filter_instance: EnvoyFilterInstance,
477    request_append: String,
478    request_prepend: String,
479    request_replace: String,
480    response_append: String,
481    response_prepend: String,
482    response_replace: String,
483}
484
485impl HttpFilterInstance for BodiesReplaceInstance {
486    fn request_headers(
487        &mut self,
488        request_headers: &RequestHeaders,
489        _end_of_stream: bool,
490    ) -> RequestHeadersStatus {
491        if let Some(value) = request_headers.get(b"append") {
492            self.request_append = std::str::from_utf8(value).unwrap().to_string();
493        }
494        if let Some(value) = request_headers.get(b"prepend") {
495            self.request_prepend = std::str::from_utf8(value).unwrap().to_string();
496        }
497        if let Some(value) = request_headers.get(b"replace") {
498            self.request_replace = std::str::from_utf8(value).unwrap().to_string();
499        }
500        request_headers.remove(b"content-length"); // Remove content-length header to avoid mismatch.
501        RequestHeadersStatus::Continue
502    }
503
504    fn request_body(
505        &mut self,
506        _request_body_frame: &RequestBodyBuffer,
507        end_of_stream: bool,
508    ) -> RequestBodyStatus {
509        if !end_of_stream {
510            // Wait for the end of the stream to see the full body.
511            return RequestBodyStatus::StopIterationAndBuffer;
512        }
513
514        let entire_body = self.envoy_filter_instance.get_request_body_buffer();
515        if !self.request_append.is_empty() {
516            entire_body.append(self.request_append.as_bytes());
517        }
518        if !self.request_prepend.is_empty() {
519            entire_body.prepend(self.request_prepend.as_bytes());
520        }
521        if !self.request_replace.is_empty() {
522            entire_body.replace(self.request_replace.as_bytes());
523        }
524        RequestBodyStatus::Continue
525    }
526
527    fn response_headers(
528        &mut self,
529        response_headers: &ResponseHeaders,
530        _end_of_stream: bool,
531    ) -> ResponseHeadersStatus {
532        if let Some(value) = response_headers.get(b"append") {
533            self.response_append = std::str::from_utf8(value).unwrap().to_string();
534        }
535        if let Some(value) = response_headers.get(b"prepend") {
536            self.response_prepend = std::str::from_utf8(value).unwrap().to_string();
537        }
538        if let Some(value) = response_headers.get(b"replace") {
539            self.response_replace = std::str::from_utf8(value).unwrap().to_string();
540        }
541        response_headers.remove(b"content-length"); // Remove content-length header to avoid mismatch.
542        ResponseHeadersStatus::Continue
543    }

Trait Implementations§

Source§

impl Clone for ResponseHeaders

Source§

fn clone(&self) -> ResponseHeaders

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ResponseHeaders

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Copy for ResponseHeaders

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.