Adding Gzip compression to Iron AfterMiddleware

Hello,

I'm using both handlebars templates in Iron and would like to add gzip compression to those results.

I'm looking at this as my example, unfortunately I can't use this library directly due to templates:
https://github.com/gsquire/compress/blob/master/src/lib.rs

Here is my code I've written:
pub struct GzipAfterMiddleware;

impl AfterMiddleware for GzipAfterMiddleware {
fn after(&self, request: &mut Request, mut response: Response) -> IronResult {

    //Check 'accept request' to see if the Request allows gzip, if so, gzip before sending.
    let raw_accept_encoding = request.headers.get_raw("Accept-Encoding").unwrap();

    for accept_encoding in raw_accept_encoding
    {
        println!("raw_accept_encoding: {}", str::from_utf8(accept_encoding).unwrap());

        if str::from_utf8(accept_encoding).unwrap().to_lowercase().contains("gzip")
        {
            response.headers.set(ContentEncoding(vec![Encoding::Gzip]));
            let mut encoder = GzEncoder::new(Vec::new(), Compression::Default);
            let _ = encoder.write_all(response.get().unwrap());

            let compressed_bytes = encoder.finish().unwrap();

            compressed_bytes.modify(response);
        }
    }

    Ok(response)
}

}

Here is the error I'm getting:
error[E0308]: mismatched types
--> src/main.rs:375:41
|
375 | compressed_bytes.modify(response);
| ^^^^^^^^ expected mutable reference, found struct iron::Response
|
= note: expected type &mut iron::Response
= note: found type iron::Response

I've tried all sorts of things.. but I'm sure I'm missing something. I'm a little rusty with my rust.. do you see what I'm doing wrong?

Thank you!

There error message is very clear, you're passing a value of type Response but the function (modify) is expecting a mutable reference instead: compressed_bytes.modify(&mut response);

1 Like

Thanks, yeah.. I should better review and remember Rust. Maybe read through the book and some examples again. That makes sense now though.

Getting an error about this line now:
let _ = encoder.write_all(response.get().unwrap());

type annotations required: cannot resolve <_ as iron::typemap::Key>::Value == _

I was playing with this one before and apparently got it wrong.. does anyone know how to get the body of an Iron response, so I can compress it?

Thanks again.

Looking at the documentation it looks like the get method on Response has something to do with plugins and nothing with reading the body.

1 Like

The documentation would seem to imply one needs to do something like this to write out the body:

let resp: Response = ...
let mut rb = ResponseBody::new(GzEncoder::new(...)); // assuming GzEncoder implements Write
resp.body.unwrap().write_body(&mut rb); // unwrap'ing the Option here to keep it brief
1 Like

Thank you, both of you!

Been tweaking and slowly fixing one thing after another one this bit of code. Getting a new error that I'm confused about. I understand why I'm getting this error.. but I don't understand how to fix it. I need to unbox respBody in order to access it's contents.. but I can't do so because sized is not implemented? Is there someway to use "?Sized" to fix this? Do I need to switch the pointer? I'd much rather avoid unsafe rust code only if it's so that I can claim 100% safe code only.

Error
error[E0277]: the trait bound iron::response::WriteBody: std::marker::Sized is not satisfied
--> src/main.rs:376:68
|
376 | let ptr = (respBody as Box<Any + 'static>).downcast::();
| ^^^^^^^^ the trait std::marker::Sized is not implemented for iron::response::WriteBody
|
= note: iron::response::WriteBody does not have a constant size known at compile-time

And yes it's getting other errors.. but I think that if I could fix this one, I could fix the rest.

** Current Code**

//Check 'accept request' to see if the Request allows gzip, if so, gzip before sending.
impl AfterMiddleware for GzipAfterMiddleware 
{
  fn after(&self, request: &mut Request, mut response: Response) -> IronResult<Response> {
    let raw_accept_encoding = request.headers.get_raw("Accept-Encoding").unwrap();

    for accept_encoding in raw_accept_encoding
    {
        println!("raw_accept_encoding: {}", str::from_utf8(accept_encoding).unwrap());

        if str::from_utf8(accept_encoding).unwrap().to_lowercase().contains("gzip")
        {
            response.headers.set(ContentEncoding(vec![Encoding::Gzip]));
            let mut encoder = GzEncoder::new(Vec::new(), Compression::Default);

            match response.body {
                Some(respBody) => 
                {
                    let ptr = (respBody as Box<Any + 'static>).downcast::<WriteBody>();

                    encoder.write_all(respBody);

                    let compressed_bytes = encoder.finish().unwrap();

                    Ok(compressed_bytes);
                },
                None => Ok(response),
            }
        }
    }

    Ok(response)
}

}