Re: [hybi] Framing Take VI (a compromise proposal)

John Tamplin <jat@google.com> Wed, 18 August 2010 03:15 UTC

Return-Path: <jat@google.com>
X-Original-To: hybi@core3.amsl.com
Delivered-To: hybi@core3.amsl.com
Received: from localhost (localhost [127.0.0.1]) by core3.amsl.com (Postfix) with ESMTP id 2A1623A684F for <hybi@core3.amsl.com>; Tue, 17 Aug 2010 20:15:38 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -104.723
X-Spam-Level:
X-Spam-Status: No, score=-104.723 tagged_above=-999 required=5 tests=[AWL=1.253, BAYES_00=-2.599, FM_FORGED_GMAIL=0.622, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_MED=-4, USER_IN_WHITELIST=-100]
Received: from mail.ietf.org ([64.170.98.32]) by localhost (core3.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id yX0ztvWCFPTx for <hybi@core3.amsl.com>; Tue, 17 Aug 2010 20:15:25 -0700 (PDT)
Received: from smtp-out.google.com (smtp-out.google.com [74.125.121.35]) by core3.amsl.com (Postfix) with ESMTP id BD3C83A67C0 for <hybi@ietf.org>; Tue, 17 Aug 2010 20:15:22 -0700 (PDT)
Received: from hpaq7.eem.corp.google.com (hpaq7.eem.corp.google.com [172.25.149.7]) by smtp-out.google.com with ESMTP id o7I3FvQ2025145 for <hybi@ietf.org>; Tue, 17 Aug 2010 20:15:57 -0700
DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=google.com; s=beta; t=1282101357; bh=eMmg3iJrE49a48KBEbPCy58xE+E=; h=MIME-Version:In-Reply-To:References:From:Date:Message-ID:Subject: To:Cc:Content-Type; b=a3g5IwDOCywA8sqtyNzzwM7xPq96yrxdAqmYWvgLB57YYw+WEEsPEZ+zU2oElVsOw cMpR5rnxeGJTNr0VVGK+A==
DomainKey-Signature: a=rsa-sha1; s=beta; d=google.com; c=nofws; q=dns; h=mime-version:in-reply-to:references:from:date:message-id: subject:to:cc:content-type:x-system-of-record; b=ruOaGAMMmdzvHbCOpWj+Hn23jo1IWrpcIjROI+fJHJLSh5lgzlR7+fX8/MAyRL1NH +V3g+8X5wB09NZadKNDZQ==
Received: from qyk33 (qyk33.prod.google.com [10.241.83.161]) by hpaq7.eem.corp.google.com with ESMTP id o7I3Ft2L018772 for <hybi@ietf.org>; Tue, 17 Aug 2010 20:15:56 -0700
Received: by qyk33 with SMTP id 33so129125qyk.19 for <hybi@ietf.org>; Tue, 17 Aug 2010 20:15:55 -0700 (PDT)
Received: by 10.229.213.212 with SMTP id gx20mr5476626qcb.60.1282101355227; Tue, 17 Aug 2010 20:15:55 -0700 (PDT)
MIME-Version: 1.0
Received: by 10.229.17.130 with HTTP; Tue, 17 Aug 2010 20:15:35 -0700 (PDT)
In-Reply-To: <4C6B3F76.2090207@caucho.com>
References: <AANLkTi=TBXO_Cbb+P+e2BVfx69shkf8E1-9ywDh_Y+Kz@mail.gmail.com> <AANLkTimJOGWgV6rx5JJYSJMC26OzQzskzVtkYz0L_EAg@mail.gmail.com> <op.vhe7qtmu64w2qv@anne-van-kesterens-macbook-pro.local> <AANLkTimqvQGJab-XdMuRFE8M2eB_xn_ipJZoNDuc28R2@mail.gmail.com> <4C66F67C.2080406@caucho.com> <AANLkTikY_ujn4rxuEPTidktL4Rwc1RizGBaa0-dpAhJP@mail.gmail.com> <4C6B3F76.2090207@caucho.com>
From: John Tamplin <jat@google.com>
Date: Tue, 17 Aug 2010 23:15:35 -0400
Message-ID: <AANLkTimNOyc-XO5SrYKgt4mpHdFc2pzsr2X1VS8ScwTO@mail.gmail.com>
To: Scott Ferguson <ferg@caucho.com>
Content-Type: multipart/alternative; boundary="0016362844f2d82a85048e107bee"
X-System-Of-Record: true
Cc: hybi@ietf.org
Subject: Re: [hybi] Framing Take VI (a compromise proposal)
X-BeenThere: hybi@ietf.org
X-Mailman-Version: 2.1.9
Precedence: list
List-Id: Server-Initiated HTTP <hybi.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/listinfo/hybi>, <mailto:hybi-request@ietf.org?subject=unsubscribe>
List-Archive: <http://www.ietf.org/mail-archive/web/hybi>
List-Post: <mailto:hybi@ietf.org>
List-Help: <mailto:hybi-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/hybi>, <mailto:hybi-request@ietf.org?subject=subscribe>
X-List-Received-Date: Wed, 18 Aug 2010 03:15:45 -0000

On Tue, Aug 17, 2010 at 10:03 PM, Scott Ferguson <ferg@caucho.com> wrote:

> Yes, but it shouldn't be in the frame itself. Any of the extension methods
> would be fine for the TML. Besides, your proposal assumes that an
> implementation is fragmenting when it knows the TML, which doesn't make
> sense. If it knows the TML, it can just send a big frame.
>

One of the reasons for supporting fragmentation in the base protocol is to
support multiplexing.  You don't want lots of small messages blocked behind
one huge message, so you want the multiplexor to be able to fragment the
large message and interleave it with messages on other channels.  Thus, you
might very well take one very large message and split it into fragments
where you do know the TML.  Also, a client might also choose to write a
fragmented message in chunks as well, though I suspect they would write it
in one large message if it is known ahead of time.

Personally, I wasn't a big fan of including TML at all, but it seemed a
necessary concession to those objecting to receiver buffer management
complication due to fragmentation.  I personally would have to problem
leaving it out entirely if that didn't prevent us from achieving consensus
(which would also allow us to go to a single "MORE" bit for fragment
support).  I would also be willing to say that the TML be included in a
multiplexing extension, as that will be the primary use-case (as I see it,
anyway).


>     * having a short length that was "mostly enough" would encourage
>>
>>      buggy implementations that don't support or test the long length
>>      case
>>
>>
> That's not a good reason to use 127 bytes. Picking an unusable length just
> to force people to test it is silly.
>

As mentioned in the previous message, it is not an unusable length for
real-world WebSocket apps.  It also is appropriate to take advantage of
wireless network characteristics.


> If you're concerned about people not properly testing variable lenghts, use
> a fixed length of 64 (or 56 to make a nice 8-byte frame.)
>
> (The weird scrambling of the hash in the handshake is silly for the same
> reason.)
>
>
>  Regarding d, as mentioned above those arguing against fragmentation
>> objected to not having the full message length, and after proposing that
>> addition they seemed happy with the compromise.  As fragmentation support
>> has been accepted as a requirement
>>
>
> Yes, the TML needs to be passed as metadata, but it should not be in the
> frame. Nor should it be tied to fragmentation. The whole point of
> fragmentation is that you don't know the total length.
>

See above for an example.


> The complication of UTF16->UTF8 conversion is the same in any other
>> length-based framing options, though supporting fragmentation means you can
>> have a fixed-size send buffer rather than having to allocate all of it at
>> once.
>>
>
> The difference between text and binary is that you know the frame length
> for binary immediately. With text, you do not. It's sufficiently different
> that you need to consider both signatures.


I believe the UTF16->UTF8 conversion works essentially the same as a
compression filter, and as such I think the result is the same:

while input left {
   transform input to buffer, stopping when buffer full or input exhausted
   write buffer (using short header if < 127 bytes, long header otherwise)
}

Again, I don't see these as showing any differences between the various
>> framing proposals, and fragmentation makes it easier to support streams as
>> you just write a fragment when the buffer fills.
>>
>
> That's only true if the frame header size is fixed. If your frame header
> size is variable, you either need to shift the bytes in the buffer, or are
> forced to use something like writev with extra pointer metadata, instead of
> just preallocating the fixed frame header size.
>

You could leave room at the beginning of the buffer for the largest header,
and start the write at the correct offset when you flush the buffer.  Or if
you don't want to bother with that, just make two write calls.  Admittedly,
there is an efficiency cost there but implementations more concerned with
implementation simplicity than overall efficiency aren't likely to care.


> Variable frame header sizes are bad.


There are three options:

   - have a fixed-size header sufficient for all messages - inefficient for
   small messages
   - have a fixed-size header and use fragmentation for larger messages -
   requires all senders to support fragmentation, which was objected to
   - use variable-length headers and use an appropriate length for the size
   required (you can define the lengths in whatever steps you like, such as the
   v76 framing protocol which essentially had 9 steps using the high bit to
   indicate there were more frame length bits)

Each has pros and cons.  It seemed the objectors to the first two were more
entrenched -- for example, I would strongly object to a fixed 8-byte length
on the grounds of efficiency of small messages, but I would be happy with a
16-bit fixed length and requiring fragmentation.


> The 127 byte short frame length can only be used efficiently for the
> sendBinaryMessage(), because you know the total length.
>

I don't see why you can't decide which header to use when you flush the
buffer -- if it is under 127 bytes, you use the short header, otherwise you
use the long header.


> For the stream-based binary sender, a simple implementation is to use a
> fixed-length buffer if you care about efficiency, and reserve the bytes for
> the frame header, which means its length needs to be fixed, no matter
> whether the message happened to be 4 bytes, 1024 bytes, or extend past the
> buffer size. It's the variable-length frame header that's a killer for
> streams.
>

Such a simple implementation could choose to send the long header if it
didn't care about efficiency of small frames.  It could also make two writes
or use scatter/gather APIs for the header buffer separate from the message
buffer.

A larger point is that with the proposals for extension using a nested
series of opcodes, the header in front of the final payload data is going to
be variable anyway.


> In the case of your proposal, the stream writer (for an 8k fixed buffer)
> needs a different frame header pre-allocation for a message that's less than
> 127 bytes, from a message that's less than 8k - 10, from a message that
> overflows and goes into fragment mode. Basically, your proposal takes away a
> simple and efficient stream implementation, forcing more complicated
> implementations (like shifting or pointers or writev, etc.).
>

Let's say I wanted to use an 8k buffer to write a stream.  I could start
putting payload data at byte 18 (in the current proposal, 10 if TML is
removed).  When I finish writing data or hit the end of the buffer, I
generate the header in the 2-18 bytes before the start of that message and
write it in a single write.  Then I start filling the next buffer the same
way, etc.  No scatter/gather required, no shifting bytes around, etc.

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