Subject: [hybi] Proposed Text: Compression

From: Patrick McManus <[email-masked]>
Date: Thu, 09 Sep 2010 13:24:47 -0400

We have certainly seen a web evolve where transport time compression of
some document types is a best practice.

http://www.websiteoptimization.com/speed/18/18-2t.html

Local conditions vary of course, and policy of when to transmit
something compressed at this layer should, imo, remain local policy. But
because this is so often a very easy win for the web in general, I
believe websockets ought to provide the mechanism to implement that
policy.

I propose doing this as an extension whose definition is provided in the
base specification. That allows any resource constrained application to
avoid implementing it if they wish. It also provides an existence proof
of an extension which I think is a very important part of the standards
process. 

I think an implementation which negotiates this "on" and then compresses
every text message over 125 bytes makes some sense - but an
implementation is free to do whatever it wants, of course.

The text below does not address the negotiation of the extension - that
is presumably part of the handshake and that is still in a confused
state which I don't help resolve here.

My text presumes this will be section 8 and the current 8->N will be
+1'd.

...

8. Known Extensions

Extensions provide a mechanism for implementations to opt-in to
additional protocol features. This section defines the meaning of
well-known extensions but implementations may use other extensions as
well.

8.1. Compression

The registered extension token for the compression extension is "gzip". 

The compression extension does not have any extension data. It uses the
RSV1 bit of the frame header to indicate compression, and it MAY be
combined with any other extension that does not use the RSV4 bit.

All frames of a message MUST have the same RSV1 value.

Compression is applied to the Application Data portion of the message
using the gzip encoding as defined by RFC 1952. The application data
length of a compressed frame represents the length of the compressed
data payload. 

...

---

From: John Tamplin <[email-masked]>
Date: Thu, 9 Sep 2010 13:59:06 -0400

> of an extension which I think is a very important part of the standards

I agree with the idea of having extensions defined in the base
specification, assuming we can come to agreement on them without holding up
the spec.  In addition to compression, I would like to see an extension for
use by other extensions to transport metadata, and a multiplexing extension.

> combined with any other extension that does not use the RSV4 bit.

I assume RSV4 should be RSV1?

> length of a compressed frame represents the length of the compressed

Using per-message compression without maintaining compression state across
messages gets poor compression (see the experiment results I posted
earlier).  I think any compression extension needs to support some form of
stream-based compression, whether it compresses the entire WebSocket stream
(the downside is that framing could interfere with the compression achieved
and you are compressing even incompressible content), or individual messages
are compressed using shared compression state.

-- 
John A. Tamplin
Software Engineer (GWT), Google

---

From: Patrick McManus <[email-masked]>
Date: Thu, 09 Sep 2010 14:01:58 -0400

> I assume RSV4 should be RSV1?

yes; sorry.

---

From: "Brian Smith" <[email-masked]>
Date: Thu, 9 Sep 2010 13:20:36 -0500

John Tamplin wrote:
> I think any compression extension needs to support some form
> of stream-based compression, whether it compresses the entire
> WebSocket stream (the downside is that framing could interfere
> with the compression achieved and you are compressing even
> incompressible content), or individual messages are
> compressed using shared compression state.

This is not necessarily true. In particular, it would be pretty easy to create an extension to the DEFLATE format that has better support for uncompressible content. In particular, define BTYPE==3 (currently unused) to be the same as BTYPE==0 (uncompressed) with the additional property that BYTE==3 blocks do not affect the compression context. Optionally, you could go farther and define a new block length encoding for BYTE==3 that more efficiently encodes the length for small blocks (compared to BTYPE==0's length encoding) and allows for larger blocks (so that mmap/sendfile of large files could be used). 

I believe such a format would be nearly optimal for WebSockets (and TLS) without complicating the core protocols; you'd just have to add a simple compression negotiation to the handshake (probably very similar to TLS's. Implementations could then optionally fiddle with the compression context's settings to get the effects that you are hoping to achieve with per-frame compression as well as more fine-grained control than what per-frame compression would allow.

Regards,
Brian

---

From: Dave Cridland <[email-masked]>
Date: Thu, 09 Sep 2010 19:50:59 +0100

> length of a compressed frame represents the length of the compressed

A few comments:

a) You almost certainly want to specify that the compression context  
lasts between frames. This means that the compression algorithm will  
essentially improve over time.

