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
3 // found in the LICENSE file.
5 #include "net/spdy/spdy_proxy_client_socket.h"
7 #include <algorithm> // min
10 #include "base/bind_helpers.h"
11 #include "base/callback_helpers.h"
12 #include "base/logging.h"
13 #include "base/strings/string_util.h"
14 #include "base/values.h"
15 #include "net/base/auth.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_util.h"
18 #include "net/http/http_auth_cache.h"
19 #include "net/http/http_auth_handler_factory.h"
20 #include "net/http/http_response_headers.h"
21 #include "net/http/proxy_connect_redirect_http_stream.h"
22 #include "net/spdy/spdy_http_utils.h"
27 SpdyProxyClientSocket::SpdyProxyClientSocket(
28 const base::WeakPtr
<SpdyStream
>& spdy_stream
,
29 const std::string
& user_agent
,
30 const HostPortPair
& endpoint
,
31 const HostPortPair
& proxy_server
,
32 const BoundNetLog
& source_net_log
,
33 HttpAuthCache
* auth_cache
,
34 HttpAuthHandlerFactory
* auth_handler_factory
)
35 : next_state_(STATE_DISCONNECTED
),
36 spdy_stream_(spdy_stream
),
38 auth_(new HttpAuthController(HttpAuth::AUTH_PROXY
,
39 GURL("https://" + proxy_server
.ToString()),
41 auth_handler_factory
)),
42 user_agent_(user_agent
),
45 was_ever_used_(false),
46 redirect_has_load_timing_info_(false),
47 net_log_(BoundNetLog::Make(spdy_stream
->net_log().net_log(),
48 NetLog::SOURCE_PROXY_CLIENT_SOCKET
)),
50 write_callback_weak_factory_(this) {
51 request_
.method
= "CONNECT";
52 request_
.url
= GURL("https://" + endpoint
.ToString());
53 net_log_
.BeginEvent(NetLog::TYPE_SOCKET_ALIVE
,
54 source_net_log
.source().ToEventParametersCallback());
56 NetLog::TYPE_HTTP2_PROXY_CLIENT_SESSION
,
57 spdy_stream
->net_log().source().ToEventParametersCallback());
59 spdy_stream_
->SetDelegate(this);
60 was_ever_used_
= spdy_stream_
->WasEverUsed();
63 SpdyProxyClientSocket::~SpdyProxyClientSocket() {
65 net_log_
.EndEvent(NetLog::TYPE_SOCKET_ALIVE
);
68 const HttpResponseInfo
* SpdyProxyClientSocket::GetConnectResponseInfo() const {
69 return response_
.headers
.get() ? &response_
: NULL
;
72 const scoped_refptr
<HttpAuthController
>&
73 SpdyProxyClientSocket::GetAuthController() const {
77 int SpdyProxyClientSocket::RestartWithAuth(const CompletionCallback
& callback
) {
78 // A SPDY Stream can only handle a single request, so the underlying
79 // stream may not be reused and a new SpdyProxyClientSocket must be
80 // created (possibly on top of the same SPDY Session).
81 next_state_
= STATE_DISCONNECTED
;
85 bool SpdyProxyClientSocket::IsUsingSpdy() const {
89 NextProto
SpdyProxyClientSocket::GetProtocolNegotiated() const {
90 // Save the negotiated protocol
92 bool was_npn_negotiated
;
93 NextProto protocol_negotiated
;
94 spdy_stream_
->GetSSLInfo(&ssl_info
, &was_npn_negotiated
,
95 &protocol_negotiated
);
96 return protocol_negotiated
;
99 HttpStream
* SpdyProxyClientSocket::CreateConnectResponseStream() {
100 return new ProxyConnectRedirectHttpStream(
101 redirect_has_load_timing_info_
? &redirect_load_timing_info_
: NULL
);
104 // Sends a SYN_STREAM frame to the proxy with a CONNECT request
105 // for the specified endpoint. Waits for the server to send back
106 // a SYN_REPLY frame. OK will be returned if the status is 200.
107 // ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status.
108 // In any of these cases, Read() may be called to retrieve the HTTP
109 // response body. Any other return values should be considered fatal.
110 // TODO(rch): handle 407 proxy auth requested correctly, perhaps
111 // by creating a new stream for the subsequent request.
112 // TODO(rch): create a more appropriate error code to disambiguate
113 // the HTTPS Proxy tunnel failure from an HTTP Proxy tunnel failure.
114 int SpdyProxyClientSocket::Connect(const CompletionCallback
& callback
) {
115 DCHECK(read_callback_
.is_null());
116 if (next_state_
== STATE_OPEN
)
119 DCHECK_EQ(STATE_DISCONNECTED
, next_state_
);
120 next_state_
= STATE_GENERATE_AUTH_TOKEN
;
123 if (rv
== ERR_IO_PENDING
)
124 read_callback_
= callback
;
128 void SpdyProxyClientSocket::Disconnect() {
129 read_buffer_queue_
.Clear();
131 user_buffer_len_
= 0;
132 read_callback_
.Reset();
134 write_buffer_len_
= 0;
135 write_callback_
.Reset();
136 write_callback_weak_factory_
.InvalidateWeakPtrs();
138 next_state_
= STATE_DISCONNECTED
;
140 if (spdy_stream_
.get()) {
141 // This will cause OnClose to be invoked, which takes care of
142 // cleaning up all the internal state.
143 spdy_stream_
->Cancel();
144 DCHECK(!spdy_stream_
.get());
148 bool SpdyProxyClientSocket::IsConnected() const {
149 return next_state_
== STATE_OPEN
;
152 bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
153 return IsConnected() && read_buffer_queue_
.IsEmpty() &&
154 spdy_stream_
->IsOpen();
157 const BoundNetLog
& SpdyProxyClientSocket::NetLog() const {
161 void SpdyProxyClientSocket::SetSubresourceSpeculation() {
162 // TODO(rch): what should this implementation be?
165 void SpdyProxyClientSocket::SetOmniboxSpeculation() {
166 // TODO(rch): what should this implementation be?
169 bool SpdyProxyClientSocket::WasEverUsed() const {
170 return was_ever_used_
|| (spdy_stream_
.get() && spdy_stream_
->WasEverUsed());
173 bool SpdyProxyClientSocket::UsingTCPFastOpen() const {
177 bool SpdyProxyClientSocket::WasNpnNegotiated() const {
181 NextProto
SpdyProxyClientSocket::GetNegotiatedProtocol() const {
182 return kProtoUnknown
;
185 bool SpdyProxyClientSocket::GetSSLInfo(SSLInfo
* ssl_info
) {
186 bool was_npn_negotiated
;
187 NextProto protocol_negotiated
;
188 return spdy_stream_
->GetSSLInfo(ssl_info
, &was_npn_negotiated
,
189 &protocol_negotiated
);
192 int SpdyProxyClientSocket::Read(IOBuffer
* buf
, int buf_len
,
193 const CompletionCallback
& callback
) {
194 DCHECK(read_callback_
.is_null());
195 DCHECK(!user_buffer_
.get());
197 if (next_state_
== STATE_DISCONNECTED
)
198 return ERR_SOCKET_NOT_CONNECTED
;
200 if (next_state_
== STATE_CLOSED
&& read_buffer_queue_
.IsEmpty()) {
204 DCHECK(next_state_
== STATE_OPEN
|| next_state_
== STATE_CLOSED
);
206 size_t result
= PopulateUserReadBuffer(buf
->data(), buf_len
);
209 user_buffer_len_
= static_cast<size_t>(buf_len
);
210 DCHECK(!callback
.is_null());
211 read_callback_
= callback
;
212 return ERR_IO_PENDING
;
218 size_t SpdyProxyClientSocket::PopulateUserReadBuffer(char* data
, size_t len
) {
219 return read_buffer_queue_
.Dequeue(data
, len
);
222 int SpdyProxyClientSocket::Write(IOBuffer
* buf
, int buf_len
,
223 const CompletionCallback
& callback
) {
224 DCHECK(write_callback_
.is_null());
225 if (next_state_
!= STATE_OPEN
)
226 return ERR_SOCKET_NOT_CONNECTED
;
228 DCHECK(spdy_stream_
.get());
229 spdy_stream_
->SendData(buf
, buf_len
, MORE_DATA_TO_SEND
);
230 net_log_
.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT
,
231 buf_len
, buf
->data());
232 write_callback_
= callback
;
233 write_buffer_len_
= buf_len
;
234 return ERR_IO_PENDING
;
237 int SpdyProxyClientSocket::SetReceiveBufferSize(int32 size
) {
238 // Since this StreamSocket sits on top of a shared SpdySession, it
239 // is not safe for callers to change this underlying socket.
240 return ERR_NOT_IMPLEMENTED
;
243 int SpdyProxyClientSocket::SetSendBufferSize(int32 size
) {
244 // Since this StreamSocket sits on top of a shared SpdySession, it
245 // is not safe for callers to change this underlying socket.
246 return ERR_NOT_IMPLEMENTED
;
249 int SpdyProxyClientSocket::GetPeerAddress(IPEndPoint
* address
) const {
251 return ERR_SOCKET_NOT_CONNECTED
;
252 return spdy_stream_
->GetPeerAddress(address
);
255 int SpdyProxyClientSocket::GetLocalAddress(IPEndPoint
* address
) const {
257 return ERR_SOCKET_NOT_CONNECTED
;
258 return spdy_stream_
->GetLocalAddress(address
);
261 void SpdyProxyClientSocket::LogBlockedTunnelResponse() const {
262 ProxyClientSocket::LogBlockedTunnelResponse(
263 response_
.headers
->response_code(),
264 /* is_https_proxy = */ true);
267 void SpdyProxyClientSocket::RunCallback(const CompletionCallback
& callback
,
269 callback
.Run(result
);
272 void SpdyProxyClientSocket::OnIOComplete(int result
) {
273 DCHECK_NE(STATE_DISCONNECTED
, next_state_
);
274 int rv
= DoLoop(result
);
275 if (rv
!= ERR_IO_PENDING
) {
276 CompletionCallback c
= read_callback_
;
277 read_callback_
.Reset();
282 int SpdyProxyClientSocket::DoLoop(int last_io_result
) {
283 DCHECK_NE(next_state_
, STATE_DISCONNECTED
);
284 int rv
= last_io_result
;
286 State state
= next_state_
;
287 next_state_
= STATE_DISCONNECTED
;
289 case STATE_GENERATE_AUTH_TOKEN
:
291 rv
= DoGenerateAuthToken();
293 case STATE_GENERATE_AUTH_TOKEN_COMPLETE
:
294 rv
= DoGenerateAuthTokenComplete(rv
);
296 case STATE_SEND_REQUEST
:
298 net_log_
.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST
);
299 rv
= DoSendRequest();
301 case STATE_SEND_REQUEST_COMPLETE
:
302 net_log_
.EndEventWithNetErrorCode(
303 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST
, rv
);
304 rv
= DoSendRequestComplete(rv
);
305 if (rv
>= 0 || rv
== ERR_IO_PENDING
) {
306 // Emit extra event so can use the same events as
307 // HttpProxyClientSocket.
309 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS
);
312 case STATE_READ_REPLY_COMPLETE
:
313 rv
= DoReadReplyComplete(rv
);
314 net_log_
.EndEventWithNetErrorCode(
315 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS
, rv
);
318 NOTREACHED() << "bad state";
322 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_DISCONNECTED
&&
323 next_state_
!= STATE_OPEN
);
327 int SpdyProxyClientSocket::DoGenerateAuthToken() {
328 next_state_
= STATE_GENERATE_AUTH_TOKEN_COMPLETE
;
329 return auth_
->MaybeGenerateAuthToken(
331 base::Bind(&SpdyProxyClientSocket::OnIOComplete
,
332 weak_factory_
.GetWeakPtr()),
336 int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result
) {
337 DCHECK_NE(ERR_IO_PENDING
, result
);
339 next_state_
= STATE_SEND_REQUEST
;
343 int SpdyProxyClientSocket::DoSendRequest() {
344 next_state_
= STATE_SEND_REQUEST_COMPLETE
;
346 // Add Proxy-Authentication header if necessary.
347 HttpRequestHeaders authorization_headers
;
348 if (auth_
->HaveAuth()) {
349 auth_
->AddAuthorizationHeader(&authorization_headers
);
352 std::string request_line
;
353 BuildTunnelRequest(endpoint_
, authorization_headers
, user_agent_
,
354 &request_line
, &request_
.extra_headers
);
357 NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS
,
358 base::Bind(&HttpRequestHeaders::NetLogCallback
,
359 base::Unretained(&request_
.extra_headers
), &request_line
));
361 scoped_ptr
<SpdyHeaderBlock
> headers(new SpdyHeaderBlock());
362 CreateSpdyHeadersFromHttpRequest(request_
, request_
.extra_headers
,
363 spdy_stream_
->GetProtocolVersion(), true,
365 // Reset the URL to be the endpoint of the connection
366 if (spdy_stream_
->GetProtocolVersion() > 2) {
367 (*headers
)[":path"] = endpoint_
.ToString();
368 headers
->erase(":scheme");
370 (*headers
)["url"] = endpoint_
.ToString();
371 headers
->erase("scheme");
374 return spdy_stream_
->SendRequestHeaders(headers
.Pass(), MORE_DATA_TO_SEND
);
377 int SpdyProxyClientSocket::DoSendRequestComplete(int result
) {
381 // Wait for SYN_REPLY frame from the server
382 next_state_
= STATE_READ_REPLY_COMPLETE
;
383 return ERR_IO_PENDING
;
386 int SpdyProxyClientSocket::DoReadReplyComplete(int result
) {
387 // We enter this method directly from DoSendRequestComplete, since
388 // we are notified by a callback when the SYN_REPLY frame arrives
393 // Require the "HTTP/1.x" status line for SSL CONNECT.
394 if (response_
.headers
->GetParsedHttpVersion() < HttpVersion(1, 0))
395 return ERR_TUNNEL_CONNECTION_FAILED
;
398 NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS
,
399 base::Bind(&HttpResponseHeaders::NetLogCallback
, response_
.headers
));
401 switch (response_
.headers
->response_code()) {
403 next_state_
= STATE_OPEN
;
406 case 302: // Found / Moved Temporarily
407 // Try to return a sanitized response so we can follow auth redirects.
408 // If we can't, fail the tunnel connection.
409 if (!SanitizeProxyRedirect(&response_
)) {
410 LogBlockedTunnelResponse();
411 return ERR_TUNNEL_CONNECTION_FAILED
;
414 redirect_has_load_timing_info_
=
415 spdy_stream_
->GetLoadTimingInfo(&redirect_load_timing_info_
);
416 // Note that this triggers a RST_STREAM_CANCEL.
417 spdy_stream_
->DetachDelegate();
418 next_state_
= STATE_DISCONNECTED
;
419 return ERR_HTTPS_PROXY_TUNNEL_RESPONSE
;
421 case 407: // Proxy Authentication Required
422 next_state_
= STATE_OPEN
;
423 if (!SanitizeProxyAuth(&response_
)) {
424 LogBlockedTunnelResponse();
425 return ERR_TUNNEL_CONNECTION_FAILED
;
427 return HandleProxyAuthChallenge(auth_
.get(), &response_
, net_log_
);
430 // Ignore response to avoid letting the proxy impersonate the target
431 // server. (See http://crbug.com/137891.)
432 LogBlockedTunnelResponse();
433 return ERR_TUNNEL_CONNECTION_FAILED
;
437 // SpdyStream::Delegate methods:
438 // Called when SYN frame has been sent.
439 // Returns true if no more data to be sent after SYN frame.
440 void SpdyProxyClientSocket::OnRequestHeadersSent() {
441 DCHECK_EQ(next_state_
, STATE_SEND_REQUEST_COMPLETE
);
446 SpdyResponseHeadersStatus
SpdyProxyClientSocket::OnResponseHeadersUpdated(
447 const SpdyHeaderBlock
& response_headers
) {
448 // If we've already received the reply, existing headers are too late.
449 // TODO(mbelshe): figure out a way to make HEADERS frames useful after the
451 if (next_state_
!= STATE_READ_REPLY_COMPLETE
)
452 return RESPONSE_HEADERS_ARE_COMPLETE
;
455 if (!SpdyHeadersToHttpResponse(
456 response_headers
, spdy_stream_
->GetProtocolVersion(), &response_
))
457 return RESPONSE_HEADERS_ARE_INCOMPLETE
;
460 return RESPONSE_HEADERS_ARE_COMPLETE
;
463 // Called when data is received or on EOF (if |buffer| is NULL).
464 void SpdyProxyClientSocket::OnDataReceived(scoped_ptr
<SpdyBuffer
> buffer
) {
466 net_log_
.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED
,
467 buffer
->GetRemainingSize(),
468 buffer
->GetRemainingData());
469 read_buffer_queue_
.Enqueue(buffer
.Pass());
471 net_log_
.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED
, 0, NULL
);
474 if (!read_callback_
.is_null()) {
475 int rv
= PopulateUserReadBuffer(user_buffer_
->data(), user_buffer_len_
);
476 CompletionCallback c
= read_callback_
;
477 read_callback_
.Reset();
479 user_buffer_len_
= 0;
484 void SpdyProxyClientSocket::OnDataSent() {
485 DCHECK(!write_callback_
.is_null());
487 int rv
= write_buffer_len_
;
488 write_buffer_len_
= 0;
490 // Proxy write callbacks result in deep callback chains. Post to allow the
491 // stream's write callback chain to unwind (see crbug.com/355511).
492 base::MessageLoop::current()->PostTask(
494 base::Bind(&SpdyProxyClientSocket::RunCallback
,
495 write_callback_weak_factory_
.GetWeakPtr(),
496 ResetAndReturn(&write_callback_
),
500 void SpdyProxyClientSocket::OnClose(int status
) {
501 was_ever_used_
= spdy_stream_
->WasEverUsed();
502 spdy_stream_
.reset();
504 bool connecting
= next_state_
!= STATE_DISCONNECTED
&&
505 next_state_
< STATE_OPEN
;
506 if (next_state_
== STATE_OPEN
)
507 next_state_
= STATE_CLOSED
;
509 next_state_
= STATE_DISCONNECTED
;
511 base::WeakPtr
<SpdyProxyClientSocket
> weak_ptr
= weak_factory_
.GetWeakPtr();
512 CompletionCallback write_callback
= write_callback_
;
513 write_callback_
.Reset();
514 write_buffer_len_
= 0;
516 // If we're in the middle of connecting, we need to make sure
517 // we invoke the connect callback.
519 DCHECK(!read_callback_
.is_null());
520 CompletionCallback read_callback
= read_callback_
;
521 read_callback_
.Reset();
522 read_callback
.Run(status
);
523 } else if (!read_callback_
.is_null()) {
524 // If we have a read_callback_, the we need to make sure we call it back.
525 OnDataReceived(scoped_ptr
<SpdyBuffer
>());
527 // This may have been deleted by read_callback_, so check first.
528 if (weak_ptr
.get() && !write_callback
.is_null())
529 write_callback
.Run(ERR_CONNECTION_CLOSED
);