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