b) You may well not want to burden implementors with whether a frame  
is compressed or not, and have the compression algorithm do this for  
you. Plenty of existing usages treat compression as an inserted layer  
(IMAP's COMPRESS=DEFLATE, XMPP's XEP-0138) which simplifies  
implementation, as the compression would become a property of the  
transport in effect. This, of course, saves any per-frame indicator.

(Of course, Brian Smith has an even better idea, and I note that if  
you shift compression down a layer, you can play with such ideas much  
more easily.)

c) There is no good reason to want to use RFC 1952. Instead, specify  
"deflate" and RFC 1951. RFC 1952 adds a chunk of overhead intended  
for file compression needs, which would be useless for WebSockets.  
RFC 1950 is similar, but in a different way. All we need here is the  
deflate blocks themselves.

Dave.
-- 
Dave Cridland - mailto:[email-masked] - xmpp:[email-masked]
  - acap://acap.dave.cridland.net/byowner/user/dwd/bookmarks/
  - http://dave.cridland.net/
Infotrope Polymer - ACAP, IMAP, ESMTP, and Lemonade

---

From: John Tamplin <[email-masked]>
Date: Thu, 9 Sep 2010 14:53:56 -0400

On Thu, Sep 9, 2010 at 2:20 PM, Brian Smith <[email-masked]> wrote:

> This is not necessarily true. In particular, it would be pretty easy to
> create an extension to the DEFLATE format that has better support for
> uncompressible content. In particular, define BTYPE==3 (currently unused) to
> be the same as BTYPE==0 (uncompressed) with the additional property that
> BYTE==3 blocks do not affect the compression context. Optionally, you could
> go farther and define a new block length encoding for BYTE==3 that more
> efficiently encodes the length for small blocks (compared to BTYPE==0's
> length encoding) and allows for larger blocks (so that mmap/sendfile of
> large files could be used).
>
> I believe such a format would be nearly optimal for WebSockets (and TLS)
> without complicating the core protocols; you'd just have to add a simple
> compression negotiation to the handshake (probably very similar to TLS's.
> Implementations could then optionally fiddle with the compression context's
> settings to get the effects that you are hoping to achieve with per-frame
> compression as well as more fine-grained control than what per-frame
> compression would allow.
>

It seems like you have the same complexity, you just moved it into the
compression library, which now has to know when to use that block type
for incompressible messages.

I would expect most implementations to basically add a compressing filter
onto the stream being processed, and communicating boundaries of when to
compress and when not to compress seems problematic for that implementation
style.

Finally, you have an issue with getting this hacked version of zlib (or
whatever) available on all platforms.

---

From: Patrick McManus <[email-masked]>
Date: Thu, 09 Sep 2010 16:12:45 -0400

On Thu, 2010-09-09 at 14:53 -0400, John Tamplin wrote:

> I would expect most implementations to basically add a compressing
> filter onto the stream being processed, and communicating boundaries
> of when to compress and when not to compress seems problematic for
> that implementation style.

I don't think stream based compression is the way to go - 

as we all know you either have to 

a] deal with actually turning the compression algorithm on and off
within the compressed stream format.. which brian has good ideas for,
but they lack reference implementations and existing standardizations
that in my mind would cripple their usefullness.. that is basically
framing for compression - and we've got framing already.

or 

b] spend time compressing uncompressable content.

If consensus is to go this route then (b) is certainly the way to go.

In contrast, message level compression models http and this is one area
where we've seen HTTP do quite well despite the statelessness of its
messages and we would do well to mimic it, imo.

rfc 1951 vs 1952 is a point well taken.

---

From: John Tamplin <[email-masked]>
Date: Thu, 9 Sep 2010 17:02:57 -0400

> messages and we would do well to mimic it, imo.

That's fine, other than the fact that we will get significantly worse
compression ratios on real-world WebSocket traffic.  I think it is
worth doing something extra in order to get that advantage.  I think
using existing zlib implementations with SYNC_FLUSH is the best
combination of compression performance and widespread availability.
To me, the remaining question is whether it is worth moving knowledge
into the framing layer so you can avoid compressing framing bits and
only compress payload bits.

---

From: Dave Cridland <[email-masked]>
Date: Thu, 09 Sep 2010 23:19:20 +0100

> framing for compression - and we've got framing already.
So has the compression algorithm, and also deflate (at least) has  
existing implementations allowing uncompressed blocks under  
application control.

Brian's suggestion was an optimization of that.

