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