You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Ian Holsman <Ia...@apache.org> on 2004/07/12 04:04:54 UTC

The Byterange filter -- a new design -- feel free to rip it to shreds

ok, now before I start this let me say one thing, this is not for *ALL* 
requests, it will only work for ones which don't have content-length 
modifiable filters (like gzip) applied to the request, and it would be
left to the webserver admin to figure out what they were, and if you 
could use this.


ok..
at the moment when a byterange request goes to a dynamic module, the
dynamic module can not use any tricks to only serve the bytes requested,
it *HAS* to serve the entire content up as buckets.

what I am proposing is something like:

1. the filter keeps a ordered list of range requests that the person 
requests.

2. it keeps state on how far it has processed in the file. thanks to 
knowing the length of the buckets processed so far.
   Q: when do the actual headers get put in.. I think they are after no?

3. it then examines the bucket + bucket length to see which range 
requests match this range, if some do it grabs that range (possibly 
splitting/copying if it meets multiple ranges) and puts it on the right 
bits of each range request.

4. if the top range request is finished, it passes those buckets through.

5. repeat until EOS/Sentinel, flushing the ordered list at the end.


now . for part 2, we would need a method of telling the byte-range 
filter that the incoming brigade only has parts X, Y, Z in it. The 
easiest way for something like mod-proxy to do this without a API change
would be for it to create a dummy bucket with the correct length, 
knowing that this would never be read. (yes I know it's a hack)

now.. this assumes that splitting a bucket (and copying) is a zero cost 
operation which doesn't actually *read* the bucket, is this true for 
most bucket types?

would this kind of thing work?

--Ian

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Nick Kew <ni...@webthing.com>.
On Tue, 13 Jul 2004, William A. Rowe, Jr. wrote:

> It would be nice in apache 2.2 to finally clean up this contract, with two
> simple metadata element to pass through the filter chain:
>
> . this request is unfiltered
> . this request has a 1:1 filter (stateless)
> . this request has a arbitrary content transformation
>
> Each filter is the stack could promote the complexity but should never set
> it to a lower state.  This would allow http/proxy modules to negotiate less
> complex transformations in more efficient ways.

Nicely put.  Thank you!

+1

-- 
Nick Kew

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Rici Lake <ri...@speedy.com.pe>.
On 1-Aug-04, at 4:41 PM, Justin Erenkrantz wrote:

> Instead, what mod_proxy could do is expose a user directive that says 
> 'no representation transformation allowed' and act accordingly: bypass 
> all filters except for the network filters.

In fact, if the filter chain cannot optimise byte ranges, there is a 
good argument for simply rejecting the byte-range request altogether, 
or at least to have a configuration directive which would allow that.

Otherwise, the server cluster is open to a very simple DoS attack: 
repeatedly request one byte in an ISO image and wait for the cluster 
backbone to collapse.


Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Justin Erenkrantz <ju...@erenkrantz.com>.
--On Sunday, August 1, 2004 11:17 PM +0200 Graham Leggett <mi...@sharp.fm> 
wrote:

> The byte ranges aren't done for the benefit of the httpd itself, but rather
> a potential multi tier backend supported by mod_proxy or mod_backhand.
>
> Right now if you range request a big file, it will work - but not before the
> entire big file has been passed from the backend application server to the
> frontend httpd over the backend network. If you think of files the sizes of
> CD ISOs (650MB) of DVD ISOs (4GB) this backend transfer is not trivial. Add
> a download accelerator to the equation (likely on a big file like a CD) and
> suddenly the entire file is transferred once for each range request, ouch.

There are two use cases here:

1) a proxy that does modify the origin representation
2) a proxy that does not modify the origin representation

The degenerate file serving case you describe is #2 - here the ideal would be 
to have the byterange request passed to the origin server and let it handle 
it.  However, there's just no way for the proxy to know ahead of time what the 
filters will do, so without explicit *user* direction, the proxy MUST get the 
full response and run it through the filters.

Instead, what mod_proxy could do is expose a user directive that says 'no 
representation transformation allowed' and act accordingly: bypass all filters 
except for the network filters.  This isn't a problem with filters per se: 
it's a problem with trying to conflate a variety of functionality in one node. 
To err on the safe side, that directive should default to the 
'representation-modifying' case but allow the admin to say 'we will not modify 
the representation.'  That would allow the degenerate case you describe to be 
resolved in a performance-efficient manner...

Also note that mod_cache suffers from this same problem.  -- justin

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Graham Leggett <mi...@sharp.fm>.
Justin Erenkrantz wrote:

> But, I'm not convinced that the benefit gained by allowing some 
> byte-range optimization is going to be worth it.  As soon as you stick 
> in mod_include and/or mod_deflate, you're going to have the ability to 
> have arbitrary content transformation.  Even EBCDIC character 
> conversions is not one-to-one.  In fact, I bet the number of filters 
> that do a 1:1 transformation (that aren't logging) is small.  So, the 
> number of cases where it isn't arbitrary would be extremely miniscule.

The byte ranges aren't done for the benefit of the httpd itself, but 
rather a potential multi tier backend supported by mod_proxy or 
mod_backhand.

Right now if you range request a big file, it will work - but not before 
the entire big file has been passed from the backend application server 
to the frontend httpd over the backend network. If you think of files 
the sizes of CD ISOs (650MB) of DVD ISOs (4GB) this backend transfer is 
not trivial. Add a download accelerator to the equation (likely on a big 
file like a CD) and suddenly the entire file is transferred once for 
each range request, ouch.

The idea is to allow the input and output filter stack to be able to 
react intelligently if it is given a byte ranged response from a content 
handler such as proxy. The CL and range info can either be parsed 
directly by the filters ("I am mod_include, I change CL and I don't 
allow Ranges, so let me strip Range off the input headers and CL off the 
output headers"), or CL and Range can be encoded into metadata that 
isn't header specific for the same purpose.

Regards,
Graham
--

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Justin Erenkrantz <ju...@erenkrantz.com>.
--On Tuesday, July 13, 2004 10:35 AM -0500 "William A. Rowe, Jr." 
<wr...@rowe-clan.net> wrote:

> The confusion results because mod_proxy isn't implemented as a content
> handler, it's a protocol handler in its own right.  Rather than insist on the
> mod_http <> mod_proxy agreeing to streamline the response, we've put
> it on every content module author to:
>
> . remove output C-L header if the size is transformed
> . remove input range headers if the content isn't 1:1 transformed
>
> This is very kludgy and more an example of where mod_http <> mod_proxy
> didn't quite get it right, and made it a little more difficult for folks who
> are just trying to transform content bodies.
>
> It would be nice in apache 2.2 to finally clean up this contract, with two
> simple metadata element to pass through the filter chain:
>
> . this request is unfiltered
> . this request has a 1:1 filter (stateless)
> . this request has a arbitrary content transformation
>
> Each filter is the stack could promote the complexity but should never set
> it to a lower state.  This would allow http/proxy modules to negotiate less
> complex transformations in more efficient ways.

Reading the Byterange thread now, I see what OtherBill's position was. 
Tossing the C-L and Range metadata from the filters is a definite good thing. 
They just shouldn't know about that metadata.  ++1 there.

But, I'm not convinced that the benefit gained by allowing some byte-range 
optimization is going to be worth it.  As soon as you stick in mod_include 
and/or mod_deflate, you're going to have the ability to have arbitrary content 
transformation.  Even EBCDIC character conversions is not one-to-one.  In 
fact, I bet the number of filters that do a 1:1 transformation (that aren't 
logging) is small.  So, the number of cases where it isn't arbitrary would be 
extremely miniscule.  Yet, perhaps I'm wrong here...  -- justin

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by "William A. Rowe, Jr." <wr...@rowe-clan.net>.
At 08:44 AM 7/13/2004, Graham Leggett wrote:
>Geoffrey Young wrote:
>
>>ok, that isn't the idea I had about output filters at all.  my own concept
>>of how this all worked (or should work) is that content handlers are
>>supposed to just generate content.  specifically, they should not care at
>>all about RFC compliance - this is why we have a separate header filter,
>>byterange filter, and so on (and why I think ap_set_last_modified foo should
>>be in its own filter ;)
>
>In terms of very simple content handlers, such as a handler that might serve content stored in a file on disk, the above is true - it doesn't care much about HTTP, that is mostly handled by higher layers.
>
>The problem starts creeping in when the content handler is less trivial than the file serving handler, such as mod_proxy, which receives an HTTP request from the input filter stack, and returns an HTTP response to the output filter stack based on content and headers generated by a backend server.

The confusion results because mod_proxy isn't implemented as a content
handler, it's a protocol handler in its own right.  Rather than insist on the
mod_http <> mod_proxy agreeing to streamline the response, we've put
it on every content module author to:

. remove output C-L header if the size is transformed
. remove input range headers if the content isn't 1:1 transformed

This is very kludgy and more an example of where mod_http <> mod_proxy
didn't quite get it right, and made it a little more difficult for folks who are
just trying to transform content bodies.

It would be nice in apache 2.2 to finally clean up this contract, with two
simple metadata element to pass through the filter chain:

. this request is unfiltered
. this request has a 1:1 filter (stateless)
. this request has a arbitrary content transformation

Each filter is the stack could promote the complexity but should never set
it to a lower state.  This would allow http/proxy modules to negotiate less
complex transformations in more efficient ways.

Bill 



Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Geoffrey Young <ge...@modperlcookbook.org>.

Graham Leggett wrote:
> Geoffrey Young wrote:
> 
>> please take the rest of this as just a friendly discussion - I don't
>> want it
>> to turn into some kind of bickering match, since that's definitely not
>> what
>> I have in mind :)
> 
> 
> Cool no problem - it's quite a complex thing this, and I was struggling
> trying to make it clear what exactly needed to be done and where (and why).

:)


> In this case, we're not just feeding content up the stack, but content
> _and_ HTTP headers. Filters cannot ignore the headers, otherwise broken
> behaviour is the result. 

hmm, yeah I see it now.

> A classic example is a filter that changes the
> length of the content (mod_gzip, or mod_include). These filters need to
> concern themselves with the HTTP Content-Length header, otherwise a
> response from mod_proxy going up the stack could get shipped to the
> browser with the wrong Content-Length.

ok.  but the difference I think I see here between C-L and Range is that if
you are a content-length altering filter, you know it - removing C-L is
required because you are the one doing the altering.

if I understand things right, Range is slightly different.  in this case it
seems like every filter would need to ask "are we byteserving" in order to
know what to do about Range.

> 
> In most cases for filters handling the headers is trivial. mod_gzip
> might strip off a Content-Length header in the hope that a filter might
> chunk the response down the line. mod_include should (in the most simple
> case) strip off any Range headers in the request in the hope that the
> byte range filter handles the range request down the line.

but all of a sudden it's not just mod_include, but every similar output
filter, right?  as in all those that API users will be writing.

> 
> But in the case of mod_proxy, mod_jk, etc it is quite valid and very
> desirable for a range request to be passed all the way to the backend,
> in the hope that the backend sends just that range back to mod_proxy,
> which in turn sends it up a filter stack that isn't going to fall over
> because it received a 206 Partial Content response.

yes, I can see that.

part of the trouble I find myself in here is that I see some kind of creep
going on.  right now filters need to handle Content-Length under some
circumstances, and you are suggesting Range as well.  both of these remove
part of the filter abstraction and replace that part with (a few) special
cases.  how many special cases will be required in the end under the current
design, and how much complexity does that add for filter writers?

so perhaps we need to be dealing less with Range specifically and more with
a second-generation filter design that addresses some of these outstanding
issues.  for instance, perhaps designate some kind of filter class system,
whereby content-altering filters register themselves differently than
pass-thru-type filters (as I'll call the current proxy issue I guess), etc?

> 
>> that's true if I'm wrong about the assumption above.  but in my mind, the
>> filter API is the most useful if content handlers (and content-altering
>> filters) can remain ignorant of 206 responses and the byterange filter
>> can
>> bat cleanup.
> 
> 
> For simplicity case the above is a noble goal - but one with some
> significant performance drawbacks in many real world applications.

indeed.  the trouble is that by streamlining for the performance of some
applications you (possibly) limit (or add sufficient complexity to) the
ability of other applications to do other, equally important things.

at least if the API isn't sufficiently worked out from all angles :)

> The problem though is not with the content handlers but with the filters
>  - filters must not make the assumption that all content handlers only
> serve content and not HTTP headers. 

good point.


> When a content handler decides that
> it wants to handle more of the HTTP spec so as to improve performance,
> it should be free to do so, and should not be stopped from doing so due
> to limitations in the output filters.

yes, that would be a good design goal.

> 
> In other words if mod_proxy is taught how to pass Range requests to the
> backend server, the output filter stack should not stop proxy from doing
> so by removing Range headers unless it is absolutely necessary.

ok, thanks for taking the time to explain it all, if only for me :)

do you think that the proxy-specific issue can be boiled down to something
more generic?  at least from here, it looks like what really needs to happen
is that certain headers need to have their origin and end-point known so
that filters know their place in the process and how to behave when they see
them.  is that kind of the issue, and can a user-level API be created around it?

--Geoff

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Nick Kew <ni...@webthing.com>.
On Tue, 13 Jul 2004, Graham Leggett wrote:

> Nick Kew wrote:
>
> > Indeed.  In a straight-through proxy that's right.
> >
> > But in the case of a cacheing proxy, it may be better for it to retrieve
> > the entire document and manage byteranges locally.  And in the case of
> > a content-transforming proxy, the filters may need the entire content to
> > function at all.
>
> Remember that in our case there is no such thing as a "caching proxy".

Of course there is!  It's apache with mod_proxy and mod_cache.  Just
as a content-transforming proxy is apache with mod_proxy and one or
more content filter module.

> > Bottom line: this has to be controlled by the server admin.  We offer
> > the options of passthrough, process locally, or ignore ranges.
>
> I think it's better to avoid adding extra directives, or giving the
> admin the power to override RFC2616. How to handle ranges is described
> fully in the HTTP/1.1 spec, the admin shouldn't really have the option
> to fiddle with it. Just adding more ways to get it wrong.

