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