blob: f870a8b72c4da87cdeb795a2227fca105c7f3521 [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"
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),
40 redirect_has_load_timing_info_(false),
41 net_log_(net_log),
42 weak_factory_(this) {
43 DCHECK(stream_->IsOpen());
44
45 request_.method = "CONNECT";
46 request_.url = GURL("https://" + endpoint.ToString());
47
48 net_log_.BeginEvent(NetLogEventType::SOCKET_ALIVE,
49 net_log_.source().ToEventParametersCallback());
50 net_log_.AddEvent(NetLogEventType::HTTP2_PROXY_CLIENT_SESSION,
51 stream_->net_log().source().ToEventParametersCallback());
52}
53
54QuicProxyClientSocket::~QuicProxyClientSocket() {
55 Disconnect();
56 net_log_.EndEvent(NetLogEventType::SOCKET_ALIVE);
57}
58
59const HttpResponseInfo* QuicProxyClientSocket::GetConnectResponseInfo() const {
60 return response_.headers.get() ? &response_ : nullptr;
61}
62
63std::unique_ptr<HttpStream>
64QuicProxyClientSocket::CreateConnectResponseStream() {
Jeremy Roman0579ed62017-08-29 15:56:1965 return std::make_unique<ProxyConnectRedirectHttpStream>(
Yixin Wang0d2c6b7e12017-08-16 21:12:5566 redirect_has_load_timing_info_ ? &redirect_load_timing_info_ : nullptr);
67}
68
69const scoped_refptr<HttpAuthController>&
70QuicProxyClientSocket::GetAuthController() const {
71 return auth_;
72}
73
Bence Béky99832232018-02-11 04:21:0074int QuicProxyClientSocket::RestartWithAuth(CompletionOnceCallback callback) {
Yixin Wang0d2c6b7e12017-08-16 21:12:5575 // A QUIC Stream can only handle a single request, so the underlying
76 // stream may not be reused and a new QuicProxyClientSocket must be
77 // created (possibly on top of the same QUIC Session).
78 next_state_ = STATE_DISCONNECTED;
79 return OK;
80}
81
82bool QuicProxyClientSocket::IsUsingSpdy() const {
83 return false;
84}
85
86NextProto QuicProxyClientSocket::GetProxyNegotiatedProtocol() const {
87 return kProtoQUIC;
88}
89
Lily Chenf11e1292018-11-29 16:42:0990void QuicProxyClientSocket::SetStreamPriority(RequestPriority priority) {
Lily Chen8116b6172018-12-04 23:30:4291 stream_->SetPriority(ConvertRequestPriorityToQuicPriority(priority));
Lily Chenf11e1292018-11-29 16:42:0992}
93
Yixin Wang0d2c6b7e12017-08-16 21:12:5594// Sends a HEADERS frame to the proxy with a CONNECT request
95// for the specified endpoint. Waits for the server to send back
96// a HEADERS frame. OK will be returned if the status is 200.
97// ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status.
98// In any of these cases, Read() may be called to retrieve the HTTP
99// response body. Any other return values should be considered fatal.
Brad Lassey3a814172018-04-26 03:30:21100int QuicProxyClientSocket::Connect(CompletionOnceCallback callback) {
Yixin Wang0d2c6b7e12017-08-16 21:12:55101 DCHECK(connect_callback_.is_null());
102 if (!stream_->IsOpen())
103 return ERR_CONNECTION_CLOSED;
104
105 DCHECK_EQ(STATE_DISCONNECTED, next_state_);
106 next_state_ = STATE_GENERATE_AUTH_TOKEN;
107
108 int rv = DoLoop(OK);
109 if (rv == ERR_IO_PENDING)
Brad Lassey3a814172018-04-26 03:30:21110 connect_callback_ = std::move(callback);
Yixin Wang0d2c6b7e12017-08-16 21:12:55111 return rv;
112}
113
114void QuicProxyClientSocket::Disconnect() {
115 connect_callback_.Reset();
116 read_callback_.Reset();
117 read_buf_ = nullptr;
118 write_callback_.Reset();
Yixin Wangdbbd8752018-01-17 21:50:02119 write_buf_len_ = 0;
Yixin Wang0d2c6b7e12017-08-16 21:12:55120
121 next_state_ = STATE_DISCONNECTED;
122
Ryan Hamilton8d9ee76e2018-05-29 23:52:52123 stream_->Reset(quic::QUIC_STREAM_CANCELLED);
Yixin Wang0d2c6b7e12017-08-16 21:12:55124}
125
126bool QuicProxyClientSocket::IsConnected() const {
127 return next_state_ == STATE_CONNECT_COMPLETE && stream_->IsOpen();
128}
129
130bool QuicProxyClientSocket::IsConnectedAndIdle() const {
131 return IsConnected() && !stream_->HasBytesToRead();
132}
133
134const NetLogWithSource& QuicProxyClientSocket::NetLog() const {
135 return net_log_;
136}
137
Yixin Wang0d2c6b7e12017-08-16 21:12:55138bool QuicProxyClientSocket::WasEverUsed() const {
139 return session_->WasEverUsed();
140}
141
142bool QuicProxyClientSocket::WasAlpnNegotiated() const {
143 return false;
144}
145
146NextProto QuicProxyClientSocket::GetNegotiatedProtocol() const {
147 return kProtoUnknown;
148}
149
150bool QuicProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
151 return session_->GetSSLInfo(ssl_info);
152}
153
154void QuicProxyClientSocket::GetConnectionAttempts(
155 ConnectionAttempts* out) const {
156 out->clear();
157}
158
159int64_t QuicProxyClientSocket::GetTotalReceivedBytes() const {
160 return stream_->NumBytesConsumed();
161}
162
Paul Jensen0f49dec2017-12-12 23:39:58163void QuicProxyClientSocket::ApplySocketTag(const SocketTag& tag) {
164 // |session_| can be tagged, but |stream_| cannot.
165 CHECK(false);
166}
167
Yixin Wang0d2c6b7e12017-08-16 21:12:55168int QuicProxyClientSocket::Read(IOBuffer* buf,
169 int buf_len,
Brad Lassey3a814172018-04-26 03:30:21170 CompletionOnceCallback callback) {
Yixin Wang0d2c6b7e12017-08-16 21:12:55171 DCHECK(connect_callback_.is_null());
172 DCHECK(read_callback_.is_null());
173 DCHECK(!read_buf_);
174
175 if (next_state_ == STATE_DISCONNECTED)
176 return ERR_SOCKET_NOT_CONNECTED;
177
178 if (!stream_->IsOpen()) {
179 return 0;
180 }
181
182 int rv = stream_->ReadBody(buf, buf_len,
183 base::Bind(&QuicProxyClientSocket::OnReadComplete,
184 weak_factory_.GetWeakPtr()));
185
186 if (rv == ERR_IO_PENDING) {
Brad Lassey3a814172018-04-26 03:30:21187 read_callback_ = std::move(callback);
Yixin Wang0d2c6b7e12017-08-16 21:12:55188 read_buf_ = buf;
189 } else if (rv == 0) {
190 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, 0,
191 nullptr);
192 } else if (rv > 0) {
193 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, rv,
194 buf->data());
195 }
196 return rv;
197}
198
199void QuicProxyClientSocket::OnReadComplete(int rv) {
200 if (!stream_->IsOpen())
201 rv = 0;
202
203 if (!read_callback_.is_null()) {
204 DCHECK(read_buf_);
205 if (rv >= 0) {
206 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_RECEIVED, rv,
207 read_buf_->data());
208 }
209 read_buf_ = nullptr;
Brad Lassey3a814172018-04-26 03:30:21210 std::move(read_callback_).Run(rv);
Yixin Wang0d2c6b7e12017-08-16 21:12:55211 }
212}
213
[email protected]a2b2cfc2017-12-06 09:06:08214int QuicProxyClientSocket::Write(
215 IOBuffer* buf,
216 int buf_len,
Brad Lassey3a814172018-04-26 03:30:21217 CompletionOnceCallback callback,
[email protected]a2b2cfc2017-12-06 09:06:08218 const NetworkTrafficAnnotationTag& traffic_annotation) {
Yixin Wang0d2c6b7e12017-08-16 21:12:55219 DCHECK(connect_callback_.is_null());
220 DCHECK(write_callback_.is_null());
221
222 if (next_state_ != STATE_CONNECT_COMPLETE)
223 return ERR_SOCKET_NOT_CONNECTED;
224
225 net_log_.AddByteTransferEvent(NetLogEventType::SOCKET_BYTES_SENT, buf_len,
226 buf->data());
227
228 int rv = stream_->WriteStreamData(
Ryan Hamilton8d9ee76e2018-05-29 23:52:52229 quic::QuicStringPiece(buf->data(), buf_len), false,
Yixin Wang0d2c6b7e12017-08-16 21:12:55230 base::Bind(&QuicProxyClientSocket::OnWriteComplete,
231 weak_factory_.GetWeakPtr()));
Yixin Wangdbbd8752018-01-17 21:50:02232 if (rv == OK)
233 return buf_len;
Yixin Wang0d2c6b7e12017-08-16 21:12:55234
Yixin Wangdbbd8752018-01-17 21:50:02235 if (rv == ERR_IO_PENDING) {
Brad Lassey3a814172018-04-26 03:30:21236 write_callback_ = std::move(callback);
Yixin Wangdbbd8752018-01-17 21:50:02237 write_buf_len_ = buf_len;
238 }
Yixin Wang0d2c6b7e12017-08-16 21:12:55239
240 return rv;
241}
242
243void QuicProxyClientSocket::OnWriteComplete(int rv) {
Yixin Wangdbbd8752018-01-17 21:50:02244 if (!write_callback_.is_null()) {
245 if (rv == OK)
246 rv = write_buf_len_;
247 write_buf_len_ = 0;
Brad Lassey3a814172018-04-26 03:30:21248 std::move(write_callback_).Run(rv);
Yixin Wangdbbd8752018-01-17 21:50:02249 }
Yixin Wang0d2c6b7e12017-08-16 21:12:55250}
251
252int QuicProxyClientSocket::SetReceiveBufferSize(int32_t size) {
253 return ERR_NOT_IMPLEMENTED;
254}
255
256int QuicProxyClientSocket::SetSendBufferSize(int32_t size) {
257 return ERR_NOT_IMPLEMENTED;
258}
259
260int QuicProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
261 return IsConnected() ? session_->GetPeerAddress(address)
262 : ERR_SOCKET_NOT_CONNECTED;
263}
264
265int QuicProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
266 return IsConnected() ? session_->GetSelfAddress(address)
267 : ERR_SOCKET_NOT_CONNECTED;
268}
269
Yixin Wang0d2c6b7e12017-08-16 21:12:55270void QuicProxyClientSocket::OnIOComplete(int result) {
271 DCHECK_NE(STATE_DISCONNECTED, next_state_);
272 int rv = DoLoop(result);
273 if (rv != ERR_IO_PENDING) {
274 // Connect() finished (successfully or unsuccessfully).
275 DCHECK(!connect_callback_.is_null());
Brad Lassey3a814172018-04-26 03:30:21276 std::move(connect_callback_).Run(rv);
Yixin Wang0d2c6b7e12017-08-16 21:12:55277 }
278}
279
280int QuicProxyClientSocket::DoLoop(int last_io_result) {
281 DCHECK_NE(next_state_, STATE_DISCONNECTED);
282 int rv = last_io_result;
283 do {
284 State state = next_state_;
285 next_state_ = STATE_DISCONNECTED;
286 switch (state) {
287 case STATE_GENERATE_AUTH_TOKEN:
288 DCHECK_EQ(OK, rv);
289 rv = DoGenerateAuthToken();
290 break;
291 case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
292 rv = DoGenerateAuthTokenComplete(rv);
293 break;
294 case STATE_SEND_REQUEST:
295 DCHECK_EQ(OK, rv);
296 net_log_.BeginEvent(
297 NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST);
298 rv = DoSendRequest();
299 break;
300 case STATE_SEND_REQUEST_COMPLETE:
301 net_log_.EndEventWithNetErrorCode(
302 NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
303 rv = DoSendRequestComplete(rv);
304 break;
305 case STATE_READ_REPLY:
306 rv = DoReadReply();
307 break;
308 case STATE_READ_REPLY_COMPLETE:
309 rv = DoReadReplyComplete(rv);
310 net_log_.EndEventWithNetErrorCode(
311 NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
312 break;
313 default:
314 NOTREACHED() << "bad state";
315 rv = ERR_UNEXPECTED;
316 break;
317 }
318 } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
319 next_state_ != STATE_CONNECT_COMPLETE);
320 return rv;
321}
322
323int QuicProxyClientSocket::DoGenerateAuthToken() {
324 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
325 return auth_->MaybeGenerateAuthToken(
326 &request_,
327 base::Bind(&QuicProxyClientSocket::OnIOComplete,
328 weak_factory_.GetWeakPtr()),
329 net_log_);
330}
331
332int QuicProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
333 DCHECK_NE(ERR_IO_PENDING, result);
334 if (result == OK)
335 next_state_ = STATE_SEND_REQUEST;
336 return result;
337}
338
339int QuicProxyClientSocket::DoSendRequest() {
340 next_state_ = STATE_SEND_REQUEST_COMPLETE;
341
342 // Add Proxy-Authentication header if necessary.
343 HttpRequestHeaders authorization_headers;
344 if (auth_->HaveAuth()) {
345 auth_->AddAuthorizationHeader(&authorization_headers);
346 }
347
348 std::string request_line;
349 BuildTunnelRequest(endpoint_, authorization_headers, user_agent_,
350 &request_line, &request_.extra_headers);
351
352 net_log_.AddEvent(
353 NetLogEventType::HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
354 base::Bind(&HttpRequestHeaders::NetLogCallback,
355 base::Unretained(&request_.extra_headers), &request_line));
356
Ryan Hamilton0239aac2018-05-19 00:03:13357 spdy::SpdyHeaderBlock headers;
Bence Béky1af94d6f2018-02-08 00:40:14358 CreateSpdyHeadersFromHttpRequest(request_, request_.extra_headers, &headers);
Yixin Wang0d2c6b7e12017-08-16 21:12:55359
360 return stream_->WriteHeaders(std::move(headers), false, nullptr);
361}
362
363int QuicProxyClientSocket::DoSendRequestComplete(int result) {
364 if (result >= 0) {
365 // Wait for HEADERS frame from the server
366 next_state_ = STATE_READ_REPLY; // STATE_READ_REPLY_COMPLETE;
367 result = OK;
368 }
369
370 if (result >= 0 || result == ERR_IO_PENDING) {
371 // Emit extra event so can use the same events as HttpProxyClientSocket.
372 net_log_.BeginEvent(NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS);
373 }
374
375 return result;
376}
377
378int QuicProxyClientSocket::DoReadReply() {
379 next_state_ = STATE_READ_REPLY_COMPLETE;
380
381 int rv = stream_->ReadInitialHeaders(
382 &response_header_block_,
383 base::Bind(&QuicProxyClientSocket::OnReadResponseHeadersComplete,
384 weak_factory_.GetWeakPtr()));
385 if (rv == ERR_IO_PENDING)
386 return ERR_IO_PENDING;
387 if (rv < 0)
388 return rv;
389
390 return ProcessResponseHeaders(response_header_block_);
391}
392
393int QuicProxyClientSocket::DoReadReplyComplete(int result) {
394 if (result < 0)
395 return result;
396
397 // Require the "HTTP/1.x" status line for SSL CONNECT.
398 if (response_.headers->GetHttpVersion() < HttpVersion(1, 0))
399 return ERR_TUNNEL_CONNECTION_FAILED;
400
401 net_log_.AddEvent(
402 NetLogEventType::HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
403 base::Bind(&HttpResponseHeaders::NetLogCallback, response_.headers));
404
405 switch (response_.headers->response_code()) {
406 case 200: // OK
407 next_state_ = STATE_CONNECT_COMPLETE;
408 return OK;
409
410 case 302: // Found / Moved Temporarily
411 // Try to return a sanitized response so we can follow auth redirects.
412 // If we can't, fail the tunnel connection.
Matt Menkeabd9da82018-05-10 19:02:58413 if (!SanitizeProxyRedirect(&response_))
Yixin Wang0d2c6b7e12017-08-16 21:12:55414 return ERR_TUNNEL_CONNECTION_FAILED;
Yixin Wang0d2c6b7e12017-08-16 21:12:55415 redirect_has_load_timing_info_ =
416 GetLoadTimingInfo(&redirect_load_timing_info_);
417 next_state_ = STATE_DISCONNECTED;
418 return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
419
420 case 407: // Proxy Authentication Required
421 next_state_ = STATE_CONNECT_COMPLETE;
Matt Menkeabd9da82018-05-10 19:02:58422 if (!SanitizeProxyAuth(&response_))
Yixin Wang0d2c6b7e12017-08-16 21:12:55423 return ERR_TUNNEL_CONNECTION_FAILED;
Yixin Wang0d2c6b7e12017-08-16 21:12:55424 return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_);
425
426 default:
427 // Ignore response to avoid letting the proxy impersonate the target
428 // server. (See http://crbug.com/137891.)
Yixin Wang0d2c6b7e12017-08-16 21:12:55429 return ERR_TUNNEL_CONNECTION_FAILED;
430 }
431}
432
433void QuicProxyClientSocket::OnReadResponseHeadersComplete(int result) {
Ryan Hamilton0239aac2018-05-19 00:03:13434 // Convert the now-populated spdy::SpdyHeaderBlock to HttpResponseInfo
Yixin Wang0d2c6b7e12017-08-16 21:12:55435 if (result > 0)
436 result = ProcessResponseHeaders(response_header_block_);
437
438 if (result != ERR_IO_PENDING)
439 OnIOComplete(result);
440}
441
442int QuicProxyClientSocket::ProcessResponseHeaders(
Ryan Hamilton0239aac2018-05-19 00:03:13443 const spdy::SpdyHeaderBlock& headers) {
Yixin Wang0d2c6b7e12017-08-16 21:12:55444 if (!SpdyHeadersToHttpResponse(headers, &response_)) {
445 DLOG(WARNING) << "Invalid headers";
446 return ERR_QUIC_PROTOCOL_ERROR;
447 }
448 // Populate |connect_timing_| when response headers are received. This
449 // should take care of 0-RTT where request is sent before handshake is
450 // confirmed.
451 connect_timing_ = session_->GetConnectTiming();
452 return OK;
453}
454
455bool QuicProxyClientSocket::GetLoadTimingInfo(
456 LoadTimingInfo* load_timing_info) const {
457 bool is_first_stream = stream_->IsFirstStream();
458 if (stream_)
459 is_first_stream = stream_->IsFirstStream();
460 if (is_first_stream) {
461 load_timing_info->socket_reused = false;
462 load_timing_info->connect_timing = connect_timing_;
463 } else {
464 load_timing_info->socket_reused = true;
465 }
466 return true;
467}
468
469} // namespace net