> messages and we would do well to mimic it, imo.

Unlike IMAP and XMPP, which are likely to be much closer to the kinds  
of flows we'll see in WebSockets?

Dave.

---

From: Greg Wilkins <[email-masked]>
Date: Fri, 10 Sep 2010 16:41:39 +1000

> combined with any other extension that does not use the RSV4 bit.

I do not think that extensions should nominate a specific bit,
otherwise it essentially becomes the gzip bit and not a RSV bit, since
any other extension that used the same bit would not be able to
interoperate.

I think that the client should simply declare which bit's and op-codes
it intends to use for which extensions. Something like:

  Sec-WebSocket-Flags: gzip=1
  Sec-WebSocket-Opcodes: mux=4-6

would mean that the gzip extension was using RSV1 and the mux
extension was using opcodes 4,5 and 6.
The server would need to echo these back to confirm their usage.

furthermore, I think that known extension names can be simple tokens
like gzip or mux, but that user extensions should be identified with a
URL, so that there is some uniqueness to them.  For example

  Sec-WebSocket-Opcodes: http://www.wilkins.net/gregs/strange/extension=4-6

cheers

---

From: John Tamplin <[email-masked]>
Date: Fri, 10 Sep 2010 03:03:30 -0400

> The server would need to echo these back to confirm their usage.

I think that is much more complex than is required -- it seems odd
that in one message you argue against the complexity of reading a
control frame type from the payload, yet here you suggest having to
look for a variable bit location to determine if an extension is in
use or not.  I would think that in general overlapping bits would only
be assigned for extensions that were essentially mutually exclusive,
and when they are used up we allocate additional bits using the
mechanisms we have already discussed.

>  Sec-WebSocket-Opcodes: http://www.wilkins.net/gregs/strange/extension=4-6

If the standard specifies the extensions, then I don't see why we need
something like this.  It might be nice to reserve an x- prefix for
private-use extensions (I understand the argument against x- prefixes,
but the reality is private-use values will be used anyway and we might
as well remove the possibility of conflicts), but otherwise the spec
will give a name for the extension and there won't be any conflicts.

---

From: Dave Cridland <[email-masked]>
Date: Fri, 10 Sep 2010 09:19:40 +0100

On Fri Sep 10 07:41:39 2010, Greg Wilkins wrote:
>   Sec-WebSocket-Opcodes:  
> http://www.wilkins.net/gregs/strange/extension=4-6

If you want to be all URLly about it - which is reasonable - the  
standard ones can be URNs, thus (something like)  
urn:ietf:params:ws:deflate (and NOT gzip, please).

Dave.

---

From: Greg Wilkins <[email-masked]>
Date: Fri, 10 Sep 2010 19:17:12 +1000

On 10 September 2010 17:03, John Tamplin <[email-masked]> wrote:
> use or not.

As I have often argued, simplicity is not an end in itself, but only a
consideration when comparing two otherwise similar solutions.

> I would think that in general overlapping bits would only

I don't understand how this would work.  How do we know that if we
have 3 reserved bits that all future extensions will fall into 3
distinct mutually exclusive families?

If gzip is to be a standard extension, and it is always to use RSV1,
we might as well just call it the GZIP bit. I'd be happy to have no
negotiation for reserved bits if we did allocate them all to all the
known use-cases.

cheers

PS. it is not very complex, specially for clients.  A client can just
always use  RSV1 for GZIP and it need not have any negotiation code
other than to check the server accepted.    The server simply needs to
have an assignable bit mask for extensions, which is hardly very
difficult.

---

From: Patrick McManus <[email-masked]>
Date: Fri, 10 Sep 2010 08:18:06 -0400

On Fri, 2010-09-10 at 03:03 -0400, John Tamplin wrote:
> I think that is much more complex than is required -

First, good to see discussion about the specific negotiation of
extensions. That's new ground for hybi, I believe.

I'm with john here - absolutely no need for dynamic mapping of bits and
opcodes. There really are only a handful of things we want to be doing
at this layer, it is fine to define a gzip bit as one of them. The
purpose of negotiating it as an extension is to allow implementations to
opt-in to the requirement, not to save a header bit.

Now that being said, there seem to be a number of voices that support
stream compression instead of my proposed message compression.. that
would save the bit anyhow as a result, but I don't think that we should
be dynamically negotiating which flags and opcodes to use with
extensions.

