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. 4 5 #include "net/spdy/spdy_proxy_client_socket.h" 6 7 #include <algorithm> // min 8 9 #include "base/bind.h" 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" 23 #include "url/gurl.h" 24 25 namespace net { 26 27 SpdyProxyClientSocket::SpdyProxyClientSocket( 28 const base::WeakPtr<SpdyStream>& spdy_stream, 29 const std::string& user_agent, 30 const HostPortPair& endpoint, 31 const GURL& url, 32 const HostPortPair& proxy_server, 33 const BoundNetLog& source_net_log, 34 HttpAuthCache* auth_cache, 35 HttpAuthHandlerFactory* auth_handler_factory) 36 : next_state_(STATE_DISCONNECTED), 37 spdy_stream_(spdy_stream), 38 endpoint_(endpoint), 39 auth_(new HttpAuthController(HttpAuth::AUTH_PROXY, 40 GURL("https://" + proxy_server.ToString()), 41 auth_cache, 42 auth_handler_factory)), 43 user_buffer_len_(0), 44 write_buffer_len_(0), 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)), 49 weak_factory_(this), 50 write_callback_weak_factory_(this) { 51 request_.method = "CONNECT"; 52 request_.url = url; 53 if (!user_agent.empty()) 54 request_.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, 55 user_agent); 56 57 net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE, 58 source_net_log.source().ToEventParametersCallback()); 59 net_log_.AddEvent( 60 NetLog::TYPE_SPDY_PROXY_CLIENT_SESSION, 61 spdy_stream->net_log().source().ToEventParametersCallback()); 62 63 spdy_stream_->SetDelegate(this); 64 was_ever_used_ = spdy_stream_->WasEverUsed(); 65 } 66 67 SpdyProxyClientSocket::~SpdyProxyClientSocket() { 68 Disconnect(); 69 net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE); 70 } 71 72 const HttpResponseInfo* SpdyProxyClientSocket::GetConnectResponseInfo() const { 73 return response_.headers.get() ? &response_ : NULL; 74 } 75 76 const scoped_refptr<HttpAuthController>& 77 SpdyProxyClientSocket::GetAuthController() const { 78 return auth_; 79 } 80 81 int SpdyProxyClientSocket::RestartWithAuth(const CompletionCallback& callback) { 82 // A SPDY Stream can only handle a single request, so the underlying 83 // stream may not be reused and a new SpdyProxyClientSocket must be 84 // created (possibly on top of the same SPDY Session). 85 next_state_ = STATE_DISCONNECTED; 86 return OK; 87 } 88 89 bool SpdyProxyClientSocket::IsUsingSpdy() const { 90 return true; 91 } 92 93 NextProto SpdyProxyClientSocket::GetProtocolNegotiated() const { 94 // Save the negotiated protocol 95 SSLInfo ssl_info; 96 bool was_npn_negotiated; 97 NextProto protocol_negotiated; 98 spdy_stream_->GetSSLInfo(&ssl_info, &was_npn_negotiated, 99 &protocol_negotiated); 100 return protocol_negotiated; 101 } 102 103 HttpStream* SpdyProxyClientSocket::CreateConnectResponseStream() { 104 return new ProxyConnectRedirectHttpStream( 105 redirect_has_load_timing_info_ ? &redirect_load_timing_info_ : NULL); 106 } 107 108 // Sends a SYN_STREAM frame to the proxy with a CONNECT request 109 // for the specified endpoint. Waits for the server to send back 110 // a SYN_REPLY frame. OK will be returned if the status is 200. 111 // ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status. 112 // In any of these cases, Read() may be called to retrieve the HTTP 113 // response body. Any other return values should be considered fatal. 114 // TODO(rch): handle 407 proxy auth requested correctly, perhaps 115 // by creating a new stream for the subsequent request. 116 // TODO(rch): create a more appropriate error code to disambiguate 117 // the HTTPS Proxy tunnel failure from an HTTP Proxy tunnel failure. 118 int SpdyProxyClientSocket::Connect(const CompletionCallback& callback) { 119 DCHECK(read_callback_.is_null()); 120 if (next_state_ == STATE_OPEN) 121 return OK; 122 123 DCHECK_EQ(STATE_DISCONNECTED, next_state_); 124 next_state_ = STATE_GENERATE_AUTH_TOKEN; 125 126 int rv = DoLoop(OK); 127 if (rv == ERR_IO_PENDING) 128 read_callback_ = callback; 129 return rv; 130 } 131 132 void SpdyProxyClientSocket::Disconnect() { 133 read_buffer_queue_.Clear(); 134 user_buffer_ = NULL; 135 user_buffer_len_ = 0; 136 read_callback_.Reset(); 137 138 write_buffer_len_ = 0; 139 write_callback_.Reset(); 140 write_callback_weak_factory_.InvalidateWeakPtrs(); 141 142 next_state_ = STATE_DISCONNECTED; 143 144 if (spdy_stream_.get()) { 145 // This will cause OnClose to be invoked, which takes care of 146 // cleaning up all the internal state. 147 spdy_stream_->Cancel(); 148 DCHECK(!spdy_stream_.get()); 149 } 150 } 151 152 bool SpdyProxyClientSocket::IsConnected() const { 153 return next_state_ == STATE_OPEN; 154 } 155 156 bool SpdyProxyClientSocket::IsConnectedAndIdle() const { 157 return IsConnected() && read_buffer_queue_.IsEmpty() && 158 spdy_stream_->IsOpen(); 159 } 160 161 const BoundNetLog& SpdyProxyClientSocket::NetLog() const { 162 return net_log_; 163 } 164 165 void SpdyProxyClientSocket::SetSubresourceSpeculation() { 166 // TODO(rch): what should this implementation be? 167 } 168 169 void SpdyProxyClientSocket::SetOmniboxSpeculation() { 170 // TODO(rch): what should this implementation be? 171 } 172 173 bool SpdyProxyClientSocket::WasEverUsed() const { 174 return was_ever_used_ || (spdy_stream_.get() && spdy_stream_->WasEverUsed()); 175 } 176 177 bool SpdyProxyClientSocket::UsingTCPFastOpen() const { 178 return false; 179 } 180 181 bool SpdyProxyClientSocket::WasNpnNegotiated() const { 182 return false; 183 } 184 185 NextProto SpdyProxyClientSocket::GetNegotiatedProtocol() const { 186 return kProtoUnknown; 187 } 188 189 bool SpdyProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) { 190 bool was_npn_negotiated; 191 NextProto protocol_negotiated; 192 return spdy_stream_->GetSSLInfo(ssl_info, &was_npn_negotiated, 193 &protocol_negotiated); 194 } 195 196 int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len, 197 const CompletionCallback& callback) { 198 DCHECK(read_callback_.is_null()); 199 DCHECK(!user_buffer_.get()); 200 201 if (next_state_ == STATE_DISCONNECTED) 202 return ERR_SOCKET_NOT_CONNECTED; 203 204 if (next_state_ == STATE_CLOSED && read_buffer_queue_.IsEmpty()) { 205 return 0; 206 } 207 208 DCHECK(next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED); 209 DCHECK(buf); 210 size_t result = PopulateUserReadBuffer(buf->data(), buf_len); 211 if (result == 0) { 212 user_buffer_ = buf; 213 user_buffer_len_ = static_cast<size_t>(buf_len); 214 DCHECK(!callback.is_null()); 215 read_callback_ = callback; 216 return ERR_IO_PENDING; 217 } 218 user_buffer_ = NULL; 219 return result; 220 } 221 222 size_t SpdyProxyClientSocket::PopulateUserReadBuffer(char* data, size_t len) { 223 return read_buffer_queue_.Dequeue(data, len); 224 } 225 226 int SpdyProxyClientSocket::Write(IOBuffer* buf, int buf_len, 227 const CompletionCallback& callback) { 228 DCHECK(write_callback_.is_null()); 229 if (next_state_ != STATE_OPEN) 230 return ERR_SOCKET_NOT_CONNECTED; 231 232 DCHECK(spdy_stream_.get()); 233 spdy_stream_->SendData(buf, buf_len, MORE_DATA_TO_SEND); 234 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, 235 buf_len, buf->data()); 236 write_callback_ = callback; 237 write_buffer_len_ = buf_len; 238 return ERR_IO_PENDING; 239 } 240 241 int SpdyProxyClientSocket::SetReceiveBufferSize(int32 size) { 242 // Since this StreamSocket sits on top of a shared SpdySession, it 243 // is not safe for callers to change this underlying socket. 244 return ERR_NOT_IMPLEMENTED; 245 } 246 247 int SpdyProxyClientSocket::SetSendBufferSize(int32 size) { 248 // Since this StreamSocket sits on top of a shared SpdySession, it 249 // is not safe for callers to change this underlying socket. 250 return ERR_NOT_IMPLEMENTED; 251 } 252 253 int SpdyProxyClientSocket::GetPeerAddress(IPEndPoint* address) const { 254 if (!IsConnected()) 255 return ERR_SOCKET_NOT_CONNECTED; 256 return spdy_stream_->GetPeerAddress(address); 257 } 258 259 int SpdyProxyClientSocket::GetLocalAddress(IPEndPoint* address) const { 260 if (!IsConnected()) 261 return ERR_SOCKET_NOT_CONNECTED; 262 return spdy_stream_->GetLocalAddress(address); 263 } 264 265 void SpdyProxyClientSocket::LogBlockedTunnelResponse() const { 266 ProxyClientSocket::LogBlockedTunnelResponse( 267 response_.headers->response_code(), 268 request_.url, 269 /* is_https_proxy = */ true); 270 } 271 272 void SpdyProxyClientSocket::RunCallback(const CompletionCallback& callback, 273 int result) const { 274 callback.Run(result); 275 } 276 277 void SpdyProxyClientSocket::OnIOComplete(int result) { 278 DCHECK_NE(STATE_DISCONNECTED, next_state_); 279 int rv = DoLoop(result); 280 if (rv != ERR_IO_PENDING) { 281 CompletionCallback c = read_callback_; 282 read_callback_.Reset(); 283 c.Run(rv); 284 } 285 } 286 287 int SpdyProxyClientSocket::DoLoop(int last_io_result) { 288 DCHECK_NE(next_state_, STATE_DISCONNECTED); 289 int rv = last_io_result; 290 do { 291 State state = next_state_; 292 next_state_ = STATE_DISCONNECTED; 293 switch (state) { 294 case STATE_GENERATE_AUTH_TOKEN: 295 DCHECK_EQ(OK, rv); 296 rv = DoGenerateAuthToken(); 297 break; 298 case STATE_GENERATE_AUTH_TOKEN_COMPLETE: 299 rv = DoGenerateAuthTokenComplete(rv); 300 break; 301 case STATE_SEND_REQUEST: 302 DCHECK_EQ(OK, rv); 303 net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST); 304 rv = DoSendRequest(); 305 break; 306 case STATE_SEND_REQUEST_COMPLETE: 307 net_log_.EndEventWithNetErrorCode( 308 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv); 309 rv = DoSendRequestComplete(rv); 310 if (rv >= 0 || rv == ERR_IO_PENDING) { 311 // Emit extra event so can use the same events as 312 // HttpProxyClientSocket. 313 net_log_.BeginEvent( 314 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS); 315 } 316 break; 317 case STATE_READ_REPLY_COMPLETE: 318 rv = DoReadReplyComplete(rv); 319 net_log_.EndEventWithNetErrorCode( 320 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv); 321 break; 322 default: 323 NOTREACHED() << "bad state"; 324 rv = ERR_UNEXPECTED; 325 break; 326 } 327 } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED && 328 next_state_ != STATE_OPEN); 329 return rv; 330 } 331 332 int SpdyProxyClientSocket::DoGenerateAuthToken() { 333 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE; 334 return auth_->MaybeGenerateAuthToken( 335 &request_, 336 base::Bind(&SpdyProxyClientSocket::OnIOComplete, 337 weak_factory_.GetWeakPtr()), 338 net_log_); 339 } 340 341 int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) { 342 DCHECK_NE(ERR_IO_PENDING, result); 343 if (result == OK) 344 next_state_ = STATE_SEND_REQUEST; 345 return result; 346 } 347 348 int SpdyProxyClientSocket::DoSendRequest() { 349 next_state_ = STATE_SEND_REQUEST_COMPLETE; 350 351 // Add Proxy-Authentication header if necessary. 352 HttpRequestHeaders authorization_headers; 353 if (auth_->HaveAuth()) { 354 auth_->AddAuthorizationHeader(&authorization_headers); 355 } 356 357 std::string request_line; 358 HttpRequestHeaders request_headers; 359 BuildTunnelRequest(request_, authorization_headers, endpoint_, &request_line, 360 &request_headers); 361 362 net_log_.AddEvent( 363 NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS, 364 base::Bind(&HttpRequestHeaders::NetLogCallback, 365 base::Unretained(&request_headers), 366 &request_line)); 367 368 request_.extra_headers.MergeFrom(request_headers); 369 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock()); 370 CreateSpdyHeadersFromHttpRequest(request_, request_headers, headers.get(), 371 spdy_stream_->GetProtocolVersion(), true); 372 // Reset the URL to be the endpoint of the connection 373 if (spdy_stream_->GetProtocolVersion() > 2) { 374 (*headers)[":path"] = endpoint_.ToString(); 375 headers->erase(":scheme"); 376 } else { 377 (*headers)["url"] = endpoint_.ToString(); 378 headers->erase("scheme"); 379 } 380 381 return spdy_stream_->SendRequestHeaders(headers.Pass(), MORE_DATA_TO_SEND); 382 } 383 384 int SpdyProxyClientSocket::DoSendRequestComplete(int result) { 385 if (result < 0) 386 return result; 387 388 // Wait for SYN_REPLY frame from the server 389 next_state_ = STATE_READ_REPLY_COMPLETE; 390 return ERR_IO_PENDING; 391 } 392 393 int SpdyProxyClientSocket::DoReadReplyComplete(int result) { 394 // We enter this method directly from DoSendRequestComplete, since 395 // we are notified by a callback when the SYN_REPLY frame arrives 396 397 if (result < 0) 398 return result; 399 400 // Require the "HTTP/1.x" status line for SSL CONNECT. 401 if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0)) 402 return ERR_TUNNEL_CONNECTION_FAILED; 403 404 net_log_.AddEvent( 405 NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS, 406 base::Bind(&HttpResponseHeaders::NetLogCallback, response_.headers)); 407 408 switch (response_.headers->response_code()) { 409 case 200: // OK 410 next_state_ = STATE_OPEN; 411 return OK; 412 413 case 302: // Found / Moved Temporarily 414 // Try to return a sanitized response so we can follow auth redirects. 415 // If we can't, fail the tunnel connection. 416 if (SanitizeProxyRedirect(&response_, request_.url)) { 417 redirect_has_load_timing_info_ = 418 spdy_stream_->GetLoadTimingInfo(&redirect_load_timing_info_); 419 // Note that this triggers a RST_STREAM_CANCEL. 420 spdy_stream_->DetachDelegate(); 421 next_state_ = STATE_DISCONNECTED; 422 return ERR_HTTPS_PROXY_TUNNEL_RESPONSE; 423 } else { 424 LogBlockedTunnelResponse(); 425 return ERR_TUNNEL_CONNECTION_FAILED; 426 } 427 428 case 407: // Proxy Authentication Required 429 next_state_ = STATE_OPEN; 430 return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_); 431 432 default: 433 // Ignore response to avoid letting the proxy impersonate the target 434 // server. (See http://crbug.com/137891.) 435 LogBlockedTunnelResponse(); 436 return ERR_TUNNEL_CONNECTION_FAILED; 437 } 438 } 439 440 // SpdyStream::Delegate methods: 441 // Called when SYN frame has been sent. 442 // Returns true if no more data to be sent after SYN frame. 443 void SpdyProxyClientSocket::OnRequestHeadersSent() { 444 DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE); 445 446 OnIOComplete(OK); 447 } 448 449 SpdyResponseHeadersStatus SpdyProxyClientSocket::OnResponseHeadersUpdated( 450 const SpdyHeaderBlock& response_headers) { 451 // If we've already received the reply, existing headers are too late. 452 // TODO(mbelshe): figure out a way to make HEADERS frames useful after the 453 // initial response. 454 if (next_state_ != STATE_READ_REPLY_COMPLETE) 455 return RESPONSE_HEADERS_ARE_COMPLETE; 456 457 // Save the response 458 if (!SpdyHeadersToHttpResponse( 459 response_headers, spdy_stream_->GetProtocolVersion(), &response_)) 460 return RESPONSE_HEADERS_ARE_INCOMPLETE; 461 462 OnIOComplete(OK); 463 return RESPONSE_HEADERS_ARE_COMPLETE; 464 } 465 466 // Called when data is received or on EOF (if |buffer| is NULL). 467 void SpdyProxyClientSocket::OnDataReceived(scoped_ptr<SpdyBuffer> buffer) { 468 if (buffer) { 469 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, 470 buffer->GetRemainingSize(), 471 buffer->GetRemainingData()); 472 read_buffer_queue_.Enqueue(buffer.Pass()); 473 } else { 474 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, 0, NULL); 475 } 476 477 if (!read_callback_.is_null()) { 478 int rv = PopulateUserReadBuffer(user_buffer_->data(), user_buffer_len_); 479 CompletionCallback c = read_callback_; 480 read_callback_.Reset(); 481 user_buffer_ = NULL; 482 user_buffer_len_ = 0; 483 c.Run(rv); 484 } 485 } 486 487 void SpdyProxyClientSocket::OnDataSent() { 488 DCHECK(!write_callback_.is_null()); 489 490 int rv = write_buffer_len_; 491 write_buffer_len_ = 0; 492 493 // Proxy write callbacks result in deep callback chains. Post to allow the 494 // stream's write callback chain to unwind (see crbug.com/355511). 495 base::MessageLoop::current()->PostTask( 496 FROM_HERE, 497 base::Bind(&SpdyProxyClientSocket::RunCallback, 498 write_callback_weak_factory_.GetWeakPtr(), 499 ResetAndReturn(&write_callback_), 500 rv)); 501 } 502 503 void SpdyProxyClientSocket::OnClose(int status) { 504 was_ever_used_ = spdy_stream_->WasEverUsed(); 505 spdy_stream_.reset(); 506 507 bool connecting = next_state_ != STATE_DISCONNECTED && 508 next_state_ < STATE_OPEN; 509 if (next_state_ == STATE_OPEN) 510 next_state_ = STATE_CLOSED; 511 else 512 next_state_ = STATE_DISCONNECTED; 513 514 base::WeakPtr<SpdyProxyClientSocket> weak_ptr = weak_factory_.GetWeakPtr(); 515 CompletionCallback write_callback = write_callback_; 516 write_callback_.Reset(); 517 write_buffer_len_ = 0; 518 519 // If we're in the middle of connecting, we need to make sure 520 // we invoke the connect callback. 521 if (connecting) { 522 DCHECK(!read_callback_.is_null()); 523 CompletionCallback read_callback = read_callback_; 524 read_callback_.Reset(); 525 read_callback.Run(status); 526 } else if (!read_callback_.is_null()) { 527 // If we have a read_callback_, the we need to make sure we call it back. 528 OnDataReceived(scoped_ptr<SpdyBuffer>()); 529 } 530 // This may have been deleted by read_callback_, so check first. 531 if (weak_ptr.get() && !write_callback.is_null()) 532 write_callback.Run(ERR_CONNECTION_CLOSED); 533 } 534 535 } // namespace net 536