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