AFAICS RFC2616 sanctions any of the three behaviours I'm proposing.
Firstly noone is required to support ranges at all.  Secondly the
following passage from #14.35 seems to sum up the other options:

   If a proxy that supports ranges receives a Range request, forwards
   the request to an inbound server, and receives an entire entity in
   reply, it SHOULD only return the requested range to its client. It
   SHOULD store the entire received response in its cache if that is
   consistent with its cache allocation policies.

That's not explicit about whether the Range was forwarded, but neither
AFAICS is anything else in the RFC.

> Any filter that could get it's hands dirty (mod_include springs to mind)
> should just strip the Range header from the request, leaving the byte
> range filter to do the dirty work for it on the full response.

That is dirtying the filter API further.  If filter modules are to
be responsible for that, we should at least provide a higher-level
API for them, ideally in a declarative form.  Maybe something along the
lines of an AP_IS_TRANSFORM flag or flags, that will transparently deal
with Content-Length, Range and Warning headers on behalf of a filter
when it is inserted.

If we can abstract out the common processing required of every
content-transforming filter into a simple magic-API, I'll be happy.

-- 
Nick Kew

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Graham Leggett <mi...@sharp.fm>.
William A. Rowe, Jr. wrote:

> I don't like where this conversation is heading at all.  You are suggesting that
> every filter needs to become progressively more aware of the http module
> characteristics, but that's what we were moving away from in apache 2.0.

Ok, this is exactly how Geoffrey Young understood it, but as you pointed 
out mod_proxy (and mod_jk etc) are protocol handlers, not content handlers.

I don't see how mod_proxy can not be a protocol handler and still 
confirm to the behaviour prescribed for how proxies should behave in 
RFC2616?

> Body/content generation or transformation should not be contending with
> these issues you raised above.  It's not unreasonable to expect some
> metadata to pass through or be transformed (such as a content length,
> which some filter can tweak or discard altogether.)  But it is getting very
> obscure to expect them to contend with byteranges.  What's next?

Not obscure at all - byteranges are used by download accerators (evil 
things as they are) and people doing download resume. Supporting them is 
a big performance win for any webserver that serves large files, which 
as the net gets faster, is going to become more of a problem.

> That's why I proposed a skip-forward semantic to support byte ranges.
> It's far abstracted from http, is an optional feature (skip if you can, or
> read if your filter must in order to transform) and trivial to implement.
> And it's typical of bytestream APIs.
> 
> The proxy solution is simpler, determine if end to end you have either
> http <> proxy, or if the intervening filters are all 1:1 stateless transformations.
> If they can't negotiate a protocol level pipe (because there are non-stateless
> content filters in the way), then it's up to http and proxy to stay out of the
> way, and make it possible for content filters to filter content.

proxy is a content handler though - it cannot stay out of the way, what 
would take it's place?

So far it seems that we're expecting proxy to

a) receive an HTTP response from a backend server.
b) parse the response, and encode metadata like range and content length 
into a filter specific metadata along with the stack.
c) pass the content up the stack.
d) get the filter stack to convert the metadata back into HTTP again.

And apart from Content-Length and Range, what about other metadata from 
proxy like Date, or Server, or Etag? At some point the "metadata" 
becomes the "headers array", and now we're back to just a stack that 
parses HTTP.

I can see a lot of virtue in keeping the modules simple, but then I'm 
zooming out a bit, and it seems that we're undoing a lot of work so that 
the work can be redone again by the output filters.

Regards,
Graham
--

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Justin Erenkrantz <ju...@erenkrantz.com>.
--On Tuesday, July 13, 2004 11:21 AM -0500 "William A. Rowe, Jr." 
<wr...@rowe-clan.net> wrote:

> Body/content generation or transformation should not be contending with
> these issues you raised above.  It's not unreasonable to expect some
> metadata to pass through or be transformed (such as a content length,
> which some filter can tweak or discard altogether.)  But it is getting very
> obscure to expect them to contend with byteranges.  What's next?

Agreed.

> That's why I proposed a skip-forward semantic to support byte ranges.
> It's far abstracted from http, is an optional feature (skip if you can, or
> read if your filter must in order to transform) and trivial to implement.
> And it's typical of bytestream APIs.

I don't see how a skip feature would work in a push model API like our output 
filters - perhaps in a pull-model (like input).  But, there are so many other 
problems with the pull model that I think it's not worth the gain of a skip 
feature.  -- justin

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by "William A. Rowe, Jr." <wr...@rowe-clan.net>.
At 10:57 AM 7/13/2004, Graham Leggett wrote:
>Nick Kew wrote:
>
>>Bottom line: this has to be controlled by the server admin.  We offer
>>the options of passthrough, process locally, or ignore ranges.
>
>I think it's better to avoid adding extra directives, or giving the admin the power to override RFC2616. How to handle ranges is described fully in the HTTP/1.1 spec, the admin shouldn't really have the option to fiddle with it. Just adding more ways to get it wrong.

Agree here, filters themselves know better than the administrator.

>>Indeed, historically (possibly still) content length has been a problem
>>for filters.  Simply removing the header may not be sufficient if the
>>content-length filter reinserts it erroneously.  Ranges are more
>>complex.
>>Basically a proxy or other content generator that takes care of
>>byteranges itself is going to be incompatible with certain output
>>filters.  That has to be documented, and there has to be an easy
>>way for filters to detect when they're not wanted, or for Apache
>>to mark them inapplicable and refuse to run them at all.
>>A situation where filters have to get their hands dirty with
>>partial responses would be a serious problem.
>
>Any filter that could get it's hands dirty (mod_include springs to mind) should just strip the Range header from the request, leaving the byte range filter to do the dirty work for it on the full response.
>
>Any filter that fiddles with Content-Length is probably also going to have to be taught about ranges - either it must remove the Range request header as in mod_include above (the simplest case), or depending on what's practical, it might be taught to do something more intelligent about ranges.
>
>The bottom line is this: a filter that might blow up on a range must either a) ignore range, as the filter is not affected, or b) be intelligent enough to handle the range or c) strip the Range header off the request and just generate a full response as happens now.
>
>I stongly suspect most filters will be choosing option c) at the outset, moving to a) or b) as we slowly teach the filters that need teaching what to do with ranges.

I don't like where this conversation is heading at all.  You are suggesting that
every filter needs to become progressively more aware of the http module
characteristics, but that's what we were moving away from in apache 2.0.

Body/content generation or transformation should not be contending with
these issues you raised above.  It's not unreasonable to expect some
metadata to pass through or be transformed (such as a content length,
which some filter can tweak or discard altogether.)  But it is getting very
obscure to expect them to contend with byteranges.  What's next?

That's why I proposed a skip-forward semantic to support byte ranges.
It's far abstracted from http, is an optional feature (skip if you can, or
read if your filter must in order to transform) and trivial to implement.
And it's typical of bytestream APIs.

