Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Renamed the I-D and provided the .txt version as well.

  • Loading branch information...
commit e9974bb501e8c7ba4534c5b9d6012073ae2071f0 1 parent ceb723f
@grmocg authored
View
186 example_code/draft-rpeon-header-compression-00.html → ...de/draft-rpeon-httpbis-header-compression-00.html
@@ -147,11 +147,11 @@
<tr><td class="header">Intended status: Informational</td><td class="header">Oct 12, 2012</td></tr>
<tr><td class="header">Expires: April 15, 2013</td><td class="header">&nbsp;</td></tr>
</table></td></tr></table>
-<h1><br />Header Delta-Compression for HTTP/2.0<br />draft-rpeon-header-compression-00</h1>
+<h1><br />Header Delta-Compression for HTTP/2.0<br />draft-rpeon-httpbis-header-compression-00</h1>
<h3>Abstract</h3>
-<p>This document describes a mechanism for compressing streams of groups key-value pairs, often known as Headers in an HTTP session. See RFC 2616 or successors for more information about headers.
+<p>This document describes a mechanism for compressing streams of groups of key-value pairs, often known as Headers in an HTTP session. See <a class='info' href='#RFC2616'>RFC 2616<span> (</span><span class='info'>Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L., Leach, P., and T. Berners-Lee, &ldquo;Hypertext Transfer Protocol -- HTTP/1.1,&rdquo; June&nbsp;1999.</span><span>)</span></a> [RFC2616] or successors for more information about headers.
</p>
<h3>Status of this Memo</h3>
<p>
@@ -219,7 +219,7 @@
<a name="rfc.section.1"></a><h3>1.&nbsp;
Overview</h3>
-<p>There have been several problems pointed out with the use of the gzip compressor in SPDY. The biggest of these problems is that it is possible for a smart attacker to inject content into the compressor, and then to test hypothesis about the prior contents of the compressor by examining the output size. The other issue is that gzip often consumes more CPU than many would like, especially in situations where one is doing forward or reverse proxying. The compressor proposed here intends to solve the first issue and significantly mitigate the second.
+<p>There have been several problems pointed out with the use of the gzip compressor in <a class='info' href='#SPDY'>SPDY<span> (</span><span class='info'>Belshe, M. and R. Peon, &ldquo;SPDY PROTOCOL,&rdquo; .</span><span>)</span></a> [SPDY]. The biggest of these problems is that it is possible for a smart attacker to inject content into the compressor, and then to test hypotheses about the prior contents of the compressor by examining the output size after each such content injection. The other issue is that gzip often consumes more CPU than many would like, especially in situations where one is doing forward or reverse proxying. The compressor proposed here intends to solve the first issue and significantly mitigate the second, while providing compression that is not too much worse than gzip.
</p>
<a name="anchor1"></a><br /><hr />
<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
@@ -236,13 +236,13 @@
<dd>client: Synonym for user-agent
</dd>
<dt></dt>
-<dd>server: The computer or device which typically accepts a connection and serves data
+<dd>server: The computer or device which typically accepts a connection, stores, and serves data
</dd>
<dt></dt>
<dd>proxy: An entity acting as a server for the client, and a client for the server
</dd>
<dt></dt>
-<dd>header: An complete set of key-value pairs. In HTTP1/1 this would be all of the data preceeding the entity-body.
+<dd>header: An complete set of key-value pairs, either request-headers, or response headers as defined in <a class='info' href='#RFC2616'>RFC2616<span> (</span><span class='info'>Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L., Leach, P., and T. Berners-Lee, &ldquo;Hypertext Transfer Protocol -- HTTP/1.1,&rdquo; June&nbsp;1999.</span><span>)</span></a> [RFC2616] section 5.3 or 6.2, respectively
</dd>
</dl></blockquote><p>
@@ -251,7 +251,9 @@
<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
<a name="rfc.section.3"></a><h3>3.&nbsp;
Example-data-model</h3>
-<div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre>
+
+<p> The author's current experimental implementation uses something very close to the following data-model for doing compression.
+</p><div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre>
struct ValEntry {
KeyMapIterator krt;
ValMapIterator vrt;
@@ -293,91 +295,91 @@
<p> Note that this only describes conceptually how the algorithm works and is very much pseudo-code
</p>
-<p> user-agents and servers compress MUST using roughly this algorithm.
+<p> User-agents and servers SHOULD NOT NOT use EREF operations.
</p>
-<p> user-agents and servers MUST use huffman-coding when so doing reduces the size of the bytes on the wire
+<p> User-agents and servers SHOULD use huffman-coding when so doing reduces the size of the bytes on the wire
</p>
-<p> proxies MAY compress using this algorithm, or something equivalent, but MAY also use EREF operations when throughput is more important than client experienced latency.
+<p> Proxies MAY use EREF operations when throughput is more important than client experienced latency.
</p>
-<p> proxies MAY ignore doing huffman coding
+<p> Proxies MAY choose to encode all strings as literal strings and skip huffman coding.
</p><div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre>
- Compress(headers):
- first_used_val_idx = CurrentValIdx()
- old_keymap = keymap
-
- For header in headers:
- if key in keymap:
- keymap[key].refcnt += 1
-
- for header in headers:
- key_map_iterator = lookup(header.key)
- if (!key_map_iterator):
- key_iterator = insert_new_key(header.key)
- insert_new_value_from_key(key_iterator, header.val)
- instructions.append(KVSto(key,val))
- else if (val_iterator = lookup_val_in_keymap_entry(key_map_iterator,
- header.val)):
- // we've found that the key,value pair already exists in the compressor
- // we now must check to see if it is already visible, in which case
- // there is nothing to do. If it isn't visible, however, we'll toggle
- // the visibility on.
- if (!header_group.includes(val_iterator_&gt;val_idx)):
- instructions.append(Toggle(val_iterator_&gt;val_idx));
- else:
- // we know we found a key, but not the entire key_value.
- // thus, we emit a clone() instructions.
- instructions.append(Clone(key_iterator_&gt;key_id, header.val)
-
- if key in old_keymap:
- keymap[key].refcnt += 1
-
- for header_group_entry in header_group:
- if header_group_entry not in header:
- instructions.append(header_group_entry_&gt;idx())
- for instruction in instructions:
- ExecuteInstruction(instructions)
- SerializeInstructions(output_buffer, instructions, first_used_val_idx)
-
-
- ExecuteInstruction(instruction):
- while (instruction.size_delta() + state_size &amp;gt; max_state_size):
- RemoveOldestLRUItem()
- if instruction.opcode == "clone":
- InsertKV(instruction.lookup_key(), instruction.val(), GetNextLRUIdx())
- elif instruction.opcode == "kvsto":
- InsertKV(instruction.key(), instruction.val(), GetNextLRUIdx())
- elif instruction.opcode == "togglerange":
- for (i=instruction.start; i &amp;lt;= instruction.last; ++i)
- bool prev_state = ToggleIdx(i)
- if prev_state == NOT_VISIBLE:
- MoveToFrontOfLRU(i)
- elif instruction.opcode == "toggle":
- bool prev_state = ToggleIdx(instruction.index)
- if prev_state == NOT_VISIBLE:
- MoveToFrontOfLRU(instruction.index)
-
-
- SerializeInstructions(output_buffer, instructions, first_val_idx):
- // Note that huffman-encoding is optional-- a bit (somwhere) would indicate
- // that it was or was not used for any particular string.
- output_buffer.WriteValIdx(first_val_idx)
- for toggle in filter(TOGGLE, instructions):
- output_buffer.WriteBits(TOGGLE)
- output_buffer.WriteValIdx(toggle.val_idx)
- for toggle in filter(TOGGLE_RANGE, instructions):
- output_buffer.WriteBits(TOGGLE_RANGE)
- output_buffer.WriteValIdx(toggle.start)
- output_buffer.WriteValIdx(toggle.last)
- for clone in filter(CLONE, instructions):
- output_buffer.WriteBits(CLONE)
- output_buffer.WriteKeyIdx(clone.key_idx)
- output_buffer.WriteString(huffman.encode(clone.val))
- for kvsto in filter(KVSTO, instructions):
- output_buffer.WriteString(huffman.encode(kvsto.key))
- output_buffer.WriteString(huffman.encode(kvsto.val))
- for eref in filter(EREF, instructions):
- output_buffer.WriteString(huffman.encode(kvsto.key))
- output_buffer.WriteString(huffman.encode(kvsto.val))
+Compress(headers):
+ first_used_val_idx = CurrentValIdx()
+ old_keymap = keymap
+
+ For header in headers:
+ if key in keymap:
+ keymap[key].refcnt += 1
+
+ for header in headers:
+ key_map_iterator = lookup(header.key)
+ if (!key_map_iterator):
+ key_iterator = insert_new_key(header.key)
+ insert_new_value_from_key(key_iterator, header.val)
+ instructions.append(KVSto(key,val))
+ else if (val_iterator = lookup_val_in_keymap_entry(key_map_iterator,
+ header.val)):
+ // we've found that the key,value pair already exists in the compressor
+ // we now must check to see if it is already visible, in which case
+ // there is nothing to do. If it isn't visible, however, we'll toggle
+ // the visibility on.
+ if (!header_group.includes(val_iterator_&gt;val_idx)):
+ instructions.append(Toggle(val_iterator_&gt;val_idx));
+ else:
+ // we know we found a key, but not the entire key_value.
+ // thus, we emit a clone() instructions.
+ instructions.append(Clone(key_iterator_&gt;key_id, header.val)
+
+ if key in old_keymap:
+ keymap[key].refcnt += 1
+
+ for header_group_entry in header_group:
+ if header_group_entry not in header:
+ instructions.append(header_group_entry_&gt;idx())
+ for instruction in instructions:
+ ExecuteInstruction(instructions)
+ SerializeInstructions(output_buffer, instructions, first_used_val_idx)
+
+
+ExecuteInstruction(instruction):
+ while (instruction.size_delta() + state_size &amp;gt; max_state_size):
+ RemoveOldestLRUItem()
+ if instruction.opcode == "clone":
+ InsertKV(instruction.lookup_key(), instruction.val(), GetNextLRUIdx())
+ elif instruction.opcode == "kvsto":
+ InsertKV(instruction.key(), instruction.val(), GetNextLRUIdx())
+ elif instruction.opcode == "togglerange":
+ for (i=instruction.start; i &amp;lt;= instruction.last; ++i)
+ bool prev_state = ToggleIdx(i)
+ if prev_state == NOT_VISIBLE:
+ MoveToFrontOfLRU(i)
+ elif instruction.opcode == "toggle":
+ bool prev_state = ToggleIdx(instruction.index)
+ if prev_state == NOT_VISIBLE:
+ MoveToFrontOfLRU(instruction.index)
+
+
+SerializeInstructions(output_buffer, instructions, first_val_idx):
+ // Note that huffman-encoding is optional-- a bit (somwhere) would
+ // indicate that it was or was not used for any particular string.
+ output_buffer.WriteValIdx(first_val_idx)
+ for toggle in filter(TOGGLE, instructions):
+ output_buffer.WriteBits(TOGGLE)
+ output_buffer.WriteValIdx(toggle.val_idx)
+ for toggle in filter(TOGGLE_RANGE, instructions):
+ output_buffer.WriteBits(TOGGLE_RANGE)
+ output_buffer.WriteValIdx(toggle.start)
+ output_buffer.WriteValIdx(toggle.last)
+ for clone in filter(CLONE, instructions):
+ output_buffer.WriteBits(CLONE)
+ output_buffer.WriteKeyIdx(clone.key_idx)
+ output_buffer.WriteString(huffman.encode(clone.val))
+ for kvsto in filter(KVSTO, instructions):
+ output_buffer.WriteString(huffman.encode(kvsto.key))
+ output_buffer.WriteString(huffman.encode(kvsto.val))
+ for eref in filter(EREF, instructions):
+ output_buffer.WriteString(huffman.encode(kvsto.key))
+ output_buffer.WriteString(huffman.encode(kvsto.val))
</pre></div>
@@ -460,9 +462,11 @@
operations have been emitted, the 'finished' bit of the HEADERS frame
which encapsulates the last instruction MUST be set to 1.
</p>
-<p> The BNF for this would be:
+<p> The rough ABNF for this would be:
</p><div style='display: table; width: 0; margin-left: 3em; margin-right: auto'><pre>
-HEADERS_FRAME: FRAME_BOILERPLATE GROUP-ID FIRST-VAL-IDX INSTRUCTIONS
+HEADERS_FRAME: HEADER_FRAME_BOILERPLATE GROUP-ID FIRST-VAL-IDX INSTRUCTIONS
+HEADER_FRAME_BOILERPLATE: LENGTH STREAM_ID HEADERS_OPCODE HEADER_DONE_FLAG
+HEADER_DONE_FLAG: (TRUE | FALSE)
INSTRUCTIONS : *(TOGGLE | TOGGLERANGE) *( CLONE | KVSTO) *(EREF)
TOGGLE: TOGGLE_OPCODE VAL_IDX
TOGGLERANGE: TOGGLERANGE_OPCODE VAL_IDX_START VAL_IDX_END
@@ -479,6 +483,8 @@
VAL_IDX: int
</pre></div>
+<p> A complete set of headers has only been expressed when the HEADERS frame has the HEADERS_DONE bit set to true. If the headers being serialized do not fit into one HEADERS frame, the HEADERS_DONE flag for all but the last HEADERS frame MUST be set to false
+</p>
<p> There are alternate serializations which have various interesting properties. Here are some interesting variations among these alternatives:
</p>
<blockquote class="text"><dl>
@@ -489,7 +495,7 @@
<dd> Have explicit sections for each opcode type, with a sentinel denoting the end of each range of opcode. Since the number of opcode types is small, this could reduce the overhead consumed in repeating the opcode.
</dd>
<dt></dt>
-<dd> As above, but have a count for the number of each opcode
+<dd> As above, but have a count for the number of each opcode instead of using a sentinel
</dd>
<dt></dt>
<dd> Use huffman coding on the entire payload of the HEADERS frame, not just the strings. TOGGLES are far more common than other operations, and using fewer bits for these could be beneficial
View
560 example_code/draft-rpeon-httpbis-header-compression-00.txt
@@ -0,0 +1,560 @@
+
+
+
+Network Working Group R. Peon
+Internet-Draft Google, Inc
+Intended status: Informational Oct 12, 2012
+Expires: April 15, 2013
+
+
+ Header Delta-Compression for HTTP/2.0
+ draft-rpeon-httpbis-header-compression-00
+
+Abstract
+
+ This document describes a mechanism for compressing streams of groups
+ of key-value pairs, often known as Headers in an HTTP session. See
+ RFC 2616 [RFC2616] or successors for more information about headers.
+
+Status of this Memo
+
+ This Internet-Draft is submitted in full conformance with the
+ provisions of BCP 78 and BCP 79.
+
+ Internet-Drafts are working documents of the Internet Engineering
+ Task Force (IETF). Note that other groups may also distribute
+ working documents as Internet-Drafts. The list of current Internet-
+ Drafts is at http://datatracker.ietf.org/drafts/current/.
+
+ Internet-Drafts are draft documents valid for a maximum of six months
+ and may be updated, replaced, or obsoleted by other documents at any
+ time. It is inappropriate to use Internet-Drafts as reference
+ material or to cite them other than as "work in progress."
+
+ This Internet-Draft will expire on April 15, 2013.
+
+Copyright Notice
+
+ Copyright (c) 2012 IETF Trust and the persons identified as the
+ document authors. All rights reserved.
+
+ This document is subject to BCP 78 and the IETF Trust's Legal
+ Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info) in effect on the date of
+ publication of this document. Please review these documents
+ carefully, as they describe your rights and restrictions with respect
+ to this document. Code Components extracted from this document must
+ include Simplified BSD License text as described in Section 4.e of
+ the Trust Legal Provisions and are provided without warranty as
+ described in the Simplified BSD License.
+
+
+
+
+
+Peon Expires April 15, 2013 [Page 1]
+
+Internet-Draft HTTP/2 Header Compression Oct 2012
+
+
+Table of Contents
+
+ 1. Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
+ 2. Definitions . . . . . . . . . . . . . . . . . . . . . . . . . 3
+ 3. Example-data-model . . . . . . . . . . . . . . . . . . . . . . 3
+ 4. Compression-overview . . . . . . . . . . . . . . . . . . . . . 4
+ 5. Decompression-overview . . . . . . . . . . . . . . . . . . . . 6
+ 6. On-the-wire-overview . . . . . . . . . . . . . . . . . . . . . 7
+ 7. Operations . . . . . . . . . . . . . . . . . . . . . . . . . . 9
+ 8. Security Considerations . . . . . . . . . . . . . . . . . . . 10
+ 9. Requirements Notation . . . . . . . . . . . . . . . . . . . . 10
+ 10. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 10
+ 11. Normative References . . . . . . . . . . . . . . . . . . . . . 10
+ Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Peon Expires April 15, 2013 [Page 2]
+
+Internet-Draft HTTP/2 Header Compression Oct 2012
+
+
+1. Overview
+
+ There have been several problems pointed out with the use of the gzip
+ compressor in SPDY [SPDY]. The biggest of these problems is that it
+ is possible for a smart attacker to inject content into the
+ compressor, and then to test hypotheses about the prior contents of
+ the compressor by examining the output size after each such content
+ injection. The other issue is that gzip often consumes more CPU than
+ many would like, especially in situations where one is doing forward
+ or reverse proxying. The compressor proposed here intends to solve
+ the first issue and significantly mitigate the second, while
+ providing compression that is not too much worse than gzip.
+
+
+2. Definitions
+
+ user-agent: The program or device which a human interacts with
+ directly and which typically initiates the transport layer
+ connection or session
+
+ client: Synonym for user-agent
+
+ server: The computer or device which typically accepts a
+ connection, stores, and serves data
+
+ proxy: An entity acting as a server for the client, and a
+ client for the server
+
+ header: An complete set of key-value pairs, either request-
+ headers, or response headers as defined in RFC2616 [RFC2616]
+ section 5.3 or 6.2, respectively
+
+
+3. Example-data-model
+
+ The author's current experimental implementation uses something very
+ close to the following data-model for doing compression.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Peon Expires April 15, 2013 [Page 3]
+
+Internet-Draft HTTP/2 Header Compression Oct 2012
+
+
+ struct ValEntry {
+ KeyMapIterator krt;
+ ValMapIterator vrt;
+ LRUIterator lrt;
+ Index val_idx;
+ };
+
+ typedef map<string, ValEntry*> ValMap;
+
+ struct KeyEntry {
+ int refcnt;
+ Index key_idx;
+ ValMap valmap;
+ }
+ typedef map<string, KeyEntry> KeyMap;
+ typedef set<Index> HeaderGroup;
+ typedef map<Index, HeaderGroup> HeaderGroups;
+
+ struct CompressorState {
+ HeaderGroups header_groups;
+ KeyMap key_map;
+ list<ValEntry*> lru;
+ };
+
+ typedef map<KeyIdx, string>; KeyIdxMap;
+ typedef map<ValIdx, pair<KeyIdxMap::iterator,string>; ValIdxMap;
+
+ struct DecompressorState {
+ KeyIdxMap key_idx_map;
+ ValIdxMap val_idx_map;
+ list<KeyIdxMap::iterator> lru;
+ };
+
+
+
+4. Compression-overview
+
+ Note that this only describes conceptually how the algorithm works
+ and is very much pseudo-code
+
+ User-agents and servers SHOULD NOT NOT use EREF operations.
+
+ User-agents and servers SHOULD use huffman-coding when so doing
+ reduces the size of the bytes on the wire
+
+ Proxies MAY use EREF operations when throughput is more important
+ than client experienced latency.
+
+
+
+
+Peon Expires April 15, 2013 [Page 4]
+
+Internet-Draft HTTP/2 Header Compression Oct 2012
+
+
+ Proxies MAY choose to encode all strings as literal strings and skip
+ huffman coding.
+
+Compress(headers):
+ first_used_val_idx = CurrentValIdx()
+ old_keymap = keymap
+
+ For header in headers:
+ if key in keymap:
+ keymap[key].refcnt += 1
+
+ for header in headers:
+ key_map_iterator = lookup(header.key)
+ if (!key_map_iterator):
+ key_iterator = insert_new_key(header.key)
+ insert_new_value_from_key(key_iterator, header.val)
+ instructions.append(KVSto(key,val))
+ else if (val_iterator = lookup_val_in_keymap_entry(key_map_iterator,
+ header.val)):
+ // we've found that the key,value pair already exists in the compressor
+ // we now must check to see if it is already visible, in which case
+ // there is nothing to do. If it isn't visible, however, we'll toggle
+ // the visibility on.
+ if (!header_group.includes(val_iterator_>val_idx)):
+ instructions.append(Toggle(val_iterator_>val_idx));
+ else:
+ // we know we found a key, but not the entire key_value.
+ // thus, we emit a clone() instructions.
+ instructions.append(Clone(key_iterator_>key_id, header.val)
+
+ if key in old_keymap:
+ keymap[key].refcnt += 1
+
+ for header_group_entry in header_group:
+ if header_group_entry not in header:
+ instructions.append(header_group_entry_>idx())
+ for instruction in instructions:
+ ExecuteInstruction(instructions)
+ SerializeInstructions(output_buffer, instructions, first_used_val_idx)
+
+
+ExecuteInstruction(instruction):
+ while (instruction.size_delta() + state_size &gt; max_state_size):
+ RemoveOldestLRUItem()
+ if instruction.opcode == "clone":
+ InsertKV(instruction.lookup_key(), instruction.val(), GetNextLRUIdx())
+ elif instruction.opcode == "kvsto":
+ InsertKV(instruction.key(), instruction.val(), GetNextLRUIdx())
+
+
+
+Peon Expires April 15, 2013 [Page 5]
+
+Internet-Draft HTTP/2 Header Compression Oct 2012
+
+
+ elif instruction.opcode == "togglerange":
+ for (i=instruction.start; i &lt;= instruction.last; ++i)
+ bool prev_state = ToggleIdx(i)
+ if prev_state == NOT_VISIBLE:
+ MoveToFrontOfLRU(i)
+ elif instruction.opcode == "toggle":
+ bool prev_state = ToggleIdx(instruction.index)
+ if prev_state == NOT_VISIBLE:
+ MoveToFrontOfLRU(instruction.index)
+
+
+SerializeInstructions(output_buffer, instructions, first_val_idx):
+ // Note that huffman-encoding is optional-- a bit (somwhere) would
+ // indicate that it was or was not used for any particular string.
+ output_buffer.WriteValIdx(first_val_idx)
+ for toggle in filter(TOGGLE, instructions):
+ output_buffer.WriteBits(TOGGLE)
+ output_buffer.WriteValIdx(toggle.val_idx)
+ for toggle in filter(TOGGLE_RANGE, instructions):
+ output_buffer.WriteBits(TOGGLE_RANGE)
+ output_buffer.WriteValIdx(toggle.start)
+ output_buffer.WriteValIdx(toggle.last)
+ for clone in filter(CLONE, instructions):
+ output_buffer.WriteBits(CLONE)
+ output_buffer.WriteKeyIdx(clone.key_idx)
+ output_buffer.WriteString(huffman.encode(clone.val))
+ for kvsto in filter(KVSTO, instructions):
+ output_buffer.WriteString(huffman.encode(kvsto.key))
+ output_buffer.WriteString(huffman.encode(kvsto.val))
+ for eref in filter(EREF, instructions):
+ output_buffer.WriteString(huffman.encode(kvsto.key))
+ output_buffer.WriteString(huffman.encode(kvsto.val))
+
+
+
+
+5. Decompression-overview
+
+ Note that this only describes conceptually how the algorithm works
+ and is very much pseudo-code
+
+
+
+
+
+
+
+
+
+
+
+Peon Expires April 15, 2013 [Page 6]
+
+Internet-Draft HTTP/2 Header Compression Oct 2012
+
+
+ Decode():
+ header_group = headers.header_group // as received from sender
+ val_idx = headers_frame.first_val_idx // as received from sender.
+ header_groups[header_group].ephemereal_headers.clear()
+ for instruction in instructions:
+ ExecuteInstruction(instruction, header_group)
+
+ ExecuteInstruction(instruction, header_group):
+ while (instruction.size_delta() + state_size &gt; max_state_size):
+ RemoveOldestLRUItem()
+ if instruction.opcode == TOGGLE:
+ if instruction.idx in header_groups[header_group]:
+ header_groups[header_group].remove(instruction.idx)
+ else:
+ header_groups[header_group].insert(instruction.idx)
+ MoveToHeadOfLRU(instruction.idx)
+ elif instruction.opcode == TOGGLE_GROUP:
+ for i=instruction.begin; i <= instruction.last; ++i:
+ ExecuteInstruction(Toggle(i), header_group);
+ elif instruction.opcode == CLONE:
+ KeyIdxMap::iterator kimi = key_idx_map.lookup(instruction.key_idx)
+ val_idx_map.insert(++val_idx, pair(kimi, instruction.val))
+ elif instruction.opcode == KVSTO:
+ key_idx = GetNextKeyIdx()
+ KeyIdxMap::iterator kimi = key_idx_map.insert(key_idx,
+ instruction.key)
+ val_idx_map.insert(++val_idx, pair(kimi, instruction.val))
+ elif instruction.opcode == EREF:
+ header_groups[header_group].ephemereal_headers.append(
+ pair(instruction.key, instruction.val))
+
+
+ // This would return the complete set of headers, e.g. for an HTTP
+ // request or response
+
+ InflateHeaders(header_group):
+ Headers headers
+ for idx in header_groups[header_group]:
+ headers.append(ValIdxMap[idx].first->first, ValIdxMap[idx].second)
+ headers.append(header_groups[header_group].ephemereal_headers);
+ return headers
+
+
+
+6. On-the-wire-overview
+
+ This would replace the current HEADERS frame described in the SPDY/2
+ draft specification.
+
+
+
+Peon Expires April 15, 2013 [Page 7]
+
+Internet-Draft HTTP/2 Header Compression Oct 2012
+
+
+ The header-group-id MUST be emitted immediately after the HEADERS
+ frame boilerplate.
+
+ A field indicating the start of the ID-sequence space MUST be
+ immediately after the header-group-id
+
+ A sequence of 'Toggle' or 'ToggleRange' operations MUST follow, if
+ any of these operations are to be emitted at all
+
+ A sequence of 'Clone' or 'KVSto' operations MUST then follow, if any
+ of these operations are to be emitted at all
+
+ A sequence of 'ERef' operations MUST then follow, if any of these are
+ to be emitted.
+
+ If the size of the aggregate set of instructions is larger than the
+ frame-length, the 'finished' bit of the HEADERS frame boilerplate
+ MUST be set to 0. This will indicate that the next subsequent
+ HEADERS frame on that stream is a continuation of a previous HEADERS
+ frame
+
+ If all of the 'Toggle', 'ToggleRange', 'Clone', and 'KVSTo'
+ operations have been emitted, the 'finished' bit of the HEADERS frame
+ which encapsulates the last instruction MUST be set to 1.
+
+ The rough ABNF for this would be:
+
+HEADERS_FRAME: HEADER_FRAME_BOILERPLATE GROUP-ID FIRST-VAL-IDX INSTRUCTIONS
+HEADER_FRAME_BOILERPLATE: LENGTH STREAM_ID HEADERS_OPCODE HEADER_DONE_FLAG
+HEADER_DONE_FLAG: (TRUE | FALSE)
+INSTRUCTIONS : *(TOGGLE | TOGGLERANGE) *( CLONE | KVSTO) *(EREF)
+TOGGLE: TOGGLE_OPCODE VAL_IDX
+TOGGLERANGE: TOGGLERANGE_OPCODE VAL_IDX_START VAL_IDX_END
+CLONE: CLONE_OPCODE KEY_IDX VAL_STRING
+KVSTO: KVSTO_OPCODE KEY_STRING VAL_STRING
+EREF: EREF_OPCODE KEY_STRING VAL_STRING
+
+KEY_STRING: MAYBE_COMPRESSED_STRING
+VAL_STRING: MAYBE_COMPRESSED_STRING
+MAYBE_COMPRESSED_STRING: TRUE HUFFMAN_COMPRESSED_STRING |
+ FALSE STRING_LITERAL
+VAL_IDX_START: int
+VAL_IDX_END: int
+VAL_IDX: int
+
+
+ A complete set of headers has only been expressed when the HEADERS
+ frame has the HEADERS_DONE bit set to true. If the headers being
+
+
+
+Peon Expires April 15, 2013 [Page 8]
+
+Internet-Draft HTTP/2 Header Compression Oct 2012
+
+
+ serialized do not fit into one HEADERS frame, the HEADERS_DONE flag
+ for all but the last HEADERS frame MUST be set to false
+
+ There are alternate serializations which have various interesting
+ properties. Here are some interesting variations among these
+ alternatives:
+
+ Serialize all constant-length information before serializing
+ any string data. This allows for a clear separation of byte-
+ aligned vs. potentially non-byte-aligned data
+
+ Have explicit sections for each opcode type, with a sentinel
+ denoting the end of each range of opcode. Since the number of
+ opcode types is small, this could reduce the overhead consumed
+ in repeating the opcode.
+
+ As above, but have a count for the number of each opcode
+ instead of using a sentinel
+
+ Use huffman coding on the entire payload of the HEADERS frame,
+ not just the strings. TOGGLES are far more common than other
+ operations, and using fewer bits for these could be beneficial
+
+
+7. Operations
+
+ Toggle: This encodes a single ValIdx, which, if already present in
+ the HeaderGroup associated with the current HEADERS frame, will be
+ removed, else added. If added, then the LRU entry which the index
+ references MUST be moved to the head of the LRU.
+
+ ToggleRange: This incodes two ValIdx, denoting an inclusive range of
+ ValIdx which shall be toggled as per the Toggle operation, above.
+
+ Clone: This encodes a KeyIdx which references a pre-existing key, and
+ references a new string. The Clone operation inserts a new ValEntry
+ into the ValMap associated with the KeyMap entry identified by the
+ KeyIdx. The ValIdx for this new ValEntry is the ++current_val_idx;
+ The new ValEntry MUST be inserted at the head of the LRU.
+
+ KVSto: This encodes a new key string and new value string. The key
+ will be inserted into the KeyMap, and assigned a new KeyIdx. The
+ value string will be inserted into the Valmap associated with the
+ KeyMap entry that was just added. As with Clone, a new unique ValIdx
+ will be associated with it in a monotonically increasing sequence by
+ using the value of ++current_val_idx. The new ValEntry MUST be
+ inserted at the head of the LRU.
+
+
+
+
+Peon Expires April 15, 2013 [Page 9]
+
+Internet-Draft HTTP/2 Header Compression Oct 2012
+
+
+ Eref: This encodes a key and value literal which, while interpreted
+ as part of the header frame, doesn't cause any persistent change in
+ the compressor state. ERef stands for "ephemereal reference".
+
+
+8. Security Considerations
+
+ The compressor algorithm described here is expected to be immune to
+ the current attacks against encrypted stream-based compressors such
+ as TLS+gzip, but more scrutiny is warranted. The reason that it is
+ believed that the algorithm(s) expressed here is immune is that any
+ backreference to a header key or value always requires a whole-text
+ match, and thus any probe of the compression context confirms no
+ hypothesis unless the attacker has guessed the entire plaintext.
+
+
+9. Requirements Notation
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC2119].
+
+
+10. Acknowledgements
+
+
+11. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
+ Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext
+ Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.
+
+ [SPDY] Belshe, M. and R. Peon, "SPDY PROTOCOL",
+ <http://tools.ietf.org/html/draft-mbelshe-httpbis-spdy>.
+
+
+Author's Address
+
+ Roberto Peon
+ Google, Inc
+
+ Email: fenix@google.com
+
+
+
+
+
+
+Peon Expires April 15, 2013 [Page 10]
+
View
245 example_code/draft-rpeon-header-compression-00.xml → ...ode/draft-rpeon-httpbis-header-compression-00.xml
@@ -13,9 +13,9 @@
<?rfc subcompact="no" ?>
<?rfc strict="no" ?>
- <rfc category="info" ipr="trust200902" docName="draft-rpeon-header-compression-00">
+ <rfc category="info" ipr="trust200902" docName="draft-rpeon-httpbis-header-compression-00">
<front>
- <title abbrev="header-compression">Header Compression for HTTP/2.0</title>
+ <title abbrev="HTTP/2 Header Compression">Header Delta-Compression for HTTP/2.0</title>
<author initials="R." surname="Peon" fullname="Roberto Peon">
<organization>Google, Inc</organization>
<address>
@@ -26,14 +26,13 @@
<area>Applications</area>
<keyword>HTTP</keyword>
<abstract>
- <t>This document describes a mechanism for compressing streams of groups key-value pairs, often known as Headers in an HTTP session. See RFC 2616 or successors for more information about headers.</t>
+ <t>This document describes a mechanism for compressing streams of groups of key-value pairs, often known as Headers in an HTTP session. See <xref target="RFC2616">RFC 2616</xref> or successors for more information about headers.</t>
</abstract>
- <note> </note>
</front>
<middle>
<section anchor="intro" title="Overview">
- <t>There have been several problems pointed out with the use of the gzip compressor in SPDY. The biggest of these problems is that it is possible for a smart attacker to inject content into the compressor, and then to test hypothesis about the prior contents of the compressor by examining the output size. The other issue is that gzip often consumes more CPU than many would like, especially in situations where one is doing forward or reverse proxying. The compressor proposed here intends to solve the first issue and significantly mitigate the second.</t>
+ <t>There have been several problems pointed out with the use of the gzip compressor in <xref target="SPDY">SPDY</xref>. The biggest of these problems is that it is possible for a smart attacker to inject content into the compressor, and then to test hypotheses about the prior contents of the compressor by examining the output size after each such content injection. The other issue is that gzip often consumes more CPU than many would like, especially in situations where one is doing forward or reverse proxying. The compressor proposed here intends to solve the first issue and significantly mitigate the second, while providing compression that is not too much worse than gzip.</t>
</section>
<section title="Definitions">
@@ -41,15 +40,15 @@
<list style="hanging" hangIndent="6">
<t>user-agent: The program or device which a human interacts with directly and which typically initiates the transport layer connection or session</t>
<t>client: Synonym for user-agent</t>
- <t>server: The computer or device which typically accepts a connection and serves data</t>
+ <t>server: The computer or device which typically accepts a connection, stores, and serves data</t>
<t>proxy: An entity acting as a server for the client, and a client for the server</t>
- <t>header: An complete set of key-value pairs. In HTTP1/1 this would be all of the data preceeding the entity-body.</t>
+ <t>header: An complete set of key-value pairs, either request-headers, or response headers as defined in <xref target="RFC2616">RFC2616</xref> section 5.3 or 6.2, respectively</t>
</list>
</t>
</section>
<section title="Example-data-model">
- <t>This will be referred to in subsequent sections</t>
+ <t> The author's current experimental implementation uses something very close to the following data-model for doing compression.</t>
<figure>
<artwork><![CDATA[
struct ValEntry {
@@ -59,30 +58,30 @@
Index val_idx;
};
- typedef map&lt;string, ValEntry*&gt; ValMap;
+ typedef map<string, ValEntry*> ValMap;
struct KeyEntry {
int refcnt;
Index key_idx;
ValMap valmap;
}
- typedef map&lt;string, KeyEntry&gt; KeyMap;
- typedef set&lt;Index&gt; HeaderGroup;
- typedef map&lt;Index, HeaderGroup&gt; HeaderGroups;
+ typedef map<string, KeyEntry> KeyMap;
+ typedef set<Index> HeaderGroup;
+ typedef map<Index, HeaderGroup> HeaderGroups;
struct CompressorState {
HeaderGroups header_groups;
KeyMap key_map;
- list&lt;ValEntry*&gt; lru;
+ list<ValEntry*> lru;
};
- typedef map&lt;KeyIdx, string&gt; KeyIdxMap;
- typedef map&lt;ValIdx, pair&lt;KeyIdxMap::iterator,string&gt; ValIdxMap;
+ typedef map<KeyIdx, string>; KeyIdxMap;
+ typedef map<ValIdx, pair<KeyIdxMap::iterator,string>; ValIdxMap;
struct DecompressorState {
KeyIdxMap key_idx_map;
ValIdxMap val_idx_map;
- list&lt;KeyIdxMap::iterator&gt; lru;
+ list<KeyIdxMap::iterator> lru;
};
]]>
</artwork>
@@ -91,91 +90,91 @@
<section title="Compression-overview">
<t> Note that this only describes conceptually how the algorithm works and is very much pseudo-code</t>
- <t> user-agents and servers compress MUST using roughly this algorithm.</t>
- <t> user-agents and servers MUST use huffman-coding when so doing reduces the size of the bytes on the wire</t>
- <t> proxies MAY compress using this algorithm, or something equivalent, but MAY also use EREF operations when throughput is more important than client experienced latency.</t>
- <t> proxies MAY ignore doing huffman coding</t>
+ <t> User-agents and servers SHOULD NOT NOT use EREF operations.</t>
+ <t> User-agents and servers SHOULD use huffman-coding when so doing reduces the size of the bytes on the wire</t>
+ <t> Proxies MAY use EREF operations when throughput is more important than client experienced latency.</t>
+ <t> Proxies MAY choose to encode all strings as literal strings and skip huffman coding.</t>
<figure>
<artwork><![CDATA[
- Compress(headers):
- first_used_val_idx = CurrentValIdx()
- old_keymap = keymap
-
- For header in headers:
- if key in keymap:
- keymap[key].refcnt += 1
-
- for header in headers:
- key_map_iterator = lookup(header.key)
- if (!key_map_iterator):
- key_iterator = insert_new_key(header.key)
- insert_new_value_from_key(key_iterator, header.val)
- instructions.append(KVSto(key,val))
- else if (val_iterator = lookup_val_in_keymap_entry(key_map_iterator,
- header.val)):
- // we've found that the key,value pair already exists in the compressor
- // we now must check to see if it is already visible, in which case
- // there is nothing to do. If it isn't visible, however, we'll toggle
- // the visibility on.
- if (!header_group.includes(val_iterator_>val_idx)):
- instructions.append(Toggle(val_iterator_>val_idx));
- else:
- // we know we found a key, but not the entire key_value.
- // thus, we emit a clone() instructions.
- instructions.append(Clone(key_iterator_>key_id, header.val)
-
- if key in old_keymap:
- keymap[key].refcnt += 1
-
- for header_group_entry in header_group:
- if header_group_entry not in header:
- instructions.append(header_group_entry_>idx())
- for instruction in instructions:
- ExecuteInstruction(instructions)
- SerializeInstructions(output_buffer, instructions, first_used_val_idx)
-
-
- ExecuteInstruction(instruction):
- while (instruction.size_delta() + state_size &gt; max_state_size):
- RemoveOldestLRUItem()
- if instruction.opcode == "clone":
- InsertKV(instruction.lookup_key(), instruction.val(), GetNextLRUIdx())
- elif instruction.opcode == "kvsto":
- InsertKV(instruction.key(), instruction.val(), GetNextLRUIdx())
- elif instruction.opcode == "togglerange":
- for (i=instruction.start; i &lt;= instruction.last; ++i)
- bool prev_state = ToggleIdx(i)
- if prev_state == NOT_VISIBLE:
- MoveToFrontOfLRU(i)
- elif instruction.opcode == "toggle":
- bool prev_state = ToggleIdx(instruction.index)
- if prev_state == NOT_VISIBLE:
- MoveToFrontOfLRU(instruction.index)
-
-
- SerializeInstructions(output_buffer, instructions, first_val_idx):
- // Note that huffman-encoding is optional-- a bit (somwhere) would indicate
- // that it was or was not used for any particular string.
- output_buffer.WriteValIdx(first_val_idx)
- for toggle in filter(TOGGLE, instructions):
- output_buffer.WriteBits(TOGGLE)
- output_buffer.WriteValIdx(toggle.val_idx)
- for toggle in filter(TOGGLE_RANGE, instructions):
- output_buffer.WriteBits(TOGGLE_RANGE)
- output_buffer.WriteValIdx(toggle.start)
- output_buffer.WriteValIdx(toggle.last)
- for clone in filter(CLONE, instructions):
- output_buffer.WriteBits(CLONE)
- output_buffer.WriteKeyIdx(clone.key_idx)
- output_buffer.WriteString(huffman.encode(clone.val))
- for kvsto in filter(KVSTO, instructions):
- output_buffer.WriteString(huffman.encode(kvsto.key))
- output_buffer.WriteString(huffman.encode(kvsto.val))
- for eref in filter(EREF, instructions):
- output_buffer.WriteString(huffman.encode(kvsto.key))
- output_buffer.WriteString(huffman.encode(kvsto.val))
-
- ]]>
+Compress(headers):
+ first_used_val_idx = CurrentValIdx()
+ old_keymap = keymap
+
+ For header in headers:
+ if key in keymap:
+ keymap[key].refcnt += 1
+
+ for header in headers:
+ key_map_iterator = lookup(header.key)
+ if (!key_map_iterator):
+ key_iterator = insert_new_key(header.key)
+ insert_new_value_from_key(key_iterator, header.val)
+ instructions.append(KVSto(key,val))
+ else if (val_iterator = lookup_val_in_keymap_entry(key_map_iterator,
+ header.val)):
+ // we've found that the key,value pair already exists in the compressor
+ // we now must check to see if it is already visible, in which case
+ // there is nothing to do. If it isn't visible, however, we'll toggle
+ // the visibility on.
+ if (!header_group.includes(val_iterator_>val_idx)):
+ instructions.append(Toggle(val_iterator_>val_idx));
+ else:
+ // we know we found a key, but not the entire key_value.
+ // thus, we emit a clone() instructions.
+ instructions.append(Clone(key_iterator_>key_id, header.val)
+
+ if key in old_keymap:
+ keymap[key].refcnt += 1
+
+ for header_group_entry in header_group:
+ if header_group_entry not in header:
+ instructions.append(header_group_entry_>idx())
+ for instruction in instructions:
+ ExecuteInstruction(instructions)
+ SerializeInstructions(output_buffer, instructions, first_used_val_idx)
+
+
+ExecuteInstruction(instruction):
+ while (instruction.size_delta() + state_size &gt; max_state_size):
+ RemoveOldestLRUItem()
+ if instruction.opcode == "clone":
+ InsertKV(instruction.lookup_key(), instruction.val(), GetNextLRUIdx())
+ elif instruction.opcode == "kvsto":
+ InsertKV(instruction.key(), instruction.val(), GetNextLRUIdx())
+ elif instruction.opcode == "togglerange":
+ for (i=instruction.start; i &lt;= instruction.last; ++i)
+ bool prev_state = ToggleIdx(i)
+ if prev_state == NOT_VISIBLE:
+ MoveToFrontOfLRU(i)
+ elif instruction.opcode == "toggle":
+ bool prev_state = ToggleIdx(instruction.index)
+ if prev_state == NOT_VISIBLE:
+ MoveToFrontOfLRU(instruction.index)
+
+
+SerializeInstructions(output_buffer, instructions, first_val_idx):
+ // Note that huffman-encoding is optional-- a bit (somwhere) would
+ // indicate that it was or was not used for any particular string.
+ output_buffer.WriteValIdx(first_val_idx)
+ for toggle in filter(TOGGLE, instructions):
+ output_buffer.WriteBits(TOGGLE)
+ output_buffer.WriteValIdx(toggle.val_idx)
+ for toggle in filter(TOGGLE_RANGE, instructions):
+ output_buffer.WriteBits(TOGGLE_RANGE)
+ output_buffer.WriteValIdx(toggle.start)
+ output_buffer.WriteValIdx(toggle.last)
+ for clone in filter(CLONE, instructions):
+ output_buffer.WriteBits(CLONE)
+ output_buffer.WriteKeyIdx(clone.key_idx)
+ output_buffer.WriteString(huffman.encode(clone.val))
+ for kvsto in filter(KVSTO, instructions):
+ output_buffer.WriteString(huffman.encode(kvsto.key))
+ output_buffer.WriteString(huffman.encode(kvsto.val))
+ for eref in filter(EREF, instructions):
+ output_buffer.WriteString(huffman.encode(kvsto.key))
+ output_buffer.WriteString(huffman.encode(kvsto.val))
+
+ ]]>
</artwork>
</figure>
</section>
@@ -209,16 +208,16 @@
elif instruction.opcode == KVSTO:
key_idx = GetNextKeyIdx()
KeyIdxMap::iterator kimi = key_idx_map.insert(key_idx,
- instruction.key)
+ instruction.key)
val_idx_map.insert(++val_idx, pair(kimi, instruction.val))
elif instruction.opcode == EREF:
header_groups[header_group].ephemereal_headers.append(
- pair(instruction.key, instruction.val))
+ pair(instruction.key, instruction.val))
- // This would return the complete set of headers, e.g. for an HTTP
- // request or response
-
+ // This would return the complete set of headers, e.g. for an HTTP
+ // request or response
+
InflateHeaders(header_group):
Headers headers
for idx in header_groups[header_group]:
@@ -228,12 +227,12 @@
]]>
</artwork>
</figure>
-
+ </section>
<section title="On-the-wire-overview">
<t> This would replace the current HEADERS frame described in the SPDY/2 draft specification.</t>
- <t> A field indicating the start of the ID-sequence space MUST be
- emitted immediately after the HEADERS frame boilerplate.</t>
+ <t> The header-group-id MUST be emitted immediately after the HEADERS frame boilerplate.</t>
+ <t> A field indicating the start of the ID-sequence space MUST be immediately after the header-group-id</t>
<t> A sequence of 'Toggle' or 'ToggleRange' operations MUST follow, if
any of these operations are to be emitted at all</t>
<t> A sequence of 'Clone' or 'KVSto' operations MUST then follow, if
@@ -247,6 +246,38 @@
<t> If all of the 'Toggle', 'ToggleRange', 'Clone', and 'KVSTo'
operations have been emitted, the 'finished' bit of the HEADERS frame
which encapsulates the last instruction MUST be set to 1.</t>
+ <t> The rough ABNF for this would be:</t>
+ <figure>
+ <artwork><![CDATA[
+HEADERS_FRAME: HEADER_FRAME_BOILERPLATE GROUP-ID FIRST-VAL-IDX INSTRUCTIONS
+HEADER_FRAME_BOILERPLATE: LENGTH STREAM_ID HEADERS_OPCODE HEADER_DONE_FLAG
+HEADER_DONE_FLAG: (TRUE | FALSE)
+INSTRUCTIONS : *(TOGGLE | TOGGLERANGE) *( CLONE | KVSTO) *(EREF)
+TOGGLE: TOGGLE_OPCODE VAL_IDX
+TOGGLERANGE: TOGGLERANGE_OPCODE VAL_IDX_START VAL_IDX_END
+CLONE: CLONE_OPCODE KEY_IDX VAL_STRING
+KVSTO: KVSTO_OPCODE KEY_STRING VAL_STRING
+EREF: EREF_OPCODE KEY_STRING VAL_STRING
+
+KEY_STRING: MAYBE_COMPRESSED_STRING
+VAL_STRING: MAYBE_COMPRESSED_STRING
+MAYBE_COMPRESSED_STRING: TRUE HUFFMAN_COMPRESSED_STRING |
+ FALSE STRING_LITERAL
+VAL_IDX_START: int
+VAL_IDX_END: int
+VAL_IDX: int
+ ]]>
+ </artwork>
+ </figure>
+ <t> A complete set of headers has only been expressed when the HEADERS frame has the HEADERS_DONE bit set to true. If the headers being serialized do not fit into one HEADERS frame, the HEADERS_DONE flag for all but the last HEADERS frame MUST be set to false</t>
+ <t> There are alternate serializations which have various interesting properties. Here are some interesting variations among these alternatives:
+ <list style="hanging" hangIndent="6">
+ <t> Serialize all constant-length information before serializing any string data. This allows for a clear separation of byte-aligned vs. potentially non-byte-aligned data</t>
+ <t> Have explicit sections for each opcode type, with a sentinel denoting the end of each range of opcode. Since the number of opcode types is small, this could reduce the overhead consumed in repeating the opcode.</t>
+ <t> As above, but have a count for the number of each opcode instead of using a sentinel</t>
+ <t> Use huffman coding on the entire payload of the HEADERS frame, not just the strings. TOGGLES are far more common than other operations, and using fewer bits for these could be beneficial</t>
+ </list>
+ </t>
</section>
<section title="Operations">
Please sign in to comment.
Something went wrong with that request. Please try again.