Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(157)

Side by Side Diff: net/quic/quic_stream_factory.cc

Issue 2120703003: QUIC - Race Cert Verification with host resolution if certs are (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/quic/quic_stream_factory.h" 5 #include "net/quic/quic_stream_factory.h"
6 6
7 #include <openssl/aead.h> 7 #include <openssl/aead.h>
8 8
9 #include <algorithm> 9 #include <algorithm>
10 #include <tuple> 10 #include <tuple>
(...skipping 16 matching lines...) Expand all
27 #include "crypto/openssl_util.h" 27 #include "crypto/openssl_util.h"
28 #include "net/base/ip_address.h" 28 #include "net/base/ip_address.h"
29 #include "net/base/net_errors.h" 29 #include "net/base/net_errors.h"
30 #include "net/cert/cert_verifier.h" 30 #include "net/cert/cert_verifier.h"
31 #include "net/cert/ct_verifier.h" 31 #include "net/cert/ct_verifier.h"
32 #include "net/dns/host_resolver.h" 32 #include "net/dns/host_resolver.h"
33 #include "net/dns/single_request_host_resolver.h" 33 #include "net/dns/single_request_host_resolver.h"
34 #include "net/http/bidirectional_stream_impl.h" 34 #include "net/http/bidirectional_stream_impl.h"
35 #include "net/quic/bidirectional_stream_quic_impl.h" 35 #include "net/quic/bidirectional_stream_quic_impl.h"
36 #include "net/quic/crypto/channel_id_chromium.h" 36 #include "net/quic/crypto/channel_id_chromium.h"
37 #include "net/quic/crypto/proof_verifier.h"
37 #include "net/quic/crypto/proof_verifier_chromium.h" 38 #include "net/quic/crypto/proof_verifier_chromium.h"
38 #include "net/quic/crypto/properties_based_quic_server_info.h" 39 #include "net/quic/crypto/properties_based_quic_server_info.h"
39 #include "net/quic/crypto/quic_random.h" 40 #include "net/quic/crypto/quic_random.h"
40 #include "net/quic/crypto/quic_server_info.h" 41 #include "net/quic/crypto/quic_server_info.h"
41 #include "net/quic/port_suggester.h" 42 #include "net/quic/port_suggester.h"
42 #include "net/quic/quic_chromium_alarm_factory.h" 43 #include "net/quic/quic_chromium_alarm_factory.h"
43 #include "net/quic/quic_chromium_connection_helper.h" 44 #include "net/quic/quic_chromium_connection_helper.h"
44 #include "net/quic/quic_chromium_packet_reader.h" 45 #include "net/quic/quic_chromium_packet_reader.h"
45 #include "net/quic/quic_chromium_packet_writer.h" 46 #include "net/quic/quic_chromium_packet_writer.h"
46 #include "net/quic/quic_client_promised_info.h" 47 #include "net/quic/quic_client_promised_info.h"
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
160 QuicConfig config; 161 QuicConfig config;
161 config.SetIdleConnectionStateLifetime( 162 config.SetIdleConnectionStateLifetime(
162 QuicTime::Delta::FromSeconds(idle_connection_timeout_seconds), 163 QuicTime::Delta::FromSeconds(idle_connection_timeout_seconds),
163 QuicTime::Delta::FromSeconds(idle_connection_timeout_seconds)); 164 QuicTime::Delta::FromSeconds(idle_connection_timeout_seconds));
164 config.SetConnectionOptionsToSend(connection_options); 165 config.SetConnectionOptionsToSend(connection_options);
165 return config; 166 return config;
166 } 167 }
167 168
168 } // namespace 169 } // namespace
169 170
171 class QuicStreamFactory::CertVerifierJob {
172 public:
173 class ProofVerifierCallbackImpl : public ProofVerifierCallback {
Ryan Hamilton 2016/07/07 00:24:36 Hm. I'm surprised this is using the ProofVerifier
ramant (doing other things) 2016/07/07 16:18:20 Wondered about the same (whether to duplicate some
174 public:
175 explicit ProofVerifierCallbackImpl(CertVerifierJob* job) : job_(job) {}
176
177 ~ProofVerifierCallbackImpl() override {}
178
179 void Run(bool ok,
180 const std::string& error_details,
181 std::unique_ptr<ProofVerifyDetails>* details) override {
182 if (job_ == nullptr)
183 return;
184 job_->verify_callback_ = nullptr;
185 job_->OnComplete();
186 }
187
188 void Cancel() { job_ = nullptr; }
189
190 private:
191 CertVerifierJob* job_;
192 };
193
194 CertVerifierJob(const QuicServerId& server_id,
195 int cert_verify_flags,
196 const BoundNetLog& net_log)
197 : server_id_(server_id),
198 verify_callback_(nullptr),
199 verify_context_(base::WrapUnique(
200 new ProofVerifyContextChromium(cert_verify_flags, net_log))),
201 net_log_(net_log),
202 weak_factory_(this) {}
203
204 ~CertVerifierJob() {
205 if (verify_callback_)
206 verify_callback_->Cancel();
207 }
208
209 QuicAsyncStatus Run(QuicCryptoClientConfig* crypto_config,
210 const CompletionCallback& callback) {
211 QuicCryptoClientConfig::CachedState* cached =
212 crypto_config->LookupOrCreate(server_id_);
213 ProofVerifierCallbackImpl* verify_callback =
214 new ProofVerifierCallbackImpl(this);
215 // Pass empty chlo_hash, signature and cert_sct so that only certificate is
216 // verified.
217 QuicAsyncStatus status = crypto_config->proof_verifier()->VerifyProof(
218 server_id_.host(), server_id_.port(), cached->server_config(),
219 QUIC_VERSION_25, /*chlo_hash=*/"", cached->certs(),
Ryan Hamilton 2016/07/07 00:24:36 hard-coding a quic version here seems scary.
ramant (doing other things) 2016/07/07 16:18:20 Deleted it. Done.
220 /*cert_sct=*/"", /*signature=*/"", verify_context_.get(),
221 &verify_error_details_, &verify_details_, verify_callback);
Ryan Hamilton 2016/07/07 00:24:36 Wow, there are a lot of empty arguments. I'd be in
ramant (doing other things) 2016/07/07 16:18:20 Done.
222 if (status == QUIC_PENDING) {
223 verify_callback_ = verify_callback;
224 callback_ = callback;
225 } else {
226 delete verify_callback;
227 }
228 return status;
229 }
230
231 void OnComplete() {
232 if (!callback_.is_null())
233 callback_.Run(OK);
234 }
235
236 const QuicServerId& server_id() const { return server_id_; }
237
238 private:
239 QuicServerId server_id_;
240 ProofVerifierCallbackImpl* verify_callback_;
241 std::unique_ptr<ProofVerifyContext> verify_context_;
242 std::unique_ptr<ProofVerifyDetails> verify_details_;
243 std::string verify_error_details_;
244 const BoundNetLog net_log_;
245 CompletionCallback callback_;
246 base::WeakPtrFactory<CertVerifierJob> weak_factory_;
247
248 DISALLOW_COPY_AND_ASSIGN(CertVerifierJob);
249 };
250
170 // Responsible for creating a new QUIC session to the specified server, and 251 // Responsible for creating a new QUIC session to the specified server, and
171 // for notifying any associated requests when complete. 252 // for notifying any associated requests when complete.
172 class QuicStreamFactory::Job { 253 class QuicStreamFactory::Job {
173 public: 254 public:
174 Job(QuicStreamFactory* factory, 255 Job(QuicStreamFactory* factory,
175 HostResolver* host_resolver, 256 HostResolver* host_resolver,
176 const QuicSessionKey& key, 257 const QuicSessionKey& key,
177 bool was_alternative_service_recently_broken, 258 bool was_alternative_service_recently_broken,
178 int cert_verify_flags, 259 int cert_verify_flags,
179 QuicServerInfo* server_info, 260 QuicServerInfo* server_info,
(...skipping 445 matching lines...) Expand 10 before | Expand all | Expand 10 after
625 int threshold_public_resets_post_handshake, 706 int threshold_public_resets_post_handshake,
626 int threshold_timeouts_with_open_streams, 707 int threshold_timeouts_with_open_streams,
627 int socket_receive_buffer_size, 708 int socket_receive_buffer_size,
628 bool delay_tcp_race, 709 bool delay_tcp_race,
629 int max_server_configs_stored_in_properties, 710 int max_server_configs_stored_in_properties,
630 bool close_sessions_on_ip_change, 711 bool close_sessions_on_ip_change,
631 bool disable_quic_on_timeout_with_open_streams, 712 bool disable_quic_on_timeout_with_open_streams,
632 int idle_connection_timeout_seconds, 713 int idle_connection_timeout_seconds,
633 bool migrate_sessions_on_network_change, 714 bool migrate_sessions_on_network_change,
634 bool migrate_sessions_early, 715 bool migrate_sessions_early,
716 bool race_cert_verification,
635 const QuicTagVector& connection_options, 717 const QuicTagVector& connection_options,
636 bool enable_token_binding) 718 bool enable_token_binding)
637 : require_confirmation_(true), 719 : require_confirmation_(true),
638 net_log_(net_log), 720 net_log_(net_log),
639 host_resolver_(host_resolver), 721 host_resolver_(host_resolver),
640 client_socket_factory_(client_socket_factory), 722 client_socket_factory_(client_socket_factory),
641 http_server_properties_(http_server_properties), 723 http_server_properties_(http_server_properties),
642 transport_security_state_(transport_security_state), 724 transport_security_state_(transport_security_state),
643 cert_transparency_verifier_(cert_transparency_verifier), 725 cert_transparency_verifier_(cert_transparency_verifier),
644 quic_crypto_client_stream_factory_(quic_crypto_client_stream_factory), 726 quic_crypto_client_stream_factory_(quic_crypto_client_stream_factory),
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
678 delay_tcp_race_(delay_tcp_race), 760 delay_tcp_race_(delay_tcp_race),
679 yield_after_packets_(kQuicYieldAfterPacketsRead), 761 yield_after_packets_(kQuicYieldAfterPacketsRead),
680 yield_after_duration_(QuicTime::Delta::FromMilliseconds( 762 yield_after_duration_(QuicTime::Delta::FromMilliseconds(
681 kQuicYieldAfterDurationMilliseconds)), 763 kQuicYieldAfterDurationMilliseconds)),
682 close_sessions_on_ip_change_(close_sessions_on_ip_change), 764 close_sessions_on_ip_change_(close_sessions_on_ip_change),
683 migrate_sessions_on_network_change_( 765 migrate_sessions_on_network_change_(
684 migrate_sessions_on_network_change && 766 migrate_sessions_on_network_change &&
685 NetworkChangeNotifier::AreNetworkHandlesSupported()), 767 NetworkChangeNotifier::AreNetworkHandlesSupported()),
686 migrate_sessions_early_(migrate_sessions_early && 768 migrate_sessions_early_(migrate_sessions_early &&
687 migrate_sessions_on_network_change_), 769 migrate_sessions_on_network_change_),
770 race_cert_verification_(race_cert_verification),
688 port_seed_(random_generator_->RandUint64()), 771 port_seed_(random_generator_->RandUint64()),
689 check_persisted_supports_quic_(true), 772 check_persisted_supports_quic_(true),
690 has_initialized_data_(false), 773 has_initialized_data_(false),
691 num_push_streams_created_(0), 774 num_push_streams_created_(0),
692 status_(OPEN), 775 status_(OPEN),
693 task_runner_(nullptr), 776 task_runner_(nullptr),
694 ssl_config_service_(ssl_config_service), 777 ssl_config_service_(ssl_config_service),
695 weak_factory_(this) { 778 weak_factory_(this) {
696 if (ssl_config_service_.get()) 779 if (ssl_config_service_.get())
697 ssl_config_service_->AddObserver(this); 780 ssl_config_service_->AddObserver(this);
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
745 CloseAllSessions(ERR_ABORTED, QUIC_CONNECTION_CANCELLED); 828 CloseAllSessions(ERR_ABORTED, QUIC_CONNECTION_CANCELLED);
746 while (!all_sessions_.empty()) { 829 while (!all_sessions_.empty()) {
747 delete all_sessions_.begin()->first; 830 delete all_sessions_.begin()->first;
748 all_sessions_.erase(all_sessions_.begin()); 831 all_sessions_.erase(all_sessions_.begin());
749 } 832 }
750 while (!active_jobs_.empty()) { 833 while (!active_jobs_.empty()) {
751 const QuicServerId server_id = active_jobs_.begin()->first; 834 const QuicServerId server_id = active_jobs_.begin()->first;
752 STLDeleteElements(&(active_jobs_[server_id])); 835 STLDeleteElements(&(active_jobs_[server_id]));
753 active_jobs_.erase(server_id); 836 active_jobs_.erase(server_id);
754 } 837 }
838 while (!active_cert_verifier_jobs_.empty()) {
839 delete active_cert_verifier_jobs_.begin()->second;
840 active_cert_verifier_jobs_.erase(active_cert_verifier_jobs_.begin());
Ryan Hamilton 2016/07/07 00:24:36 I wonder if the value in the map could be an std::
ramant (doing other things) 2016/07/07 16:18:20 Done.
841 }
755 if (ssl_config_service_.get()) 842 if (ssl_config_service_.get())
756 ssl_config_service_->RemoveObserver(this); 843 ssl_config_service_->RemoveObserver(this);
757 if (migrate_sessions_on_network_change_) { 844 if (migrate_sessions_on_network_change_) {
758 NetworkChangeNotifier::RemoveNetworkObserver(this); 845 NetworkChangeNotifier::RemoveNetworkObserver(this);
759 } else if (close_sessions_on_ip_change_) { 846 } else if (close_sessions_on_ip_change_) {
760 NetworkChangeNotifier::RemoveIPAddressObserver(this); 847 NetworkChangeNotifier::RemoveIPAddressObserver(this);
761 } 848 }
762 } 849 }
763 850
764 void QuicStreamFactory::set_require_confirmation(bool require_confirmation) { 851 void QuicStreamFactory::set_require_confirmation(bool require_confirmation) {
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
881 if (!ContainsKey(quic_supported_servers_at_startup_, destination)) { 968 if (!ContainsKey(quic_supported_servers_at_startup_, destination)) {
882 // If there is no entry for QUIC, consider that as a new server and 969 // If there is no entry for QUIC, consider that as a new server and
883 // don't wait for Cache thread to load the data for that server. 970 // don't wait for Cache thread to load the data for that server.
884 load_from_disk_cache = false; 971 load_from_disk_cache = false;
885 } 972 }
886 if (load_from_disk_cache && CryptoConfigCacheIsEmpty(server_id)) { 973 if (load_from_disk_cache && CryptoConfigCacheIsEmpty(server_id)) {
887 quic_server_info = quic_server_info_factory_->GetForServer(server_id); 974 quic_server_info = quic_server_info_factory_->GetForServer(server_id);
888 } 975 }
889 } 976 }
890 977
978 StartCertVerifyJob(server_id, cert_verify_flags, net_log);
979
891 QuicSessionKey key(destination, server_id); 980 QuicSessionKey key(destination, server_id);
892 std::unique_ptr<Job> job( 981 std::unique_ptr<Job> job(
893 new Job(this, host_resolver_, key, WasQuicRecentlyBroken(server_id), 982 new Job(this, host_resolver_, key, WasQuicRecentlyBroken(server_id),
894 cert_verify_flags, quic_server_info, net_log)); 983 cert_verify_flags, quic_server_info, net_log));
895 int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete, 984 int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
896 base::Unretained(this), job.get())); 985 base::Unretained(this), job.get()));
897 if (rv == ERR_IO_PENDING) { 986 if (rv == ERR_IO_PENDING) {
898 active_requests_[request] = server_id; 987 active_requests_[request] = server_id;
899 job_requests_map_[server_id].insert(request); 988 job_requests_map_[server_id].insert(request);
900 active_jobs_[server_id].insert(job.release()); 989 active_jobs_[server_id].insert(job.release());
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
1013 for (Job* other_job : active_jobs_[server_id]) { 1102 for (Job* other_job : active_jobs_[server_id]) {
1014 if (other_job != job) 1103 if (other_job != job)
1015 other_job->Cancel(); 1104 other_job->Cancel();
1016 } 1105 }
1017 1106
1018 STLDeleteElements(&(active_jobs_[server_id])); 1107 STLDeleteElements(&(active_jobs_[server_id]));
1019 active_jobs_.erase(server_id); 1108 active_jobs_.erase(server_id);
1020 job_requests_map_.erase(server_id); 1109 job_requests_map_.erase(server_id);
1021 } 1110 }
1022 1111
1112 void QuicStreamFactory::OnCertVerifyJobComplete(CertVerifierJob* job, int rv) {
1113 const QuicServerId server_id(job->server_id());
1114 delete job;
1115 active_cert_verifier_jobs_.erase(server_id);
1116 }
1117
1023 std::unique_ptr<QuicHttpStream> QuicStreamFactory::CreateFromSession( 1118 std::unique_ptr<QuicHttpStream> QuicStreamFactory::CreateFromSession(
1024 QuicChromiumClientSession* session) { 1119 QuicChromiumClientSession* session) {
1025 return std::unique_ptr<QuicHttpStream>( 1120 return std::unique_ptr<QuicHttpStream>(
1026 new QuicHttpStream(session->GetWeakPtr())); 1121 new QuicHttpStream(session->GetWeakPtr()));
1027 } 1122 }
1028 1123
1029 QuicChromiumClientSession::QuicDisabledReason 1124 QuicChromiumClientSession::QuicDisabledReason
1030 QuicStreamFactory::QuicDisabledReason(uint16_t port) const { 1125 QuicStreamFactory::QuicDisabledReason(uint16_t port) const {
1031 if (max_number_of_lossy_connections_ > 0 && 1126 if (max_number_of_lossy_connections_ > 0 &&
1032 number_of_lossy_connections_.find(port) != 1127 number_of_lossy_connections_.find(port) !=
(...skipping 508 matching lines...) Expand 10 before | Expand all | Expand 10 after
1541 // TODO(rtenneti): crbug.com/498823 - delete active_sessions_.empty() check. 1636 // TODO(rtenneti): crbug.com/498823 - delete active_sessions_.empty() check.
1542 if (active_sessions_.empty()) 1637 if (active_sessions_.empty())
1543 return false; 1638 return false;
1544 return ContainsKey(active_sessions_, server_id); 1639 return ContainsKey(active_sessions_, server_id);
1545 } 1640 }
1546 1641
1547 bool QuicStreamFactory::HasActiveJob(const QuicServerId& server_id) const { 1642 bool QuicStreamFactory::HasActiveJob(const QuicServerId& server_id) const {
1548 return ContainsKey(active_jobs_, server_id); 1643 return ContainsKey(active_jobs_, server_id);
1549 } 1644 }
1550 1645
1646 bool QuicStreamFactory::HasActiveCertVerifierJob(
1647 const QuicServerId& server_id) const {
1648 return ContainsKey(active_cert_verifier_jobs_, server_id);
1649 }
1650
1551 int QuicStreamFactory::ConfigureSocket(DatagramClientSocket* socket, 1651 int QuicStreamFactory::ConfigureSocket(DatagramClientSocket* socket,
1552 IPEndPoint addr, 1652 IPEndPoint addr,
1553 NetworkHandle network) { 1653 NetworkHandle network) {
1554 if (enable_non_blocking_io_ && 1654 if (enable_non_blocking_io_ &&
1555 client_socket_factory_ == ClientSocketFactory::GetDefaultFactory()) { 1655 client_socket_factory_ == ClientSocketFactory::GetDefaultFactory()) {
1556 #if defined(OS_WIN) 1656 #if defined(OS_WIN)
1557 static_cast<UDPClientSocket*>(socket)->UseNonBlockingIO(); 1657 static_cast<UDPClientSocket*>(socket)->UseNonBlockingIO();
1558 #endif 1658 #endif
1559 } 1659 }
1560 1660
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
1748 alternative_service); 1848 alternative_service);
1749 } 1849 }
1750 1850
1751 bool QuicStreamFactory::CryptoConfigCacheIsEmpty( 1851 bool QuicStreamFactory::CryptoConfigCacheIsEmpty(
1752 const QuicServerId& server_id) { 1852 const QuicServerId& server_id) {
1753 QuicCryptoClientConfig::CachedState* cached = 1853 QuicCryptoClientConfig::CachedState* cached =
1754 crypto_config_.LookupOrCreate(server_id); 1854 crypto_config_.LookupOrCreate(server_id);
1755 return cached->IsEmpty(); 1855 return cached->IsEmpty();
1756 } 1856 }
1757 1857
1858 void QuicStreamFactory::StartCertVerifyJob(const QuicServerId& server_id,
1859 int cert_verify_flags,
1860 const BoundNetLog& net_log) {
1861 if (!race_cert_verification_)
1862 return;
1863 QuicCryptoClientConfig::CachedState* cached =
1864 crypto_config_.LookupOrCreate(server_id);
1865 if (!cached || cached->certs().empty() ||
1866 HasActiveCertVerifierJob(server_id)) {
1867 return;
1868 }
1869 std::unique_ptr<CertVerifierJob> cert_verifier_job(
1870 new CertVerifierJob(server_id, cert_verify_flags, net_log));
1871 QuicAsyncStatus status = cert_verifier_job->Run(
1872 &crypto_config_,
1873 base::Bind(&QuicStreamFactory::OnCertVerifyJobComplete,
1874 base::Unretained(this), cert_verifier_job.get()));
1875 if (status == QUIC_PENDING)
1876 active_cert_verifier_jobs_[server_id] = cert_verifier_job.release();
1877 }
1878
1758 void QuicStreamFactory::InitializeCachedStateInCryptoConfig( 1879 void QuicStreamFactory::InitializeCachedStateInCryptoConfig(
1759 const QuicServerId& server_id, 1880 const QuicServerId& server_id,
1760 const std::unique_ptr<QuicServerInfo>& server_info, 1881 const std::unique_ptr<QuicServerInfo>& server_info,
1761 QuicConnectionId* connection_id) { 1882 QuicConnectionId* connection_id) {
1762 QuicCryptoClientConfig::CachedState* cached = 1883 QuicCryptoClientConfig::CachedState* cached =
1763 crypto_config_.LookupOrCreate(server_id); 1884 crypto_config_.LookupOrCreate(server_id);
1764 if (cached->has_server_designated_connection_id()) 1885 if (cached->has_server_designated_connection_id())
1765 *connection_id = cached->GetNextServerDesignatedConnectionId(); 1886 *connection_id = cached->GetNextServerDesignatedConnectionId();
1766 1887
1767 if (!cached->IsEmpty()) 1888 if (!cached->IsEmpty())
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
1869 // Since the session was active, there's no longer an 1990 // Since the session was active, there's no longer an
1870 // HttpStreamFactoryImpl::Job running which can mark it broken, unless the TCP 1991 // HttpStreamFactoryImpl::Job running which can mark it broken, unless the TCP
1871 // job also fails. So to avoid not using QUIC when we otherwise could, we mark 1992 // job also fails. So to avoid not using QUIC when we otherwise could, we mark
1872 // it as recently broken, which means that 0-RTT will be disabled but we'll 1993 // it as recently broken, which means that 0-RTT will be disabled but we'll
1873 // still race. 1994 // still race.
1874 http_server_properties_->MarkAlternativeServiceRecentlyBroken( 1995 http_server_properties_->MarkAlternativeServiceRecentlyBroken(
1875 alternative_service); 1996 alternative_service);
1876 } 1997 }
1877 1998
1878 } // namespace net 1999 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698