Closed FlogramMatt closed 7 years ago
Hi @FractalMatt, is it possible to read the body from response in your AfterMiddleware
and gz it? I think that's how the middlewarepattern designed to be.
Yeah that probably is the 'correct' way to do it. I've had a lot of trouble with the AfterMiddleware part though. Specifically the reading the old body response body. Maybe you would have some ideas or pointers there?
I can send you the complete code I have so far in a little bit when I have access to my home computer.
So I could copy and paste in the code but I guess the problem is I tried a few different approaches.. and none of them were working.
Can you just call response.body
to get a boxed reference to rendered string generated by handlebars middleware? Then you can gz it and set the compressed bytes as body.
I tried that, response.body gets me a "ResponseBody".. I then tried using "write_all" to try to get access to the actual body.
For example this snippet: match response.body { Some(body) => //Who? { let mut respBody: ResponseBody; let mut ActualContent: [u8];
body.write_body(&mut respBody);
respBody.write_all(&mut ActualContent);
//let ptr = (respBody as Box<Any + 'static>).downcast::<WriteBody>();
encoder.write_all(body);
let compressed_bytes = encoder.finish().unwrap();
Ok(compressed_bytes);
//compressed_bytes.modify(res);
},
None => Ok(response),
}
The biggest issue I'm then getting is this error:
error[E0277]: the trait bound [u8]: std::marker::Sized is not satisfied
--> src/main.rs:389:29 |
389 | let mut ActualContent: [u8]; | ^^^^^^^^^^^^^^^^^ the trait std::marker::Sized is not implemented for [u8] |
---|
= note: `[u8]` does not have a constant size known at compile-time
= note: all local variables must have a statically known size
Any thoughts? Thank you!!! You'd be welcome to include this MiddleWare into handlebars if we get it working, would be great if others didn't have to go through this to get this working.
Thank you and I will look into this issue later. It seems that we cannot easliy cast response body to concrete type.
But I still suggest to do the compression task outside the template middleware. Ideally if we can find a way to access the body, we will add another AfterMiddleware. And if we failed, I'm strongly recommend you to use nginx for the gzip thing.
I have a compression middleware that should work for you:
pub struct GzMiddleware;
impl AfterMiddleware for GzMiddleware {
fn after(&self, _: &mut Request, mut resp: Response) -> IronResult<Response> {
let compressed_bytes = resp.body.as_mut().map(|mut b| {
let mut encoder = GzEncoder::new(Vec::new(), Compression::Best);
{
let _ = b.write_body(&mut encoder);
}
encoder.finish().unwrap()
});
if let Some(b) = compressed_bytes {
resp.headers.set(ContentEncoding(vec![Encoding::Gzip]));
resp.set_mut(b);
}
Ok(resp)
}
}
And this middleware is pretty generic which could work with most Iron app. You just need to put it at the end of chain.
Full code: https://github.com/sunng87/handlebars-iron/blob/master/examples/compress.rs
Thank you sunng87! I very much appreciate it.
Still getting an error when trying to plug that into my code and compile it though: [leo@new-host-2 user-panel]$ sudo cargo build --no-default-features Compiling fractal-website v0.2.0 (file:///home/leo/Fractal/user-panel) error[E0308]: mismatched types --> src/main.rs:365:38 | 365 | let _ = b.write_body(&mut encoder); | ^^^^^^^^^^^^ expected struct iron::response::ResponseBody , found struct flate2::write::EncoderWriter |
---|
= note: expected type `&mut iron::response::ResponseBody<'_>`
= note: found type `&mut flate2::write::EncoderWriter<std::vec::Vec<u8>>`
error: aborting due to previous error
Is that code compiling for you? I will play with this a little bit myself.
I think should be writing the body into the encoder, this looks like it's trying to write the encoder into the body.
What version of Iron you were using?
Thanks sunng87, I was using iron ^0.4, I switched to ^0.5 I'm now getting 330 errors.. and I can't tell for certain but I'm not seeing these ones amongst them.
Thanks though, that probably fixed the gzip error...
Now the question is, do I switch to rocket or fix all of these errors? Will think about it.
Rocket has a different programming model. Personally I still believe Iron's middleware architecture is elegant and simple to understand. It's totally up to you :)
It's easy enough to add gzip to non-handlebars iron responses: https://github.com/gsquire/compress (which might be a great example of how to use the other library)
BUT, when using handlebars, it's much trickier to do. I was working on my own AfterMiddleware handler that would mutate the output of handlerbars.. but still being new to Rust, and the AfterMIddleware piece has been a little tricky. Should be fairly simple given you already have that piece working if it's directly integrating into the library... and probably an appreciated feature by all. https://github.com/alexcrichton/flate2-rs
Here's the snippet of code I already have working, to be added to the AfterMiddleware, potentially adding gzip as an extra option: let raw_accept_encoding = request.headers.get_raw("Accept-Encoding").unwrap();
}