Set-Cookie and Cookie Optimized Binary Encoding

James M Snell <jasnell@gmail.com> Fri, 18 January 2013 17:51 UTC

Return-Path: <ietf-http-wg-request@listhub.w3.org>
X-Original-To: ietfarch-httpbisa-archive-bis2Juki@ietfa.amsl.com
Delivered-To: ietfarch-httpbisa-archive-bis2Juki@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id E996B21F87AC for <ietfarch-httpbisa-archive-bis2Juki@ietfa.amsl.com>; Fri, 18 Jan 2013 09:51:28 -0800 (PST)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -8.599
X-Spam-Level:
X-Spam-Status: No, score=-8.599 tagged_above=-999 required=5 tests=[AWL=0.799, BAYES_00=-2.599, HTML_MESSAGE=0.001, J_CHICKENPOX_35=0.6, J_CHICKENPOX_73=0.6, RCVD_IN_DNSWL_HI=-8]
Received: from mail.ietf.org ([64.170.98.30]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id qRpuykjdAOx6 for <ietfarch-httpbisa-archive-bis2Juki@ietfa.amsl.com>; Fri, 18 Jan 2013 09:51:27 -0800 (PST)
Received: from frink.w3.org (frink.w3.org [128.30.52.56]) by ietfa.amsl.com (Postfix) with ESMTP id C0B5721F87A6 for <httpbisa-archive-bis2Juki@lists.ietf.org>; Fri, 18 Jan 2013 09:51:27 -0800 (PST)
Received: from lists by frink.w3.org with local (Exim 4.72) (envelope-from <ietf-http-wg-request@listhub.w3.org>) id 1TwG4u-0004PN-4I for ietf-http-wg-dist@listhub.w3.org; Fri, 18 Jan 2013 17:50:08 +0000
Resent-Date: Fri, 18 Jan 2013 17:50:08 +0000
Resent-Message-Id: <E1TwG4u-0004PN-4I@frink.w3.org>
Received: from maggie.w3.org ([128.30.52.39]) by frink.w3.org with esmtp (Exim 4.72) (envelope-from <jasnell@gmail.com>) id 1TwG4p-0003Wq-2H for ietf-http-wg@listhub.w3.org; Fri, 18 Jan 2013 17:50:03 +0000
Received: from mail-ie0-f173.google.com ([209.85.223.173]) by maggie.w3.org with esmtps (TLS1.0:RSA_ARCFOUR_SHA1:16) (Exim 4.72) (envelope-from <jasnell@gmail.com>) id 1TwG4n-0004Si-KH for ietf-http-wg@w3.org; Fri, 18 Jan 2013 17:50:03 +0000
Received: by mail-ie0-f173.google.com with SMTP id e13so6645825iej.32 for <ietf-http-wg@w3.org>; Fri, 18 Jan 2013 09:49:35 -0800 (PST)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:mime-version:from:date:message-id:subject:to :content-type; bh=Z3FuqdbFEQ1cJIBtcgaGhn4iBMzFtHCDdCTKCHVXPbw=; b=A4+sSPPAgq3NltHnlor9xeyVxVCriFfTEjR4QycLirM1MP7YnKV28nQ6odQr+6N6a9 RQEj+Z135+mK8o6b8t9UXafYlnHOv15AT2XaNpS+seTzxqtqR5kAN3WiaWm9PfV1/4R8 4Uuj3TyiZcvdgR825yQ/M1+i7FEzYeQdi7+NzWgP+lXBcsoYBHAIHXY72YJhnBPgUTDd IdWB/SKLW1+8mA2FMr7OoSnmwGgCYQhct7fC6f/u9Njeaa07aEyiJ/TUMny5heGpj7gr UNKfqS6kQ6UanvLTGJ8FZB43z8GbrJgNPinuBLTzkrC0/7bn4zANLMONcVE8VGyfrdiR ugTQ==
X-Received: by 10.50.150.174 with SMTP id uj14mr2777688igb.19.1358531375804; Fri, 18 Jan 2013 09:49:35 -0800 (PST)
MIME-Version: 1.0
Received: by 10.64.26.137 with HTTP; Fri, 18 Jan 2013 09:49:15 -0800 (PST)
From: James M Snell <jasnell@gmail.com>
Date: Fri, 18 Jan 2013 09:49:15 -0800
Message-ID: <CABP7RbcYw4=Lv_JHRAq9HNKQZ9GthE1ZhbXGC4fPna1aD3eZeA@mail.gmail.com>
To: "ietf-http-wg@w3.org" <ietf-http-wg@w3.org>
Content-Type: multipart/alternative; boundary="f46d043d644b1232e204d393bce4"
Received-SPF: pass client-ip=209.85.223.173; envelope-from=jasnell@gmail.com; helo=mail-ie0-f173.google.com
X-W3C-Hub-Spam-Status: No, score=-4.5
X-W3C-Hub-Spam-Report: AWL=-1.760, BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, FREEMAIL_FROM=0.001, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_LOW=-0.7, SPF_PASS=-0.001
X-W3C-Scan-Sig: maggie.w3.org 1TwG4n-0004Si-KH a7518cc5ef7c6463a8b7bb2cde7ad7a3
X-Original-To: ietf-http-wg@w3.org
Subject: Set-Cookie and Cookie Optimized Binary Encoding
Archived-At: <http://www.w3.org/mid/CABP7RbcYw4=Lv_JHRAq9HNKQZ9GthE1ZhbXGC4fPna1aD3eZeA@mail.gmail.com>
Resent-From: ietf-http-wg@w3.org
X-Mailing-List: <ietf-http-wg@w3.org> archive/latest/16008
X-Loop: ietf-http-wg@w3.org
Resent-Sender: ietf-http-wg-request@w3.org
Precedence: list
List-Id: <ietf-http-wg.w3.org>
List-Help: <http://www.w3.org/Mail/>
List-Post: <mailto:ietf-http-wg@w3.org>
List-Unsubscribe: <mailto:ietf-http-wg-request@w3.org?subject=unsubscribe>

Just continuing on with my exploration of improvements we could possibly
make for the binary encoding of various headers. Currently I'm looking at
Cookie and Set-Cookie. Here's what I've come up with so far...

Here's an random example of a simple Set-Cookie taken from the har file
samples Mark has collected:

  Set-Cookie: skin=noskin; path=/; domain=.amazon.com; expires=Sat,
03-Nov-2012 13:04:26 GMT

Looking at this, ignoring the actual key and value for a minute, there is
quite a bit of wasted overhead in this format (Set-Cookie2 is even worse,
but I'll get to that in a bit). The main question is, can we do better and
still provide an easy 1.1 <-> 2.0 transform for Cookies. I definitely think
we can. Here's what I have in mind:

+-----------------------------------------+
|H|S|B|P|M|X|X|X|len(key)|key|len(val)|val|
+-----------------------------------------+
|len(path)|path|len(domain)|domain|expires|
+-----------------------------------------+
|num_params|... repeating param key block |
+-----------------------------------------+

  H = Http-Only Bit
  S = Secure-Only Bit
  B = Binary-Value Bit
  P = Optional-Param List Bit (indicates whether the Set-Cookie includes
the optional parameters block (third row in the record above)
  M = Max-Age Bit - If set, the expires field specifies the Maximum-Age of
the Cookie, otherwise expires is a compact encoded date time (as we've
already discussed). Either way, the expires field is always 4 bytes.
  X = Reserved Flag Bit

  len(key)    -> 1 byte
  len(val)    -> 4 bytes
  len(path)   -> 2 bytes
  len(domain) -> 2 bytes
  expires     -> 4 bytes

For typical Set-Cookies, with no optional parameters (I'll explain this
later) this gives us 14-bytes of overhead. For the example cookie above, we
go from 78 bytes down to 36 bytes. In the Http/1 format, adding HttpOnly
and Secure flags adds at least 18 bytes of additional data. In this
optimized form those require no additional space.

When set, the Binary Value bit indicates that the val field contains
arbitrary binary data as opposed to encoded-text. As Mark and others have
pointed out, allowing for Binary cookie values can lead to trouble with
backwards compatibility with 1.1 applications. To address this I propose
the following: When translating an optimized Http/2 Set-Cookie to Http/1,
if the Binary Value Bit is set, the val is Base64 encoded and a new
"Binary" flag is added to the Http/1 Set-Cookie. For example:

  Set-Cookie: data={base64}; path=/; domain=.example.net; expires=...;
HttpOnly; Secure; Binary

When translating from Http/1 to Http/2, if the Set-Cookie includes the
Binary flag, we can attempt to base64 decode the value and include it
directly in the optimized Http/2 record syntax directly, setting the Binary
Value Bit, and hopefully saving significant amount of space.

For the Cookie Header itself, we would also have a binary syntax..

+-----------------------------------+
|B|XXXXXXX|len(key)|key|len(val)|val|
+-----------------------------------+

  B = Binary-Value Bit

When value is simple text, this record format actually comes out longer
than the existing format, but that's OK, I think. For binary cookie values,
however, this format gives us the option of reducing the record size
significantly. So it's a tradeoff. Here, however, the translation to and
from Http/1 gets a bit more difficult because the Cookie header does not
allow for optional flags the way Set-Cookie does, so we have to get a bit
creative. What I propose is that we use a convention similar to that used
in RFC5987 and append an asterisk * to the key name..

For example, when translating a Http/2 Binary Cookie header to Http/1, it
would be encoded as:

  Cookie: key*={base64}

When translating back to Http/2, if we see the * in the key name, we can
attempt to base64 decode the value and set the Binary Value Flag in the
Http/2 Binary Cookie header. It's not perfect, but it ought to work.

Getting back to Set-Cookie, there's the part about the optional parameters.
While the current Set-Cookie format is pretty stable, there have been times
in the past (Set-Cookie2) where additional parameters were specified.
Set-Cookie is one of those things where it may make sense to support some
long term extensibility. For that purpose, I have included 3 additional
reserved flag bits and an optional block of extension key=value pairs that
can be tacked on at the end. If the Optional Parameter Bit is set, we know
to go looking for those additional parameters at the end. The value of
those parameters must be text, keys are text. len(key) = 1 byte, len(val) =
4 bytes. This gives a significant amount of latitude for extension and
translates fine to the existing Http/1 Set-Cookie syntax.

With these changes, I estimate that we can reduce the overall overhead of
transmitting Set-Cookie and Cookie values by around 50% on average, while
maintaining compatibility with Http/1.

Thoughts?