Even though I have a preference, to me the stream vs message oriented
compression debate is largely unimportant as long as the definition can
be implemented with existing tools and compression is supported in the
base document.

Later on I will update the proposed extension text to reflect stream
semantics which seem to be favored by the group (both in this iteration
and in a review of the archives).

---

From: Hector Santos <[email-masked]>
Date: Fri, 10 Sep 2010 09:55:08 -0400

I'm sure all this is going to get worked out, but I sincerely hope its 
not done at the expense of making all this very complex for 
application developers.

I can understand the browser people will be the first to implement 
this, but the server side will need a WS stack too and not everyone 
uses the same tools.

Need to keep this simple.  Do not assume everyone uses Java on the 
server side or the same tools uses to do the proof of concepts on the 
client side.

Thanks

Hector Santos, CTO
http://www.santronics.com

--

---

From: John Tamplin <[email-masked]>
Date: Fri, 10 Sep 2010 10:36:28 -0400

> and in a review of the archives).

What I am arguing for is maintaining compression state across messages
-- there are two distinct ways to do that:
 - compress the entire stream
    - doesn't require a bit in the frame header
    - either compresses headers or requires switching compression
levels around the header bytes, which probably negates any advantage
    - likewise requires switching compression levels around
incompressible content
    - without modification to existing implementations, uncompressed
bytes still clutter the compression dictionary reducing compression
ratios
 - compress only the payload of individual frames
    - requires a per-frame bit to indicate compressed frames
    - only compresses payload data, and incompressible content doesn't
clutter the compression dictionary.

In either case, the framing layer would either need to figure out on
its own whether a frame was compressible or not (which might put
excess restrictions on the compression implementation, so that it can
clone the compression state and do a trial compression without
impacting the state) or the upper-layer API would have to indicate the
frame was compressible.  Alternatively, we could simply assume that
text frames are compressed and binary frames aren't, but that removes
the option of compressing binary data that is compressible (which the
GWT Quake sample traces shows is realistic).