The proxy solution is simpler, determine if end to end you have either
http <> proxy, or if the intervening filters are all 1:1 stateless transformations.
If they can't negotiate a protocol level pipe (because there are non-stateless
content filters in the way), then it's up to http and proxy to stay out of the
way, and make it possible for content filters to filter content.

Bill



Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Graham Leggett <mi...@sharp.fm>.
Nick Kew wrote:

> Indeed.  In a straight-through proxy that's right.
> 
> But in the case of a cacheing proxy, it may be better for it to retrieve
> the entire document and manage byteranges locally.  And in the case of
> a content-transforming proxy, the filters may need the entire content to
> function at all.

Remember that in our case there is no such thing as a "caching proxy". 
mod_proxy in v2.0 has no caching capability whatsoever. Caching has been 
moved to mod_cache, and is handled completely separately from proxy.

mod_cache has it's own ranging issues, which are probably already solved 
by the byte range filter anyway.

> Bottom line: this has to be controlled by the server admin.  We offer
> the options of passthrough, process locally, or ignore ranges.

I think it's better to avoid adding extra directives, or giving the 
admin the power to override RFC2616. How to handle ranges is described 
fully in the HTTP/1.1 spec, the admin shouldn't really have the option 
to fiddle with it. Just adding more ways to get it wrong.

> Indeed, historically (possibly still) content length has been a problem
> for filters.  Simply removing the header may not be sufficient if the
> content-length filter reinserts it erroneously.  Ranges are more
> complex.
> 
> Basically a proxy or other content generator that takes care of
> byteranges itself is going to be incompatible with certain output
> filters.  That has to be documented, and there has to be an easy
> way for filters to detect when they're not wanted, or for Apache
> to mark them inapplicable and refuse to run them at all.
> A situation where filters have to get their hands dirty with
> partial responses would be a serious problem.

Any filter that could get it's hands dirty (mod_include springs to mind) 
should just strip the Range header from the request, leaving the byte 
range filter to do the dirty work for it on the full response.

Any filter that fiddles with Content-Length is probably also going to 
have to be taught about ranges - either it must remove the Range request 
header as in mod_include above (the simplest case), or depending on 
what's practical, it might be taught to do something more intelligent 
about ranges.

The bottom line is this: a filter that might blow up on a range must 
either a) ignore range, as the filter is not affected, or b) be 
intelligent enough to handle the range or c) strip the Range header off 
the request and just generate a full response as happens now.

I stongly suspect most filters will be choosing option c) at the outset, 
moving to a) or b) as we slowly teach the filters that need teaching 
what to do with ranges.

Regards,
Graham
--

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Nick Kew <ni...@webthing.com>.
On Tue, 13 Jul 2004, Graham Leggett wrote:

> But in the case of mod_proxy, mod_jk, etc it is quite valid and very
> desirable for a range request to be passed all the way to the backend,
> in the hope that the backend sends just that range back to mod_proxy,
> which in turn sends it up a filter stack that isn't going to fall over
> because it received a 206 Partial Content response.

Indeed.  In a straight-through proxy that's right.

But in the case of a cacheing proxy, it may be better for it to retrieve
the entire document and manage byteranges locally.  And in the case of
a content-transforming proxy, the filters may need the entire content to
function at all.

Bottom line: this has to be controlled by the server admin.  We offer
the options of passthrough, process locally, or ignore ranges.

> The above is still true - there is (and should be) very little for the
> content handler to worry about when it comes to HTTP compliance, and
> content handlers should have the option to just generate content, as
> they do now.

Agreed.  That applies both to content handlers and content filters.

> The problem though is not with the content handlers but with the filters
>   - filters must not make the assumption that all content handlers only
> serve content and not HTTP headers. When a content handler decides that
> it wants to handle more of the HTTP spec so as to improve performance,
> it should be free to do so, and should not be stopped from doing so due
> to limitations in the output filters.

Indeed, historically (possibly still) content length has been a problem
for filters.  Simply removing the header may not be sufficient if the
content-length filter reinserts it erroneously.  Ranges are more
complex.

Basically a proxy or other content generator that takes care of
byteranges itself is going to be incompatible with certain output
filters.  That has to be documented, and there has to be an easy
way for filters to detect when they're not wanted, or for Apache
to mark them inapplicable and refuse to run them at all.
A situation where filters have to get their hands dirty with
partial responses would be a serious problem.

> In other words if mod_proxy is taught how to pass Range requests to the
> backend server, the output filter stack should not stop proxy from doing
> so by removing Range headers unless it is absolutely necessary.

Indeed.  So in httpd.conf we have options for the proxy to pass range
requests through or not.

-- 
Nick Kew

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Graham Leggett <mi...@sharp.fm>.
Geoffrey Young wrote:

> please take the rest of this as just a friendly discussion - I don't want it
> to turn into some kind of bickering match, since that's definitely not what
> I have in mind :)

Cool no problem - it's quite a complex thing this, and I was struggling 
trying to make it clear what exactly needed to be done and where (and why).

> ok, that isn't the idea I had about output filters at all.  my own concept
> of how this all worked (or should work) is that content handlers are
> supposed to just generate content.  specifically, they should not care at
> all about RFC compliance - this is why we have a separate header filter,
> byterange filter, and so on (and why I think ap_set_last_modified foo should
> be in its own filter ;)

In terms of very simple content handlers, such as a handler that might 
serve content stored in a file on disk, the above is true - it doesn't 
care much about HTTP, that is mostly handled by higher layers.

The problem starts creeping in when the content handler is less trivial 
than the file serving handler, such as mod_proxy, which receives an HTTP 
request from the input filter stack, and returns an HTTP response to the 
output filter stack based on content and headers generated by a backend 
server.

In this case, we're not just feeding content up the stack, but content 
_and_ HTTP headers. Filters cannot ignore the headers, otherwise broken 
behaviour is the result. A classic example is a filter that changes the 
length of the content (mod_gzip, or mod_include). These filters need to 
concern themselves with the HTTP Content-Length header, otherwise a 
response from mod_proxy going up the stack could get shipped to the 
browser with the wrong Content-Length.

In most cases for filters handling the headers is trivial. mod_gzip 
might strip off a Content-Length header in the hope that a filter might 
chunk the response down the line. mod_include should (in the most simple 
case) strip off any Range headers in the request in the hope that the 
byte range filter handles the range request down the line.

But in the case of mod_proxy, mod_jk, etc it is quite valid and very 
desirable for a range request to be passed all the way to the backend, 
in the hope that the backend sends just that range back to mod_proxy, 
which in turn sends it up a filter stack that isn't going to fall over 
because it received a 206 Partial Content response.

> that's true if I'm wrong about the assumption above.  but in my mind, the
> filter API is the most useful if content handlers (and content-altering
> filters) can remain ignorant of 206 responses and the byterange filter can
> bat cleanup.

For simplicity case the above is a noble goal - but one with some 
significant performance drawbacks in many real world applications.

Apart from the mod_proxy case, think of a webserver (or bank of 
webservers) serving content hosted on an NFS server. The entire 650MB 
ISO file (for example) needs to be transferred from the NFS server to 
the webserver for every hit to that file - even when a user is 
continuing a download (which in the case of a file the size of an ISO 
will likely be often).

