decoder.d 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. module vibe.http.internal.http2.hpack.decoder;
  2. import vibe.http.internal.http2.hpack.huffman;
  3. import vibe.http.internal.http2.hpack.tables;
  4. import vibe.http.internal.http2.hpack.util;
  5. import vibe.http.internal.http2.hpack.exception;
  6. import vibe.internal.array : AllocAppender;
  7. import vibe.core.log;
  8. import std.range; // Decoder
  9. import std.string;
  10. import std.experimental.allocator;
  11. import std.experimental.allocator.mallocator;
  12. import std.exception;
  13. /** Module to implement an header decoder consistent with HPACK specifications (RFC 7541)
  14. * The detailed description of the decoding process, examples and binary format details can
  15. * be found at:
  16. * Section 3: https://tools.ietf.org/html/rfc7541#section-3
  17. * Section 6: https://tools.ietf.org/html/rfc7541#section-6
  18. * Appendix C: https://tools.ietf.org/html/rfc7541#appendix-C
  19. */
  20. alias HTTP2SettingValue = uint;
  21. void decode(I, R, T)(ref I src, ref R dst, ref IndexingTable table, ref T alloc, ulong maxTableSize=4096) @trusted
  22. {
  23. ubyte bbuf = src[0];
  24. src = src[1..$];
  25. if(bbuf & 128) {
  26. auto res = decodeInteger(src, bbuf, 7);
  27. dst.put(table[res]);
  28. } else {
  29. HTTP2HeaderTableField hres;
  30. bool update = false;
  31. auto adst = AllocAppender!string(alloc);
  32. if (bbuf & 64) { // inserted in dynamic table
  33. size_t idx = decodeInteger(src, bbuf, 6);
  34. if(idx > 0) { // name == table[index].name, value == literal
  35. hres.name = table[idx].name;
  36. } else { // name == literal, value == literal
  37. decodeLiteral(src, adst);
  38. hres.name.setReset(adst);
  39. }
  40. decodeLiteral(src, adst);
  41. hres.value.setReset(adst);
  42. hres.index = true;
  43. hres.neverIndex = false;
  44. } else if(bbuf & 32) {
  45. update = true;
  46. auto nsize = decodeInteger(src, bbuf, 3);
  47. enforce(nsize <= maxTableSize, "Invalid table size update");
  48. table.updateSize(cast(HTTP2SettingValue)nsize);
  49. logDebug("Updated dynamic table size to: %d octets", nsize);
  50. } else if(bbuf & 16) { // NEVER inserted in dynamic table
  51. size_t idx = decodeInteger(src, bbuf, 4);
  52. if(idx > 0) { // name == table[index].name, value == literal
  53. hres.name = table[idx].name;
  54. } else { // name == literal, value == literal
  55. decodeLiteral(src, adst);
  56. hres.name.setReset(adst);
  57. }
  58. decodeLiteral(src, adst);
  59. hres.value.setReset(adst);
  60. hres.index = false;
  61. hres.neverIndex = true;
  62. } else { // this occourrence is not inserted in dynamic table
  63. size_t idx = decodeInteger(src, bbuf, 4);
  64. if(idx > 0) { // name == table[index].name, value == literal
  65. hres.name = table[idx].name;
  66. } else { // name == literal, value == literal
  67. decodeLiteral(src, adst);
  68. hres.name.setReset(adst);
  69. }
  70. decodeLiteral(src, adst);
  71. hres.value.setReset(adst);
  72. hres.index = hres.neverIndex = false;
  73. }
  74. assert(!(hres.index && hres.neverIndex), "Invalid header indexing information");
  75. if(!update) dst.put(hres);
  76. }
  77. }
  78. private void setReset(I,R)(ref I dst, ref R buf)
  79. if(is(R == AllocAppender!string) || is(R == AllocAppender!(immutable(ubyte)[])))
  80. {
  81. dst = buf.data;
  82. buf.reset;
  83. }
  84. private size_t decodeInteger(I)(ref I src, ubyte bbuf, uint nbits) @safe @nogc
  85. {
  86. auto res = bbuf.toInteger(8-nbits);
  87. if (res < (1 << nbits) - 1) {
  88. return res;
  89. } else {
  90. uint m = 0;
  91. do {
  92. // take another octet
  93. bbuf = src[0];
  94. src = src[1..$];
  95. // concatenate it to the result
  96. res = res + bbuf.toInteger(1)*(1 << m);
  97. m += 7;
  98. } while((bbuf & 128) == 128);
  99. return res;
  100. }
  101. }
  102. private void decodeLiteral(I,R)(ref I src, ref R dst) @safe
  103. {
  104. enforceHPACK(!src.empty, "Invalid literal header block");
  105. ubyte bbuf = src[0];
  106. src = src[1..$];
  107. bool huffman = (bbuf & 128) ? true : false;
  108. assert(!src.empty, "Cannot decode from empty range block");
  109. // take a buffer of remaining octets
  110. auto vlen = decodeInteger(src, bbuf, 7); // value length
  111. enforceHPACK(vlen <= src.length, "Invalid literal decoded");
  112. auto buf = src[0..vlen];
  113. src = src[vlen..$];
  114. if(huffman) { // huffman encoded
  115. decodeHuffman(buf, dst);
  116. } else { // raw encoded
  117. dst.put(cast(string)buf);
  118. }
  119. }