blob: f5058e8e416051253ba747a278e2473510a72f6d [file] [log] [blame]
Yixin Wang0d2c6b7e12017-08-16 21:12:551// Copyright (c) 2017 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
Ryan Hamiltona3ee93a72018-08-01 22:03:085#include "net/quic/quic_proxy_client_socket.h"
Yixin Wang0d2c6b7e12017-08-16 21:12:556
Bence Béky99832232018-02-11 04:21:007#include <cstdio>
8#include <utility>
9
Yixin Wang0d2c6b7e12017-08-16 21:12:5510#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/callback_helpers.h"
13#include "base/values.h"
14#include "net/http/http_auth_controller.h"
Eric Roman06bd9742019-07-13 15:19:1315#include "net/http/http_log_util.h"
Yixin Wang0d2c6b7e12017-08-16 21:12:5516#include "net/http/http_response_headers.h"
Yixin Wang0d2c6b7e12017-08-16 21:12:5517#include "net/log/net_log_source.h"
18#include "net/log/net_log_source_type.h"
Lily Chen8116b6172018-12-04 23:30:4219#include "net/quic/quic_http_utils.h"
Bence Béky94658bf2018-05-11 19:22:5820#include "net/spdy/spdy_http_utils.h"
[email protected]a2b2cfc2017-12-06 09:06:0821#include "net/traffic_annotation/network_traffic_annotation.h"
Yixin Wang0d2c6b7e12017-08-16 21:12:5522
23namespace net {
24
25QuicProxyClientSocket::QuicProxyClientSocket(
26 std::unique_ptr<QuicChromiumClientStream::Handle> stream,
27 std::unique_ptr<QuicChromiumClientSession::Handle> session,
28 const std::string& user_agent,
29 const HostPortPair& endpoint,
30 const NetLogWithSource& net_log,
31 HttpAuthController* auth_controller)
32 : next_state_(STATE_DISCONNECTED),
33 stream_(std::move(stream)),
34 session_(std::move(session)),
35 read_buf_(nullptr),
Yixin Wangdbbd8752018-01-17 21:50:0236 write_buf_len_(0),
Yixin Wang0d2c6b7e12017-08-16 21:12:5537 endpoint_(endpoint),
38 auth_(auth_controller),
39 user_agent_(user_agent),
Jeremy Romand54000b22019-07-08 18:40:1640 net_log_(net_log) {
Yixin Wang0d2c6b7e12017-08-16 21:12:5541 DCHECK(stream_->IsOpen());
42
43 request_.method = "CONNECT";
44 request_.url = GURL("https://" + endpoint.ToString());
45
Eric Roman06bd9742019-07-13 15:19:1346 net_log_.BeginEventReferencingSource(NetLogEventType::SOCKET_ALIVE,
47 net_log_.source());
48 net_log_.AddEventReferencingSource(
49 NetLogEventType::HTTP2_PROXY_CLIENT_SESSION, stream_->net_log().source());
Yixin Wang0d2c6b7e12017-08-16 21:12:5550}
51
52QuicProxyClientSocket::~QuicProxyClientSocket() {
53 Disconnect();
54 net_log_.EndEvent(NetLogEventType::SOCKET_ALIVE);
55}
56
57const HttpResponseInfo* QuicProxyClientSocket::GetConnectResponseInfo() const {
58 return response_.headers.get() ? &response_ : nullptr;
59}
60
Yixin Wang0d2c6b7e12017-08-16 21:12:5561const scoped_refptr<HttpAuthController>&
62QuicProxyClientSocket::GetAuthController() const {
63 return auth_;
64}
65
Bence Béky99832232018-02-11 04:21:0066int QuicProxyClientSocket::RestartWithAuth(CompletionOnceCallback callback) {
Yixin Wang0d2c6b7e12017-08-16 21:12:5567 // A QUIC Stream can only handle a single request, so the underlying
68 // stream may not be reused and a new QuicProxyClientSocket must be
69 // created (possibly on top of the same QUIC Session).
70 next_state_ = STATE_DISCONNECTED;
Matt Menkeae2cbb82019-02-21 20:20:1371 return ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
Yixin Wang0d2c6b7e12017-08-16 21:12:5572}
73
74bool QuicProxyClientSocket::IsUsingSpdy() const {
75 return false;
76}
77
78NextProto QuicProxyClientSocket::GetProxyNegotiatedProtocol() const {
79 return kProtoQUIC;
80}
81
Matt Menkeedaf3b82019-03-14 21:39:4482// Ignore priority changes, just use priority of initial request. Since multiple
83// requests are pooled on the QuicProxyClientSocket, reprioritization doesn't
84// really work.
85//
86// TODO(mmenke): Use a single priority value for all QuicProxyClientSockets,
87// regardless of what priority they're created with.
88void QuicProxyClientSocket::SetStreamPriority(RequestPriority priority) {}
Lily Chenf11e1292018-11-29 16:42:0989
Yixin Wang0d2c6b7e12017-08-16 21:12:5590// Sends a HEADERS frame to the proxy with a CONNECT request
91// for the specified endpoint. Waits for the server to send back
92// a HEADERS frame. OK will be returned if the status is 200.
93// ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status.
94// In any of these cases, Read() may be called to retrieve the HTTP
95// response body. Any other return values should be considered fatal.
Brad Lassey3a814172018-04-26 03:30:2196int QuicProxyClientSocket::Connect(CompletionOnceCallback callback) {
Yixin Wang0d2c6b7e12017-08-16 21:12:5597 DCHECK(connect_callback_.is_null());
98 if (!stream_->IsOpen())
99 return ERR_CONNECTION_CLOSED;
100
101 DCHECK_EQ(STATE_DISCONNECTED, next_state_);
102 next_state_ = STATE_GENERATE_AUTH_TOKEN;
103
104 int rv = DoLoop(OK);
105 if (rv == ERR_IO_PENDING)
Brad Lassey3a814172018-04-26 03:30:21106 connect_callback_ = std::move(callback);
Yixin Wang0d2c6b7e12017-08-16 21:12:55107 return rv;
108}
109
110void QuicProxyClientSocket::Disconnect() {
111 connect_callback_.Reset();
112 read_callback_.Reset();
113 read_buf_ = nullptr;
114 write_callback_.Reset();
Yixin Wangdbbd8752018-01-17 21:50:02115 write_buf_len_ = 0;
Yixin Wang0d2c6b7e12017-08-16 21:12:55116
117 next_state_ = STATE_DISCONNECTED;
118
Ryan Hamilton8d9ee76e2018-05-29 23:52:52119 stream_->Reset(quic::QUIC_STREAM_CANCELLED);
Yixin Wang0d2c6b7e12017-08-16 21:12:55120}
121
122bool QuicProxyClientSocket::IsConnected() const {
123 return next_state_ == STATE_CONNECT_COMPLETE && stream_->IsOpen();
124}
125
126bool QuicProxyClientSocket::IsConnectedAndIdle() const {
127 return IsConnected() && !stream_->HasBytesToRead();
128}
129
130const NetLogWithSource& QuicProxyClientSocket::NetLog() const {
131 return net_log_;
132}
133
Yixin Wang0d2c6b7e12017-08-16 21:12:55134bool QuicProxyClientSocket::WasEverUsed() const {
135 return session_->WasEverUsed();
136}
137
138bool QuicProxyClientSocket::WasAlpnNegotiated() const {
139 return false;
140}
141
142NextProto QuicProxyClientSocket::GetNegotiatedProtocol() const {
143 return kProtoUnknown;
144}
145
146bool QuicProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
147 return session_->GetSSLInfo(ssl_info);
148}
149
150void QuicProxyClientSocket::GetConnectionAttempts(
151 ConnectionAttempts* out) const {
152 out->clear();
153}
154
155int64_t QuicProxyClientSocket::GetTotalReceivedBytes() const {
156 return stream_->NumBytesConsumed();
157}
158
Paul Jensen0f49dec2017-12-12 23:39:58159void QuicProxyClientSocket::ApplySocketTag(const SocketTag& tag) {
Matt Menke5b7974fa2019-03-09 01:18:12160 // In the case of a connection to the proxy using HTTP/2 or HTTP/3 where the
161 // underlying socket may multiplex multiple streams, applying this request's
162 // socket tag to the multiplexed session would incorrectly apply the socket
163 // tag to all mutliplexed streams. Fortunately socket tagging is only
164 // supported on Android without the data reduction proxy, so only simple HTTP
165 // proxies are supported, so proxies won't be using HTTP/2 or HTTP/3. Enforce
166 // that a specific (non-default) tag isn't being applied.
167 CHECK(tag == SocketTag());
Paul Jensen0f49dec2017-12-12 23:39:58168}
169
Yixin Wang0d2c6b7e12017-08-16 21:12:55170int QuicProxyClientSocket::Read(IOBuffer* buf,
171 int buf_len,
Brad Lassey3a814172018-04-26 03:30:21172 CompletionOnceCallback callback) {
Yixin Wang0d2c6b7e12017-08-16 21:12:55173 DCHECK(connect_callback_.is_null());
174 DCHECK(read_callback_.is_null());
175 DCHECK(!read_buf_);
176
177 if (next_state_ == STATE_DISCONNECTED)
178 return ERR_SOCKET_NOT_CONNECTED;
179
180 if (!stream_->IsOpen()) {
181 return 0;
182 }
183
Yannic Bonenberger3c96beb2019-09-03 20:41:37184 int rv =
185 stream_->ReadBody(buf, buf_len,
186 base::BindOnce(&QuicProxyClientSocket::OnReadComplete,
187 weak_factory_.GetWeakPtr()));
Yixin Wang0d2c6b7e12017-08-16 21:12:55188
189 if (rv == ERR_IO_PENDING) {
Brad Lassey3a814172018-04-26 03:30:21190 read_callback_ = std::move(callback);
Yixin Wang0d2c6b7e12017-08-16 21:12:55191 read_buf_ = buf;
192 } else if (rv == 0) {
193 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, 0,
194 nullptr);
195 } else if (rv > 0) {
196 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, rv,
197 buf->data());
198 }
199 return rv;
200}
201
202void QuicProxyClientSocket::OnReadComplete(int rv) {
203 if (!stream_->IsOpen())
204 rv = 0;
205
206 if (!read_callback_.is_null()) {
207 DCHECK(read_buf_);
208 if (rv >= 0) {
209 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, rv,
210 read_buf_->data());
211 }
212 read_buf_ = nullptr;
Brad Lassey3a814172018-04-26 03:30:21213 std::move(read_callback_).Run(rv);
Yixin Wang0d2c6b7e12017-08-16 21:12:55214 }
215}
216
[email protected]a2b2cfc2017-12-06 09:06:08217int QuicProxyClientSocket::Write(
218 IOBuffer* buf,
219 int buf_len,
Brad Lassey3a814172018-04-26 03:30:21220 CompletionOnceCallback callback,
[email protected]a2b2cfc2017-12-06 09:06:08221 const NetworkTrafficAnnotationTag& traffic_annotation) {
Yixin Wang0d2c6b7e12017-08-16 21:12:55222 DCHECK(connect_callback_.is_null());
223 DCHECK(write_callback_.is_null());
224
225 if (next_state_ != STATE_CONNECT_COMPLETE)
226 return ERR_SOCKET_NOT_CONNECTED;
227
228 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_SENT, buf_len,
229 buf->data());
230
231 int rv = stream_->WriteStreamData(
Ryan Hamilton8d9ee76e2018-05-29 23:52:52232 quic::QuicStringPiece(buf->data(), buf_len), false,
Yannic Bonenberger3c96beb2019-09-03 20:41:37233 base::BindOnce(&QuicProxyClientSocket::OnWriteComplete,
234 weak_factory_.GetWeakPtr()));
Yixin Wangdbbd8752018-01-17 21:50:02235 if (rv == OK)
236 return buf_len;
Yixin Wang0d2c6b7e12017-08-16 21:12:55237
Yixin Wangdbbd8752018-01-17 21:50:02238 if (rv == ERR_IO_PENDING) {
Brad Lassey3a814172018-04-26 03:30:21239 write_callback_ = std::move(callback);
Yixin Wangdbbd8752018-01-17 21:50:02240 write_buf_len_ = buf_len;
241 }
Yixin Wang0d2c6b7e12017-08-16 21:12:55242
243 return rv;
244}
245
246void QuicProxyClientSocket::OnWriteComplete(int rv) {
Yixin Wangdbbd8752018-01-17 21:50:02247 if (!write_callback_.is_null()) {
248 if (rv == OK)
249 rv = write_buf_len_;
250 write_buf_len_ = 0;
Brad Lassey3a814172018-04-26 03:30:21251 std::move(write_callback_).Run(rv);
Yixin Wangdbbd8752018-01-17 21:50:02252 }
Yixin Wang0d2c6b7e12017-08-16 21:12:55253}
254
255int QuicProxyClientSocket::SetReceiveBufferSize(int32_t size) {
256 return ERR_NOT_IMPLEMENTED;
257}
258
259int QuicProxyClientSocket::SetSendBufferSize(int32_t size) {
260 return ERR_NOT_IMPLEMENTED;
261}
262
263int QuicProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
264 return IsConnected() ? session_->GetPeerAddress(address)
265 : ERR_SOCKET_NOT_CONNECTED;
266}
267
268int QuicProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
269 return IsConnected() ? session_->GetSelfAddress(address)
270 : ERR_SOCKET_NOT_CONNECTED;
271}
272
Yixin Wang0d2c6b7e12017-08-16 21:12:55273void QuicProxyClientSocket::OnIOComplete(int result) {
274 DCHECK_NE(STATE_DISCONNECTED, next_state_);
275 int rv = DoLoop(result);
276 if (rv != ERR_IO_PENDING) {
277 // Connect() finished (successfully or unsuccessfully).
278 DCHECK(!connect_callback_.is_null());
Brad Lassey3a814172018-04-26 03:30:21279 std::move(connect_callback_).Run(rv);
Yixin Wang0d2c6b7e12017-08-16 21:12:55280 }
281}
282
283int QuicProxyClientSocket::DoLoop(int last_io_result) {
284 DCHECK_NE(next_state_, STATE_DISCONNECTED);
285 int rv = last_io_result;
286 do {
287 State state = next_state_;
288 next_state_ = STATE_DISCONNECTED;
289 switch (state) {
290 case STATE_GENERATE_AUTH_TOKEN:
291 DCHECK_EQ(OK, rv);
292 rv = DoGenerateAuthToken();
293 break;
294 case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
295 rv = DoGenerateAuthTokenComplete(rv);
296 break;
297 case STATE_SEND_REQUEST:
298 DCHECK_EQ(OK, rv);
299 net_log_.BeginEvent(
300 NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST);
301 rv = DoSendRequest();
302 break;
303 case STATE_SEND_REQUEST_COMPLETE:
304 net_log_.EndEventWithNetErrorCode(
305 NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
306 rv = DoSendRequestComplete(rv);
307 break;
308 case STATE_READ_REPLY:
309 rv = DoReadReply();
310 break;
311 case STATE_READ_REPLY_COMPLETE:
312 rv = DoReadReplyComplete(rv);
313 net_log_.EndEventWithNetErrorCode(
314 NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
315 break;
316 default:
317 NOTREACHED() << "bad state";
318 rv = ERR_UNEXPECTED;
319 break;
320 }
321 } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
322 next_state_ != STATE_CONNECT_COMPLETE);
323 return rv;
324}
325
326int QuicProxyClientSocket::DoGenerateAuthToken() {
327 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
328 return auth_->MaybeGenerateAuthToken(
329 &request_,
Yannic Bonenberger3c96beb2019-09-03 20:41:37330 base::BindOnce(&QuicProxyClientSocket::OnIOComplete,
331 weak_factory_.GetWeakPtr()),
Yixin Wang0d2c6b7e12017-08-16 21:12:55332 net_log_);
333}
334
335int QuicProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
336 DCHECK_NE(ERR_IO_PENDING, result);
337 if (result == OK)
338 next_state_ = STATE_SEND_REQUEST;
339 return result;
340}
341
342int QuicProxyClientSocket::DoSendRequest() {
343 next_state_ = STATE_SEND_REQUEST_COMPLETE;
344
345 // Add Proxy-Authentication header if necessary.
346 HttpRequestHeaders authorization_headers;
347 if (auth_->HaveAuth()) {
348 auth_->AddAuthorizationHeader(&authorization_headers);
349 }
350
351 std::string request_line;
352 BuildTunnelRequest(endpoint_, authorization_headers, user_agent_,
353 &request_line, &request_.extra_headers);
354
Eric Roman06bd9742019-07-13 15:19:13355 NetLogRequestHeaders(net_log_,
356 NetLogEventType::HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
357 request_line, &request_.extra_headers);
Yixin Wang0d2c6b7e12017-08-16 21:12:55358
Ryan Hamilton0239aac2018-05-19 00:03:13359 spdy::SpdyHeaderBlock headers;
Bence Béky1af94d6f2018-02-08 00:40:14360 CreateSpdyHeadersFromHttpRequest(request_, request_.extra_headers, &headers);
Yixin Wang0d2c6b7e12017-08-16 21:12:55361
362 return stream_->WriteHeaders(std::move(headers), false, nullptr);
363}
364
365int QuicProxyClientSocket::DoSendRequestComplete(int result) {
366 if (result >= 0) {
367 // Wait for HEADERS frame from the server
368 next_state_ = STATE_READ_REPLY; // STATE_READ_REPLY_COMPLETE;
369 result = OK;
370 }
371
372 if (result >= 0 || result == ERR_IO_PENDING) {
373 // Emit extra event so can use the same events as HttpProxyClientSocket.
374 net_log_.BeginEvent(NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS);
375 }
376
377 return result;
378}
379
380int QuicProxyClientSocket::DoReadReply() {
381 next_state_ = STATE_READ_REPLY_COMPLETE;
382
383 int rv = stream_->ReadInitialHeaders(
384 &response_header_block_,
Yannic Bonenberger3c96beb2019-09-03 20:41:37385 base::BindOnce(&QuicProxyClientSocket::OnReadResponseHeadersComplete,
386 weak_factory_.GetWeakPtr()));
Yixin Wang0d2c6b7e12017-08-16 21:12:55387 if (rv == ERR_IO_PENDING)
388 return ERR_IO_PENDING;
389 if (rv < 0)
390 return rv;
391
392 return ProcessResponseHeaders(response_header_block_);
393}
394
395int QuicProxyClientSocket::DoReadReplyComplete(int result) {
396 if (result < 0)
397 return result;
398
399 // Require the "HTTP/1.x" status line for SSL CONNECT.
400 if (response_.headers->GetHttpVersion() < HttpVersion(1, 0))
401 return ERR_TUNNEL_CONNECTION_FAILED;
402
Eric Roman06bd9742019-07-13 15:19:13403 NetLogResponseHeaders(
404 net_log_, NetLogEventType::HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
405 response_.headers.get());
Yixin Wang0d2c6b7e12017-08-16 21:12:55406
407 switch (response_.headers->response_code()) {
408 case 200: // OK
409 next_state_ = STATE_CONNECT_COMPLETE;
410 return OK;
411
Yixin Wang0d2c6b7e12017-08-16 21:12:55412 case 407: // Proxy Authentication Required
413 next_state_ = STATE_CONNECT_COMPLETE;
Matt Menkeabd9da82018-05-10 19:02:58414 if (!SanitizeProxyAuth(&response_))
Yixin Wang0d2c6b7e12017-08-16 21:12:55415 return ERR_TUNNEL_CONNECTION_FAILED;
Yixin Wang0d2c6b7e12017-08-16 21:12:55416 return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_);
417
418 default:
419 // Ignore response to avoid letting the proxy impersonate the target
420 // server. (See http://crbug.com/137891.)
Yixin Wang0d2c6b7e12017-08-16 21:12:55421 return ERR_TUNNEL_CONNECTION_FAILED;
422 }
423}
424
425void QuicProxyClientSocket::OnReadResponseHeadersComplete(int result) {
Ryan Hamilton0239aac2018-05-19 00:03:13426 // Convert the now-populated spdy::SpdyHeaderBlock to HttpResponseInfo
Yixin Wang0d2c6b7e12017-08-16 21:12:55427 if (result > 0)
428 result = ProcessResponseHeaders(response_header_block_);
429
430 if (result != ERR_IO_PENDING)
431 OnIOComplete(result);
432}
433
434int QuicProxyClientSocket::ProcessResponseHeaders(
Ryan Hamilton0239aac2018-05-19 00:03:13435 const spdy::SpdyHeaderBlock& headers) {
Yixin Wang0d2c6b7e12017-08-16 21:12:55436 if (!SpdyHeadersToHttpResponse(headers, &response_)) {
437 DLOG(WARNING) << "Invalid headers";
438 return ERR_QUIC_PROTOCOL_ERROR;
439 }
440 // Populate |connect_timing_| when response headers are received. This
441 // should take care of 0-RTT where request is sent before handshake is
442 // confirmed.
443 connect_timing_ = session_->GetConnectTiming();
444 return OK;
445}
446
447bool QuicProxyClientSocket::GetLoadTimingInfo(
448 LoadTimingInfo* load_timing_info) const {
449 bool is_first_stream = stream_->IsFirstStream();
450 if (stream_)
451 is_first_stream = stream_->IsFirstStream();
452 if (is_first_stream) {
453 load_timing_info->socket_reused = false;
454 load_timing_info->connect_timing = connect_timing_;
455 } else {
456 load_timing_info->socket_reused = true;
457 }
458 return true;
459}
460
461} // namespace net