QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 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 |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h" |
| 6 | |
| 7 | #include <utility> |
| 8 | |
| 9 | #include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" |
| 10 | #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" |
| 11 | #include "net/third_party/quiche/src/quic/platform/api/quic_file_utils.h" |
| 12 | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" |
| 13 | #include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 14 | #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" |
| 15 | #include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 16 | |
| 17 | using spdy::kV3LowestPriority; |
| 18 | using spdy::SpdyHeaderBlock; |
| 19 | |
| 20 | namespace quic { |
| 21 | |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 22 | QuicMemoryCacheBackend::ResourceFile::ResourceFile(const std::string& file_name) |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 23 | : file_name_(file_name) {} |
| 24 | |
| 25 | QuicMemoryCacheBackend::ResourceFile::~ResourceFile() = default; |
| 26 | |
| 27 | void QuicMemoryCacheBackend::ResourceFile::Read() { |
| 28 | ReadFileContents(file_name_, &file_contents_); |
| 29 | |
| 30 | // First read the headers. |
| 31 | size_t start = 0; |
| 32 | while (start < file_contents_.length()) { |
| 33 | size_t pos = file_contents_.find("\n", start); |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 34 | if (pos == std::string::npos) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 35 | QUIC_LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file_name_; |
| 36 | return; |
| 37 | } |
| 38 | size_t len = pos - start; |
| 39 | // Support both dos and unix line endings for convenience. |
| 40 | if (file_contents_[pos - 1] == '\r') { |
| 41 | len -= 1; |
| 42 | } |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 43 | quiche::QuicheStringPiece line(file_contents_.data() + start, len); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 44 | start = pos + 1; |
| 45 | // Headers end with an empty line. |
| 46 | if (line.empty()) { |
| 47 | break; |
| 48 | } |
| 49 | // Extract the status from the HTTP first line. |
| 50 | if (line.substr(0, 4) == "HTTP") { |
| 51 | pos = line.find(" "); |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 52 | if (pos == std::string::npos) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 53 | QUIC_LOG(DFATAL) << "Headers invalid or empty, ignoring: " |
| 54 | << file_name_; |
| 55 | return; |
| 56 | } |
| 57 | spdy_headers_[":status"] = line.substr(pos + 1, 3); |
| 58 | continue; |
| 59 | } |
| 60 | // Headers are "key: value". |
| 61 | pos = line.find(": "); |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 62 | if (pos == std::string::npos) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 63 | QUIC_LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file_name_; |
| 64 | return; |
| 65 | } |
| 66 | spdy_headers_.AppendValueOrAddHeader( |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 67 | quiche::QuicheTextUtils::ToLower(line.substr(0, pos)), |
| 68 | line.substr(pos + 2)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 69 | } |
| 70 | |
| 71 | // The connection header is prohibited in HTTP/2. |
| 72 | spdy_headers_.erase("connection"); |
| 73 | |
| 74 | // Override the URL with the X-Original-Url header, if present. |
| 75 | auto it = spdy_headers_.find("x-original-url"); |
| 76 | if (it != spdy_headers_.end()) { |
| 77 | x_original_url_ = it->second; |
| 78 | HandleXOriginalUrl(); |
| 79 | } |
| 80 | |
| 81 | // X-Push-URL header is a relatively quick way to support sever push |
| 82 | // in the toy server. A production server should use link=preload |
| 83 | // stuff as described in https://w3c.github.io/preload/. |
| 84 | it = spdy_headers_.find("x-push-url"); |
| 85 | if (it != spdy_headers_.end()) { |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 86 | quiche::QuicheStringPiece push_urls = it->second; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 87 | size_t start = 0; |
| 88 | while (start < push_urls.length()) { |
| 89 | size_t pos = push_urls.find('\0', start); |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 90 | if (pos == std::string::npos) { |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 91 | push_urls_.push_back(quiche::QuicheStringPiece( |
| 92 | push_urls.data() + start, push_urls.length() - start)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 93 | break; |
| 94 | } |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 95 | push_urls_.push_back( |
| 96 | quiche::QuicheStringPiece(push_urls.data() + start, pos)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 97 | start += pos + 1; |
| 98 | } |
| 99 | } |
| 100 | |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 101 | body_ = quiche::QuicheStringPiece(file_contents_.data() + start, |
| 102 | file_contents_.size() - start); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 103 | } |
| 104 | |
| 105 | void QuicMemoryCacheBackend::ResourceFile::SetHostPathFromBase( |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 106 | quiche::QuicheStringPiece base) { |
rch | 3706b23 | 2019-12-12 21:40:54 -0800 | [diff] [blame] | 107 | DCHECK(base[0] != '/') << base; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 108 | size_t path_start = base.find_first_of('/'); |
rch | f0ff493 | 2019-12-16 16:25:56 -0800 | [diff] [blame] | 109 | if (path_start == quiche::QuicheStringPiece::npos) { |
| 110 | host_ = std::string(base); |
| 111 | path_ = ""; |
| 112 | return; |
| 113 | } |
| 114 | |
rch | 3706b23 | 2019-12-12 21:40:54 -0800 | [diff] [blame] | 115 | host_ = std::string(base.substr(0, path_start)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 116 | size_t query_start = base.find_first_of(','); |
| 117 | if (query_start > 0) { |
rch | 3706b23 | 2019-12-12 21:40:54 -0800 | [diff] [blame] | 118 | path_ = std::string(base.substr(path_start, query_start - 1)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 119 | } else { |
rch | 3706b23 | 2019-12-12 21:40:54 -0800 | [diff] [blame] | 120 | path_ = std::string(base.substr(path_start)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 121 | } |
| 122 | } |
| 123 | |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 124 | quiche::QuicheStringPiece QuicMemoryCacheBackend::ResourceFile::RemoveScheme( |
| 125 | quiche::QuicheStringPiece url) { |
| 126 | if (quiche::QuicheTextUtils::StartsWith(url, "https://")) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 127 | url.remove_prefix(8); |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 128 | } else if (quiche::QuicheTextUtils::StartsWith(url, "http://")) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 129 | url.remove_prefix(7); |
| 130 | } |
| 131 | return url; |
| 132 | } |
| 133 | |
| 134 | void QuicMemoryCacheBackend::ResourceFile::HandleXOriginalUrl() { |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 135 | quiche::QuicheStringPiece url(x_original_url_); |
rch | 3706b23 | 2019-12-12 21:40:54 -0800 | [diff] [blame] | 136 | SetHostPathFromBase(RemoveScheme(url)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 137 | } |
| 138 | |
| 139 | const QuicBackendResponse* QuicMemoryCacheBackend::GetResponse( |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 140 | quiche::QuicheStringPiece host, |
| 141 | quiche::QuicheStringPiece path) const { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 142 | QuicWriterMutexLock lock(&response_mutex_); |
| 143 | |
| 144 | auto it = responses_.find(GetKey(host, path)); |
| 145 | if (it == responses_.end()) { |
rch | 7bd5476 | 2019-10-15 10:53:24 -0700 | [diff] [blame] | 146 | uint64_t ignored = 0; |
| 147 | if (generate_bytes_response_) { |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 148 | if (quiche::QuicheTextUtils::StringToUint64( |
| 149 | quiche::QuicheStringPiece(path.data() + 1, path.size() - 1), |
| 150 | &ignored)) { |
rch | 7bd5476 | 2019-10-15 10:53:24 -0700 | [diff] [blame] | 151 | // The actual parsed length is ignored here and will be recomputed |
| 152 | // by the caller. |
| 153 | return generate_bytes_response_.get(); |
| 154 | } |
| 155 | } |
bnc | 5de8705 | 2019-05-03 14:21:53 -0700 | [diff] [blame] | 156 | QUIC_DVLOG(1) << "Get response for resource failed: host " << host |
| 157 | << " path " << path; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 158 | if (default_response_) { |
| 159 | return default_response_.get(); |
| 160 | } |
| 161 | return nullptr; |
| 162 | } |
| 163 | return it->second.get(); |
| 164 | } |
| 165 | |
| 166 | typedef QuicBackendResponse::ServerPushInfo ServerPushInfo; |
| 167 | typedef QuicBackendResponse::SpecialResponseType SpecialResponseType; |
| 168 | |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 169 | void QuicMemoryCacheBackend::AddSimpleResponse(quiche::QuicheStringPiece host, |
| 170 | quiche::QuicheStringPiece path, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 171 | int response_code, |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 172 | quiche::QuicheStringPiece body) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 173 | SpdyHeaderBlock response_headers; |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 174 | response_headers[":status"] = |
| 175 | quiche::QuicheTextUtils::Uint64ToString(response_code); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 176 | response_headers["content-length"] = |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 177 | quiche::QuicheTextUtils::Uint64ToString(body.length()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 178 | AddResponse(host, path, std::move(response_headers), body); |
| 179 | } |
| 180 | |
| 181 | void QuicMemoryCacheBackend::AddSimpleResponseWithServerPushResources( |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 182 | quiche::QuicheStringPiece host, |
| 183 | quiche::QuicheStringPiece path, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 184 | int response_code, |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 185 | quiche::QuicheStringPiece body, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 186 | std::list<ServerPushInfo> push_resources) { |
| 187 | AddSimpleResponse(host, path, response_code, body); |
| 188 | MaybeAddServerPushResources(host, path, push_resources); |
| 189 | } |
| 190 | |
| 191 | void QuicMemoryCacheBackend::AddDefaultResponse(QuicBackendResponse* response) { |
| 192 | QuicWriterMutexLock lock(&response_mutex_); |
| 193 | default_response_.reset(response); |
| 194 | } |
| 195 | |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 196 | void QuicMemoryCacheBackend::AddResponse( |
| 197 | quiche::QuicheStringPiece host, |
| 198 | quiche::QuicheStringPiece path, |
| 199 | SpdyHeaderBlock response_headers, |
| 200 | quiche::QuicheStringPiece response_body) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 201 | AddResponseImpl(host, path, QuicBackendResponse::REGULAR_RESPONSE, |
| 202 | std::move(response_headers), response_body, SpdyHeaderBlock(), |
| 203 | 0); |
| 204 | } |
| 205 | |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 206 | void QuicMemoryCacheBackend::AddResponse( |
| 207 | quiche::QuicheStringPiece host, |
| 208 | quiche::QuicheStringPiece path, |
| 209 | SpdyHeaderBlock response_headers, |
| 210 | quiche::QuicheStringPiece response_body, |
| 211 | SpdyHeaderBlock response_trailers) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 212 | AddResponseImpl(host, path, QuicBackendResponse::REGULAR_RESPONSE, |
| 213 | std::move(response_headers), response_body, |
| 214 | std::move(response_trailers), 0); |
| 215 | } |
| 216 | |
| 217 | void QuicMemoryCacheBackend::AddSpecialResponse( |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 218 | quiche::QuicheStringPiece host, |
| 219 | quiche::QuicheStringPiece path, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 220 | SpecialResponseType response_type) { |
| 221 | AddResponseImpl(host, path, response_type, SpdyHeaderBlock(), "", |
| 222 | SpdyHeaderBlock(), 0); |
| 223 | } |
| 224 | |
| 225 | void QuicMemoryCacheBackend::AddSpecialResponse( |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 226 | quiche::QuicheStringPiece host, |
| 227 | quiche::QuicheStringPiece path, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 228 | spdy::SpdyHeaderBlock response_headers, |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 229 | quiche::QuicheStringPiece response_body, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 230 | SpecialResponseType response_type) { |
| 231 | AddResponseImpl(host, path, response_type, std::move(response_headers), |
| 232 | response_body, SpdyHeaderBlock(), 0); |
| 233 | } |
| 234 | |
| 235 | void QuicMemoryCacheBackend::AddStopSendingResponse( |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 236 | quiche::QuicheStringPiece host, |
| 237 | quiche::QuicheStringPiece path, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 238 | spdy::SpdyHeaderBlock response_headers, |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 239 | quiche::QuicheStringPiece response_body, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 240 | uint16_t stop_sending_code) { |
| 241 | AddResponseImpl(host, path, SpecialResponseType::STOP_SENDING, |
| 242 | std::move(response_headers), response_body, SpdyHeaderBlock(), |
| 243 | stop_sending_code); |
| 244 | } |
| 245 | |
| 246 | QuicMemoryCacheBackend::QuicMemoryCacheBackend() : cache_initialized_(false) {} |
| 247 | |
| 248 | bool QuicMemoryCacheBackend::InitializeBackend( |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 249 | const std::string& cache_directory) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 250 | if (cache_directory.empty()) { |
| 251 | QUIC_BUG << "cache_directory must not be empty."; |
| 252 | return false; |
| 253 | } |
| 254 | QUIC_LOG(INFO) |
| 255 | << "Attempting to initialize QuicMemoryCacheBackend from directory: " |
| 256 | << cache_directory; |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 257 | std::vector<std::string> files = ReadFileContents(cache_directory); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 258 | std::list<std::unique_ptr<ResourceFile>> resource_files; |
| 259 | for (const auto& filename : files) { |
| 260 | std::unique_ptr<ResourceFile> resource_file(new ResourceFile(filename)); |
| 261 | |
| 262 | // Tease apart filename into host and path. |
rch | 3706b23 | 2019-12-12 21:40:54 -0800 | [diff] [blame] | 263 | std::string base(resource_file->file_name()); |
| 264 | // Transform windows path separators to URL path separators. |
| 265 | for (size_t i = 0; i < base.length(); ++i) { |
| 266 | if (base[i] == '\\') { |
| 267 | base[i] = '/'; |
| 268 | } |
| 269 | } |
| 270 | base.erase(0, cache_directory.length()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 271 | if (base[0] == '/') { |
rch | 3706b23 | 2019-12-12 21:40:54 -0800 | [diff] [blame] | 272 | base.erase(0, 1); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 273 | } |
| 274 | |
| 275 | resource_file->SetHostPathFromBase(base); |
| 276 | resource_file->Read(); |
| 277 | |
| 278 | AddResponse(resource_file->host(), resource_file->path(), |
| 279 | resource_file->spdy_headers().Clone(), resource_file->body()); |
| 280 | |
| 281 | resource_files.push_back(std::move(resource_file)); |
| 282 | } |
| 283 | |
| 284 | for (const auto& resource_file : resource_files) { |
| 285 | std::list<ServerPushInfo> push_resources; |
| 286 | for (const auto& push_url : resource_file->push_urls()) { |
| 287 | QuicUrl url(push_url); |
| 288 | const QuicBackendResponse* response = GetResponse(url.host(), url.path()); |
| 289 | if (!response) { |
| 290 | QUIC_BUG << "Push URL '" << push_url << "' not found."; |
| 291 | return false; |
| 292 | } |
| 293 | push_resources.push_back(ServerPushInfo(url, response->headers().Clone(), |
| 294 | kV3LowestPriority, |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 295 | (std::string(response->body())))); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 296 | } |
| 297 | MaybeAddServerPushResources(resource_file->host(), resource_file->path(), |
| 298 | push_resources); |
| 299 | } |
rch | 7bd5476 | 2019-10-15 10:53:24 -0700 | [diff] [blame] | 300 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 301 | cache_initialized_ = true; |
| 302 | return true; |
| 303 | } |
| 304 | |
rch | 7bd5476 | 2019-10-15 10:53:24 -0700 | [diff] [blame] | 305 | void QuicMemoryCacheBackend::GenerateDynamicResponses() { |
| 306 | QuicWriterMutexLock lock(&response_mutex_); |
| 307 | // Add a generate bytes response. |
| 308 | spdy::SpdyHeaderBlock response_headers; |
rch | 7bd5476 | 2019-10-15 10:53:24 -0700 | [diff] [blame] | 309 | response_headers[":status"] = "200"; |
| 310 | generate_bytes_response_ = std::make_unique<QuicBackendResponse>(); |
| 311 | generate_bytes_response_->set_headers(std::move(response_headers)); |
| 312 | generate_bytes_response_->set_response_type( |
| 313 | QuicBackendResponse::GENERATE_BYTES); |
| 314 | } |
| 315 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 316 | bool QuicMemoryCacheBackend::IsBackendInitialized() const { |
| 317 | return cache_initialized_; |
| 318 | } |
| 319 | |
| 320 | void QuicMemoryCacheBackend::FetchResponseFromBackend( |
| 321 | const SpdyHeaderBlock& request_headers, |
dschinazi | 17d4242 | 2019-06-18 16:35:07 -0700 | [diff] [blame] | 322 | const std::string& /*request_body*/, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 323 | QuicSimpleServerBackend::RequestHandler* quic_stream) { |
| 324 | const QuicBackendResponse* quic_response = nullptr; |
| 325 | // Find response in cache. If not found, send error response. |
| 326 | auto authority = request_headers.find(":authority"); |
| 327 | auto path = request_headers.find(":path"); |
| 328 | if (authority != request_headers.end() && path != request_headers.end()) { |
| 329 | quic_response = GetResponse(authority->second, path->second); |
| 330 | } |
| 331 | |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 332 | std::string request_url = |
| 333 | std::string(authority->second) + std::string(path->second); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 334 | std::list<ServerPushInfo> resources = GetServerPushResources(request_url); |
| 335 | QUIC_DVLOG(1) |
| 336 | << "Fetching QUIC response from backend in-memory cache for url " |
| 337 | << request_url; |
| 338 | quic_stream->OnResponseBackendComplete(quic_response, resources); |
| 339 | } |
| 340 | |
| 341 | // The memory cache does not have a per-stream handler |
| 342 | void QuicMemoryCacheBackend::CloseBackendResponseStream( |
dschinazi | 17d4242 | 2019-06-18 16:35:07 -0700 | [diff] [blame] | 343 | QuicSimpleServerBackend::RequestHandler* /*quic_stream*/) {} |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 344 | |
| 345 | std::list<ServerPushInfo> QuicMemoryCacheBackend::GetServerPushResources( |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 346 | std::string request_url) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 347 | QuicWriterMutexLock lock(&response_mutex_); |
| 348 | |
| 349 | std::list<ServerPushInfo> resources; |
| 350 | auto resource_range = server_push_resources_.equal_range(request_url); |
| 351 | for (auto it = resource_range.first; it != resource_range.second; ++it) { |
| 352 | resources.push_back(it->second); |
| 353 | } |
| 354 | QUIC_DVLOG(1) << "Found " << resources.size() << " push resources for " |
| 355 | << request_url; |
| 356 | return resources; |
| 357 | } |
| 358 | |
| 359 | QuicMemoryCacheBackend::~QuicMemoryCacheBackend() { |
| 360 | { |
| 361 | QuicWriterMutexLock lock(&response_mutex_); |
| 362 | responses_.clear(); |
| 363 | } |
| 364 | } |
| 365 | |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 366 | void QuicMemoryCacheBackend::AddResponseImpl( |
| 367 | quiche::QuicheStringPiece host, |
| 368 | quiche::QuicheStringPiece path, |
| 369 | SpecialResponseType response_type, |
| 370 | SpdyHeaderBlock response_headers, |
| 371 | quiche::QuicheStringPiece response_body, |
| 372 | SpdyHeaderBlock response_trailers, |
| 373 | uint16_t stop_sending_code) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 374 | QuicWriterMutexLock lock(&response_mutex_); |
| 375 | |
| 376 | DCHECK(!host.empty()) << "Host must be populated, e.g. \"www.google.com\""; |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 377 | std::string key = GetKey(host, path); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 378 | if (QuicContainsKey(responses_, key)) { |
| 379 | QUIC_BUG << "Response for '" << key << "' already exists!"; |
| 380 | return; |
| 381 | } |
vasilvv | 0fc587f | 2019-09-06 13:33:08 -0700 | [diff] [blame] | 382 | auto new_response = std::make_unique<QuicBackendResponse>(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 383 | new_response->set_response_type(response_type); |
| 384 | new_response->set_headers(std::move(response_headers)); |
| 385 | new_response->set_body(response_body); |
| 386 | new_response->set_trailers(std::move(response_trailers)); |
| 387 | new_response->set_stop_sending_code(stop_sending_code); |
| 388 | QUIC_DVLOG(1) << "Add response with key " << key; |
| 389 | responses_[key] = std::move(new_response); |
| 390 | } |
| 391 | |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 392 | std::string QuicMemoryCacheBackend::GetKey( |
| 393 | quiche::QuicheStringPiece host, |
| 394 | quiche::QuicheStringPiece path) const { |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 395 | std::string host_string = std::string(host); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 396 | size_t port = host_string.find(':'); |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 397 | if (port != std::string::npos) |
| 398 | host_string = std::string(host_string.c_str(), port); |
| 399 | return host_string + std::string(path); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 400 | } |
| 401 | |
| 402 | void QuicMemoryCacheBackend::MaybeAddServerPushResources( |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 403 | quiche::QuicheStringPiece request_host, |
| 404 | quiche::QuicheStringPiece request_path, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 405 | std::list<ServerPushInfo> push_resources) { |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 406 | std::string request_url = GetKey(request_host, request_path); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 407 | |
| 408 | for (const auto& push_resource : push_resources) { |
| 409 | if (PushResourceExistsInCache(request_url, push_resource)) { |
| 410 | continue; |
| 411 | } |
| 412 | |
| 413 | QUIC_DVLOG(1) << "Add request-resource association: request url " |
| 414 | << request_url << " push url " |
| 415 | << push_resource.request_url.ToString() |
| 416 | << " response headers " |
| 417 | << push_resource.headers.DebugString(); |
| 418 | { |
| 419 | QuicWriterMutexLock lock(&response_mutex_); |
| 420 | server_push_resources_.insert(std::make_pair(request_url, push_resource)); |
| 421 | } |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 422 | std::string host = push_resource.request_url.host(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 423 | if (host.empty()) { |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 424 | host = std::string(request_host); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 425 | } |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 426 | std::string path = push_resource.request_url.path(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 427 | bool found_existing_response = false; |
| 428 | { |
| 429 | QuicWriterMutexLock lock(&response_mutex_); |
| 430 | found_existing_response = QuicContainsKey(responses_, GetKey(host, path)); |
| 431 | } |
| 432 | if (!found_existing_response) { |
| 433 | // Add a server push response to responses map, if it is not in the map. |
QUICHE team | 5015e2e | 2019-12-11 09:38:06 -0800 | [diff] [blame] | 434 | quiche::QuicheStringPiece body = push_resource.body; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 435 | QUIC_DVLOG(1) << "Add response for push resource: host " << host |
| 436 | << " path " << path; |
| 437 | AddResponse(host, path, push_resource.headers.Clone(), body); |
| 438 | } |
| 439 | } |
| 440 | } |
| 441 | |
| 442 | bool QuicMemoryCacheBackend::PushResourceExistsInCache( |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 443 | std::string original_request_url, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 444 | ServerPushInfo resource) { |
| 445 | QuicWriterMutexLock lock(&response_mutex_); |
| 446 | auto resource_range = |
| 447 | server_push_resources_.equal_range(original_request_url); |
| 448 | for (auto it = resource_range.first; it != resource_range.second; ++it) { |
| 449 | ServerPushInfo push_resource = it->second; |
| 450 | if (push_resource.request_url.ToString() == |
| 451 | resource.request_url.ToString()) { |
| 452 | return true; |
| 453 | } |
| 454 | } |
| 455 | return false; |
| 456 | } |
| 457 | |
| 458 | } // namespace quic |