Chromium Code Reviews| OLD | NEW |
|---|---|
| 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/http/http_network_transaction.h" | 5 #include "net/http/http_network_transaction.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/base64.h" | |
| 10 #include "base/bind.h" | 11 #include "base/bind.h" |
| 11 #include "base/bind_helpers.h" | 12 #include "base/bind_helpers.h" |
| 12 #include "base/compiler_specific.h" | 13 #include "base/compiler_specific.h" |
| 13 #include "base/format_macros.h" | 14 #include "base/format_macros.h" |
| 14 #include "base/memory/scoped_ptr.h" | 15 #include "base/memory/scoped_ptr.h" |
| 15 #include "base/metrics/field_trial.h" | 16 #include "base/metrics/field_trial.h" |
| 16 #include "base/metrics/histogram_macros.h" | 17 #include "base/metrics/histogram_macros.h" |
| 17 #include "base/metrics/sparse_histogram.h" | 18 #include "base/metrics/sparse_histogram.h" |
| 18 #include "base/profiler/scoped_tracker.h" | 19 #include "base/profiler/scoped_tracker.h" |
| 19 #include "base/stl_util.h" | 20 #include "base/stl_util.h" |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 53 #include "net/socket/client_socket_factory.h" | 54 #include "net/socket/client_socket_factory.h" |
| 54 #include "net/socket/socks_client_socket_pool.h" | 55 #include "net/socket/socks_client_socket_pool.h" |
| 55 #include "net/socket/ssl_client_socket.h" | 56 #include "net/socket/ssl_client_socket.h" |
| 56 #include "net/socket/ssl_client_socket_pool.h" | 57 #include "net/socket/ssl_client_socket_pool.h" |
| 57 #include "net/socket/transport_client_socket_pool.h" | 58 #include "net/socket/transport_client_socket_pool.h" |
| 58 #include "net/spdy/spdy_http_stream.h" | 59 #include "net/spdy/spdy_http_stream.h" |
| 59 #include "net/spdy/spdy_session.h" | 60 #include "net/spdy/spdy_session.h" |
| 60 #include "net/spdy/spdy_session_pool.h" | 61 #include "net/spdy/spdy_session_pool.h" |
| 61 #include "net/ssl/ssl_cert_request_info.h" | 62 #include "net/ssl/ssl_cert_request_info.h" |
| 62 #include "net/ssl/ssl_connection_status_flags.h" | 63 #include "net/ssl/ssl_connection_status_flags.h" |
| 64 #include "net/ssl/token_binding.h" | |
| 63 #include "url/gurl.h" | 65 #include "url/gurl.h" |
| 64 #include "url/url_canon.h" | 66 #include "url/url_canon.h" |
| 65 | 67 |
| 66 namespace net { | 68 namespace net { |
| 67 | 69 |
| 68 namespace { | 70 namespace { |
| 69 | 71 |
| 70 void ProcessAlternativeServices(HttpNetworkSession* session, | 72 void ProcessAlternativeServices(HttpNetworkSession* session, |
| 71 const HttpResponseHeaders& headers, | 73 const HttpResponseHeaders& headers, |
| 72 const HostPortPair& http_host_port_pair) { | 74 const HostPortPair& http_host_port_pair) { |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 191 proxy_ssl_config_.rev_checking_enabled = false; | 193 proxy_ssl_config_.rev_checking_enabled = false; |
| 192 } | 194 } |
| 193 | 195 |
| 194 if (request_->load_flags & LOAD_PREFETCH) | 196 if (request_->load_flags & LOAD_PREFETCH) |
| 195 response_.unused_since_prefetch = true; | 197 response_.unused_since_prefetch = true; |
| 196 | 198 |
| 197 // Channel ID is disabled if privacy mode is enabled for this request. | 199 // Channel ID is disabled if privacy mode is enabled for this request. |
| 198 if (request_->privacy_mode == PRIVACY_MODE_ENABLED) | 200 if (request_->privacy_mode == PRIVACY_MODE_ENABLED) |
| 199 server_ssl_config_.channel_id_enabled = false; | 201 server_ssl_config_.channel_id_enabled = false; |
| 200 | 202 |
| 203 if (session_->params().enable_token_binding && | |
| 204 session_->params().channel_id_service) { | |
| 205 server_ssl_config_.token_binding_params.push_back(TB_PARAM_ECDSAP256); | |
| 206 } | |
| 207 | |
| 201 next_state_ = STATE_NOTIFY_BEFORE_CREATE_STREAM; | 208 next_state_ = STATE_NOTIFY_BEFORE_CREATE_STREAM; |
| 202 int rv = DoLoop(OK); | 209 int rv = DoLoop(OK); |
| 203 if (rv == ERR_IO_PENDING) | 210 if (rv == ERR_IO_PENDING) |
| 204 callback_ = callback; | 211 callback_ = callback; |
| 205 return rv; | 212 return rv; |
| 206 } | 213 } |
| 207 | 214 |
| 208 int HttpNetworkTransaction::RestartIgnoringLastError( | 215 int HttpNetworkTransaction::RestartIgnoringLastError( |
| 209 const CompletionCallback& callback) { | 216 const CompletionCallback& callback) { |
| 210 DCHECK(!stream_.get()); | 217 DCHECK(!stream_.get()); |
| (...skipping 396 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 607 | 614 |
| 608 void HttpNetworkTransaction::GetConnectionAttempts( | 615 void HttpNetworkTransaction::GetConnectionAttempts( |
| 609 ConnectionAttempts* out) const { | 616 ConnectionAttempts* out) const { |
| 610 *out = connection_attempts_; | 617 *out = connection_attempts_; |
| 611 } | 618 } |
| 612 | 619 |
| 613 bool HttpNetworkTransaction::IsSecureRequest() const { | 620 bool HttpNetworkTransaction::IsSecureRequest() const { |
| 614 return request_->url.SchemeIsCryptographic(); | 621 return request_->url.SchemeIsCryptographic(); |
| 615 } | 622 } |
| 616 | 623 |
| 624 bool HttpNetworkTransaction::IsTokenBindingEnabled() const { | |
| 625 if (!IsSecureRequest()) | |
| 626 return false; | |
| 627 SSLInfo ssl_info; | |
| 628 stream_->GetSSLInfo(&ssl_info); | |
| 629 if (!ssl_info.token_binding_negotiated) | |
| 630 return false; | |
| 631 if (ssl_info.token_binding_key_param != TB_PARAM_ECDSAP256) | |
| 632 return false; | |
| 633 if (!session_->params().channel_id_service) | |
| 634 return false; | |
| 635 return true; | |
| 636 } | |
| 637 | |
| 638 void HttpNetworkTransaction::RecordTokenBindingSupport() const { | |
| 639 enum { | |
| 640 DISABLED = 0, | |
| 641 CLIENT_ONLY = 1, | |
| 642 CLIENT_AND_SERVER = 2, | |
| 643 CLIENT_NO_CHANNEL_ID_SERVICE = 3, | |
| 644 UNSUPPORTED_KEY_PARAM = 4, | |
| 645 TOKEN_BINDING_SUPPORT_MAX | |
| 646 } supported; | |
| 647 if (!IsSecureRequest()) | |
| 648 return; | |
| 649 SSLInfo ssl_info; | |
| 650 stream_->GetSSLInfo(&ssl_info); | |
| 651 if (!session_->params().enable_token_binding) { | |
| 652 supported = DISABLED; | |
| 653 } else if (!session_->params().channel_id_service) { | |
| 654 supported = CLIENT_NO_CHANNEL_ID_SERVICE; | |
| 655 } else if (ssl_info.token_binding_key_param != TB_PARAM_ECDSAP256) { | |
| 656 supported = UNSUPPORTED_KEY_PARAM; | |
|
davidben
2015/11/18 20:49:00
This can never happen, no? You'll only negotiate t
nharper
2015/12/04 01:42:19
My thought here was if the server can negotiate to
| |
| 657 } else if (ssl_info.token_binding_negotiated) { | |
| 658 supported = CLIENT_AND_SERVER; | |
| 659 } else { | |
| 660 supported = CLIENT_ONLY; | |
| 661 } | |
| 662 UMA_HISTOGRAM_ENUMERATION("TokenBinding.Support", supported, | |
| 663 TOKEN_BINDING_SUPPORT_MAX); | |
| 664 } | |
| 665 | |
| 617 bool HttpNetworkTransaction::UsingHttpProxyWithoutTunnel() const { | 666 bool HttpNetworkTransaction::UsingHttpProxyWithoutTunnel() const { |
| 618 return (proxy_info_.is_http() || proxy_info_.is_https() || | 667 return (proxy_info_.is_http() || proxy_info_.is_https() || |
| 619 proxy_info_.is_quic()) && | 668 proxy_info_.is_quic()) && |
| 620 !(request_->url.SchemeIs("https") || request_->url.SchemeIsWSOrWSS()); | 669 !(request_->url.SchemeIs("https") || request_->url.SchemeIsWSOrWSS()); |
| 621 } | 670 } |
| 622 | 671 |
| 623 void HttpNetworkTransaction::DoCallback(int rv) { | 672 void HttpNetworkTransaction::DoCallback(int rv) { |
| 624 DCHECK_NE(rv, ERR_IO_PENDING); | 673 DCHECK_NE(rv, ERR_IO_PENDING); |
| 625 DCHECK(!callback_.is_null()); | 674 DCHECK(!callback_.is_null()); |
| 626 | 675 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 669 case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE: | 718 case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE: |
| 670 rv = DoGenerateProxyAuthTokenComplete(rv); | 719 rv = DoGenerateProxyAuthTokenComplete(rv); |
| 671 break; | 720 break; |
| 672 case STATE_GENERATE_SERVER_AUTH_TOKEN: | 721 case STATE_GENERATE_SERVER_AUTH_TOKEN: |
| 673 DCHECK_EQ(OK, rv); | 722 DCHECK_EQ(OK, rv); |
| 674 rv = DoGenerateServerAuthToken(); | 723 rv = DoGenerateServerAuthToken(); |
| 675 break; | 724 break; |
| 676 case STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE: | 725 case STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE: |
| 677 rv = DoGenerateServerAuthTokenComplete(rv); | 726 rv = DoGenerateServerAuthTokenComplete(rv); |
| 678 break; | 727 break; |
| 728 case STATE_GET_TOKEN_BINDING_KEY: | |
| 729 DCHECK_EQ(OK, rv); | |
| 730 rv = DoGetTokenBindingKey(); | |
| 731 break; | |
| 732 case STATE_GET_TOKEN_BINDING_KEY_COMPLETE: | |
| 733 rv = DoGetTokenBindingKeyComplete(rv); | |
| 734 break; | |
| 679 case STATE_INIT_REQUEST_BODY: | 735 case STATE_INIT_REQUEST_BODY: |
| 680 DCHECK_EQ(OK, rv); | 736 DCHECK_EQ(OK, rv); |
| 681 rv = DoInitRequestBody(); | 737 rv = DoInitRequestBody(); |
| 682 break; | 738 break; |
| 683 case STATE_INIT_REQUEST_BODY_COMPLETE: | 739 case STATE_INIT_REQUEST_BODY_COMPLETE: |
| 684 rv = DoInitRequestBodyComplete(rv); | 740 rv = DoInitRequestBodyComplete(rv); |
| 685 break; | 741 break; |
| 686 case STATE_BUILD_REQUEST: | 742 case STATE_BUILD_REQUEST: |
| 687 DCHECK_EQ(OK, rv); | 743 DCHECK_EQ(OK, rv); |
| 688 net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST); | 744 net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST); |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 884 if (!ShouldApplyServerAuth()) | 940 if (!ShouldApplyServerAuth()) |
| 885 return OK; | 941 return OK; |
| 886 return auth_controllers_[target]->MaybeGenerateAuthToken(request_, | 942 return auth_controllers_[target]->MaybeGenerateAuthToken(request_, |
| 887 io_callback_, | 943 io_callback_, |
| 888 net_log_); | 944 net_log_); |
| 889 } | 945 } |
| 890 | 946 |
| 891 int HttpNetworkTransaction::DoGenerateServerAuthTokenComplete(int rv) { | 947 int HttpNetworkTransaction::DoGenerateServerAuthTokenComplete(int rv) { |
| 892 DCHECK_NE(ERR_IO_PENDING, rv); | 948 DCHECK_NE(ERR_IO_PENDING, rv); |
| 893 if (rv == OK) | 949 if (rv == OK) |
| 894 next_state_ = STATE_INIT_REQUEST_BODY; | 950 next_state_ = STATE_GET_TOKEN_BINDING_KEY; |
| 895 return rv; | 951 return rv; |
| 896 } | 952 } |
| 897 | 953 |
| 954 int HttpNetworkTransaction::DoGetTokenBindingKey() { | |
| 955 next_state_ = STATE_GET_TOKEN_BINDING_KEY_COMPLETE; | |
| 956 if (!IsTokenBindingEnabled()) | |
| 957 return OK; | |
| 958 | |
| 959 net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_GET_TOKEN_BINDING_KEY); | |
| 960 ChannelIDService* channel_id_service = session_->params().channel_id_service; | |
| 961 return channel_id_service->GetOrCreateChannelID( | |
| 962 request_->url.host(), &token_binding_key_, | |
| 963 base::Bind(&HttpNetworkTransaction::OnIOComplete, base::Unretained(this)), | |
|
davidben
2015/11/18 20:49:00
io_callback_
nharper
2015/12/04 01:42:19
Done.
| |
| 964 &token_binding_request_); | |
| 965 } | |
| 966 | |
| 967 int HttpNetworkTransaction::DoGetTokenBindingKeyComplete(int rv) { | |
| 968 DCHECK_NE(ERR_IO_PENDING, rv); | |
| 969 next_state_ = STATE_INIT_REQUEST_BODY; | |
| 970 if (!IsTokenBindingEnabled()) | |
| 971 return OK; | |
| 972 | |
| 973 if (rv == OK && !token_binding_key_) | |
| 974 rv = ERR_CHANNEL_ID_IMPORT_FAILED; | |
|
davidben
2015/11/18 20:49:00
Is this possible?
nharper
2015/12/04 01:42:19
I can't see how this would be possible (GetOrCreat
| |
| 975 | |
| 976 net_log_.EndEventWithNetErrorCode( | |
| 977 NetLog::TYPE_HTTP_TRANSACTION_GET_TOKEN_BINDING_KEY, rv); | |
| 978 return rv; | |
| 979 } | |
| 980 | |
| 898 void HttpNetworkTransaction::BuildRequestHeaders( | 981 void HttpNetworkTransaction::BuildRequestHeaders( |
| 899 bool using_http_proxy_without_tunnel) { | 982 bool using_http_proxy_without_tunnel) { |
| 900 request_headers_.SetHeader(HttpRequestHeaders::kHost, | 983 request_headers_.SetHeader(HttpRequestHeaders::kHost, |
| 901 GetHostAndOptionalPort(request_->url)); | 984 GetHostAndOptionalPort(request_->url)); |
| 902 | 985 |
| 903 // For compat with HTTP/1.0 servers and proxies: | 986 // For compat with HTTP/1.0 servers and proxies: |
| 904 if (using_http_proxy_without_tunnel) { | 987 if (using_http_proxy_without_tunnel) { |
| 905 request_headers_.SetHeader(HttpRequestHeaders::kProxyConnection, | 988 request_headers_.SetHeader(HttpRequestHeaders::kProxyConnection, |
| 906 "keep-alive"); | 989 "keep-alive"); |
| 907 } else { | 990 } else { |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 921 } else if (request_->method == "POST" || request_->method == "PUT") { | 1004 } else if (request_->method == "POST" || request_->method == "PUT") { |
| 922 // An empty POST/PUT request still needs a content length. As for HEAD, | 1005 // An empty POST/PUT request still needs a content length. As for HEAD, |
| 923 // IE and Safari also add a content length header. Presumably it is to | 1006 // IE and Safari also add a content length header. Presumably it is to |
| 924 // support sending a HEAD request to an URL that only expects to be sent a | 1007 // support sending a HEAD request to an URL that only expects to be sent a |
| 925 // POST or some other method that normally would have a message body. | 1008 // POST or some other method that normally would have a message body. |
| 926 // Firefox (40.0) does not send the header, and RFC 7230 & 7231 | 1009 // Firefox (40.0) does not send the header, and RFC 7230 & 7231 |
| 927 // specify that it should not be sent due to undefined behavior. | 1010 // specify that it should not be sent due to undefined behavior. |
| 928 request_headers_.SetHeader(HttpRequestHeaders::kContentLength, "0"); | 1011 request_headers_.SetHeader(HttpRequestHeaders::kContentLength, "0"); |
| 929 } | 1012 } |
| 930 | 1013 |
| 1014 RecordTokenBindingSupport(); | |
| 1015 if (token_binding_key_) { | |
|
davidben
2015/11/18 20:49:00
An HttpNetworkSession may run through a request mu
nharper
2015/12/04 01:42:19
Done.
| |
| 1016 std::string token_binding_header = BuildTokenBindingHeader(); | |
| 1017 if (token_binding_header != "") { | |
| 1018 request_headers_.SetHeader(HttpRequestHeaders::kTokenBinding, | |
| 1019 token_binding_header); | |
| 1020 } | |
| 1021 } | |
| 1022 | |
| 931 // Honor load flags that impact proxy caches. | 1023 // Honor load flags that impact proxy caches. |
| 932 if (request_->load_flags & LOAD_BYPASS_CACHE) { | 1024 if (request_->load_flags & LOAD_BYPASS_CACHE) { |
| 933 request_headers_.SetHeader(HttpRequestHeaders::kPragma, "no-cache"); | 1025 request_headers_.SetHeader(HttpRequestHeaders::kPragma, "no-cache"); |
| 934 request_headers_.SetHeader(HttpRequestHeaders::kCacheControl, "no-cache"); | 1026 request_headers_.SetHeader(HttpRequestHeaders::kCacheControl, "no-cache"); |
| 935 } else if (request_->load_flags & LOAD_VALIDATE_CACHE) { | 1027 } else if (request_->load_flags & LOAD_VALIDATE_CACHE) { |
| 936 request_headers_.SetHeader(HttpRequestHeaders::kCacheControl, "max-age=0"); | 1028 request_headers_.SetHeader(HttpRequestHeaders::kCacheControl, "max-age=0"); |
| 937 } | 1029 } |
| 938 | 1030 |
| 939 if (ShouldApplyProxyAuth() && HaveAuth(HttpAuth::AUTH_PROXY)) | 1031 if (ShouldApplyProxyAuth() && HaveAuth(HttpAuth::AUTH_PROXY)) |
| 940 auth_controllers_[HttpAuth::AUTH_PROXY]->AddAuthorizationHeader( | 1032 auth_controllers_[HttpAuth::AUTH_PROXY]->AddAuthorizationHeader( |
| 941 &request_headers_); | 1033 &request_headers_); |
| 942 if (ShouldApplyServerAuth() && HaveAuth(HttpAuth::AUTH_SERVER)) | 1034 if (ShouldApplyServerAuth() && HaveAuth(HttpAuth::AUTH_SERVER)) |
| 943 auth_controllers_[HttpAuth::AUTH_SERVER]->AddAuthorizationHeader( | 1035 auth_controllers_[HttpAuth::AUTH_SERVER]->AddAuthorizationHeader( |
| 944 &request_headers_); | 1036 &request_headers_); |
| 945 | 1037 |
| 946 request_headers_.MergeFrom(request_->extra_headers); | 1038 request_headers_.MergeFrom(request_->extra_headers); |
| 947 | 1039 |
| 948 if (using_http_proxy_without_tunnel && | 1040 if (using_http_proxy_without_tunnel && |
| 949 !before_proxy_headers_sent_callback_.is_null()) | 1041 !before_proxy_headers_sent_callback_.is_null()) |
| 950 before_proxy_headers_sent_callback_.Run(proxy_info_, &request_headers_); | 1042 before_proxy_headers_sent_callback_.Run(proxy_info_, &request_headers_); |
| 951 | 1043 |
| 952 response_.did_use_http_auth = | 1044 response_.did_use_http_auth = |
| 953 request_headers_.HasHeader(HttpRequestHeaders::kAuthorization) || | 1045 request_headers_.HasHeader(HttpRequestHeaders::kAuthorization) || |
| 954 request_headers_.HasHeader(HttpRequestHeaders::kProxyAuthorization); | 1046 request_headers_.HasHeader(HttpRequestHeaders::kProxyAuthorization); |
| 955 } | 1047 } |
| 956 | 1048 |
| 1049 std::string HttpNetworkTransaction::BuildTokenBindingHeader() { | |
| 1050 std::string provided_token_binding; | |
| 1051 int rv = stream_->GetProvidedTokenBindingWithKey(token_binding_key_, | |
| 1052 &provided_token_binding); | |
| 1053 if (rv != OK) | |
| 1054 return ""; | |
|
davidben
2015/11/18 20:49:00
If this or below fails, it should probably be fata
nharper
2015/12/04 01:42:19
Done.
| |
| 1055 std::vector<std::string> token_bindings; | |
| 1056 token_bindings.push_back(provided_token_binding); | |
| 1057 std::string header; | |
| 1058 rv = BuildTokenBindingMessageFromTokenBindings(token_bindings, &header); | |
| 1059 if (rv != OK) | |
| 1060 return ""; | |
| 1061 std::string base64_header; | |
| 1062 base::Base64Encode(header, &base64_header); | |
| 1063 base::ReplaceChars(base64_header, "+", "-", &base64_header); | |
| 1064 base::ReplaceChars(base64_header, "/", "_", &base64_header); | |
|
davidben
2015/11/18 20:49:00
base/base64url.h
nharper
2015/12/04 01:42:19
Done.
| |
| 1065 return base64_header; | |
| 1066 } | |
| 1067 | |
| 957 int HttpNetworkTransaction::DoInitRequestBody() { | 1068 int HttpNetworkTransaction::DoInitRequestBody() { |
| 958 next_state_ = STATE_INIT_REQUEST_BODY_COMPLETE; | 1069 next_state_ = STATE_INIT_REQUEST_BODY_COMPLETE; |
| 959 int rv = OK; | 1070 int rv = OK; |
| 960 if (request_->upload_data_stream) | 1071 if (request_->upload_data_stream) |
| 961 rv = request_->upload_data_stream->Init(io_callback_); | 1072 rv = request_->upload_data_stream->Init(io_callback_); |
| 962 return rv; | 1073 return rv; |
| 963 } | 1074 } |
| 964 | 1075 |
| 965 int HttpNetworkTransaction::DoInitRequestBodyComplete(int result) { | 1076 int HttpNetworkTransaction::DoInitRequestBodyComplete(int result) { |
| 966 if (result == OK) | 1077 if (result == OK) |
| (...skipping 706 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1673 DCHECK(stream_request_); | 1784 DCHECK(stream_request_); |
| 1674 | 1785 |
| 1675 // Since the transaction can restart with auth credentials, it may create a | 1786 // Since the transaction can restart with auth credentials, it may create a |
| 1676 // stream more than once. Accumulate all of the connection attempts across | 1787 // stream more than once. Accumulate all of the connection attempts across |
| 1677 // those streams by appending them to the vector: | 1788 // those streams by appending them to the vector: |
| 1678 for (const auto& attempt : stream_request_->connection_attempts()) | 1789 for (const auto& attempt : stream_request_->connection_attempts()) |
| 1679 connection_attempts_.push_back(attempt); | 1790 connection_attempts_.push_back(attempt); |
| 1680 } | 1791 } |
| 1681 | 1792 |
| 1682 } // namespace net | 1793 } // namespace net |
| OLD | NEW |