| Index: net/spdy/http2_decompressor.cc
|
| diff --git a/net/spdy/http2_decompressor.cc b/net/spdy/http2_decompressor.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c431f6e878ad074d3e8a7cbef3e8f24482876731
|
| --- /dev/null
|
| +++ b/net/spdy/http2_decompressor.cc
|
| @@ -0,0 +1,272 @@
|
| +// Copyright (c) 2013 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/http2_decompressor.h"
|
| +
|
| +#include <cstddef>
|
| +
|
| +#include "base/logging.h"
|
| +
|
| +namespace net {
|
| +
|
| +namespace {
|
| +
|
| +class Http2Decoder {
|
| + public:
|
| + Http2Decoder(const char* buffer,
|
| + size_t buffer_size,
|
| + EncodingContext* encoding_context,
|
| + SpdyNameValueBlock* out);
|
| +
|
| + bool HasMoreData() const;
|
| +
|
| + void EmitHeader(const std::string& name, const std::string& value);
|
| +
|
| + bool ProcessNextHeaderRepresentation();
|
| +
|
| + private:
|
| + bool PeekNextOctet(uint8* next_octet);
|
| +
|
| + bool DecodeNextOctet(uint8* next_octet);
|
| +
|
| + bool DecodeNextInteger(uint8 N, uint32* I);
|
| +
|
| + bool DecodeNextOctetSequence(std::string* str);
|
| +
|
| + bool DecodeNextName(uint8 N, std::string* next_name);
|
| +
|
| + bool DecodeNextValue(std::string* next_value);
|
| +
|
| + const char* const buffer_;
|
| + const size_t buffer_size_;
|
| + EncodingContext* const encoding_context_;
|
| + SpdyNameValueBlock* out_;
|
| + size_t i_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(Http2Decoder);
|
| +};
|
| +
|
| +Http2Decoder::Http2Decoder(const char* buffer,
|
| + size_t buffer_size,
|
| + EncodingContext* encoding_context,
|
| + SpdyNameValueBlock* out)
|
| + : buffer_(buffer),
|
| + buffer_size_(buffer_size),
|
| + encoding_context_(encoding_context),
|
| + out_(out),
|
| + i_(0) {}
|
| +
|
| +bool Http2Decoder::HasMoreData() const {
|
| + return i_ < buffer_size_;
|
| +}
|
| +
|
| +void Http2Decoder::EmitHeader(const std::string& name,
|
| + const std::string& value) {
|
| + VLOG(1) << "Emitting " << name << " " << value;
|
| + if (out_->find(name) == out_->end()) {
|
| + (*out_)[name] = value;
|
| + } else {
|
| + std::string new_value = (*out_)[name];
|
| + new_value.append(1, '\0'); // +=() doesn't append 0's
|
| + new_value += value;
|
| + (*out_)[name] = new_value;
|
| + }
|
| +}
|
| +
|
| +bool Http2Decoder::ProcessNextHeaderRepresentation() {
|
| + uint8 next_octet = 0;
|
| + if (!PeekNextOctet(&next_octet))
|
| + return false;
|
| +
|
| + if ((next_octet >> kIndexN) == kIndexOpcode) {
|
| + uint32 index = 0;
|
| + DecodeNextInteger(7, &index);
|
| + encoding_context_->ProcessIndexedHeader(index);
|
| + if (!encoding_context_->IsReferenced(index)) {
|
| + return true;
|
| + }
|
| + encoding_context_->AddTouches(index, 0);
|
| + std::string name;
|
| + if (!encoding_context_->GetIndexedHeaderName(index, &name)) {
|
| + return false;
|
| + }
|
| + std::string value;
|
| + if (!encoding_context_->GetIndexedHeaderValue(index, &value)) {
|
| + return false;
|
| + }
|
| + EmitHeader(name, value);
|
| + return true;
|
| + }
|
| +
|
| + if ((next_octet >> kLiteralNoIndexN) == kLiteralNoIndexOpcode) {
|
| + std::string name;
|
| + if (!DecodeNextName(kLiteralNoIndexN, &name))
|
| + return false;
|
| + std::string value;
|
| + if (!DecodeNextValue(&value))
|
| + return false;
|
| + EmitHeader(name, value);
|
| + return true;
|
| + }
|
| +
|
| + if ((next_octet >> kLiteralIncrementalN) == kLiteralIncrementalOpcode) {
|
| + std::string name;
|
| + if (!DecodeNextName(kLiteralIncrementalN, &name))
|
| + return false;
|
| + std::string value;
|
| + if (!DecodeNextValue(&value))
|
| + return false;
|
| + int32 index = 0;
|
| + std::vector<uint32> removed_referenced_indices;
|
| + encoding_context_->ProcessLiteralHeaderWithIncrementalIndexing(
|
| + name, value, &index, &removed_referenced_indices);
|
| + if (index >= 0) {
|
| + encoding_context_->AddTouches(index, 0);
|
| + }
|
| + EmitHeader(name, value);
|
| + return true;
|
| + }
|
| +
|
| + if ((next_octet >> kLiteralSubstitutionN) == kLiteralSubstitutionOpcode) {
|
| + std::string name;
|
| + if (!DecodeNextName(kLiteralSubstitutionN, &name))
|
| + return false;
|
| + uint32 substituted_index = 0;
|
| + if (!DecodeNextInteger(0, &substituted_index))
|
| + return false;
|
| + std::string value;
|
| + if (!DecodeNextValue(&value))
|
| + return false;
|
| + int32 index = 0;
|
| + std::vector<uint32> removed_referenced_indices;
|
| + encoding_context_->ProcessLiteralHeaderWithSubstitutionIndexing(
|
| + name, substituted_index, value, &index, &removed_referenced_indices);
|
| + if (index >= 0) {
|
| + encoding_context_->AddTouches(index, 0);
|
| + }
|
| + EmitHeader(name, value);
|
| + return true;
|
| + }
|
| +
|
| + LOG(ERROR) << "Invalid opcode 0x" << std::hex << static_cast<int>(next_octet);
|
| + return false;
|
| +}
|
| +
|
| +bool Http2Decoder::PeekNextOctet(uint8* next_octet) {
|
| + if (!HasMoreData()) {
|
| + LOG(ERROR) << "HasMoreData returned false in PeekNextOctet";
|
| + return false;
|
| + }
|
| +
|
| + *next_octet = buffer_[i_];
|
| + return true;
|
| +}
|
| +
|
| +bool Http2Decoder::DecodeNextOctet(uint8* next_octet) {
|
| + if (!PeekNextOctet(next_octet))
|
| + return false;
|
| +
|
| + ++i_;
|
| + return true;
|
| +}
|
| +
|
| +bool Http2Decoder::DecodeNextInteger(uint8 N, uint32* I) {
|
| + *I = 0;
|
| + bool has_more = true;
|
| + uint8 shift = N;
|
| +
|
| + if (N > 0) {
|
| + uint8 next_marker = (1 << N) - 1;
|
| + uint8 next_octet = 0;
|
| + if (!DecodeNextOctet(&next_octet))
|
| + return false;
|
| + *I = next_octet & next_marker;
|
| + has_more = (*I == next_marker);
|
| + }
|
| +
|
| + while (has_more) {
|
| + uint8 next_octet = 0;
|
| + if (!DecodeNextOctet(&next_octet))
|
| + return false;
|
| + has_more = (next_octet & 0x80) != 0;
|
| + *I += (next_octet % 128) << shift;
|
| + shift += 7;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool Http2Decoder::DecodeNextOctetSequence(std::string* str) {
|
| + uint32 size = 0;
|
| + if (!DecodeNextInteger(0, &size)) {
|
| + return false;
|
| + }
|
| + if ((i_ + size) > buffer_size_) {
|
| + LOG(ERROR) << "Invalid size " << size;
|
| + return false;
|
| + }
|
| + str->assign(buffer_ + i_, size);
|
| + i_ += size;
|
| + return true;
|
| +}
|
| +
|
| +bool Http2Decoder::DecodeNextName(uint8 N, std::string* next_name) {
|
| + uint32 index_plus_one_or_zero = 0;
|
| + if (!DecodeNextInteger(N, &index_plus_one_or_zero))
|
| + return false;
|
| + if (index_plus_one_or_zero == 0) {
|
| + if (!DecodeNextOctetSequence(next_name))
|
| + return false;
|
| + } else {
|
| + uint32 index = index_plus_one_or_zero - 1;
|
| + if (!encoding_context_->GetIndexedHeaderName(index, next_name)) {
|
| + LOG(ERROR) << "Invalid index " << index;
|
| + return false;
|
| + }
|
| + }
|
| + if (!IsValidHeaderName(*next_name)) {
|
| + LOG(ERROR) << "Invalid name " << *next_name;
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Http2Decoder::DecodeNextValue(std::string* next_value) {
|
| + if (!DecodeNextOctetSequence(next_value))
|
| + return false;
|
| + if (!IsValidHeaderValue(*next_value)) {
|
| + LOG(ERROR) << "Invalid value " << *next_value;
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +Http2Decompressor::Http2Decompressor() : encoding_context_(HTTP2_REQUEST) {}
|
| +
|
| +Http2Decompressor::~Http2Decompressor() {}
|
| +
|
| +bool Http2Decompressor::DecodeNameValueBlock(const char* data, size_t len,
|
| + SpdyNameValueBlock* out) {
|
| + Http2Decoder decoder(data, len, &encoding_context_, out);
|
| + while (decoder.HasMoreData()) {
|
| + if (!decoder.ProcessNextHeaderRepresentation()) {
|
| + LOG(INFO) << "Error processing next header representation";
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + for (size_t i = 0; i < encoding_context_.GetEntryCount(); ++i) {
|
| + HeaderTableEntry* entry = encoding_context_.GetMutableEntry(i);
|
| + if (entry->referenced && (entry->touch_count == kUntouched)) {
|
| + decoder.EmitHeader(entry->name, entry->value);
|
| + }
|
| + entry->touch_count = kUntouched;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +} // namespace net
|
|
|