idnits 2.17.1 draft-snell-httpbis-bohe-11.txt: Checking boilerplate required by RFC 5378 and the IETF Trust (see https://trustee.ietf.org/license-info): ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/1id-guidelines.txt: ---------------------------------------------------------------------------- No issues found here. Checking nits according to https://www.ietf.org/id-info/checklist : ---------------------------------------------------------------------------- ** The document seems to lack an Introduction section. ** The document seems to lack an IANA Considerations section. (See Section 2.2 of https://www.ietf.org/id-info/checklist for how to handle the case when there are no actions for IANA.) Miscellaneous warnings: ---------------------------------------------------------------------------- == The copyright year in the IETF Trust and authors Copyright Line does not match the current year == The document seems to lack the recommended RFC 2119 boilerplate, even if it appears to use RFC 2119 keywords. (The document does seem to have the reference to RFC 2119 which the ID-Checklist requires). -- The document date (July 10, 2013) is 3942 days in the past. Is this intentional? Checking references for intended status: Informational ---------------------------------------------------------------------------- == Unused Reference: 'RFC2119' is defined on line 391, but no explicit reference was found in the text == Unused Reference: 'RFC6265' is defined on line 396, but no explicit reference was found in the text == Outdated reference: A later version (-12) exists of draft-ietf-httpbis-header-compression-01 Summary: 2 errors (**), 0 flaws (~~), 5 warnings (==), 1 comment (--). Run idnits with the --verbose option for more detailed information about the items above. -------------------------------------------------------------------------------- 2 Network Working Group J. Snell 3 Internet-Draft 4 Intended status: Informational July 10, 2013 5 Expires: January 11, 2014 7 HTTP/2.0 Discussion: Stored Header Encoding 8 draft-snell-httpbis-bohe-11 10 Abstract 12 This memo describes a proposed alternative encoding for headers that 13 combines the best concepts from the proposed Delta and HeaderDiff 14 options with the typed value codecs introduced by previous versions 15 of this draft. 17 Status of This Memo 19 This Internet-Draft is submitted in full conformance with the 20 provisions of BCP 78 and BCP 79. 22 Internet-Drafts are working documents of the Internet Engineering 23 Task Force (IETF). Note that other groups may also distribute 24 working documents as Internet-Drafts. The list of current Internet- 25 Drafts is at http://datatracker.ietf.org/drafts/current/. 27 Internet-Drafts are draft documents valid for a maximum of six months 28 and may be updated, replaced, or obsoleted by other documents at any 29 time. It is inappropriate to use Internet-Drafts as reference 30 material or to cite them other than as "work in progress." 32 This Internet-Draft will expire on January 11, 2014. 34 Copyright Notice 36 Copyright (c) 2013 IETF Trust and the persons identified as the 37 document authors. All rights reserved. 39 This document is subject to BCP 78 and the IETF Trust's Legal 40 Provisions Relating to IETF Documents 41 (http://trustee.ietf.org/license-info) in effect on the date of 42 publication of this document. Please review these documents 43 carefully, as they describe your rights and restrictions with respect 44 to this document. Code Components extracted from this document must 45 include Simplified BSD License text as described in Section 4.e of 46 the Trust Legal Provisions and are provided without warranty as 47 described in the Simplified BSD License. 49 Table of Contents 51 1. Stored Header Encoding . . . . . . . . . . . . . . . . . . . 2 52 2. Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 53 3. Header Encoding and Decoding . . . . . . . . . . . . . . . . 3 54 3.1. Literal (name,value) Representation . . . . . . . . . . . 5 55 3.2. Indexed Representation . . . . . . . . . . . . . . . . . 7 56 3.3. Non-Indexed Literal Representation . . . . . . . . . . . 7 57 3.4. Indexed Literal Representation . . . . . . . . . . . . . 7 58 3.5. Indexed Literal Replacement Representation . . . . . . . 8 59 4. Unsigned Variable Length Integer Syntax . . . . . . . . . . . 8 60 5. Security Considerations . . . . . . . . . . . . . . . . . . . 9 61 6. References . . . . . . . . . . . . . . . . . . . . . . . . . 9 62 6.1. Normative References . . . . . . . . . . . . . . . . . . 9 63 6.2. Informational References . . . . . . . . . . . . . . . . 9 64 Appendix A. Initial Header Table Entries . . . . . . . . . . . . 9 65 Appendix B. Updated Standard Header Definitions . . . . . . . . 11 66 Appendix C. Example . . . . . . . . . . . . . . . . . . . . . . 13 67 C.1. First Header Set: . . . . . . . . . . . . . . . . . . . . 13 68 C.2. Second Header Set: . . . . . . . . . . . . . . . . . . . 14 69 C.3. Third Header Set: . . . . . . . . . . . . . . . . . . . . 15 70 Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 15 72 1. Stored Header Encoding 74 The Stored Header Encoding is an alternative "binary header encoding" 75 for HTTP/2.0 that combines the best elements from three other 76 proposed encodings, including: 78 o The "Header Delta Compression" scheme proposed by Roberto Peon in 79 http://tools.ietf.org/html/draft-rpeon-httpbis-header- 80 compression-03 82 o The "Header Diff" encoding proposed by Herve Reullan, Jun 83 Fujisawa, Romain Bellessort, and Youenn Fablet in http:// 84 tools.ietf.org/html/draft-ruellan-headerdiff-00 86 o The "Binary Optimized Header Encoding" proposed by James Snell 87 (me) in http://tools.ietf.org/html/draft-snell-httpbis-bohe-03 89 The Stored Header Encoding seeks to find an elegant, efficient and 90 simple marriage of the best concepts from each of these separate 91 proposals. 93 2. Model 94 A "header" is a (name,value) pair. The name is a sequence of lower- 95 case ASCII characters. The value is either a UTF-8 string, an 96 integer, a Date-Time, or an arbitrary sequence of binary octets. 98 The compressor and decompressor each maintain a synchronized cache of 99 up to 256 headers. Every header in the cache is referenced by an 100 8-bit identifier. Note that the Nil byte (0x00) is a valid 101 identifier. 103 The cache is managed in a "least recently written" style, that is, as 104 the cache fills to capacity in both number of entries and maximum 105 stored byte size, the least recently written items are cleared and 106 their index positions are reused. 108 Index positions from the cache are assigned in "encounter order", 109 beginning from 0x00 and increasing monotonically to 0xFF. That is to 110 say, the positions are assigned in precisely the same order that they 111 are serialized, and thereby encountered by the decompressor upon 112 reading and processing the block. 114 The available size of the stored compression state can be capped by 115 the decompressor using the SETTINGS_MAX_BUFFER_SIZE setting. Each 116 stored header contributes to the accumulated size of the storage 117 state. As new haeder pairs are assigned positions in the cache, the 118 least-recently assigned items must be cleared, if necessary, to free 119 up the required space. Clearing existing items does not change the 120 index positions of the other items in the cache. 122 The size of a header is calculated as: The number of ASCII octets 123 required for the name plus the number of octets required for the 124 value plus 32-bytes to account for any internal storage overhead. 125 The number of octets required for the value depends on the value 126 type: 128 o String values are measured by the number of UTF-8 encoded octets 129 required to represent the character sequence. 131 o Number and Date-Time values are measured by the number of unsigned 132 variable length integer (uvarint) encoded bytes required to 133 represent the value using a 5-bit prefix. 135 o Binary values are measured by the number of octets contained by 136 the sequence. 138 3. Header Encoding and Decoding 140 The set of headers is encoded for transmission using the following 141 process: 143 1. For each header, determine if the (name,value) pair already 144 exists in the table. 146 * If an exact match is found in the header table, encode the 147 indexed position of the header as an Indexed Reference and 148 advance to the next header (name,value) pair. 150 * Otherwise, move to the step #2. 152 2. Determine if a header (name,value) pair with the same name 153 already exists in the table. If a matching name is found, make 154 note of the indexed position of the matching name and continue to 155 step #3. 157 3. Determine whether the new header (name,value) pair ought to be 158 added to the header table or not. 160 * If the header is not to be added to the header table, encode 161 the header as a Non-Indexed Literal Representation and 162 continue to the next header (name,value) pair. 164 * Otherwise, continue to step #4. 166 4. If an existing indexed header using the same name was found in 167 the header table in step #2, determine if the new header 168 (name,value) pair ought to replace that existing entry or if it 169 ought to be added as a new entry. 171 * If the header is to replace the existing entry, encode the 172 header as an Indexed Literal Replacement Representation. 174 * Otherwise encode the header as an Indexed Literal 175 Representation. 177 Following these steps, headers are serialized into one of four 178 representation types, each represented by a two-bit prefix code. The 179 types and their codes are: 181 o Indexed - 10 183 o Non-Indexed Literal - 00 185 o Indexed Literal Replacement - 01 187 o Indexed Literal - 10 189 Each representation type is encoded into groups of up to 64 190 instances. Each group is prefixed by a single octet prefix. The two 191 most significant bits identify the representation type, the six least 192 significant bits specify the number of instances in the group, with 193 000000 indicating a single instance and 111111 indicating 64. 195 If a particular serialization block requires more than 64 intances of 196 a given type, then multiple instances of the group type can be 197 encoded. For instance, if a given message contains 65 Indexed 198 Representations, the encoded block would contain two separate Indexed 199 Representation groups. 201 Decoding simply reverses the encoding steps: 203 1. First initialize an empty working set of headers. 205 2. Begin iterating through each representation group: 207 * If it is an Indexed group, iterate through each index included 208 in the group, look up the corresponding (name,value) pair in 209 the header table and add that to the working set. If no 210 matching (name,value) is found, terminate and report an error. 212 * If it's a Non-Indexed Literal group, iterate through each 213 (name,value) pair included in the group and add that to the 214 working set. 216 * If it's an Indexed Literal group, iterate through each 217 (name,value) pair included in the group and add that to both 218 the header table and the working set. 220 * If it's an Indexed Literal Replacement group, iterate through 221 each (name,value) pair included in the group, replace the 222 existing entry in the header table at the identified index, 223 and add the new (name,value) to the working set. If no 224 matching (name,value) is found, terminate and report an error. 226 3. Continue with each representation group until the complete block 227 has been decoded. 229 3.1. Literal (name,value) Representation 231 The structure of an encoded (name,value) pair consists of: 233 o A 3-bit value type identifier, 235 o The name, encoded either as a literal string or as the header 236 table index position of another existing header sharing the same 237 name, and 239 o The encoded value. 241 The three most-sigificant bits of the first octet identify the value 242 type. 244 This design allows for a maximum of 7 value types, only four of which 245 are defined by this specification. The three remaining value types 246 are reserved for future use. The currently defined value types are: 248 UTF-8 Text (000) 250 Integer (001) 252 Timestamp (010) 254 Raw Binary (111) 256 If the name is encoded using an index reference to another existing 257 (name,value) pair in the header table, the remaining five least 258 significant bits of the first octet are set to zero and the next byte 259 identifies the referenced header table index position. 261 If the name is encoded as a literal string, the number of ASCII bytes 262 required to represent the name is encoded as a unsigned variable 263 length integer with a five-bit prefix, filling the 5-remaining least 264 significant bits of the first octet. 266 The encoding of the value depends on the value type. 268 UTF-8 Text: 269 First, the number of UTF-8 encoded bytes required to represent the 270 value is encoded as an unsigned variable length integer with a 271 0-bit prefix, followed by the full sequence of UTF-8 bytes. 273 Integer 274 The integer's value is encoded as an unsigned variable length 275 integer with a 0-bit prefix. Negative or fractional numbers 276 cannot be represented. 278 Timestamp 279 The timestamp is represented as the number of milliseconds 280 ellapsed since the standard Epoch (1970-01-01T00:00:00 GMT), 281 encoded as an unsigned variable length integer with a 0-bit 282 prefix. Timestamps that predate the Epoch cannot be represented. 284 Raw Binary 285 The number of octets in the sequence is encoded as an unsigned 286 variable length integer with a 0-bit prefix, followed by the full 287 sequence of octets. 289 3.2. Indexed Representation 291 The serialization of an Indexed Representation group consists of the 292 one-octet header group prefix followed by up to 64 single-octet 293 header table index references. 295 +--------+--------+--------+--------+---+ 296 |10xxxxxx| Index positions (1...64) ...| 297 +--------+--------------------------+---+ 299 For instance: 301 0x80 0x00 302 References item #0 from the header table. 304 0x81 0x00 0x01 305 References items #0 and #1 from the header table. 307 Indexed Representations do not modify the header table state in any 308 way. If an Indexed References specifies a header index that has not 309 yet been allocated or whose value has been cleared, decoding MUST 310 terminate with an error. 312 3.3. Non-Indexed Literal Representation 314 The serialization of a group of Non-Indexed Literal representations 315 consists of the one-octet header prefix followed by up to 64 Literal 316 (name,value) Representations. 318 +--------+--------+--------+--------+---+ 319 |00xxxxxx| (name,value)'s (1...64) ... 320 +--------+--------------------------+---+ 322 For instance: 324 0x00 0x01 0x61 0x01 0x62 325 Specifies a single header with name "a" and a UTF-8 String value 326 of "b" is to be handled as a Non-Indexed header (it is not added 327 to the header table). 329 3.4. Indexed Literal Representation 330 The serialization of a group of Indexed Literal representations 331 consists of the one-octet header prefix followed by up to 64 Literal 332 (name,value) Representations. 334 +--------+--------+--------+--------+---+ 335 |01xxxxxx| (name,value)'s (1...64) ... 336 +--------+--------------------------+---+ 338 For instance: 340 0x40 0x01 0x61 0x01 0x62 341 Specifies a single header with name "a" and a UTF-8 String value 342 of "b" is to be handled as an Indexed header (it will be added to 343 the header table). 345 0x40 0x21 0x61 0x03 346 Specifies a single header with name "a" and Integer value of 3 is 347 to be handled as an Indexed header (it will be added to the header 348 table). 350 3.5. Indexed Literal Replacement Representation 352 The serialization of a group of Indexed Literal representations 353 consists of the one-octet header prefix followed by up to 64 single 354 octet index references identifying an existing header table entry 355 followed by the new Literal (name,value) representation meant to 356 replace it. 358 +--------+--------+--------+--------+--------+ 359 |11xxxxxx| (INDEX | (name,value)(1...64)) ... 360 +--------+--------------------------+--------+ 362 For instance: 364 0xC0 0x03 0x01 0x61 0x01 0x62 365 Specifies that a single header with name "a" and a UTF-8 String 366 value of "b" is to replace the existing (name,value) entry in the 367 header table located at index position #3. 369 0xC0 0x03 0x21 0x61 0x03 370 Specifies that a single header with name "a" and Integer value of 371 3 is to replace the existing (name,value) entry in the header 372 table located at index position #3. 374 4. Unsigned Variable Length Integer Syntax 375 Unsigned integers are encoded as defined in 376 [I-D.ietf-httpbis-header-compression]. 378 5. Security Considerations 380 TBD 382 6. References 384 6.1. Normative References 386 [I-D.ietf-httpbis-header-compression] 387 Peon, R. and H. Ruellan, "HTTP/2.0 Header Compression", 388 draft-ietf-httpbis-header-compression-01 (work in 389 progress), July 2013. 391 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 392 Requirement Levels", BCP 14, RFC 2119, March 1997. 394 6.2. Informational References 396 [RFC6265] Barth, A., "HTTP State Management Mechanism", RFC 6265, 397 April 2011. 399 Appendix A. Initial Header Table Entries 401 +-------+-----------------------------+-------+---------+ 402 | Index | Name | Value | Type | 403 +-------+-----------------------------+-------+---------+ 404 | 0 | :scheme | http | Text | 405 | 1 | :scheme | https | Text | 406 | 2 | :host | | | 407 | 3 | :path | / | | 408 | 4 | :method | GET | Text | 409 | 5 | accept | | | 410 | 6 | accept-charset | | | 411 | 7 | accept-encoding | | | 412 | 8 | accept-language | | | 413 | 9 | cookie | | | 414 | 10 | if-modified-since | | | 415 | 11 | keep-alive | | | 416 | 12 | user-agent | | | 417 | 13 | proxy-connection | | | 418 | 14 | referer | | | 419 | 15 | accept-datetime | | | 420 | 16 | authorization | | | 421 | 17 | allow | | | 422 | 18 | cache-control | | | 423 | 19 | connection | | | 424 | 20 | content-length | | | 425 | 21 | content-md5 | | | 426 | 22 | content-type | | | 427 | 23 | date | | | 428 | 24 | expect | | | 429 | 25 | from | | | 430 | 26 | if-match | | | 431 | 27 | if-none-match | | | 432 | 28 | if-range | | | 433 | 29 | if-unmodified-since | | | 434 | 30 | max-forwards | | | 435 | 31 | pragma | | | 436 | 32 | proxy-authorization | | | 437 | 33 | range | | | 438 | 34 | te | | | 439 | 35 | upgrade | | | 440 | 36 | via | | | 441 | 37 | warning | | | 442 | 38 | :status | 200 | Integer | 443 | 39 | age | | | 444 | 40 | cache-control | | | 445 | 41 | content-length | | | 446 | 42 | content-type | | | 447 | 43 | date | | | 448 | 44 | etag | | | 449 | 45 | expires | | | 450 | 46 | last-modified | | | 451 | 47 | server | | | 452 | 48 | set-cookie | | | 453 | 49 | vary | | | 454 | 50 | via | | | 455 | 51 | access-control-allow-origin | | | 456 | 52 | accept-ranges | | | 457 | 53 | allow | | | 458 | 54 | connection | | | 459 | 55 | content-disposition | | | 460 | 56 | content-encoding | | | 461 | 57 | content-language | | | 462 | 58 | content-location | | | 463 | 59 | content-md5 | | | 464 | 60 | content-range | | | 465 | 61 | link | | | 466 | 62 | location | | | 467 | 63 | p3p | | | 468 | 64 | pragma | | | 469 | 65 | proxy-authenticate | | | 470 | 66 | refresh | | | 471 | 67 | retry-after | | | 472 | 68 | strict-transport-security | | | 473 | 69 | trailer | | | 474 | 70 | transfer-encoding | | | 475 | 71 | warning | | | 476 | 72 | www-authenticate | | | 477 | 73 | user-agent | | | 478 +-------+-----------------------------+-------+---------+ 480 Appendix B. Updated Standard Header Definitions 482 In order to properly deal with the backwards compatibility concerns 483 for HTTP/1, there are several important rules for use of Typed Codecs 484 in HTTP headers: 486 o All header fields MUST be explicitly defined to use the new header 487 types. All existing HTTP/1 header fields, then, will continue to 488 be represented as ISO-8859-1 Text unless their standard 489 definitions are updated. The HTTP/2 specification would update 490 the definition of specific known header fields (e.g. content- 491 length, date, if-modified-since, etc). 493 o Extension header fields that use the typed codecs will have 494 specific normative transformations to ISO-8859-1 defined. 496 * UTF-8 Text will be converted to ISO-8859-1 with extended 497 characters pct-encoded 499 * Numbers will be converted to their ASCII equivalent values. 501 * Date Times will be converted to their HTTP-Date equivalent 502 values. 504 * Binary fields will be Base64-encoded. 506 o There will be no normative transformation from ISO-8859-1 values 507 into the typed codecs. Implementations are free to apply 508 transformation where those impls determine it is appropriate, but 509 it will be perfectly legal for an implementation to pass a text 510 value through even if it is known that a given header type has a 511 typed codec equivalent (for instance, Content-Length may come 512 through as a number or a text value, either will be valid). This 513 means that when translating from HTTP/1 -> HTTP/2, receiving 514 implementations need to be prepared to handle either value form. 516 A Note of warning: Individual header fields MAY be defined such that 517 they can be represented using multiple types. Numeric fields, for 518 instance, can be represented using either the uvarint encoding or 519 using the equivalent sequence of ASCII numbers. Implementers will 520 need to be capable of supporting each of the possible variations. 521 Designers of header field definitions need to be aware of the 522 additional complexity and possible issues that allowing for such 523 alternatives can introduce for implementers. 525 Based on an initial survey of header fields currently defined by the 526 HTTPbis specification documents, the following header field 527 definitions can be updated to make use of the new types 529 +---------------------+---------------+-----------------------------+ 530 | Field | Type | Description | 531 +---------------------+---------------+-----------------------------+ 532 | content-length | Numeric or | Can be represented as | 533 | | Text | either an unsigned, | 534 | | | variable-length integer or | 535 | | | a sequence of ASCII | 536 | | | numbers. | 537 | date | Timestamp or | Can be represented as | 538 | | Text | either a uvarint encoded | 539 | | | timestamp or as text (HTTP- | 540 | | | date). | 541 | max-forwards | Numeric or | Can be represented as | 542 | | Text | either an unsigned, | 543 | | | variable-length integer or | 544 | | | a sequence of ASCII | 545 | | | numbers. | 546 | retry-after | Timestamp, | Can be represented as | 547 | | Numeric or | either a uvarint encoded | 548 | | Text | timestamp, an unsigned, | 549 | | | variable-length integer, or | 550 | | | the text equivalents of | 551 | | | either (HTTP-date or | 552 | | | sequence of ASCII numbers) | 553 | if-modified-since | Timestamp or | Can be represented as | 554 | | Text | either a uvarint encoded | 555 | | | timestamp or as text (HTTP- | 556 | | | date). | 557 | if-unmodified-since | Timestamp or | Can be represented as | 558 | | Text | either a uvarint encoded | 559 | | | timestamp or as text (HTTP- | 560 | | | date). | 561 | last-modified | Timestamp or | Can be represented as | 562 | | Text | either a uvarint encoded | 563 | | | timestamp or as text (HTTP- | 564 | | | date). | 565 | age | Numeric or | Can be represented as | 566 | | Text | either an unsigned, | 567 | | | variable-length integer or | 568 | | | a sequence of ASCII | 569 | | | numbers. | 570 | expires | Timestamp or | Can be represented as | 571 | | Text | either a uvarint encoded | 572 | | | timestamp or as text (HTTP- | 573 | | | date). | 574 | etag | Binary or | Can be represented as | 575 | | Text | either a sequence of binary | 576 | | | octets or using the | 577 | | | currently defined text | 578 | | | format. When represented as | 579 | | | binary octets, the Entity | 580 | | | Tag MUST be considered to | 581 | | | be a Strong Entity tag. | 582 | | | Weak Entity Tags cannot be | 583 | | | represented using the | 584 | | | binary octet option. | 585 +---------------------+---------------+-----------------------------+ 587 Appendix C. Example 589 C.1. First Header Set: 591 The first header set to represent is the following: 593 :path: /my-example/index.html 594 user-agent: my-user-agent 595 x-my-header: first 597 The header table is prefilled as defined in Appendix A, however, none 598 of the values represented in the initial set can be found in the 599 header table. All headers, then, are encoding using the Indexed 600 Literal Representation: 602 43 00 03 16 2f 6d 79 2d 65 78 603 61 6d 70 6c 65 2f 69 6e 64 65 604 78 2e 68 74 6d 6c 00 49 6d 79 605 2d 75 73 65 72 2d 61 67 65 6e 606 74 0b 78 2d 6d 79 2d 68 65 61 607 64 65 72 05 66 69 72 73 74 609 Three new entries are added to the header table: 611 +-------+-------------+------------------------+ 612 | Index | Name | Value | 613 +-------+-------------+------------------------+ 614 | 74 | :path | /my-example/index.html | 615 | 75 | user-agent | my-user-agent | 616 | 76 | x-my-header | first | 617 +-------+-------------+------------------------+ 619 C.2. Second Header Set: 621 The second header set to represent is the following: 623 :path: /my-example/resources/script.js 624 user-agent: my-user-agent 625 x-my-header: second 627 Comparing this second header set to the first, we see that the :path 628 and x-my-header headers have new values, while the user-agent value 629 remains unchanged. For the sake of the example let's encode the 630 :path and x-my-header headers using Indexed Literal Replacement 631 representations. The user-agent header will be encoded as an Indexed 632 Representation. 634 80 4b a3 4a 00 4a 1f 2f 6d 79 635 2d 65 78 61 6d 70 6c 65 2f 72 636 65 73 6f 75 72 63 65 73 2f 73 637 63 72 69 70 74 2e 6a 73 00 4c 638 06 73 65 63 6f 6e 64 640 Items #74 and #76 added by the previous header set are replaced: 642 +-------+-------------+---------------------------------+ 643 | Index | Name | Value | 644 +-------+-------------+---------------------------------+ 645 | 74 | :path | /my-example/resources/script.js | 646 | 75 | user-agent | my-user-agent | 647 | 76 | x-my-header | second | 648 +-------+-------------+---------------------------------+ 650 C.3. Third Header Set: 652 Let's suppose a third header set that is identical to the second is 653 sent: 655 82 4b 4c 4d 657 Author's Address 659 James M Snell 661 Email: jasnell@gmail.com