OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/spdy/spdy_header_block.h" | 5 #include "net/spdy/spdy_header_block.h" |
6 | 6 |
| 7 #include <algorithm> |
| 8 #include <ios> |
| 9 #include <utility> |
| 10 #include <vector> |
| 11 |
| 12 #include "base/logging.h" |
7 #include "base/values.h" | 13 #include "base/values.h" |
8 #include "net/http/http_log_util.h" | 14 #include "net/http/http_log_util.h" |
9 | 15 |
| 16 using std::dec; |
| 17 using std::hex; |
| 18 using std::max; |
| 19 using std::min; |
| 20 |
10 namespace net { | 21 namespace net { |
| 22 namespace { |
| 23 |
| 24 // SpdyHeaderBlock::Storage uses a small initial block in case we only have a |
| 25 // minimal set of headers. |
| 26 const size_t kInitialStorageBlockSize = 512; |
| 27 |
| 28 // SpdyHeaderBlock::Storage allocates blocks of this size by default. |
| 29 const size_t kDefaultStorageBlockSize = 2048; |
| 30 |
| 31 // When copying a SpdyHeaderBlock, the new block will allocate at most this |
| 32 // much memory for the initial contiguous block. |
| 33 const size_t kMaxContiguousAllocation = 16 * 1024; |
| 34 |
| 35 } // namespace |
| 36 |
| 37 // This class provides a backing store for StringPieces. It uses a sequence of |
| 38 // large, contiguous blocks. It has the property that StringPieces that refer |
| 39 // to data in Storage are never invalidated until the Storage is deleted. |
| 40 // |
| 41 // Write operations always append to the last block. If there is not enough |
| 42 // space to perform the write, a new block is allocated, and any unused space |
| 43 // is wasted. |
| 44 class SpdyHeaderBlock::Storage { |
| 45 public: |
| 46 Storage() : bytes_used_(0) {} |
| 47 ~Storage() { Clear(); } |
| 48 |
| 49 void Reserve(size_t additional_space) { |
| 50 if (blocks_.empty()) { |
| 51 AllocBlock(max(additional_space, kInitialStorageBlockSize)); |
| 52 } else { |
| 53 const Block& last = blocks_.back(); |
| 54 if (last.size - last.used < additional_space) { |
| 55 AllocBlock(max(additional_space, kDefaultStorageBlockSize)); |
| 56 } |
| 57 } |
| 58 } |
| 59 |
| 60 StringPiece Write(const StringPiece s) { |
| 61 Reserve(s.size()); |
| 62 Block* last = &blocks_.back(); |
| 63 memcpy(last->data + last->used, s.data(), s.size()); |
| 64 StringPiece out(last->data + last->used, s.size()); |
| 65 VLOG(3) << "Write result: " << hex |
| 66 << reinterpret_cast<const void*>(out.data()) << ", " << dec |
| 67 << out.size(); |
| 68 last->used += s.size(); |
| 69 bytes_used_ += s.size(); |
| 70 return out; |
| 71 } |
| 72 |
| 73 void Clear() { |
| 74 while (!blocks_.empty()) { |
| 75 delete[] blocks_.back().data; |
| 76 blocks_.pop_back(); |
| 77 } |
| 78 bytes_used_ = 0; |
| 79 } |
| 80 |
| 81 size_t BytesUsed() const { return bytes_used_; } |
| 82 |
| 83 private: |
| 84 // TODO(bnc): As soon as move semantics are allowed, change from naked pointer |
| 85 // to scoped_ptr<>, or better yet, unique_ptr<>. |
| 86 struct Block { |
| 87 char* data; |
| 88 size_t size = 0; |
| 89 size_t used = 0; |
| 90 |
| 91 Block(char* data, size_t s) : data(data), size(s), used(0) {} |
| 92 }; |
| 93 |
| 94 void AllocBlock(size_t size) { |
| 95 blocks_.push_back(Block(new char[size], size)); |
| 96 } |
| 97 |
| 98 std::vector<Block> blocks_; |
| 99 size_t bytes_used_; |
| 100 |
| 101 DISALLOW_COPY_AND_ASSIGN(Storage); |
| 102 }; |
| 103 |
| 104 SpdyHeaderBlock::StringPieceProxy::StringPieceProxy( |
| 105 SpdyHeaderBlock::MapType* block, |
| 106 SpdyHeaderBlock::Storage* storage, |
| 107 SpdyHeaderBlock::MapType::iterator lookup_result, |
| 108 const StringPiece key) |
| 109 : block_(block), |
| 110 storage_(storage), |
| 111 lookup_result_(lookup_result), |
| 112 key_(key) {} |
| 113 |
| 114 SpdyHeaderBlock::StringPieceProxy::~StringPieceProxy() {} |
| 115 |
| 116 SpdyHeaderBlock::StringPieceProxy& SpdyHeaderBlock::StringPieceProxy::operator=( |
| 117 const StringPiece value) { |
| 118 if (lookup_result_ == block_->end()) { |
| 119 VLOG(1) << "Inserting: (" << key_ << ", " << value << ")"; |
| 120 lookup_result_ = |
| 121 block_->insert(std::make_pair(key_, storage_->Write(value))).first; |
| 122 } else { |
| 123 VLOG(1) << "Updating key: " << key_ << " with value: " << value; |
| 124 lookup_result_->second = storage_->Write(value); |
| 125 } |
| 126 return *this; |
| 127 } |
| 128 |
| 129 SpdyHeaderBlock::StringPieceProxy::operator StringPiece() const { |
| 130 return (lookup_result_ == block_->end()) ? StringPiece() |
| 131 : lookup_result_->second; |
| 132 } |
| 133 |
| 134 void SpdyHeaderBlock::StringPieceProxy::reserve(size_t size) { |
| 135 storage_->Reserve(size); |
| 136 } |
| 137 |
| 138 SpdyHeaderBlock::SpdyHeaderBlock() : storage_(new Storage) {} |
| 139 |
| 140 SpdyHeaderBlock::~SpdyHeaderBlock() {} |
| 141 |
| 142 SpdyHeaderBlock::SpdyHeaderBlock(const SpdyHeaderBlock& other) |
| 143 : storage_(new Storage) { |
| 144 storage_->Reserve(min(other.storage_->BytesUsed(), kMaxContiguousAllocation)); |
| 145 for (auto iter : other) { |
| 146 AppendHeader(iter.first, iter.second); |
| 147 } |
| 148 } |
| 149 |
| 150 SpdyHeaderBlock& SpdyHeaderBlock::operator=(const SpdyHeaderBlock& other) { |
| 151 clear(); |
| 152 storage_->Reserve(min(other.storage_->BytesUsed(), kMaxContiguousAllocation)); |
| 153 for (auto iter : other) { |
| 154 AppendHeader(iter.first, iter.second); |
| 155 } |
| 156 return *this; |
| 157 } |
| 158 |
| 159 void SpdyHeaderBlock::clear() { |
| 160 block_.clear(); |
| 161 storage_->Clear(); |
| 162 } |
| 163 |
| 164 void SpdyHeaderBlock::insert( |
| 165 const SpdyHeaderBlock::MapType::value_type& value) { |
| 166 ReplaceOrAppendHeader(value.first, value.second); |
| 167 } |
| 168 |
| 169 SpdyHeaderBlock::StringPieceProxy SpdyHeaderBlock::operator[]( |
| 170 const StringPiece key) { |
| 171 VLOG(2) << "Operator[] saw key: " << key; |
| 172 StringPiece out_key; |
| 173 auto iter = block_.find(key); |
| 174 if (iter == block_.end()) { |
| 175 // We write the key first, to assure that the StringPieceProxy has a |
| 176 // reference to a valid StringPiece in its operator=. |
| 177 out_key = storage_->Write(key); |
| 178 VLOG(2) << "Key written as: " << hex << static_cast<const void*>(key.data()) |
| 179 << ", " << dec << key.size(); |
| 180 } else { |
| 181 out_key = iter->first; |
| 182 } |
| 183 return StringPieceProxy(&block_, storage_.get(), iter, out_key); |
| 184 } |
| 185 |
| 186 bool SpdyHeaderBlock::operator==(const SpdyHeaderBlock& other) const { |
| 187 return size() == other.size() && std::equal(begin(), end(), other.begin()); |
| 188 } |
| 189 |
| 190 void SpdyHeaderBlock::ReplaceOrAppendHeader(const StringPiece key, |
| 191 const StringPiece value) { |
| 192 // TODO(birenroy): Write new value in place of old value, if it fits. |
| 193 auto iter = block_.find(key); |
| 194 if (iter == block_.end()) { |
| 195 VLOG(1) << "Inserting: (" << key << ", " << value << ")"; |
| 196 AppendHeader(key, value); |
| 197 } else { |
| 198 VLOG(1) << "Updating key: " << iter->first << " with value: " << value; |
| 199 iter->second = storage_->Write(value); |
| 200 } |
| 201 } |
| 202 |
| 203 void SpdyHeaderBlock::AppendHeader(const StringPiece key, |
| 204 const StringPiece value) { |
| 205 block_.insert(make_pair(storage_->Write(key), storage_->Write(value))); |
| 206 } |
11 | 207 |
12 scoped_ptr<base::Value> SpdyHeaderBlockNetLogCallback( | 208 scoped_ptr<base::Value> SpdyHeaderBlockNetLogCallback( |
13 const SpdyHeaderBlock* headers, | 209 const SpdyHeaderBlock* headers, |
14 NetLogCaptureMode capture_mode) { | 210 NetLogCaptureMode capture_mode) { |
15 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); | 211 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |
16 base::DictionaryValue* headers_dict = new base::DictionaryValue(); | 212 base::DictionaryValue* headers_dict = new base::DictionaryValue(); |
17 for (SpdyHeaderBlock::const_iterator it = headers->begin(); | 213 for (SpdyHeaderBlock::const_iterator it = headers->begin(); |
18 it != headers->end(); ++it) { | 214 it != headers->end(); ++it) { |
19 headers_dict->SetWithoutPathExpansion( | 215 headers_dict->SetWithoutPathExpansion( |
20 it->first, new base::StringValue(ElideHeaderValueForNetLog( | 216 it->first.as_string(), |
21 capture_mode, it->first, it->second))); | 217 new base::StringValue(ElideHeaderValueForNetLog( |
| 218 capture_mode, it->first.as_string(), it->second.as_string()))); |
22 } | 219 } |
23 dict->Set("headers", headers_dict); | 220 dict->Set("headers", headers_dict); |
24 return dict.Pass(); | 221 return dict.Pass(); |
25 } | 222 } |
26 | 223 |
27 bool SpdyHeaderBlockFromNetLogParam( | 224 bool SpdyHeaderBlockFromNetLogParam( |
28 const base::Value* event_param, | 225 const base::Value* event_param, |
29 SpdyHeaderBlock* headers) { | 226 SpdyHeaderBlock* headers) { |
30 headers->clear(); | 227 headers->clear(); |
31 | 228 |
32 const base::DictionaryValue* dict = NULL; | 229 const base::DictionaryValue* dict = NULL; |
33 const base::DictionaryValue* header_dict = NULL; | 230 const base::DictionaryValue* header_dict = NULL; |
34 | 231 |
35 if (!event_param || | 232 if (!event_param || |
36 !event_param->GetAsDictionary(&dict) || | 233 !event_param->GetAsDictionary(&dict) || |
37 !dict->GetDictionary("headers", &header_dict)) { | 234 !dict->GetDictionary("headers", &header_dict)) { |
38 return false; | 235 return false; |
39 } | 236 } |
40 | 237 |
41 for (base::DictionaryValue::Iterator it(*header_dict); !it.IsAtEnd(); | 238 for (base::DictionaryValue::Iterator it(*header_dict); !it.IsAtEnd(); |
42 it.Advance()) { | 239 it.Advance()) { |
43 if (!it.value().GetAsString(&(*headers)[it.key()])) { | 240 std::string value; |
| 241 if (!it.value().GetAsString(&value)) { |
44 headers->clear(); | 242 headers->clear(); |
45 return false; | 243 return false; |
46 } | 244 } |
| 245 (*headers)[it.key()] = value; |
47 } | 246 } |
48 return true; | 247 return true; |
49 } | 248 } |
50 | 249 |
51 } // namespace net | 250 } // namespace net |
OLD | NEW |