> sure :)  I guess where we have different ideas, then, is in who exactly
> should be responsible for RFC compliance.  I had always assumed that there
> was (or should be) very little that a content handler needed to worry about
> in this respect, and that it was the job of the core server engine (via
> various early or late-running filters) to take care of things like HEAD
> requests, HTTP/0.9 requests/responses, chunked encoding, range requests, etc.

The above is still true - there is (and should be) very little for the 
content handler to worry about when it comes to HTTP compliance, and 
content handlers should have the option to just generate content, as 
they do now.

The problem though is not with the content handlers but with the filters 
  - filters must not make the assumption that all content handlers only 
serve content and not HTTP headers. When a content handler decides that 
it wants to handle more of the HTTP spec so as to improve performance, 
it should be free to do so, and should not be stopped from doing so due 
to limitations in the output filters.

In other words if mod_proxy is taught how to pass Range requests to the 
backend server, the output filter stack should not stop proxy from doing 
so by removing Range headers unless it is absolutely necessary.

Regards,
Graham
--

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Geoffrey Young <ge...@modperlcookbook.org>.

Graham Leggett wrote:
> Geoffrey Young wrote:
> 
>> while I'm all for reducing server overhead (who isn't :) playing these
>> kind
>> of games with the filter API seems like such a bad idea.  what we have
>> now
>> is a modular design that is simple and works - content handlers
>> generate a
>> response, while various filters adjust that response based on interesting
>> criteria.  requiring that one know anything about the other breaks that
>> model and has the potential to get us into a hole from which it is
>> difficult
>> to escape.
> 
> 
> Which is a point I made in part of the post that you didn't quote above.

ok, sorry :)

please take the rest of this as just a friendly discussion - I don't want it
to turn into some kind of bickering match, since that's definitely not what
I have in mind :)

> 
> To try and make my point again more clearly:
> 
> Content is generated in compliance with the HTTP/1.1 specification. This
> HTTP/1.1 compliant content is then fed through several filters, which
> potentially alter the data in compliance with the HTTP/1.1
> specification. Eventually the filtered content is sent out over the
> network in compliance with the HTTP/1.1 specification.

ok, that isn't the idea I had about output filters at all.  my own concept
of how this all worked (or should work) is that content handlers are
supposed to just generate content.  specifically, they should not care at
all about RFC compliance - this is why we have a separate header filter,
byterange filter, and so on (and why I think ap_set_last_modified foo should
be in its own filter ;)

everyone else here is much more intimate with how filters came to be, so
I'll accept that I was wrong with that assumption, but a number of factors
brought me to that conclusion, such as the removal of the
ap_set_byterange/ap_each_byterange API, the C-L filter, etc - all of these
things make it possible for content handlers to focus on the simple job of
generating content, while relying on core features to make the complete
response RFC compliant.

> 
> If the byte range filter is not capable of receiving and intelligently
> handling a 206 Partial Content from a content handler, then the byte
> range filter is not compliant with HTTP/1.1, and is therefore broken.

clearly.

> 
> If any other filter is not capable of processing data that has come from
> a 206 Partial Content response, AND that filter does not either a)
> remove itself from the filter stack, or b) remove the Range header from
> the request, then that filter is not compliant with the HTTP/1.1
> specification, and is therefore broken.

that's true if I'm wrong about the assumption above.  but in my mind, the
filter API is the most useful if content handlers (and content-altering
filters) can remain ignorant of 206 responses and the byterange filter can
bat cleanup.

> 
> Up until now it has been simplistically assumed that ALL content
> handlers will only ever generate full responses, and so filters and
> certain content handlers have ignored the Range part of RFC2616.

well, I assumed that was by design for the reason mentioned above, namely
that the API set/each_byterange API we used to use in 1.3 was replaced with
the byterange filter.

> With
> the existance of mod_proxy, mod_jk, mod_backhand (etc) taking this
> shortcut does not make sense.

I don't grok what you're trying to say here, unless you mean that it doesn't
make sense to take the long way around, with content handlers always
producing the full response (as you have indicated elsewhere).

> 
> Nowhere in the above is any requirement laid down that one module must
> depend on another one. The only requirement is that content handlers and
> filters must behave in a way that is compliant with the HTTP/1.1
> specification.

sure :)  I guess where we have different ideas, then, is in who exactly
should be responsible for RFC compliance.  I had always assumed that there
was (or should be) very little that a content handler needed to worry about
in this respect, and that it was the job of the core server engine (via
various early or late-running filters) to take care of things like HEAD
requests, HTTP/0.9 requests/responses, chunked encoding, range requests, etc.

anyway, again, I'm not trying to be belligerant - the main reason I'm trying
to flesh this all out (even if I'm the only one who doesn't know it already
:) is that if the ideas behind the current API design are clear to me then I
can help design the Perl interface to the C API so that it matches the real
intent here.

--Geoff

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Graham Leggett <mi...@sharp.fm>.
Geoffrey Young wrote:

> while I'm all for reducing server overhead (who isn't :) playing these kind
> of games with the filter API seems like such a bad idea.  what we have now
> is a modular design that is simple and works - content handlers generate a
> response, while various filters adjust that response based on interesting
> criteria.  requiring that one know anything about the other breaks that
> model and has the potential to get us into a hole from which it is difficult
> to escape.

Which is a point I made in part of the post that you didn't quote above.

To try and make my point again more clearly:

Content is generated in compliance with the HTTP/1.1 specification. This 
HTTP/1.1 compliant content is then fed through several filters, which 
potentially alter the data in compliance with the HTTP/1.1 
specification. Eventually the filtered content is sent out over the 
network in compliance with the HTTP/1.1 specification.

If the byte range filter is not capable of receiving and intelligently 
handling a 206 Partial Content from a content handler, then the byte 
range filter is not compliant with HTTP/1.1, and is therefore broken.

If any other filter is not capable of processing data that has come from 
a 206 Partial Content response, AND that filter does not either a) 
remove itself from the filter stack, or b) remove the Range header from 
the request, then that filter is not compliant with the HTTP/1.1 
specification, and is therefore broken.

Up until now it has been simplistically assumed that ALL content 
handlers will only ever generate full responses, and so filters and 
certain content handlers have ignored the Range part of RFC2616. With 
the existance of mod_proxy, mod_jk, mod_backhand (etc) taking this 
shortcut does not make sense.

Nowhere in the above is any requirement laid down that one module must 
depend on another one. The only requirement is that content handlers and 
filters must behave in a way that is compliant with the HTTP/1.1 
specification.

Regards,
Graham
--

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Geoffrey Young <ge...@modperlcookbook.org>.
> Which in turn means that every filter, now blissfully unaware of ranges,
> is forced to generate a full response for each byterange request. In the
> case of a downloaded ISO (for example), this means a significant amount
> of data (many hundreds of MB) is being processed by filters on each
> request.
> 
> Thus this discussion.

