diff options
Diffstat (limited to 'net/spdy/hpack/hpack_encoder.cc')
-rw-r--r-- | net/spdy/hpack/hpack_encoder.cc | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/net/spdy/hpack/hpack_encoder.cc b/net/spdy/hpack/hpack_encoder.cc new file mode 100644 index 0000000..4509127 --- /dev/null +++ b/net/spdy/hpack/hpack_encoder.cc @@ -0,0 +1,197 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/spdy/hpack/hpack_encoder.h" + +#include <algorithm> + +#include "base/logging.h" +#include "net/spdy/hpack/hpack_header_table.h" +#include "net/spdy/hpack/hpack_huffman_table.h" +#include "net/spdy/hpack/hpack_output_stream.h" + +namespace net { + +using base::StringPiece; +using std::string; + +HpackEncoder::HpackEncoder(const HpackHuffmanTable& table) + : output_stream_(), + allow_huffman_compression_(true), + huffman_table_(table), + char_counts_(NULL), + total_char_counts_(NULL) {} + +HpackEncoder::~HpackEncoder() {} + +bool HpackEncoder::EncodeHeaderSet(const SpdyHeaderBlock& header_set, + string* output) { + // Separate header set into pseudo-headers and regular headers. + Representations pseudo_headers; + Representations regular_headers; + for (const auto& header : header_set) { + if (header.first == "cookie") { + // Note that there can only be one "cookie" header, because header_set is + // a map. + CookieToCrumbs(header, ®ular_headers); + } else if (header.first[0] == kPseudoHeaderPrefix) { + DecomposeRepresentation(header, &pseudo_headers); + } else { + DecomposeRepresentation(header, ®ular_headers); + } + } + + // Encode pseudo-headers. + for (const auto& header : pseudo_headers) { + const HpackEntry* entry = + header_table_.GetByNameAndValue(header.first, header.second); + if (entry != NULL) { + EmitIndex(entry); + } else { + if (header.first == ":authority") { + // :authority is always present and rarely changes, and has moderate + // length, therefore it makes a lot of sense to index (insert in the + // header table). + EmitIndexedLiteral(header); + } else { + // Most common pseudo-header fields are represented in the static table, + // while uncommon ones are small, so do not index them. + EmitNonIndexedLiteral(header); + } + } + } + + // Encode regular headers. + for (const auto& header : regular_headers) { + const HpackEntry* entry = + header_table_.GetByNameAndValue(header.first, header.second); + if (entry != NULL) { + EmitIndex(entry); + } else { + EmitIndexedLiteral(header); + } + } + + output_stream_.TakeString(output); + return true; +} + +bool HpackEncoder::EncodeHeaderSetWithoutCompression( + const SpdyHeaderBlock& header_set, + string* output) { + allow_huffman_compression_ = false; + for (const auto& header : header_set) { + // Note that cookies are not crumbled in this case. + EmitNonIndexedLiteral(header); + } + allow_huffman_compression_ = true; + output_stream_.TakeString(output); + return true; +} + +void HpackEncoder::EmitIndex(const HpackEntry* entry) { + output_stream_.AppendPrefix(kIndexedOpcode); + output_stream_.AppendUint32(header_table_.IndexOf(entry)); +} + +void HpackEncoder::EmitIndexedLiteral(const Representation& representation) { + output_stream_.AppendPrefix(kLiteralIncrementalIndexOpcode); + EmitLiteral(representation); + header_table_.TryAddEntry(representation.first, representation.second); +} + +void HpackEncoder::EmitNonIndexedLiteral(const Representation& representation) { + output_stream_.AppendPrefix(kLiteralNoIndexOpcode); + output_stream_.AppendUint32(0); + EmitString(representation.first); + EmitString(representation.second); +} + +void HpackEncoder::EmitLiteral(const Representation& representation) { + const HpackEntry* name_entry = header_table_.GetByName(representation.first); + if (name_entry != NULL) { + output_stream_.AppendUint32(header_table_.IndexOf(name_entry)); + } else { + output_stream_.AppendUint32(0); + EmitString(representation.first); + } + EmitString(representation.second); +} + +void HpackEncoder::EmitString(StringPiece str) { + size_t encoded_size = + (!allow_huffman_compression_ ? str.size() + : huffman_table_.EncodedSize(str)); + if (encoded_size < str.size()) { + output_stream_.AppendPrefix(kStringLiteralHuffmanEncoded); + output_stream_.AppendUint32(encoded_size); + huffman_table_.EncodeString(str, &output_stream_); + } else { + output_stream_.AppendPrefix(kStringLiteralIdentityEncoded); + output_stream_.AppendUint32(str.size()); + output_stream_.AppendBytes(str); + } + UpdateCharacterCounts(str); +} + +void HpackEncoder::SetCharCountsStorage(std::vector<size_t>* char_counts, + size_t* total_char_counts) { + CHECK_LE(256u, char_counts->size()); + char_counts_ = char_counts; + total_char_counts_ = total_char_counts; +} + +void HpackEncoder::UpdateCharacterCounts(base::StringPiece str) { + if (char_counts_ == NULL || total_char_counts_ == NULL) { + return; + } + for (StringPiece::const_iterator it = str.begin(); it != str.end(); ++it) { + ++(*char_counts_)[static_cast<uint8>(*it)]; + } + (*total_char_counts_) += str.size(); +} + +// static +void HpackEncoder::CookieToCrumbs(const Representation& cookie, + Representations* out) { + size_t prior_size = out->size(); + + // See Section 8.1.2.5. "Compressing the Cookie Header Field" in the HTTP/2 + // specification at https://tools.ietf.org/html/draft-ietf-httpbis-http2-14. + // Cookie values are split into individually-encoded HPACK representations. + for (size_t pos = 0;;) { + size_t end = cookie.second.find(";", pos); + + if (end == StringPiece::npos) { + out->push_back(std::make_pair(cookie.first, cookie.second.substr(pos))); + break; + } + out->push_back( + std::make_pair(cookie.first, cookie.second.substr(pos, end - pos))); + + // Consume next space if present. + pos = end + 1; + if (pos != cookie.second.size() && cookie.second[pos] == ' ') { + pos++; + } + } + // Sort crumbs and remove duplicates. + std::sort(out->begin() + prior_size, out->end()); + out->erase(std::unique(out->begin() + prior_size, out->end()), out->end()); +} + +// static +void HpackEncoder::DecomposeRepresentation(const Representation& header_field, + Representations* out) { + size_t pos = 0; + size_t end = 0; + while (end != StringPiece::npos) { + end = header_field.second.find('\0', pos); + out->push_back(std::make_pair(header_field.first, + header_field.second.substr(pos, end - pos))); + pos = end + 1; + } +} + +} // namespace net |