hpack.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Copyright 2014 The Go Authors.
  2. // See https://code.google.com/p/go/source/browse/CONTRIBUTORS
  3. // Licensed under the same terms as Go itself:
  4. // https://code.google.com/p/go/source/browse/LICENSE
  5. // Package hpack implements HPACK, a compression format for
  6. // efficiently representing HTTP header fields in the context of HTTP/2.
  7. //
  8. // See http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09
  9. package hpack
  10. import "fmt"
  11. // A DecodingError is something the spec defines as a decoding error.
  12. type DecodingError struct {
  13. Err error
  14. }
  15. func (de DecodingError) Error() string {
  16. return fmt.Sprintf("decoding error: %v", de.Err)
  17. }
  18. // An InvalidIndexError is returned when an encoder references a table
  19. // entry before the static table or after the end of the dynamic table.
  20. type InvalidIndexError int
  21. func (e InvalidIndexError) Error() string {
  22. return fmt.Sprintf("invalid indexed representation index %d", int(e))
  23. }
  24. // A HeaderField is a name-value pair. Both the name and value are
  25. // treated as opaque sequences of octets.
  26. type HeaderField struct {
  27. Name, Value string
  28. }
  29. func (hf *HeaderField) size() uint32 {
  30. // http://http2.github.io/http2-spec/compression.html#rfc.section.4.1
  31. // "The size of the dynamic table is the sum of the size of
  32. // its entries. The size of an entry is the sum of its name's
  33. // length in octets (as defined in Section 5.2), its value's
  34. // length in octets (see Section 5.2), plus 32. The size of
  35. // an entry is calculated using the length of the name and
  36. // value without any Huffman encoding applied."
  37. // This can overflow if somebody makes a large HeaderField
  38. // Name and/or Value by hand, but we don't care, because that
  39. // won't happen on the wire because the encoding doesn't allow
  40. // it.
  41. return uint32(len(hf.Name) + len(hf.Value) + 32)
  42. }
  43. // A Decoder is the decoding context for incremental processing of
  44. // header blocks.
  45. type Decoder struct {
  46. dynTab dynamicTable
  47. emit func(f HeaderField, sensitive bool)
  48. }
  49. func NewDecoder(maxSize uint32, emitFunc func(f HeaderField, sensitive bool)) *Decoder {
  50. d := &Decoder{
  51. emit: emitFunc,
  52. }
  53. d.dynTab.setMaxSize(maxSize)
  54. return d
  55. }
  56. // TODO: add method *Decoder.Reset(maxSize, emitFunc) to let callers re-use Decoders and their
  57. // underlying buffers for garbage reasons.
  58. func (d *Decoder) SetMaxDynamicTableSize(v uint32) {
  59. d.dynTab.setMaxSize(v)
  60. }
  61. type dynamicTable struct {
  62. // s is the FIFO described at
  63. // http://http2.github.io/http2-spec/compression.html#rfc.section.2.3.2
  64. // The newest (low index) is append at the end, and items are
  65. // evicted from the front.
  66. ents []HeaderField
  67. size uint32
  68. maxSize uint32
  69. }
  70. func (dt *dynamicTable) setMaxSize(v uint32) {
  71. dt.maxSize = v
  72. dt.evict()
  73. }
  74. // TODO: change dynamicTable to be a struct with a slice and a size int field,
  75. // per http://http2.github.io/http2-spec/compression.html#rfc.section.4.1:
  76. //
  77. //
  78. // Then make add increment the size. maybe the max size should move from Decoder to
  79. // dynamicTable and add should return an ok bool if there was enough space.
  80. //
  81. // Later we'll need a remove operation on dynamicTable.
  82. func (dt *dynamicTable) add(f HeaderField) {
  83. dt.ents = append(dt.ents, f)
  84. dt.size += f.size()
  85. dt.evict()
  86. }
  87. // If we're too big, evict old stuff (front of the slice)
  88. func (dt *dynamicTable) evict() {
  89. base := dt.ents // keep base pointer of slice
  90. for dt.size > dt.maxSize {
  91. dt.size -= dt.ents[0].size()
  92. dt.ents = dt.ents[1:]
  93. }
  94. // Shift slice contents down if we evicted things.
  95. if len(dt.ents) != len(base) {
  96. copy(base, dt.ents)
  97. dt.ents = base[:len(dt.ents)]
  98. }
  99. }
  100. func (d *Decoder) at(i int) (hf HeaderField, ok bool) {
  101. if i < 1 {
  102. return
  103. }
  104. dents := d.dynTab.ents
  105. max := len(dents) + len(staticTable)
  106. if i > max {
  107. return
  108. }
  109. if i <= len(staticTable) {
  110. return staticTable[i-1], true
  111. }
  112. return dents[len(dents)-(i-len(staticTable))], true
  113. }
  114. // Decode decodes an entire block.
  115. //
  116. // TODO: remove this method and make it incremental later? This is
  117. // easier for debugging now.
  118. func (d *Decoder) Decode(p []byte) ([]HeaderField, error) {
  119. var hf []HeaderField
  120. // TODO: This is trashy. temporary development aid.
  121. saveFunc := d.emit
  122. defer func() { d.emit = saveFunc }()
  123. d.emit = func(f HeaderField, sensitive bool) {
  124. hf = append(hf, f)
  125. }
  126. for len(p) > 0 {
  127. // Look at first byte to see what we're dealing with.
  128. switch {
  129. case p[0]&(1<<7) != 0:
  130. // Indexed representation.
  131. // http://http2.github.io/http2-spec/compression.html#rfc.section.6.1
  132. idx := p[0] & ((1 << 7) - 1)
  133. if idx == 127 {
  134. panic("TODO: varuint decoding")
  135. }
  136. hf, ok := d.at(int(idx))
  137. if !ok {
  138. return nil, DecodingError{InvalidIndexError(idx)}
  139. }
  140. d.emit(hf, false /* TODO: sensitive ? */)
  141. p = p[1:]
  142. default:
  143. panic("TODO")
  144. }
  145. }
  146. return hf, nil
  147. }