while I'm all for reducing server overhead (who isn't :) playing these kind
of games with the filter API seems like such a bad idea.  what we have now
is a modular design that is simple and works - content handlers generate a
response, while various filters adjust that response based on interesting
criteria.  requiring that one know anything about the other breaks that
model and has the potential to get us into a hole from which it is difficult
to escape.  for an example, see a post I made about the (still present)
problems with filter_init, which was an attempt to fix a problem that
resulted from a similar attempt at short-circuiting existing logic:

  http://marc.theaimsgroup.com/?l=apache-httpd-dev&m=107090791508163&w=2

in my mind the filter API works best when everyone is blissfully ignorant of
eachother so that the net result is that all requests are handled
appropriately with a minimum of programmatic effort.  sure, it would be nice
to be as svelte as possible when serving large files, but the flip side to
that is being svelt means that users of your API are more likely to get
things wrong or encounter real-world situations you never thought about.

just my $0.02.

--Geoff

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Graham Leggett <mi...@sharp.fm>.
William A. Rowe, Jr. wrote:

> The solution to this problem is *not* to become tightly coupled with the
> placement of filters, directly handling file streams, etc.
> 
> The clean solution is a new forward-space semantic for the filter or 
> brigade, which would allow you to skip n bytes.  This would allow those
> filters which know their transformation (1:1 mappings, etc) to simply
> pass this request forward, until it ultimately hits the core filesystem
> filter which can seek(fd, CUR, n).
> 
> Those filters without expectations of the transformation without fully
> reprocessing the stream (e.g. includes) would have to reprocess the 
> data, no surprise there.  

I don't follow...

If mod_proxy gets a Range header, in theory it should pass the range 
header as is to the backend server. In return, the backend server says 
"206 Partial Content" and returns content matching the range. mod_proxy 
now sends this already-ranged content up the filter stack.

At this point, what happens?

a) the filter stack falls over, as it does not understand 206 Partial 
Content

b) mod_proxy converts the range into something resembling the full 
response, but with dummy bits filling in the parts of the range that is 
not there, to be stripped out again by the byte range filter?

c) the filter stack recognises the response as a ranged response, and 
backs off as appropriate (by filters excusing themselves from the filter 
stack, or by filters like mod_include quitely removing the Range request 
header so proxy never sees it in the first place).

Regards,
Graham
--

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by "William A. Rowe, Jr." <wr...@rowe-clan.net>.
At 05:22 AM 7/13/2004, Graham Leggett wrote:

>The problem arises when large data sizes (say a 650MB CD ISO) are stored in a multi-tier webserver architecture (mod_proxy in front of a backend, for example), and somebody comes along and tries to download it using a download accelerator, or they simply try to resume a failed download.
>
>The full 650MB CD ISO is then transferred from the backend to the frontend, which then pulls out the bits the frontend needs dumping the rest. And this happens once for every single byte range request.

The solution to this problem is *not* to become tightly coupled with the
placement of filters, directly handling file streams, etc.

The clean solution is a new forward-space semantic for the filter or 
brigade, which would allow you to skip n bytes.  This would allow those
filters which know their transformation (1:1 mappings, etc) to simply
pass this request forward, until it ultimately hits the core filesystem
filter which can seek(fd, CUR, n).

Those filters without expectations of the transformation without fully
reprocessing the stream (e.g. includes) would have to reprocess the 
data, no surprise there.  

Bill




Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Graham Leggett <mi...@sharp.fm>.
Joe Orton wrote:

> Yes, that's exactly what it's supposed to do already.  I haven't checked
> that it works via the proxy but it certainly does for other cases.

Ok, then most of the problem is solved :)

>>The full 650MB CD ISO is then transferred from the backend to the 
>>frontend, which then pulls out the bits the frontend needs dumping the 
>>rest. And this happens once for every single byte range request.

> And buffered into RAM each time too, nice.  This is another good case
> for the byterange filter not doing any work for anything other than
> "simple" content per my other mail.

I don't see where it would be buffered in RAM - proxy doesn't buffer 
much, and a byte range filter would either be passing data through, or 
dropping data on the floor. Unless I am missing something?

Regards,
Graham
--

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Joe Orton <jo...@redhat.com>.
On Tue, Jul 13, 2004 at 12:22:20PM +0200, Graham Leggett wrote:
> Joe Orton wrote:
> 
> >As far as I can tell the byterange filter should handle all such cases
> >correctly already: read ap_set_byterange() - it punts on a non-200
> >r->status or when r->headers_out contains a Content-Range header etc. 
> >Is this side-discussion just theoretical pondering or is there some real
> >issue you're trying to solve?
> 
> When you say "it punts" what do you mean - that it removes itself from 
> the filter stack (as in "my work here is already done")? If this is the 
> case, then it works correctly already.

Yes, that's exactly what it's supposed to do already.  I haven't checked
that it works via the proxy but it certainly does for other cases.

> The problem arises when large data sizes (say a 650MB CD ISO) are stored 
> in a multi-tier webserver architecture (mod_proxy in front of a backend, 
> for example), and somebody comes along and tries to download it using a 
> download accelerator, or they simply try to resume a failed download.
> 
> The full 650MB CD ISO is then transferred from the backend to the 
> frontend, which then pulls out the bits the frontend needs dumping the 
> rest. And this happens once for every single byte range request.

And buffered into RAM each time too, nice.  This is another good case
for the byterange filter not doing any work for anything other than
"simple" content per my other mail.

joe

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Graham Leggett <mi...@sharp.fm>.
Joe Orton wrote:

> As far as I can tell the byterange filter should handle all such cases
> correctly already: read ap_set_byterange() - it punts on a non-200
> r->status or when r->headers_out contains a Content-Range header etc. 
> Is this side-discussion just theoretical pondering or is there some real
> issue you're trying to solve?

When you say "it punts" what do you mean - that it removes itself from 
the filter stack (as in "my work here is already done")? If this is the 
case, then it works correctly already.

The problem arises when large data sizes (say a 650MB CD ISO) are stored 
in a multi-tier webserver architecture (mod_proxy in front of a backend, 
for example), and somebody comes along and tries to download it using a 
download accelerator, or they simply try to resume a failed download.

The full 650MB CD ISO is then transferred from the backend to the 
frontend, which then pulls out the bits the frontend needs dumping the 
rest. And this happens once for every single byte range request.

Regards,
Graham
--

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Joe Orton <jo...@redhat.com>.
On Mon, Jul 12, 2004 at 03:49:08PM +0200, Graham Leggett wrote:
> I am assuming the current behaviour of the byte range filter would apply 
> the range to the content without checking first whether this has already 
> been done. This fix is very straightforward.

As far as I can tell the byterange filter should handle all such cases
correctly already: read ap_set_byterange() - it punts on a non-200
r->status or when r->headers_out contains a Content-Range header etc. 
Is this side-discussion just theoretical pondering or is there some real
issue you're trying to solve?

joe

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Graham Leggett <mi...@sharp.fm>.
Nick Kew wrote:

> That will not always be practicable.  mod_proxy should be configurable
> to pass byteranges headers straight through to the backend or strip them
> and assume the proxy will handle the ranges.

