bodies that respond to :path

7 views
Skip to first unread message

Ryan Tomayko

unread,
Dec 7, 2008, 8:43:31 PM12/7/08
to rack-...@googlegroups.com
This X-Sendfile middleware I'm working on checks if the body responds to
":to_file" (with a string containing the body's file path) and, if so,
uses the file contents instead of iterating of body.each.

Today, I was looking at the source of rack/file.rb and came across this
comment:

# Handlers can detect if bodies are a Rack::File, and use mechanisms
# like sendfile on the +path+.
class File
attr_accessor :root
attr_accessor :path
...

If I read this correctly, a body that responds to "path" can be treated
specially and the contents of the file can be assumed to be what one
would get by reading body#each. Is this just something that's not
documented in the SPEC?

SPEC has the following to say about "The Body":

> The Body must respond to each and must only yield String
> values. If the Body responds to close, it will be called
> after iteration. The Body commonly is an Array of Strings,
> the application instance itself, or a File-like object.

I suggest adding the following as the third sentence if we want to
document this:

> If the Body responds to path, it must identify the location
> of a file whose contents are identical to that produced by
> the each method.

Also, do any of the handlers use sendfile(2) (or similar
platform-specific system call) in the manner described in Rack::File?

Thanks,
Ryan

Christian Neukirchen

unread,
Dec 8, 2008, 9:29:54 AM12/8/08
to rack-...@googlegroups.com
On Mon, Dec 8, 2008 at 2:43 AM, Ryan Tomayko <r...@tomayko.com> wrote:
>
> This X-Sendfile middleware I'm working on checks if the body responds to
> ":to_file" (with a string containing the body's file path) and, if so,
> uses the file contents instead of iterating of body.each.
>
> Today, I was looking at the source of rack/file.rb and came across this
> comment:
>
> # Handlers can detect if bodies are a Rack::File, and use mechanisms
> # like sendfile on the +path+.
> class File
> attr_accessor :root
> attr_accessor :path
> ...
>
> If I read this correctly, a body that responds to "path" can be treated
> specially and the contents of the file can be assumed to be what one
> would get by reading body#each. Is this just something that's not
> documented in the SPEC?

No. My intent was that you can *explicitly check* for Rack::File, like
if body.kind_of? Rack::File
But your proposal may be better. Others, please comment.

> SPEC has the following to say about "The Body":
>
>> The Body must respond to each and must only yield String
>> values. If the Body responds to close, it will be called
>> after iteration. The Body commonly is an Array of Strings,
>> the application instance itself, or a File-like object.
>
> I suggest adding the following as the third sentence if we want to
> document this:
>
>> If the Body responds to path, it must identify the location
>> of a file whose contents are identical to that produced by
>> the each method.
>
> Also, do any of the handlers use sendfile(2) (or similar
> platform-specific system call) in the manner described in Rack::File?

Not as far as I can tell.

> Thanks,
> Ryan
>
--
Christian Neukirchen <chneuk...@gmail.com> http://chneukirchen.org

Scytrin dai Kinthra

unread,
Dec 8, 2008, 12:01:58 PM12/8/08
to rack-...@googlegroups.com
+1 It sounds like a useful feature to me.

--
stadik.net

James Tucker

unread,
Dec 8, 2008, 12:29:49 PM12/8/08
to rack-...@googlegroups.com

It's just some conditional property, the question is do you want to
lock authors into a specific ancestor list?

Maybe you want to do that with a module?

Being a child of Rack::File makes the definition very explicit, just
responding to path could be clobbered for some unsuspecting user,
doing something like a string class that responds to path and proxies
the method :"/", something horrible like that could trip people up.

Ryan King

unread,
Dec 8, 2008, 1:26:15 PM12/8/08
to rack-...@googlegroups.com
On Dec 8, 2008, at 9:29 AM, James Tucker wrote:
> Being a child of Rack::File makes the definition very explicit, just
> responding to path could be clobbered for some unsuspecting user,
> doing something like a string class that responds to path and
> proxies the method :"/", something horrible like that could trip
> people up.

Perhaps #to_pathname returning a Pathname might be more useful then?

-ryan

James Tucker

unread,
Dec 8, 2008, 1:56:22 PM12/8/08
to rack-...@googlegroups.com

hmm, not trying to force the ancestor idea, just adding some more
food...

module Rack::Forwardable::File; end

class MyThingy
include Rack::Forwardable::File

... some user code ...
end

[200, {}, my_thingy] # => maybe X-Sendfile: my_thingy.to_s

I guess I like this as one could also go down a route like:

module Rack::Forwardable::Asset; end

class MyAsset
include Rack::Forwardable::Asset

... some user code ...
end

[200, {}, my_asset] # => maybe a sendfile, maybe a remote uri, etc,
depends on what the server is configured to do for assets


- that's it, it's who decides, server or app? can the api provide a mix?

Also, what about middleware for these?

>
>
> -ryan

Ryan Tomayko

unread,
Dec 8, 2008, 2:48:28 PM12/8/08
to rack-...@googlegroups.com
On 12/8/08 10:56 AM, James Tucker wrote:
> On 8 Dec 2008, at 18:26, Ryan King wrote:
>> On Dec 8, 2008, at 9:29 AM, James Tucker wrote:
>>> Being a child of Rack::File makes the definition very explicit, just
>>> responding to path could be clobbered for some unsuspecting user,
>>> doing something like a string class that responds to path and proxies
>>> the method :"/", something horrible like that could trip people up.
>>
>> Perhaps #to_pathname returning a Pathname might be more useful then?
>
> hmm, not trying to force the ancestor idea, just adding some more food...

I definitely prefer defining a message that bodies can optionally
respond to in order to signal a file location over requiring a specific
class/module in the ancestor chain. "path" may be too general and cause
clashes. I think "to_pathname" or "to_file" would be better.

There's no part of the Rack spec that requires the use of Rack's classes
or modules and I think we should try to preserve this. If we want a
Rack::Asset class/module that simplifies strapping a method on bodies by
making it easy to expose the correct methods than that's easy enough to
add on top.

My proposal at this point would be adding the following to the SPEC and
make Rack::File respond to "to_path":

> If the Body responds to "to_path", it must return a String
> identifying the location of a file whose contents are identical


> to that produced by the each method.

It's the simplest thing that can possibly work and would let us build
middleware, handlers, and utility code against an agreed upon protocol.

Ryan

Matt Todd

unread,
Dec 8, 2008, 2:55:13 PM12/8/08
to rack-...@googlegroups.com
+1 for to_file or to_path

Very simple, transparent, non-magical, simple to convey, etc.

Matt

--
Matt Todd
Highgroove Studios
www.highgroove.com
cell: 404-314-2612
blog: maraby.org

Scout - Web Monitoring and Reporting Software
www.scoutapp.com

Ryan Tomayko

unread,
Dec 9, 2008, 1:50:23 AM12/9/08
to rack-...@googlegroups.com
On 12/8/08 11:48 AM, Ryan Tomayko wrote:
> On 12/8/08 10:56 AM, James Tucker wrote:
>> On 8 Dec 2008, at 18:26, Ryan King wrote:
>>> On Dec 8, 2008, at 9:29 AM, James Tucker wrote:
>>>> Being a child of Rack::File makes the definition very explicit, just
>>>> responding to path could be clobbered for some unsuspecting user,
>>>> doing something like a string class that responds to path and proxies
>>>> the method :"/", something horrible like that could trip people up.
>>> Perhaps #to_pathname returning a Pathname might be more useful then?
>> hmm, not trying to force the ancestor idea, just adding some more food...
>
> I definitely prefer defining a message that bodies can optionally
> respond to in order to signal a file location over requiring a specific
> class/module in the ancestor chain. "path" may be too general and cause
> clashes. I think "to_pathname" or "to_file" would be better.

Here's a crack at a patch:

http://github.com/rtomayko/rack/commit/0748e77e

Add body.to_path to SPEC; implement in Rack::File

Rack::File responds to #to_path. Rack::Lint checks that the files
given in #to_path exist and will WARN on rack.errors if not.

Thanks,
Ryan

Christian Neukirchen

unread,
Dec 9, 2008, 1:55:43 AM12/9/08
to rack-...@googlegroups.com

Another idea: what about setting X-Sendfile in the headers?

Ryan Tomayko

unread,
Dec 9, 2008, 2:10:46 AM12/9/08
to rack-...@googlegroups.com
On 12/8/08 10:55 PM, Christian Neukirchen wrote:
> Another idea: what about setting X-Sendfile in the headers?

That's what I'm attempting to do here:

http://github.com/rtomayko/rack-contrib/tree/master/lib/rack/sendfile.rb

It's kind of complicated, though:

1. Ideally, you want X-Sendfile to be conditional based on whether
there's a server upstream that's going to process the header. It's nice
to get the response body when going directly to the backend. Right now
I'm relying on an X-Sendfile-Type request header being set (can also be
passed as a param when the middleware is created).

2. Nginx's X-Sendfile workalike (X-Accel-Redirect) requires mapping
filesystem paths into "private URI space" in nginx config. You need to
know how Nginx is configured to provide the right header value. Here
again I'm relying on a request header to explicitly map between the two
spaces but ... ewww.

I'm thinking about splitting them into separate classes -- have a
Rack::Sendfile and a Rack::AccelRedirect.

I really thought the whole exercise would be easy but I'm not happy at
all with the thing as it is now.

Ryan

Reply all
Reply to author
Forward
0 new messages