How about:
class GzipOutputFilter
def initialize(app)
@app = app
end
def call(env)
out = @app.call(env)
gzip = IO.popen('gzip', 'r+')
gzip.write out[2] # handles String only, would be nice to check
for IO object too
gzip.close_write
out[1]['Content-encoding'] = 'gzip'
out[2] = gzip
out
end
end
class HelloHandler
def call(env)
[ 200, { 'Content-type' => 'text/plain' }, 'hello, world' ]
end
end
use Rack::Lint
use GzipOutputFilter
run HelloHandler.new
Adam
Here is a middleware adapted from Mongrel's Deflate Handler:
require 'zlib'
class Deflater
include Zlib
def initialize(app)
@app = app
end
def call(env)
accept = env['HTTP_ACCEPT_ENCODING']
status, headers, body = @app.call(env)
if accept and accept.include? 'deflate'
headers["Content-Encoding"] = "deflate"
body = deflate(body)
end
[status, headers, body]
end
def deflate(body)
deflater = Deflate.new(
DEFAULT_COMPRESSION,
# drop the zlib header which causes both Safari and IE to choke
-MAX_WBITS,
DEF_MEM_LEVEL,
DEFAULT_STRATEGY)
case body
when IO
body.rewind if body.respond_to? :rewind
gzout = StringIO.new(deflater.deflate(body.read, FINISH))
body.close
when String
gzout = StringIO.new(deflater.deflate(body, FINISH))
end
gzout.rewind
gzout
end
end
Cheers-
- Ezra Zygmuntowicz
-- Founder & Software Architect
-- ez...@engineyard.com
-- EngineYard.com
> On Jun 11, 2008, at 8:30 AM, spicyj wrote:
>
>>
>> Anyone know how to write a Rack middleware to serve static files as
>> gzipped?
>>
>> I can see how to set the Content-Encoding header, but the file still
>> needs to be zipped up.
>
> Here is a middleware adapted from Mongrel's Deflate Handler:
I'd like to import this to the Rack tree.
--
Christian Neukirchen <chneuk...@gmail.com> http://chneukirchen.org
>
> Ezra Zygmuntowicz <e...@engineyard.com> writes:
>
>> On Jun 11, 2008, at 8:30 AM, spicyj wrote:
>>
>>>
>>> Anyone know how to write a Rack middleware to serve static files as
>>> gzipped?
>>>
>>> I can see how to set the Content-Encoding header, but the file still
>>> needs to be zipped up.
>>
>> Here is a middleware adapted from Mongrel's Deflate Handler:
>
> I'd like to import this to the Rack tree.
Please feel free, maybe just add a note that it was adapted from the
mongrel deflate handler.
>
> According to that document (and some others), http://developer.yahoo.com/performance/rules.html
> , the returned header is:
>
> Content-Encoding: gzip
>
> ( headers["Content-Encoding"] = "gzip" )
>
> Which is better/right - deflate of gzip?
I guess we should support both:
def call(env)
accept = env['HTTP_ACCEPT_ENCODING']
status, headers, body = @app.call(env)
if accept and [ 'deflate', 'gzip'].include? accept
headers["Content-Encoding"] = "deflate"
body = deflate(body)
end
[status, headers, body]
end
Cheers-
- Ezra
> def call(env)
> accept = env['HTTP_ACCEPT_ENCODING']
> status, headers, body = @app.call(env)
> if accept and [ 'deflate', 'gzip'].include? accept
> - headers["Content-Encoding"] = "deflate"
> + headers["Content-Encoding"] = accept
> body = deflate(body)
> end
> [status, headers, body]
> end
- Ezra
> if accept and [ 'deflate', 'gzip'].include? accept
Is this the best way we can parse HTTP headers? Seems a bit to eager to me.
>
> Ezra Zygmuntowicz <ezmo...@gmail.com> writes:
>
>> if accept and [ 'deflate', 'gzip'].include? accept
>
> Is this the best way we can parse HTTP headers? Seems a bit to
> eager to me.
I don't get what you are saying? There is no parsing going on here.
- Ezra
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
Accessing the value of Accept-Encoding seems to be more complex to me?
I took a quick stab at implementing it properly:
http://pastie.org/private/gkznbcflnc3uhwxoebxa
The accepted_encodings methods definitely needs a second look and a new name.
Provide a little feedback and I might finish it for inclusion. :-)
Cheers,
Christoffer Sawicki
This looks good. How does it compare to
http://chneukirchen.org/repos/coset/lib/coset/mimeparse.rb ?
> Cheers,
> Christoffer Sawicki
Encoding names are not on the type/subtype form so we can't use
MIMEParse as is for Accept-Encoding.
I don't think it's worth generalizing the current mimeparse.rb code to
cover both Accept and Accept-Encoding; handling Accept-Encoding is
much simpler than Accept (mostly because there's no need to deal with
type specificity).
Once I extend my Accept-Encoding code to handle * and drop the complex
regexp I'll ask the list for a review.
Cheers,
Christoffer Sawicki
> I don't think it's worth generalizing the current mimeparse.rb code to
> cover both Accept and Accept-Encoding; handling Accept-Encoding is
> much simpler than Accept (mostly because there's no need to deal with
> type specificity).
>
> Once I extend my Accept-Encoding code to handle * and drop the complex
> regexp I'll ask the list for a review.
Okay, so let's these things stay seperate.
I finished it yesterday: http://pastie.org/private/mfn66wqhxkjpky1rnvdha
Comments are very welcome - especially on the select_best_encoding tests.
Cheers,
Christoffer Sawicki
Okay, thanks. I pushed the patch to my public repo if anyone wants to fetch it:
http://github.com/Qerub/rack/commit/bd1f55e6729243b08f689a21d7716c208d6a2b62
Cheers,
Christoffer Sawicki
Is the rest of the gzip stuff good enough for merging now? (And does
it have tests?)
I'm writing tests at the moment.
Cheers,
Christoffer Sawicki
Okay, here comes Rack::Deflater:
http://github.com/Qerub/rack/commit/56a1e316b69303e642dc5146def52dc93db18d0c
Comments are very welcome, as usual.
Cheers,
Christoffer Sawicki
Add the missing newline and a useful error message, and I'll merge it.
>> Okay, here comes Rack::Deflater:http://github.com/Qerub/rack/commit/56a1e316b69303e642dc5146def52dc93...
>
> I've added gzip support: http://github.com/soprano/rack/commit/6d835ba292c5dc2af68784ccf6a05352700d26b5
>
> I can't get a full test case, because gzip needs a header for the
> mtime of the data, changing the output. Is there a good way to test
> the output (maybe gunzipping the output and comparing it to the
> original)? I should probably also change it to use the Last-Modified
> header as the mtime.
Sounds good.
> Also, should this be in a separate file? Apache's mod_deflate handles
> both deflate and gzip encodings.
One file is okay.
> ~spicyj
That sounds like the best way to me.
> I should probably also change it to use the Last-Modified
> header as the mtime.
Sounds good.
> Also, should this be in a separate file? Apache's mod_deflate handles
> both deflate and gzip encodings.
I think it should be in the same file but that the file needs a better
name. Any ideas?
Cheers,
Christoffer Sawicki
Okay, done!
I'd like you to merge these commits:
+ bd1f55e6729243b08f689a21d7716c208d6a2b62 Added support for
Accept-Encoding (via Request#accept_encoding and
Utils.select_best_encoding)
+ 56a1e316b69303e642dc5146def52dc93db18d0c Implemented Rack::Deflater
+ bb427052c1ecf1e8e673349c43c06cd683b28d4a deflater.rb - Added a TODO
+ e0bb0e4a22eeaeb09eea6977ef4eff481ac8ab5a deflater.rb - Removed an
unused require
+ 13a661e5af07fc1cdab88aa05c21d29667856c28 added gzip support to Rack::Deflater
+ 3fd03e6067164f63af4e3fe1ac60ca1e2dea07e9 deflater.rb - Added a
newline to the end
+ 070561230bc4de06b260a4f1a60aa72f1a6fad27 deflater.rb - Added an
error message for the 406 Not Acceptable case
+ 05fd9d0900f28ffdec5d62a9edba4fb7060a46e3 Removed some stray whitespace
Please note that spicyj's gzip support is included.
And while we're at it, these might also be a good candidate for inclusion:
+ e3f11a5d615fca9e7f6cb7ff0b97f1f4b05c659d spec_rack_utils.rb -
Reformulated two test case descriptions
+ dfe616dad05bf9fe38c2fcff07c15d7dec9fb8e9 spec_rack_utils.rb - Don't
print things on STDOUT in the Context tests
+ d2b8af6076e9159a0faeef21500fb501f83b71e9 spec_rack_handler.rb - Fixed typos
(Just to exercise my git-fu: Would it be a good way for you to cherry
pick most of these commits to a new local branch and then merge that
branch into master? This commit history isn't that interesting. Also,
I realize I should have used a feature branch for Rack::Deflater.)
Cheers,
Christoffer Sawicki
Oops, you're right... I wonder what went wrong. I either forgot to
pull from upstream before doing that commit (most likely) or you
waited more than 2 days before pushing your commit to GitHub. I find
it interesting that Git didn't inform me about the duplicate change
(in the shape of a merge conflict) but just ignored it.
Cheers,
Christoffer Sawicki
It is there in the middle of the list -- trust me. Our commit SHA1s
are not the same though.
Please excuse the horrible formatting -- I promise to not paste the
output of git-cherry -v directly next time.
Cheers,
Christoffer Sawicki
There is a simple explanation: I'm a moron and forgot to push my
changes to GitHub. My mind has obviously not gotten completely used to
the decentralized nature of Git yet. That said, everything should be
right now; take a look at http://github.com/Qerub/rack/commits/master.
Cheers,
Christoffer Sawicki
> (Just to exercise my git-fu: Would it be a good way for you to cherry
> pick most of these commits to a new local branch and then merge that
> branch into master? This commit history isn't that interesting. Also,
> I realize I should have used a feature branch for Rack::Deflater.)
Use rebase -i and remove all patch-lines you don't want in it.
Oh, right! I've rewritten the history now and pushed the new commits to GitHub:
http://github.com/Qerub/rack/commits/master
I'm not 100% certain it was a good idea and I therefore consider it an
experiment.
spicyj: Your next pull will include a notice about "forced update" and
you will probably have to --force the push afterwards because of my
rebasing.
Cheers,
Christoffer Sawicki
I think it's up to the user of Rack::Deflater to only use when it makes sense.
Cheers,
Christoffer Sawicki
> Oh, right! I've rewritten the history now and pushed the new commits to GitHub:
> http://github.com/Qerub/rack/commits/master
>
> I'm not 100% certain it was a good idea and I therefore consider it an
> experiment.
I rebased it on master and removed the unrelated commits, cherry
picking the ones I want too.
Thanks!
Cheers,
Christoffer Sawicki