Byte ranges are a part of HTTP/1.1, and mod_proxy claims to be an 
HTTP/1.1 proxy. mod_proxy should behave how RFC2616 tells it to behave, 
and such behaviour should definitely not be configurable unless 
absolutely necessary.

> Doesn't that break modularity rather badly?  mod_include is concerned with
> simple content modifications, not HTTP.  It doesn't need more complexity.

It would not break modularity no - because the behaviour is in most 
cases "if Range header present, remove it, else do nothing". If 
mod_include was processing a non HTTP stream (for whatever reason) the 
"else do nothing" would apply, no problem.

>>In theory, as the byte range filter should be one of the topmost filters
>>run, it would have seen the Range header and noted what range it should
>>have been returning, so a downstream filter removing the Range header
>>should not cause a problem for the byte range filter.

> But if you adopt that approach, then *every* filter has to faff about
> with range headers (just in case),

Why would every filter have to faff with range headers?

> the first one strips it out, and
> the others run in blissful ignorance.  Makes more sense if only the
> byterange filter concerns itself with the header.

Which in turn means that every filter, now blissfully unaware of ranges, 
is forced to generate a full response for each byterange request. In the 
case of a downloaded ISO (for example), this means a significant amount 
of data (many hundreds of MB) is being processed by filters on each request.

Thus this discussion.

>>In fact thinking about this some more - mod_include might look at the
>>byte range, and then intelligently decide to either include / not
>>include certain included content based on the byte range. This could
>>improve performance on some sites.

> For mod_include to do that is an order of magnitude extra complexity
> (even if you solve the problem of measuring the length of each include
> without actually executing it).  For modules that generate entirely new
> data - such as those based on a markup processor (accessibility, xmlns,
> xinclude/xslt, proxy_html, annot - to name but a few) it becomes even
> bigger: we'd have to count every byte we write!

The key word above was "might". Obviously teaching modules about byte 
ranges would only make sense where it was justified.

If it were too complex for a module to handle byte ranged content, it's 
really simple - just tell it to strip the Range header off so the 
content handler never sees it.

>>So to sum up:
>>
>>- Teach the byte range filter that it might receive content from a
>>content handler that already has the byte range applied, and to react
>>intelligently when this happens. A content handler will indicate this by
>>returning a 206 Partial Content and/or a Content-Range header, which is
>>easily parsed by the byte range filter - no need for special flags or
>>buckets.

> That has to be configurable, as some filters can only run on a
> complete datastream.

The above does not need to be configurable at all - as the above is only 
concerned with the byte range filter receiving content from downstream 
that is already a byte range.

I am assuming the current behaviour of the byte range filter would apply 
the range to the content without checking first whether this has already 
been done. This fix is very straightforward.

Regards,
Graham
--

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Nick Kew <ni...@webthing.com>.
On Mon, 12 Jul 2004, Graham Leggett wrote:

> > at the moment when a byterange request goes to a dynamic module, the
> > dynamic module can not use any tricks to only serve the bytes requested,
> > it *HAS* to serve the entire content up as buckets.
>
> In theory, if mod_proxy (for example) gets a byte range request, it
> should only serve that byte range - ideally modules/filters should not
> "prop up" other modules/filters.

That will not always be practicable.  mod_proxy should be configurable
to pass byteranges headers straight through to the backend or strip them
and assume the proxy will handle the ranges.

> If a filter somewhere in the filter stack is going to break the byte
> range request in any way (for example something like mod_include) then
> that filter should be responsible for removing the Range header from the
> request before mod_proxy gets a chance to service the request.

Doesn't that break modularity rather badly?  mod_include is concerned with
simple content modifications, not HTTP.  It doesn't need more complexity.

> In theory, as the byte range filter should be one of the topmost filters
> run, it would have seen the Range header and noted what range it should
> have been returning, so a downstream filter removing the Range header
> should not cause a problem for the byte range filter.

But if you adopt that approach, then *every* filter has to faff about
with range headers (just in case), the first one strips it out, and
the others run in blissful ignorance.  Makes more sense if only the
byterange filter concerns itself with the header.

