1 // Copyright 2018 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 <limits.h>
6 #include <stddef.h>
7 #include <stdint.h>
8
9 #include <iostream>
10 #include <limits>
11 #include <map>
12 #include <set>
13 #include <utility>
14 #include <vector>
15
16 #include "base/bind.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/message_loop/message_pump_type.h"
19 #include "build/build_config.h"
20 #include "net/base/load_flags.h"
21 #include "net/base/net_errors.h"
22 #include "net/base/url_util.h"
23 #include "net/cert/cert_verifier.h"
24 #include "net/cookies/cookie_monster.h"
25 #include "net/http/http_auth_handler_factory.h"
26 #include "net/proxy_resolution/proxy_config_service_fixed.h"
27 #include "net/tools/quic/quic_http_proxy_backend.h"
28 #include "net/tools/quic/quic_http_proxy_backend_stream.h"
29 #include "net/url_request/url_request_context.h"
30 #include "net/url_request/url_request_context_builder.h"
31 #include "net/url_request/url_request_interceptor.h"
32
33 namespace net {
34
QuicHttpProxyBackend()35 QuicHttpProxyBackend::QuicHttpProxyBackend()
36 : context_(nullptr), thread_initialized_(false), proxy_thread_(nullptr) {}
37
~QuicHttpProxyBackend()38 QuicHttpProxyBackend::~QuicHttpProxyBackend() {
39 backend_stream_map_.clear();
40 thread_initialized_ = false;
41 proxy_task_runner_->DeleteSoon(FROM_HERE, context_.release());
42 if (proxy_thread_ != nullptr) {
43 LOG(INFO) << "QUIC Proxy thread: " << proxy_thread_->thread_name()
44 << " has stopped !";
45 proxy_thread_.reset();
46 }
47 }
48
InitializeBackend(const std::string & backend_url)49 bool QuicHttpProxyBackend::InitializeBackend(const std::string& backend_url) {
50 if (!ValidateBackendUrl(backend_url)) {
51 return false;
52 }
53 if (proxy_thread_ == nullptr) {
54 proxy_thread_ = std::make_unique<base::Thread>("quic proxy thread");
55 base::Thread::Options options;
56 options.message_pump_type = base::MessagePumpType::IO;
57 bool result = proxy_thread_->StartWithOptions(options);
58 proxy_task_runner_ = proxy_thread_->task_runner();
59 CHECK(result);
60 }
61 thread_initialized_ = true;
62 return true;
63 }
64
ValidateBackendUrl(const std::string & backend_url)65 bool QuicHttpProxyBackend::ValidateBackendUrl(const std::string& backend_url) {
66 backend_url_ = GURL(backend_url);
67 // Only Http(s) backend supported
68 if (!backend_url_.is_valid() || !backend_url_.SchemeIsHTTPOrHTTPS()) {
69 LOG(ERROR) << "QUIC Proxy Backend URL '" << backend_url
70 << "' is not valid !";
71 return false;
72 }
73
74 LOG(INFO)
75 << "Successfully configured to run as a QUIC Proxy with Backend URL: "
76 << backend_url_.spec();
77 return true;
78 }
79
IsBackendInitialized() const80 bool QuicHttpProxyBackend::IsBackendInitialized() const {
81 return thread_initialized_;
82 }
83
FetchResponseFromBackend(const spdy::SpdyHeaderBlock & request_headers,const std::string & incoming_body,QuicSimpleServerBackend::RequestHandler * quic_server_stream)84 void QuicHttpProxyBackend::FetchResponseFromBackend(
85 const spdy::SpdyHeaderBlock& request_headers,
86 const std::string& incoming_body,
87 QuicSimpleServerBackend::RequestHandler* quic_server_stream) {
88 QuicHttpProxyBackendStream* proxy_backend_stream =
89 InitializeQuicProxyBackendStream(quic_server_stream);
90
91 LOG(INFO) << " Forwarding QUIC request to the Backend Thread Asynchronously.";
92 if (proxy_backend_stream == nullptr ||
93 proxy_backend_stream->SendRequestToBackend(&request_headers,
94 incoming_body) != true) {
95 std::list<quic::QuicBackendResponse::ServerPushInfo> empty_resources;
96 quic_server_stream->OnResponseBackendComplete(nullptr, empty_resources);
97 }
98 }
99
100 QuicHttpProxyBackendStream*
InitializeQuicProxyBackendStream(QuicSimpleServerBackend::RequestHandler * quic_server_stream)101 QuicHttpProxyBackend::InitializeQuicProxyBackendStream(
102 QuicSimpleServerBackend::RequestHandler* quic_server_stream) {
103 if (!thread_initialized_) {
104 return nullptr;
105 }
106 QuicHttpProxyBackendStream* proxy_backend_stream =
107 new QuicHttpProxyBackendStream(this);
108 proxy_backend_stream->set_delegate(quic_server_stream);
109 proxy_backend_stream->Initialize(quic_server_stream->connection_id(),
110 quic_server_stream->stream_id(),
111 quic_server_stream->peer_host());
112 {
113 // Aquire write lock for this scope
114 base::AutoLock lock(backend_stream_mutex_);
115
116 auto inserted = backend_stream_map_.insert(std::make_pair(
117 quic_server_stream, base::WrapUnique(proxy_backend_stream)));
118 DCHECK(inserted.second);
119 }
120 return proxy_backend_stream;
121 }
122
CloseBackendResponseStream(QuicSimpleServerBackend::RequestHandler * quic_server_stream)123 void QuicHttpProxyBackend::CloseBackendResponseStream(
124 QuicSimpleServerBackend::RequestHandler* quic_server_stream) {
125 // Clean close of the backend stream handler
126 if (quic_server_stream == nullptr) {
127 return;
128 }
129 // Cleanup the handler on the proxy thread, since it owns the url_request
130 if (!proxy_task_runner_->BelongsToCurrentThread()) {
131 proxy_task_runner_->PostTask(
132 FROM_HERE,
133 base::BindOnce(&QuicHttpProxyBackend::CloseBackendResponseStream,
134 base::Unretained(this), quic_server_stream));
135 } else {
136 // Aquire write lock for this scope and cancel if the request is still
137 // pending
138 base::AutoLock lock(backend_stream_mutex_);
139 QuicHttpProxyBackendStream* proxy_backend_stream = nullptr;
140
141 auto it = backend_stream_map_.find(quic_server_stream);
142 if (it != backend_stream_map_.end()) {
143 proxy_backend_stream = it->second.get();
144 proxy_backend_stream->CancelRequest();
145 proxy_backend_stream->reset_delegate();
146 LOG(INFO) << " Quic Proxy cleaned-up backend handler on context/main "
147 "thread for quic_conn_id: "
148 << proxy_backend_stream->quic_connection_id()
149 << " quic_stream_id: "
150 << proxy_backend_stream->quic_stream_id();
151 size_t erased = backend_stream_map_.erase(quic_server_stream);
152 DCHECK_EQ(1u, erased);
153 }
154 }
155 }
156
InitializeURLRequestContext()157 void QuicHttpProxyBackend::InitializeURLRequestContext() {
158 DCHECK(context_ == nullptr);
159 net::URLRequestContextBuilder context_builder;
160 // Quic reverse proxy does not cache HTTP objects
161 context_builder.DisableHttpCache();
162 // Enable HTTP2, but disable QUIC on the backend
163 context_builder.SetSpdyAndQuicEnabled(true /* http2 */, false /* quic */);
164
165 #if defined(OS_LINUX) || defined(OS_BSD)
166 // On Linux, use a fixed ProxyConfigService, since the default one
167 // depends on glib.
168 context_builder.set_proxy_config_service(
169 std::make_unique<ProxyConfigServiceFixed>(
170 ProxyConfigWithAnnotation::CreateDirect()));
171 #endif
172
173 // Disable net::CookieStore.
174 context_builder.SetCookieStore(nullptr);
175 context_ = context_builder.Build();
176 }
177
GetURLRequestContext()178 net::URLRequestContext* QuicHttpProxyBackend::GetURLRequestContext() {
179 // Access to URLRequestContext is only available on Backend Thread
180 DCHECK(proxy_task_runner_->BelongsToCurrentThread());
181 if (context_ == nullptr) {
182 InitializeURLRequestContext();
183 }
184 return context_.get();
185 }
186
187 scoped_refptr<base::SingleThreadTaskRunner>
GetProxyTaskRunner() const188 QuicHttpProxyBackend::GetProxyTaskRunner() const {
189 return proxy_task_runner_;
190 }
191
192 } // namespace net
193