// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include #include #include #include #include "build/build_config.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" #include "net/base/url_util.h" #include "net/cert/cert_verifier.h" #include "net/cookies/cookie_monster.h" #include "net/http/http_auth_handler_factory.h" #include "net/proxy_resolution/proxy_config_service_fixed.h" #include "net/ssl/channel_id_service.h" #include "net/tools/quic/quic_http_proxy_backend.h" #include "net/tools/quic/quic_http_proxy_backend_stream.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_interceptor.h" namespace net { QuicHttpProxyBackend::QuicHttpProxyBackend() : context_(nullptr), thread_initialized_(false), proxy_thread_(nullptr) {} QuicHttpProxyBackend::~QuicHttpProxyBackend() { backend_stream_map_.clear(); thread_initialized_ = false; proxy_task_runner_->DeleteSoon(FROM_HERE, context_.release()); if (proxy_thread_ != nullptr) { LOG(INFO) << "QUIC Proxy thread: " << proxy_thread_->thread_name() << " has stopped !"; proxy_thread_.reset(); } } bool QuicHttpProxyBackend::InitializeBackend(const std::string& backend_url) { if (!ValidateBackendUrl(backend_url)) { return false; } if (proxy_thread_ == nullptr) { proxy_thread_ = std::make_unique("quic proxy thread"); base::Thread::Options options; options.message_loop_type = base::MessageLoop::TYPE_IO; bool result = proxy_thread_->StartWithOptions(options); proxy_task_runner_ = proxy_thread_->task_runner(); CHECK(result); } thread_initialized_ = true; return true; } bool QuicHttpProxyBackend::ValidateBackendUrl(const std::string& backend_url) { backend_url_ = GURL(backend_url); // Only Http(s) backend supported if (!backend_url_.is_valid() || !backend_url_.SchemeIsHTTPOrHTTPS()) { LOG(ERROR) << "QUIC Proxy Backend URL '" << backend_url << "' is not valid !"; return false; } LOG(INFO) << "Successfully configured to run as a QUIC Proxy with Backend URL: " << backend_url_.spec(); return true; } bool QuicHttpProxyBackend::IsBackendInitialized() const { return thread_initialized_; } void QuicHttpProxyBackend::FetchResponseFromBackend( const spdy::SpdyHeaderBlock& request_headers, const std::string& incoming_body, QuicSimpleServerBackend::RequestHandler* quic_server_stream) { QuicHttpProxyBackendStream* proxy_backend_stream = InitializeQuicProxyBackendStream(quic_server_stream); LOG(INFO) << " Forwarding QUIC request to the Backend Thread Asynchronously."; if (proxy_backend_stream == nullptr || proxy_backend_stream->SendRequestToBackend(&request_headers, incoming_body) != true) { std::list empty_resources; quic_server_stream->OnResponseBackendComplete(nullptr, empty_resources); } } QuicHttpProxyBackendStream* QuicHttpProxyBackend::InitializeQuicProxyBackendStream( QuicSimpleServerBackend::RequestHandler* quic_server_stream) { if (!thread_initialized_) { return nullptr; } QuicHttpProxyBackendStream* proxy_backend_stream = new QuicHttpProxyBackendStream(this); proxy_backend_stream->set_delegate(quic_server_stream); proxy_backend_stream->Initialize(quic_server_stream->connection_id(), quic_server_stream->stream_id(), quic_server_stream->peer_host()); { // Aquire write lock for this scope base::AutoLock lock(backend_stream_mutex_); auto inserted = backend_stream_map_.insert(std::make_pair( quic_server_stream, base::WrapUnique(proxy_backend_stream))); DCHECK(inserted.second); } return proxy_backend_stream; } void QuicHttpProxyBackend::CloseBackendResponseStream( QuicSimpleServerBackend::RequestHandler* quic_server_stream) { // Clean close of the backend stream handler if (quic_server_stream == nullptr) { return; } // Cleanup the handler on the proxy thread, since it owns the url_request if (!proxy_task_runner_->BelongsToCurrentThread()) { proxy_task_runner_->PostTask( FROM_HERE, base::BindOnce(&QuicHttpProxyBackend::CloseBackendResponseStream, base::Unretained(this), quic_server_stream)); } else { // Aquire write lock for this scope and cancel if the request is still // pending base::AutoLock lock(backend_stream_mutex_); QuicHttpProxyBackendStream* proxy_backend_stream = nullptr; ProxyBackendStreamMap::iterator it = backend_stream_map_.find(quic_server_stream); if (it != backend_stream_map_.end()) { proxy_backend_stream = it->second.get(); proxy_backend_stream->CancelRequest(); proxy_backend_stream->reset_delegate(); LOG(INFO) << " Quic Proxy cleaned-up backend handler on context/main " "thread for quic_conn_id: " << proxy_backend_stream->quic_connection_id() << " quic_stream_id: " << proxy_backend_stream->quic_stream_id(); size_t erased = backend_stream_map_.erase(quic_server_stream); DCHECK_EQ(1u, erased); } } } void QuicHttpProxyBackend::InitializeURLRequestContext() { DCHECK(context_ == nullptr); net::URLRequestContextBuilder context_builder; // Quic reverse proxy does not cache HTTP objects context_builder.DisableHttpCache(); // Enable HTTP2, but disable QUIC on the backend context_builder.SetSpdyAndQuicEnabled(true /* http2 */, false /* quic */); #if defined(OS_LINUX) // On Linux, use a fixed ProxyConfigService, since the default one // depends on glib. context_builder.set_proxy_config_service( std::make_unique( ProxyConfigWithAnnotation::CreateDirect())); #endif // Disable net::CookieStore and net::ChannelIDService. context_builder.SetCookieAndChannelIdStores(nullptr, nullptr); context_ = context_builder.Build(); } net::URLRequestContext* QuicHttpProxyBackend::GetURLRequestContext() { // Access to URLRequestContext is only available on Backend Thread DCHECK(proxy_task_runner_->BelongsToCurrentThread()); if (context_ == nullptr) { InitializeURLRequestContext(); } return context_.get(); } scoped_refptr QuicHttpProxyBackend::GetProxyTaskRunner() const { return proxy_task_runner_; } } // namespace net