blob: ebcb3148286d4fde9ffbb5916e307439c3641463 [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"
15#include "net/http/http_response_headers.h"
16#include "net/http/proxy_connect_redirect_http_stream.h"
17#include "net/log/net_log_source.h"
18#include "net/log/net_log_source_type.h"
Bence Béky94658bf2018-05-11 19:22:5819#include "net/spdy/spdy_http_utils.h"
[email protected]a2b2cfc2017-12-06 09:06:0820#include "net/traffic_annotation/network_traffic_annotation.h"
Yixin Wang0d2c6b7e12017-08-16 21:12:5521
22namespace net {
23
24QuicProxyClientSocket::QuicProxyClientSocket(
25 std::unique_ptr<QuicChromiumClientStream::Handle> stream,
26 std::unique_ptr<QuicChromiumClientSession::Handle> session,
27 const std::string& user_agent,
28 const HostPortPair& endpoint,
29 const NetLogWithSource& net_log,
30 HttpAuthController* auth_controller)
31 : next_state_(STATE_DISCONNECTED),
32 stream_(std::move(stream)),
33 session_(std::move(session)),
34 read_buf_(nullptr),
Yixin Wangdbbd8752018-01-17 21:50:0235 write_buf_len_(0),
Yixin Wang0d2c6b7e12017-08-16 21:12:5536 endpoint_(endpoint),
37 auth_(auth_controller),
38 user_agent_(user_agent),
39 redirect_has_load_timing_info_(false),
40 net_log_(net_log),
41 weak_factory_(this) {
42 DCHECK(stream_->IsOpen());
43
44 request_.method = "CONNECT";
45 request_.url = GURL("https://" + endpoint.ToString());
46
47 net_log_.BeginEvent(NetLogEventType::SOCKET_ALIVE,
48 net_log_.source().ToEventParametersCallback());
49 net_log_.AddEvent(NetLogEventType::HTTP2_PROXY_CLIENT_SESSION,
50 stream_->net_log().source().ToEventParametersCallback());
51}
52
53QuicProxyClientSocket::~QuicProxyClientSocket() {
54 Disconnect();
55 net_log_.EndEvent(NetLogEventType::SOCKET_ALIVE);
56}
57
58const HttpResponseInfo* QuicProxyClientSocket::GetConnectResponseInfo() const {
59 return response_.headers.get() ? &response_ : nullptr;
60}
61
62std::unique_ptr<HttpStream>
63QuicProxyClientSocket::CreateConnectResponseStream() {
Jeremy Roman0579ed62017-08-29 15:56:1964 return std::make_unique<ProxyConnectRedirectHttpStream>(
Yixin Wang0d2c6b7e12017-08-16 21:12:5565 redirect_has_load_timing_info_ ? &redirect_load_timing_info_ : nullptr);
66}
67
68const scoped_refptr<HttpAuthController>&
69QuicProxyClientSocket::GetAuthController() const {
70 return auth_;
71}
72
Bence Béky99832232018-02-11 04:21:0073int QuicProxyClientSocket::RestartWithAuth(CompletionOnceCallback callback) {
Yixin Wang0d2c6b7e12017-08-16 21:12:5574 // A QUIC Stream can only handle a single request, so the underlying
75 // stream may not be reused and a new QuicProxyClientSocket must be
76 // created (possibly on top of the same QUIC Session).
77 next_state_ = STATE_DISCONNECTED;
78 return OK;
79}
80
81bool QuicProxyClientSocket::IsUsingSpdy() const {
82 return false;
83}
84
85NextProto QuicProxyClientSocket::GetProxyNegotiatedProtocol() const {
86 return kProtoQUIC;
87}
88
89// Sends a HEADERS frame to the proxy with a CONNECT request
90// for the specified endpoint. Waits for the server to send back
91// a HEADERS frame. OK will be returned if the status is 200.
92// ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status.
93// In any of these cases, Read() may be called to retrieve the HTTP
94// response body. Any other return values should be considered fatal.
Brad Lassey3a814172018-04-26 03:30:2195int QuicProxyClientSocket::Connect(CompletionOnceCallback callback) {
Yixin Wang0d2c6b7e12017-08-16 21:12:5596 DCHECK(connect_callback_.is_null());
97 if (!stream_->IsOpen())
98 return ERR_CONNECTION_CLOSED;
99
100 DCHECK_EQ(STATE_DISCONNECTED, next_state_);
101 next_state_ = STATE_GENERATE_AUTH_TOKEN;
102
103 int rv = DoLoop(OK);
104 if (rv == ERR_IO_PENDING)
Brad Lassey3a814172018-04-26 03:30:21105 connect_callback_ = std::move(callback);
Yixin Wang0d2c6b7e12017-08-16 21:12:55106 return rv;
107}
108
109void QuicProxyClientSocket::Disconnect() {
110 connect_callback_.Reset();
111 read_callback_.Reset();
112 read_buf_ = nullptr;
113 write_callback_.Reset();
Yixin Wangdbbd8752018-01-17 21:50:02114 write_buf_len_ = 0;
Yixin Wang0d2c6b7e12017-08-16 21:12:55115
116 next_state_ = STATE_DISCONNECTED;
117
Ryan Hamilton8d9ee76e2018-05-29 23:52:52118 stream_->Reset(quic::QUIC_STREAM_CANCELLED);
Yixin Wang0d2c6b7e12017-08-16 21:12:55119}
120
121bool QuicProxyClientSocket::IsConnected() const {
122 return next_state_ == STATE_CONNECT_COMPLETE && stream_->IsOpen();
123}
124
125bool QuicProxyClientSocket::IsConnectedAndIdle() const {
126 return IsConnected() && !stream_->HasBytesToRead();
127}
128
129const NetLogWithSource& QuicProxyClientSocket::NetLog() const {
130 return net_log_;
131}
132
Yixin Wang0d2c6b7e12017-08-16 21:12:55133bool QuicProxyClientSocket::WasEverUsed() const {
134 return session_->WasEverUsed();
135}
136
137bool QuicProxyClientSocket::WasAlpnNegotiated() const {
138 return false;
139}
140
141NextProto QuicProxyClientSocket::GetNegotiatedProtocol() const {
142 return kProtoUnknown;
143}
144
145bool QuicProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
146 return session_->GetSSLInfo(ssl_info);
147}
148
149void QuicProxyClientSocket::GetConnectionAttempts(
150 ConnectionAttempts* out) const {
151 out->clear();
152}
153
154int64_t QuicProxyClientSocket::GetTotalReceivedBytes() const {
155 return stream_->NumBytesConsumed();
156}
157
Paul Jensen0f49dec2017-12-12 23:39:58158void QuicProxyClientSocket::ApplySocketTag(const SocketTag& tag) {
159 // |session_| can be tagged, but |stream_| cannot.
160 CHECK(false);
161}
162
Yixin Wang0d2c6b7e12017-08-16 21:12:55163int QuicProxyClientSocket::Read(IOBuffer* buf,
164 int buf_len,
Brad Lassey3a814172018-04-26 03:30:21165 CompletionOnceCallback callback) {
Yixin Wang0d2c6b7e12017-08-16 21:12:55166 DCHECK(connect_callback_.is_null());
167 DCHECK(read_callback_.is_null());
168 DCHECK(!read_buf_);
169
170 if (next_state_ == STATE_DISCONNECTED)
171 return ERR_SOCKET_NOT_CONNECTED;
172
173 if (!stream_->IsOpen()) {
174 return 0;
175 }
176
177 int rv = stream_->ReadBody(buf, buf_len,
178 base::Bind(&QuicProxyClientSocket::OnReadComplete,
179 weak_factory_.GetWeakPtr()));
180
181 if (rv == ERR_IO_PENDING) {
Brad Lassey3a814172018-04-26 03:30:21182 read_callback_ = std::move(callback);
Yixin Wang0d2c6b7e12017-08-16 21:12:55183 read_buf_ = buf;
184 } else if (rv == 0) {
185 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, 0,
186 nullptr);
187 } else if (rv > 0) {
188 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, rv,
189 buf->data());
190 }
191 return rv;
192}
193
194void QuicProxyClientSocket::OnReadComplete(int rv) {
195 if (!stream_->IsOpen())
196 rv = 0;
197
198 if (!read_callback_.is_null()) {
199 DCHECK(read_buf_);
200 if (rv >= 0) {
201 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, rv,
202 read_buf_->data());
203 }
204 read_buf_ = nullptr;
Brad Lassey3a814172018-04-26 03:30:21205 std::move(read_callback_).Run(rv);
Yixin Wang0d2c6b7e12017-08-16 21:12:55206 }
207}
208
[email protected]a2b2cfc2017-12-06 09:06:08209int QuicProxyClientSocket::Write(
210 IOBuffer* buf,
211 int buf_len,
Brad Lassey3a814172018-04-26 03:30:21212 CompletionOnceCallback callback,
[email protected]a2b2cfc2017-12-06 09:06:08213 const NetworkTrafficAnnotationTag& traffic_annotation) {
Yixin Wang0d2c6b7e12017-08-16 21:12:55214 DCHECK(connect_callback_.is_null());
215 DCHECK(write_callback_.is_null());
216
217 if (next_state_ != STATE_CONNECT_COMPLETE)
218 return ERR_SOCKET_NOT_CONNECTED;
219
220 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_SENT, buf_len,
221 buf->data());
222
223 int rv = stream_->WriteStreamData(
Ryan Hamilton8d9ee76e2018-05-29 23:52:52224 quic::QuicStringPiece(buf->data(), buf_len), false,
Yixin Wang0d2c6b7e12017-08-16 21:12:55225 base::Bind(&QuicProxyClientSocket::OnWriteComplete,
226 weak_factory_.GetWeakPtr()));
Yixin Wangdbbd8752018-01-17 21:50:02227 if (rv == OK)
228 return buf_len;
Yixin Wang0d2c6b7e12017-08-16 21:12:55229
Yixin Wangdbbd8752018-01-17 21:50:02230 if (rv == ERR_IO_PENDING) {
Brad Lassey3a814172018-04-26 03:30:21231 write_callback_ = std::move(callback);
Yixin Wangdbbd8752018-01-17 21:50:02232 write_buf_len_ = buf_len;
233 }
Yixin Wang0d2c6b7e12017-08-16 21:12:55234
235 return rv;
236}
237
238void QuicProxyClientSocket::OnWriteComplete(int rv) {
Yixin Wangdbbd8752018-01-17 21:50:02239 if (!write_callback_.is_null()) {
240 if (rv == OK)
241 rv = write_buf_len_;
242 write_buf_len_ = 0;
Brad Lassey3a814172018-04-26 03:30:21243 std::move(write_callback_).Run(rv);
Yixin Wangdbbd8752018-01-17 21:50:02244 }
Yixin Wang0d2c6b7e12017-08-16 21:12:55245}
246
247int QuicProxyClientSocket::SetReceiveBufferSize(int32_t size) {
248 return ERR_NOT_IMPLEMENTED;
249}
250
251int QuicProxyClientSocket::SetSendBufferSize(int32_t size) {
252 return ERR_NOT_IMPLEMENTED;
253}
254
255int QuicProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
256 return IsConnected() ? session_->GetPeerAddress(address)
257 : ERR_SOCKET_NOT_CONNECTED;
258}
259
260int QuicProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
261 return IsConnected() ? session_->GetSelfAddress(address)
262 : ERR_SOCKET_NOT_CONNECTED;
263}
264
Yixin Wang0d2c6b7e12017-08-16 21:12:55265void QuicProxyClientSocket::OnIOComplete(int result) {
266 DCHECK_NE(STATE_DISCONNECTED, next_state_);
267 int rv = DoLoop(result);
268 if (rv != ERR_IO_PENDING) {
269 // Connect() finished (successfully or unsuccessfully).
270 DCHECK(!connect_callback_.is_null());
Brad Lassey3a814172018-04-26 03:30:21271 std::move(connect_callback_).Run(rv);
Yixin Wang0d2c6b7e12017-08-16 21:12:55272 }
273}
274
275int QuicProxyClientSocket::DoLoop(int last_io_result) {
276 DCHECK_NE(next_state_, STATE_DISCONNECTED);
277 int rv = last_io_result;
278 do {
279 State state = next_state_;
280 next_state_ = STATE_DISCONNECTED;
281 switch (state) {
282 case STATE_GENERATE_AUTH_TOKEN:
283 DCHECK_EQ(OK, rv);
284 rv = DoGenerateAuthToken();
285 break;
286 case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
287 rv = DoGenerateAuthTokenComplete(rv);
288 break;
289 case STATE_SEND_REQUEST:
290 DCHECK_EQ(OK, rv);
291 net_log_.BeginEvent(
292 NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST);
293 rv = DoSendRequest();
294 break;
295 case STATE_SEND_REQUEST_COMPLETE:
296 net_log_.EndEventWithNetErrorCode(
297 NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
298 rv = DoSendRequestComplete(rv);
299 break;
300 case STATE_READ_REPLY:
301 rv = DoReadReply();
302 break;
303 case STATE_READ_REPLY_COMPLETE:
304 rv = DoReadReplyComplete(rv);
305 net_log_.EndEventWithNetErrorCode(
306 NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
307 break;
308 default:
309 NOTREACHED() << "bad state";
310 rv = ERR_UNEXPECTED;
311 break;
312 }
313 } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
314 next_state_ != STATE_CONNECT_COMPLETE);
315 return rv;
316}
317
318int QuicProxyClientSocket::DoGenerateAuthToken() {
319 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
320 return auth_->MaybeGenerateAuthToken(
321 &request_,
322 base::Bind(&QuicProxyClientSocket::OnIOComplete,
323 weak_factory_.GetWeakPtr()),
324 net_log_);
325}
326
327int QuicProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
328 DCHECK_NE(ERR_IO_PENDING, result);
329 if (result == OK)
330 next_state_ = STATE_SEND_REQUEST;
331 return result;
332}
333
334int QuicProxyClientSocket::DoSendRequest() {
335 next_state_ = STATE_SEND_REQUEST_COMPLETE;
336
337 // Add Proxy-Authentication header if necessary.
338 HttpRequestHeaders authorization_headers;
339 if (auth_->HaveAuth()) {
340 auth_->AddAuthorizationHeader(&authorization_headers);
341 }
342
343 std::string request_line;
344 BuildTunnelRequest(endpoint_, authorization_headers, user_agent_,
345 &request_line, &request_.extra_headers);
346
347 net_log_.AddEvent(
348 NetLogEventType::HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
349 base::Bind(&HttpRequestHeaders::NetLogCallback,
350 base::Unretained(&request_.extra_headers), &request_line));
351
Ryan Hamilton0239aac2018-05-19 00:03:13352 spdy::SpdyHeaderBlock headers;
Bence Béky1af94d6f2018-02-08 00:40:14353 CreateSpdyHeadersFromHttpRequest(request_, request_.extra_headers, &headers);
Yixin Wang0d2c6b7e12017-08-16 21:12:55354
355 return stream_->WriteHeaders(std::move(headers), false, nullptr);
356}
357
358int QuicProxyClientSocket::DoSendRequestComplete(int result) {
359 if (result >= 0) {
360 // Wait for HEADERS frame from the server
361 next_state_ = STATE_READ_REPLY; // STATE_READ_REPLY_COMPLETE;
362 result = OK;
363 }
364
365 if (result >= 0 || result == ERR_IO_PENDING) {
366 // Emit extra event so can use the same events as HttpProxyClientSocket.
367 net_log_.BeginEvent(NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS);
368 }
369
370 return result;
371}
372
373int QuicProxyClientSocket::DoReadReply() {
374 next_state_ = STATE_READ_REPLY_COMPLETE;
375
376 int rv = stream_->ReadInitialHeaders(
377 &response_header_block_,
378 base::Bind(&QuicProxyClientSocket::OnReadResponseHeadersComplete,
379 weak_factory_.GetWeakPtr()));
380 if (rv == ERR_IO_PENDING)
381 return ERR_IO_PENDING;
382 if (rv < 0)
383 return rv;
384
385 return ProcessResponseHeaders(response_header_block_);
386}
387
388int QuicProxyClientSocket::DoReadReplyComplete(int result) {
389 if (result < 0)
390 return result;
391
392 // Require the "HTTP/1.x" status line for SSL CONNECT.
393 if (response_.headers->GetHttpVersion() < HttpVersion(1, 0))
394 return ERR_TUNNEL_CONNECTION_FAILED;
395
396 net_log_.AddEvent(
397 NetLogEventType::HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
398 base::Bind(&HttpResponseHeaders::NetLogCallback, response_.headers));
399
400 switch (response_.headers->response_code()) {
401 case 200: // OK
402 next_state_ = STATE_CONNECT_COMPLETE;
403 return OK;
404
405 case 302: // Found / Moved Temporarily
406 // Try to return a sanitized response so we can follow auth redirects.
407 // If we can't, fail the tunnel connection.
Matt Menkeabd9da82018-05-10 19:02:58408 if (!SanitizeProxyRedirect(&response_))
Yixin Wang0d2c6b7e12017-08-16 21:12:55409 return ERR_TUNNEL_CONNECTION_FAILED;
Yixin Wang0d2c6b7e12017-08-16 21:12:55410 redirect_has_load_timing_info_ =
411 GetLoadTimingInfo(&redirect_load_timing_info_);
412 next_state_ = STATE_DISCONNECTED;
413 return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
414
415 case 407: // Proxy Authentication Required
416 next_state_ = STATE_CONNECT_COMPLETE;
Matt Menkeabd9da82018-05-10 19:02:58417 if (!SanitizeProxyAuth(&response_))
Yixin Wang0d2c6b7e12017-08-16 21:12:55418 return ERR_TUNNEL_CONNECTION_FAILED;
Yixin Wang0d2c6b7e12017-08-16 21:12:55419 return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_);
420
421 default:
422 // Ignore response to avoid letting the proxy impersonate the target
423 // server. (See http://crbug.com/137891.)
Yixin Wang0d2c6b7e12017-08-16 21:12:55424 return ERR_TUNNEL_CONNECTION_FAILED;
425 }
426}
427
428void QuicProxyClientSocket::OnReadResponseHeadersComplete(int result) {
Ryan Hamilton0239aac2018-05-19 00:03:13429 // Convert the now-populated spdy::SpdyHeaderBlock to HttpResponseInfo
Yixin Wang0d2c6b7e12017-08-16 21:12:55430 if (result > 0)
431 result = ProcessResponseHeaders(response_header_block_);
432
433 if (result != ERR_IO_PENDING)
434 OnIOComplete(result);
435}
436
437int QuicProxyClientSocket::ProcessResponseHeaders(
Ryan Hamilton0239aac2018-05-19 00:03:13438 const spdy::SpdyHeaderBlock& headers) {
Yixin Wang0d2c6b7e12017-08-16 21:12:55439 if (!SpdyHeadersToHttpResponse(headers, &response_)) {
440 DLOG(WARNING) << "Invalid headers";
441 return ERR_QUIC_PROTOCOL_ERROR;
442 }
443 // Populate |connect_timing_| when response headers are received. This
444 // should take care of 0-RTT where request is sent before handshake is
445 // confirmed.
446 connect_timing_ = session_->GetConnectTiming();
447 return OK;
448}
449
450bool QuicProxyClientSocket::GetLoadTimingInfo(
451 LoadTimingInfo* load_timing_info) const {
452 bool is_first_stream = stream_->IsFirstStream();
453 if (stream_)
454 is_first_stream = stream_->IsFirstStream();
455 if (is_first_stream) {
456 load_timing_info->socket_reused = false;
457 load_timing_info->connect_timing = connect_timing_;
458 } else {
459 load_timing_info->socket_reused = true;
460 }
461 return true;
462}
463
464} // namespace net