> In turn, if a downstream filter/content handler has returned a 206
> Partial Content response, the byte range filter should know what to do
> (has my job already been done by a downstream filter?

Yes, quietly remove itself from the chain.

> In fact thinking about this some more - mod_include might look at the
> byte range, and then intelligently decide to either include / not
> include certain included content based on the byte range. This could
> improve performance on some sites.

For mod_include to do that is an order of magnitude extra complexity
(even if you solve the problem of measuring the length of each include
without actually executing it).  For modules that generate entirely new
data - such as those based on a markup processor (accessibility, xmlns,
xinclude/xslt, proxy_html, annot - to name but a few) it becomes even
bigger: we'd have to count every byte we write!

> So to sum up:
>
> - Teach the byte range filter that it might receive content from a
> content handler that already has the byte range applied, and to react
> intelligently when this happens. A content handler will indicate this by
> returning a 206 Partial Content and/or a Content-Range header, which is
> easily parsed by the byte range filter - no need for special flags or
> buckets.

That has to be configurable, as some filters can only run on a
complete datastream.

> - Teach certain content handlers (such as mod_proxy or mod_cache) to
> handle byte range requests themselves, using the standard RFC2616
> headers and responses to indicate whether ranges have been applied.
> Which content handlers will be taught this will depend on whether there
> is a performance gain to be had by getting the content handler to know
> about byte ranges.

mod_proxy needs only two modes: transparent (leave it to the backend)
or opaque (get the entire document and leave it to the byteranges filter).
The latter would be appropriate when cacheing and/or content-filtering.

mod_cache in quick-handler mode is a special case.  But since that's
only serving from a complete document in-memory or on-disc, it's
straightforward.

> - Teach certain problem modules (mod_gzip if appropriate) to react

That'll be mod_byteranges.

-- 
Nick Kew

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Graham Leggett <mi...@sharp.fm>.
Ian Holsman wrote:

> ok, now before I start this let me say one thing, this is not for *ALL* 
> requests, it will only work for ones which don't have content-length 
> modifiable filters (like gzip) applied to the request, and it would be
> left to the webserver admin to figure out what they were, and if you 
> could use this.

Question:

When a request for a byte range arrives, and the byte range is returned, 
_and_ a transfer encoding is applied (eg gzip) (different to a content 
encoding) - is the gzip transfer encoding not applied to the byte range 
as a single unit?

As I understand it, if transfer encoding was done on the data, then it's 
done on the byte range, so handling something like mod_gzip separately 
in theory should not be a problem.

> ok..
> at the moment when a byterange request goes to a dynamic module, the
> dynamic module can not use any tricks to only serve the bytes requested,
> it *HAS* to serve the entire content up as buckets.

In theory, if mod_proxy (for example) gets a byte range request, it 
should only serve that byte range - ideally modules/filters should not 
"prop up" other modules/filters.

If a filter somewhere in the filter stack is going to break the byte 
range request in any way (for example something like mod_include) then 
that filter should be responsible for removing the Range header from the 
request before mod_proxy gets a chance to service the request.

In theory, as the byte range filter should be one of the topmost filters 
run, it would have seen the Range header and noted what range it should 
have been returning, so a downstream filter removing the Range header 
should not cause a problem for the byte range filter.

In turn, if a downstream filter/content handler has returned a 206 
Partial Content response, the byte range filter should know what to do 
(has my job already been done by a downstream filter? if so, I do 
nothing. Has a downstream filter given me a range bigger than what I was 
asked for? If so, intelligently chop up what I am receiving so that I 
return the byte range I was asked for).

This keeps each modules' behaviour simple and easy to predict, and 
doesn't need to include any new bucket types or "special" handling.

In fact thinking about this some more - mod_include might look at the 
byte range, and then intelligently decide to either include / not 
include certain included content based on the byte range. This could 
improve performance on some sites.

So to sum up:

- Teach the byte range filter that it might receive content from a 
content handler that already has the byte range applied, and to react 
intelligently when this happens. A content handler will indicate this by 
returning a 206 Partial Content and/or a Content-Range header, which is 
easily parsed by the byte range filter - no need for special flags or 
buckets.

- Teach certain content handlers (such as mod_proxy or mod_cache) to 
handle byte range requests themselves, using the standard RFC2616 
headers and responses to indicate whether ranges have been applied. 
Which content handlers will be taught this will depend on whether there 
is a performance gain to be had by getting the content handler to know 
about byte ranges.

- Teach certain problem modules (mod_gzip if appropriate) to react 
intelligently to the byte range headers, either by stripping them from 
the request (thus causing the downstream content handler to follow 
current behaviour of creating the full content), or by being more 
intelligent about it as each case arises.

Regards,
Graham
--

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Nick Kew <ni...@webthing.com>.
On Tue, 13 Jul 2004, Joe Orton wrote:

> On Mon, Jul 12, 2004 at 03:35:12AM +0100, Nick Kew wrote:
> > This doesn't completely address the issue that this might cause
> > excessive memory usage; particularly if we have to serve ranges in a
> > perverse order. I would propose two admin-configurable limits:
> >
> > (1) Total data buffered in memory by the byterange filter.  This can be
> > computed in advance from the request headers.  If this is exceeded, the
> > filter should create a file bucket to store the data, and the ordered
> > list then references offsets into the file.
>
> Buffering responses into temporary files so that the byterange filter
> can do its job sounds extremely messy.

Not as messy as buffering it in memory regardless of size.  But I agree
it's got to be configurable, and probably not a default.

> Being able to send byteranges of arbitrary dynamically generated content
> doesn't seem like an essential feature; the filter is just saying "I
> can't efficiently process a byterange request for this content".
> Clients must handle the 200 response fallback already.

Indeed.

The question is: can we offer admins alternatives, that might be
better in some situations, without messing memory.  I've put forward
suggestions, amplifying Ian's, on how I believe we can.

-- 
Nick Kew

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Joshua Slive <jo...@slive.ca>.
On Tue, 13 Jul 2004, Joe Orton wrote:
> I'm beginning to think the only sane way to fix the byterange filter is
> to have it do nothing if it isn't passed a complete EOS-terminated
> brigade with a predetermined length, i.e. no morphing CGI buckets which
> will eat all your RAM when they get ->read().
>
> Being able to send byteranges of arbitrary dynamically generated content
> doesn't seem like an essential feature; the filter is just saying "I
> can't efficiently process a byterange request for this content".
> Clients must handle the 200 response fallback already.

I'd tend to agree with this.  But as long as the byte-ranges are in-order 
and not-overlapping, the byterange filter should still be able to handle 
the request without buffering anything.  I couldn't tell you what 
percentage of byterange requests are like this (I've heard that Acrobat 
uses out-of-order ranges), but it should handle many simple cases.

Joshua.

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Joe Orton <jo...@redhat.com>.
On Mon, Jul 12, 2004 at 03:35:12AM +0100, Nick Kew wrote:
> This doesn't completely address the issue that this might cause
> excessive memory usage; particularly if we have to serve ranges in a
> perverse order. I would propose two admin-configurable limits:
> 
> (1) Total data buffered in memory by the byterange filter.  This can be
> computed in advance from the request headers.  If this is exceeded, the
> filter should create a file bucket to store the data, and the ordered
> list then references offsets into the file.

Buffering responses into temporary files so that the byterange filter
can do its job sounds extremely messy.

I'm beginning to think the only sane way to fix the byterange filter is
to have it do nothing if it isn't passed a complete EOS-terminated
brigade with a predetermined length, i.e. no morphing CGI buckets which
will eat all your RAM when they get ->read().

Being able to send byteranges of arbitrary dynamically generated content
doesn't seem like an essential feature; the filter is just saying "I
can't efficiently process a byterange request for this content". 
Clients must handle the 200 response fallback already.

joe

Re: The Byterange filter -- a new design -- feel free to rip it to shreds

Posted by Nick Kew <ni...@webthing.com>.
On Mon, 12 Jul 2004, Ian Holsman wrote:

> ok, now before I start this let me say one thing, this is not for *ALL*
> requests, it will only work for ones which don't have content-length
> modifiable filters (like gzip) applied to the request, and it would be
> left to the webserver admin to figure out what they were, and if you
> could use this.

But that's not an issue if the byterange filter comes after any filters
that modify content (CONTENT_SET).

> ok..
> at the moment when a byterange request goes to a dynamic module, the
> dynamic module can not use any tricks to only serve the bytes requested,
> it *HAS* to serve the entire content up as buckets.

Indeed.  That only becomes a problem when a filter breaks pipelining.

> what I am proposing is something like:
>
> 1. the filter keeps a ordered list of range requests that the person
> requests.

> 2. it keeps state on how far it has processed in the file. thanks to
> knowing the length of the buckets processed so far.
>    Q: when do the actual headers get put in.. I think they are after no?

ITYM data, not "the file".  The case of a single file is trivial, and
can more efficiently be handled in a separate optimised execution path.
And some bucket types have to be read to get their length.

> 3. it then examines the bucket + bucket length to see which range
> requests match this range, if some do it grabs that range (possibly
> splitting/copying if it meets multiple ranges) and puts it on the right
> bits of each range request.
>
> 4. if the top range request is finished, it passes those buckets through.
>
> 5. repeat until EOS/Sentinel, flushing the ordered list at the end.

This doesn't completely address the issue that this might cause excessive
memory usage; particularly if we have to serve ranges in a perverse order.
I would propose two admin-configurable limits:

(1) Total data buffered in memory by the byterange filter.  This can be
computed in advance from the request headers.  If this is exceeded, the
filter should create a file bucket to store the data, and the ordered
list then references offsets into the file.

(2) A limit above which byteranges won't be served at all: most of us
have neither the memory nor the /tmp space for a gigabyte.

> now.. this assumes that splitting a bucket (and copying) is a zero cost
> operation which doesn't actually *read* the bucket, is this true for
> most bucket types?
>
> would this kind of thing work?

As I said, the trivial cases should (transparently) be treated separately
and more simply.  Otherwise ... well, as discussed on IRC.

-- 
Nick Kew