Adding Gzip compression to Iron AfterMiddleware


#1

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:

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!


#2

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);


#3

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.


#4

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


#5

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

#6

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)
}

}