In either case, this requires the equivalent of zlib's SYNC_FLUSH to
round a compressed frame to a byte boundary, which means that Java's
built-in deflate support is insufficient (hacks of changing the
compression level don't appear to be portable).

So, to me, the choice between these two options depends on whether the
benefits of the second option outweigh the cost of using one of the
reserved bits.

---

From: John Tamplin <[email-masked]>
Date: Fri, 10 Sep 2010 10:45:47 -0400

> distinct mutually exclusive families?

We don't.  We simply have the option to overlay them if it makes sense
to do so.  If we use all the reserved bits and need more because we
can't be sure that it is mutually exclusive with an existing use of a
reserved bit, then we do something to get more, such as perhaps the
previously discussed extension metadata mechanism.

> negotiation for reserved bits if we did allocate them all to all the

Yes, I think most likely nothing else will ever be assigned to that
bit and it will mean only compression.  Certainly an implementation
that negotiations compression defined using that bit can simple refuse
to negotiate any extension with conflicting requirements, but we do
have the option of choosing to assign the same bit to different
extensions if we see fit.

---

From: Joe Hildebrand <[email-masked]>
Date: Fri, 10 Sep 2010 15:39:07 -0600

On 9/9/10 2:12 PM, "Patrick McManus" <[email-masked]> wrote:

> within the compressed stream format..
> 
> or 
> 

Or, define the multiplexing stuff first, and negotiate compression
per-channel.  For things that you really don't want to compress, put them on
a different channel.

People that want simplicity can just stay with a single compressed channel.

-- 
Joe Hildebrand
(as individual)

---

From: Patrick McManus <[email-masked]>
Date: Fri, 10 Sep 2010 19:34:39 -0400

> compression level don't appear to be portable).

I want whatever we decide on to be doable in java and that is not an
area I have much expertise in. But if our spec requires a working flush
to force out the trailing bits of the message then an implementor could
reasonably just use a different java deflate implementation, right?
(e.g. http://www.jcraft.com/jzlib/)

As long as there is a reasonable alternative implementation, the
shortcoming in the standard jdk doesn't bother me.

---

From: John Tamplin <[email-masked]>
Date: Fri, 10 Sep 2010 19:36:34 -0400

> shortcoming in the standard jdk doesn't bother me.

Yes, jzlib solves the problem (though it is a bit awkward because it
doesn't directly mirror the zlib API) -- but certainly there are
plenty of options for a reasonable solution.

---

From: Self Disconnect <[email-masked]>
Date: Sat, 11 Sep 2010 12:04:55 -0700

Why is there a proposal to add compression in the WebSocket protocol? What
is wrong with using HTTP compression via Accept-Encoding/Content-Encoding in
the HTTP header? Some will probably say that they want to compress just the
payload. I'm not buying it.

On Fri, Sep 10, 2010 at 4:36 PM, John Tamplin <[email-masked]> wrote:

> _______________________________________________
> hybi mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/hybi
>

-- 
def self.disconnect
  redirect_to http://github.com/disconnect/apache-websocket
end

---

From: Benjamin Black <[email-masked]>
Date: Sat, 11 Sep 2010 13:28:33 -0700

I quite agree, and have the same question and objection on the other
areas in which HTTP is being reinvented in WebSockets.

b

---

From: Joe Hildebrand <[email-masked]>
Date: Sat, 11 Sep 2010 21:31:29 -0600

> payload. I'm not buying it.

The assertion is that if you do per-message compression, you can't compress
as well as if you were doing per-stream, particularly with smaller message
sizes.  Since it has also been asserted that there are a lot of applications
of websockets that will have small messages, there is some energy to specify
something.

As well, we want to make sure we can do extensions effectively, and
compression seems like it ought to be easy enough to specify, since there
are several participants who have experience specifying and implementing
compression for other streaming protocols.

-- 
Joe Hildebrand
(as individual)

---

From: John Tamplin <[email-masked]>
Date: Sun, 12 Sep 2010 00:40:18 -0400

On Sat, Sep 11, 2010 at 3:04 PM, Self Disconnect
<[email-masked]> wrote:
> the HTTP header?

That is solving a different problem, and it isn't clear a solution to
HTTP is appropriate for WebSocket.  For example, HTTP is a bunch of
disconnected requests, while WebSocket is a long-running stream of
messages.  Many of the interactive apps envisioned for WebSocket
exchange small messages, and compressing them individually results in
poor compression.  A single HTTP request fetches a single type of
resource, while WebSocket being a stream is likely to see different
messages containing different types of resource, which may have
different compressibility characteristics.

If you think about HTTP pipelining as an analog to WebSocket and
individual requests are client->server messages, it would work the
same way -- each message is compressed separately, and may have
different compression than other messages (the foo.html response is
compressed but the bar.gif response isn't).  However, sample traces of
real-world WebSocket apps shows that there is substantial redundancy
between messages, so maintaining compression state across messages is
a big win.

> Some will probably say that they want to compress just the

HTTP compresses the payload, and not the header -- if you are arguing
we should emulate HTTP it seems strange to argue that you want to
compress the header equally with the payload.

That said, this certainly hasn't been decided and there are two main
choices for how to do it, outlined earlier in this thread.  One of
which would be to negotiate compression for the entire stream and be
done with it.  The downside is you are compressing things that aren't
compressible, which both wastes space by expanding the payload and
reduces future compression ratios due to cluttering up the compression
dictionary.  Also, the payload lengths are the uncompressed lengths,
using more bytes to encode the length rather than encoding the
compressed length (compressing the result will reclaim some but not
all of those extra bytes).

---

From: Hector Santos <[email-masked]>
Date: Sun, 12 Sep 2010 01:22:39 -0400

John,

The only thing that keeps crossing my head, and I've been out of the 
technological loop since the modem days, is that the network providers 
already employ compression and error correction protocols, especially 
for mobile  networks, which I believe (or it appears) much of your 
consideration is based on.

I just find it strange the network providers will wait for websocket 
stacks to provide this functionality.  Is there is a really a need for 
software compression?  If so, then how does error correction (drops, 
reconnects, etc) play into this?

I can better understanding multiplexing or port sharing, and there 
again I would think the network providers will deal with this as the 
scalability needs grow, but the compression part seems redundant to me.

What am I missing here?

-- 
Hector Santos, CTO
http://www.santronics.com
http://santronics.blogspot.com

---

From: Benjamin Black <[email-masked]>
Date: Sat, 11 Sep 2010 22:28:52 -0700

John,

My earlier suggestion for authn, that the WebSockets specification
just call out HTTP authn as a required mechanism with other extensions
permitted later, applies just as well here.  That HTTPs existing
mechanisms _might_ be insufficient is exactly what this makes sense:
without a compelling argument against them, we should start with the
existing things, and extend as and when needed based on experience.

b

---

From: Greg Wilkins <[email-masked]>
Date: Sun, 12 Sep 2010 17:50:12 +1000

> extensions. That's new ground for hybi, I believe.

Nah - I think we've been to this particular ground hog day at least
once before :)
But I agree that we are making progress.

> opt-in to the requirement, not to save a header bit.

If we can really agree on what the handful of things are in this
layer, then I totally agree that we should just define a few optional
features (compression, mux, etc) and have simple negotiation switches
to turn them on/off. I also agree that the number of things we want to
do here is going to be pretty small. I can only think of 2 or 3, and
that is a stretch.

The problem is, that I'm not sure that when it comes down to the
detail of any given optional feature, that we have any real hope of
agreeing a one-size-fits-all solution for things like compression, let
alone mux.

I think advocates of mux and specific compression mechanisms have been
encouraged to step back from insisting they are supported in the core
framing mechanism, because it has been suggested that we will have a
flexible extension mechanism that will avoid us trying to pick the
best mux/compression mechanisms now and allow some degree of
experimentation and development into the future.      If the only
framing extension mechanism we support is simply switching on/off
pre-agreed features, then this group is going to have to pre-agree all
the features.    I think that might not be possible, or at the very
least will delay standardization of the plain vanilla un-extended
protocol.

Thus I think it is far simpler to have a simple dynamic extension
mechanism that will allow arbitrary extension, even if we can only
imagine 2 or 3 varieties.  This will avoid the need for this WG to
pick winners and will allow future experimentation and trials to
determine the most appropriate and supported extensions.

To make this work, I think we would need to put a SHOULD requirement
on extensions that they never change the application semantics of
websockets.  Ie it should not be possible to write an application that
would only work if a particular extension is supported.  Extensions
should not make websocket applications work, they should only make
them work better or more efficiently.

cheers

---

From: Patrick McManus <[email-masked]>
Date: Sun, 12 Sep 2010 09:35:31 -0400

On Sun, 2010-09-12 at 17:50 +1000, Greg Wilkins wrote:

> agreeing a one-size-fits-all solution for things like compression, 

The compression proposal (and its still to arrive streaming successor)
is not a one-size-fits-all solution. It is meant to be a well defined
extension for compression - if the websocket peers would like to
negotiate its use they are free to do so. If they would like to
negotiate the use of a different compression extension that is well
known to them they are also free to do that instead.

Obviously it is expected that defining a well known extension like this
will serve the needs of many implementors and they are saved the work of
defining an alternative. 

Also, from a working group perspective I really think we need to
actually make use of the extension space rather than just tabling
complex items to it. IMO the latter just leads to areas of a spec that
are not interoperable.

---

From: Greg Wilkins <[email-masked]>
Date: Mon, 13 Sep 2010 00:34:44 +1000

Patrick,

I'm not sure I understand how you intend for different compression
extensions to be negotiated?

Are you saying that bit is just for gzip and that other compression
algorithms will need to use the extension data area in the frame?
If so, then I don't think that is reasonable, as they will probably
suffer a 2 byte overhead just to add a boolean.

Or are you saying that they should also use the RSV1 bit?   In which
case it really should be called the compression bit (or transport
encoding bit) and the actual compression (or transport encoding) can
be negotiated, with gzip being the default.

I'm not opposed to this later interpretation, just so long as we don't
call the bit RSV1, because it is not a reserved bit as it is allocated
to the compression extension.  I think it would be wrong to encourage
the reuse of bits allocated to well know extensions.

cheers

---

From: Patrick McManus <[email-masked]>
Date: Sun, 12 Sep 2010 11:29:57 -0400

> extensions to be negotiated?

I don't expect they will be, honestly :) But if someone felt that the
standard "deflate" extension wasn't sufficient they would negotiate
"better-deflate" in the exact same manner at handshake time instead of
deflate. (whatever that may be - I'm kicking the handshake down the road
a bit.) 

better-deflate is free to define bits, opcodes, and extension data in
any way it sees fit. If it wants to reuse rsv1 from deflate that's ok
though it disallows the two extensions from both being active
simultaneously. As it limits composability you normally wouldn't want to
reuse bits like that, but in the case of a strict replacement it is
fine. And it isn't a problem for implementations because they would need
to understand the semantics of any two extensions in order to compose
them anyhow.

In any event, I don't see why the conditions under which the rsv'd bits
and opcodes may be reused needs to be specified by the websockets
document.