Index: net/spdy/http2_encoding_context.cc |
diff --git a/net/spdy/http2_encoding_context.cc b/net/spdy/http2_encoding_context.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..993c924ed07aaddab66f7d65542f98afc5f578f0 |
--- /dev/null |
+++ b/net/spdy/http2_encoding_context.cc |
@@ -0,0 +1,379 @@ |
+// 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_encoding_context.h" |
+ |
+#include "base/logging.h" |
+ |
+namespace net { |
+ |
+const uint8 kIndexOpcode = 0x1; |
+const uint8 kIndexN = 7; |
+ |
+const uint8 kLiteralNoIndexOpcode = 0x3; |
+const uint8 kLiteralNoIndexN= 5; |
+ |
+const uint8 kLiteralIncrementalOpcode = 0x2; |
+const uint8 kLiteralIncrementalN = 5; |
+ |
+const uint8 kLiteralSubstitutionOpcode = 0x0; |
+const uint8 kLiteralSubstitutionN = 6; |
+ |
+const uint32 kUntouched = kuint32max; |
+ |
+namespace { |
+ |
+const char* kPreDefinedRequestHeaderTable[][2] = { |
+ { ":scheme", "http" }, // 0 |
+ { ":scheme", "https" }, // 1 |
+ { ":host", "" }, // 2 |
+ { ":path", "/" }, // 3 |
+ { ":method", "GET" }, // 4 |
+ { "accept", "" }, // 5 |
+ { "accept-charset", "" }, // 6 |
+ { "accept-encoding", "" }, // 7 |
+ { "accept-language", "" }, // 8 |
+ { "cookie", "" }, // 9 |
+ { "if-modified-since", "" }, // 10 |
+ { "user-agent", "" }, // 11 |
+ { "referer", "" }, // 12 |
+ { "authorization", "" }, // 13 |
+ { "allow", "" }, // 14 |
+ { "cache-control", "" }, // 15 |
+ { "connection", "" }, // 16 |
+ { "content-length", "" }, // 17 |
+ { "content-type", "" }, // 18 |
+ { "date", "" }, // 19 |
+ { "expect", "" }, // 20 |
+ { "from", "" }, // 21 |
+ { "if-match", "" }, // 22 |
+ { "if-none-match", "" }, // 23 |
+ { "if-range", "" }, // 24 |
+ { "if-unmodified-since", "" }, // 25 |
+ { "max-forwards", "" }, // 26 |
+ { "proxy-authorization", "" }, // 27 |
+ { "range", "" }, // 28 |
+ { "via", "" } // 29 |
+}; |
+ |
+const char* kPreDefinedResponseHeaderTable[][2] = { |
+ { ":status", "200" }, // 0 |
+ { "age", "" }, // 1 |
+ { "cache-control", "" }, // 2 |
+ { "content-length", "" }, // 3 |
+ { "content-type", "" }, // 4 |
+ { "date", "" }, // 5 |
+ { "etag", "" }, // 6 |
+ { "expires", "" }, // 7 |
+ { "last-modified", "" }, // 8 |
+ { "server", "" }, // 9 |
+ { "set-cookie", "" }, // 10 |
+ { "vary", "" }, // 11 |
+ { "via", "" }, // 12 |
+ { "access-control-allow-origin", "" }, // 13 |
+ { "accept-ranges", "" }, // 14 |
+ { "allow", "" }, // 15 |
+ { "connection", "" }, // 16 |
+ { "content-disposition", "" }, // 17 |
+ { "content-encoding", "" }, // 18 |
+ { "content-language", "" }, // 19 |
+ { "content-location", "" }, // 20 |
+ { "content-range", "" }, // 21 |
+ { "link", "" }, // 22 |
+ { "location", "" }, // 23 |
+ { "proxy-authenticate", "" }, // 24 |
+ { "refresh", "" }, // 25 |
+ { "retry-after", "" }, // 26 |
+ { "strict-transport-security", "" }, // 27 |
+ { "transfer-encoding", "" }, // 28 |
+ { "www-authenticate", "" }, // 29 |
+}; |
+ |
+} // namespace |
+ |
+bool IsValidHeaderName(const std::string& str) { |
+ size_t start = (str.empty() || str[0] != ':') ? 0 : 1; |
+ |
+ if (start == str.size()) |
+ return false; |
+ |
+ for (size_t i = start; i < str.size(); ++i) { |
+ switch (str[i]) { |
+ case '!': |
+ case '#': |
+ case '$': |
+ case '%': |
+ case '&': |
+ case '\'': |
+ |
+ case '*': |
+ case '+': |
+ case '-': |
+ case '.': |
+ case '^': |
+ case '_': |
+ |
+ case '`': |
+ case '|': |
+ case '~': |
+ break; |
+ |
+ default: |
+ if (str[i] >= '0' && str[i] <= '9') |
+ break; |
+ |
+ if (str[i] >= 'a' && str[i] <= 'z') |
+ break; |
+ |
+ return false; |
+ } |
+ } |
+ |
+ return true; |
+} |
+ |
+bool IsValidHeaderValue(const std::string& str) { |
+ return true; |
+} |
+ |
+bool StringsEqualConstantTime(const std::string& str1, |
+ const std::string& str2) { |
+ size_t size = str1.size(); |
+ if (str2.size() != size) |
+ return false; |
+ |
+ uint8 x = 0; |
+ for (size_t i = 0; i < size; ++i) { |
+ x |= str1[i] ^ str2[i]; |
+ } |
+ return x == 0; |
+} |
+ |
+HeaderTableEntry::HeaderTableEntry() |
+ : referenced(false), |
+ touch_count(kUntouched) {} |
+ |
+HeaderTableEntry::HeaderTableEntry(const std::string& name, |
+ const std::string& value) |
+ : name(name), |
+ value(value), |
+ referenced(false), |
+ touch_count(kUntouched) {} |
+ |
+bool HeaderTableEntry::Equals(const HeaderTableEntry& other) const { |
+ return |
+ StringsEqualConstantTime(name, other.name) && |
+ StringsEqualConstantTime(value, other.value) && |
+ (referenced == other.referenced) && |
+ (touch_count == other.touch_count); |
+} |
+ |
+size_t HeaderTableEntry::Size() const { |
+ return name.size() + value.size() + 32; |
+} |
+ |
+HeaderTable::HeaderTable() : size_(0), max_size_(4096) {} |
+ |
+HeaderTable::~HeaderTable() {} |
+ |
+size_t HeaderTable::GetEntryCount() const { |
+ return entries_.size(); |
+} |
+ |
+void HeaderTable::SetMaxSize(size_t max_size) { |
+ max_size_ = max_size; |
+ while (size_ > max_size_) { |
+ RemoveFirstEntry(); |
+ } |
+} |
+ |
+const HeaderTableEntry* HeaderTable::GetEntry(int32 index) const { |
+ DCHECK_GE(index, 0); |
+ DCHECK_LT(static_cast<size_t>(index), entries_.size()); |
+ return &entries_[index]; |
+} |
+ |
+HeaderTableEntry* HeaderTable::GetMutableEntry(int32 index) { |
+ DCHECK_GE(index, 0); |
+ DCHECK_LT(static_cast<size_t>(index), entries_.size()); |
+ return &entries_[index]; |
+} |
+ |
+void HeaderTable::TryAppendEntry( |
+ const HeaderTableEntry& entry, |
+ int32* index, |
+ std::vector<uint32>* removed_referenced_indices) { |
+ DCHECK(IsValidHeaderName(entry.name)); |
+ DCHECK(IsValidHeaderValue(entry.value)); |
+ |
+ *index = -1; |
+ removed_referenced_indices->clear(); |
+ |
+ size_t size_delta = entry.Size(); |
+ size_t num_to_shift = 0; |
+ size_t size_after_shift = size_; |
+ while ((num_to_shift < entries_.size()) && |
+ (size_after_shift + size_delta) > max_size_) { |
+ size_after_shift -= entries_[num_to_shift].Size(); |
+ ++num_to_shift; |
+ } |
+ |
+ for (size_t i = 0; i < num_to_shift; ++i) { |
+ if (entries_[i].referenced) { |
+ removed_referenced_indices->push_back(i); |
+ } |
+ } |
+ |
+ entries_.erase(entries_.begin(), entries_.begin() + num_to_shift); |
+ size_ = size_after_shift; |
+ if ((size_ + size_delta) <= max_size_) { |
+ size_ += size_delta; |
+ *index = entries_.size(); |
+ entries_.push_back(entry); |
+ } |
+} |
+ |
+void HeaderTable::TryReplaceEntry( |
+ uint32 substituted_index, |
+ const HeaderTableEntry& entry, |
+ int32* index, |
+ std::vector<uint32>* removed_referenced_indices) { |
+ DCHECK(IsValidHeaderName(entry.name)); |
+ DCHECK(IsValidHeaderValue(entry.value)); |
+ |
+ *index = substituted_index; |
+ removed_referenced_indices->clear(); |
+ |
+ size_t size_delta = entry.Size() - GetEntry(*index)->Size(); |
+ size_t num_to_shift = 0; |
+ size_t size_after_shift = size_; |
+ while ((num_to_shift < entries_.size()) && |
+ (size_after_shift + size_delta) > max_size_) { |
+ size_after_shift -= entries_[num_to_shift].Size(); |
+ ++num_to_shift; |
+ if (num_to_shift >= static_cast<size_t>(*index)) { |
+ size_delta = entry.Size(); |
+ } |
+ } |
+ |
+ for (size_t i = 0; i < num_to_shift; ++i) { |
+ if (entries_[i].referenced) { |
+ removed_referenced_indices->push_back(i); |
+ } |
+ } |
+ |
+ entries_.erase(entries_.begin(), entries_.begin() + num_to_shift); |
+ *index -= num_to_shift; |
+ size_ = size_after_shift; |
+ if ((size_ + size_delta) <= max_size_) { |
+ size_ += size_delta; |
+ if (*index >= 0) { |
+ entries_[*index] = entry; |
+ } else { |
+ *index = 0; |
+ entries_.push_front(entry); |
+ } |
+ } else { |
+ *index = -1; |
+ } |
+} |
+ |
+void HeaderTable::RemoveFirstEntry() { |
+ DCHECK(!entries_.empty()); |
+ size_ -= entries_.front().Size(); |
+ entries_.pop_front(); |
+} |
+ |
+EncodingContext::EncodingContext(Http2Direction direction) { |
+ const char* (*initial_header_table)[2] = NULL; |
+ size_t initial_header_table_size = 0; |
+ switch (direction) { |
+ case HTTP2_REQUEST: |
+ initial_header_table = kPreDefinedRequestHeaderTable; |
+ initial_header_table_size = arraysize(kPreDefinedRequestHeaderTable); |
+ case HTTP2_RESPONSE: |
+ initial_header_table = kPreDefinedResponseHeaderTable; |
+ initial_header_table_size = arraysize(kPreDefinedResponseHeaderTable); |
+ } |
+ DCHECK(initial_header_table); |
+ DCHECK_GT(initial_header_table_size, 0u); |
+ |
+ for (size_t i = 0; i < initial_header_table_size; ++i) { |
+ int32 index = 0; |
+ std::vector<uint32> removed_referenced_indices; |
+ header_table_.TryAppendEntry( |
+ HeaderTableEntry(initial_header_table[i][0], |
+ initial_header_table[i][1]), |
+ &index, |
+ &removed_referenced_indices); |
+ DCHECK_GE(index, 0); |
+ DCHECK(removed_referenced_indices.empty()); |
+ } |
+} |
+ |
+EncodingContext::~EncodingContext() {} |
+ |
+size_t EncodingContext::GetEntryCount() const { |
+ return header_table_.GetEntryCount(); |
+} |
+ |
+bool EncodingContext::IsReferenced(uint32 index) const { |
+ return header_table_.GetEntry(index)->referenced; |
+} |
+ |
+bool EncodingContext::GetIndexedHeaderName(uint32 index, |
+ std::string* name) const { |
+ *name = header_table_.GetEntry(index)->name; |
+ return true; |
+} |
+ |
+bool EncodingContext::GetIndexedHeaderValue(uint32 index, |
+ std::string* value) const { |
+ *value = header_table_.GetEntry(index)->value; |
+ return true; |
+} |
+ |
+void EncodingContext::ProcessIndexedHeader(uint32 index) { |
+ HeaderTableEntry* entry = header_table_.GetMutableEntry(index); |
+ entry->referenced = !entry->referenced; |
+} |
+ |
+void EncodingContext::ProcessLiteralHeaderWithIncrementalIndexing( |
+ const std::string& name, |
+ const std::string& value, |
+ int32* index, |
+ std::vector<uint32>* removed_referenced_indices) { |
+ header_table_.TryAppendEntry(HeaderTableEntry(name, value), |
+ index, removed_referenced_indices); |
+ if (*index >= 0) { |
+ header_table_.GetMutableEntry(*index)->referenced = true; |
+ } |
+} |
+ |
+void EncodingContext::ProcessLiteralHeaderWithSubstitutionIndexing( |
+ const std::string& name, |
+ uint32 substituted_index, |
+ const std::string& value, |
+ int32* index, |
+ std::vector<uint32>* removed_referenced_indices) { |
+ header_table_.TryReplaceEntry(substituted_index, |
+ HeaderTableEntry(name, value), |
+ index, removed_referenced_indices); |
+ if (*index >= 0) { |
+ header_table_.GetMutableEntry(*index)->referenced = true; |
+ } |
+} |
+ |
+void EncodingContext::AddTouches(uint32 index, uint32 touch_count) { |
+ HeaderTableEntry* entry = header_table_.GetMutableEntry(index); |
+ if (entry->touch_count == kUntouched) |
+ entry->touch_count = 0; |
+ entry->touch_count += touch_count; |
+} |
+ |
+HeaderTableEntry* EncodingContext::GetMutableEntry(int32 index) { |
+ return header_table_.GetMutableEntry(index); |
+} |
+ |
+} // namespace net |