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_( 40 new HttpAuthController(HttpAuth::AUTH_PROXY, 41 GURL("https://" + proxy_server.ToString()), 42 auth_cache, 43 auth_handler_factory)), 44 user_buffer_len_(0), 45 write_buffer_len_(0), 46 was_ever_used_(false), 47 redirect_has_load_timing_info_(false), 48 weak_factory_(this), 49 net_log_(BoundNetLog::Make(spdy_stream->net_log().net_log(), 50 NetLog::SOURCE_PROXY_CLIENT_SOCKET)) { 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 141 next_state_ = STATE_DISCONNECTED; 142 143 if (spdy_stream_.get()) { 144 // This will cause OnClose to be invoked, which takes care of 145 // cleaning up all the internal state. 146 spdy_stream_->Cancel(); 147 DCHECK(!spdy_stream_.get()); 148 } 149 } 150 151 bool SpdyProxyClientSocket::IsConnected() const { 152 return next_state_ == STATE_OPEN; 153 } 154 155 bool SpdyProxyClientSocket::IsConnectedAndIdle() const { 156 return IsConnected() && read_buffer_queue_.IsEmpty() && 157 spdy_stream_->IsIdle(); 158 } 159 160 const BoundNetLog& SpdyProxyClientSocket::NetLog() const { 161 return net_log_; 162 } 163 164 void SpdyProxyClientSocket::SetSubresourceSpeculation() { 165 // TODO(rch): what should this implementation be? 166 } 167 168 void SpdyProxyClientSocket::SetOmniboxSpeculation() { 169 // TODO(rch): what should this implementation be? 170 } 171 172 bool SpdyProxyClientSocket::WasEverUsed() const { 173 return was_ever_used_ || (spdy_stream_.get() && spdy_stream_->WasEverUsed()); 174 } 175 176 bool SpdyProxyClientSocket::UsingTCPFastOpen() const { 177 return false; 178 } 179 180 bool SpdyProxyClientSocket::WasNpnNegotiated() const { 181 return false; 182 } 183 184 NextProto SpdyProxyClientSocket::GetNegotiatedProtocol() const { 185 return kProtoUnknown; 186 } 187 188 bool SpdyProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) { 189 bool was_npn_negotiated; 190 NextProto protocol_negotiated; 191 return spdy_stream_->GetSSLInfo(ssl_info, &was_npn_negotiated, 192 &protocol_negotiated); 193 } 194 195 int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len, 196 const CompletionCallback& callback) { 197 DCHECK(read_callback_.is_null()); 198 DCHECK(!user_buffer_.get()); 199 200 if (next_state_ == STATE_DISCONNECTED) 201 return ERR_SOCKET_NOT_CONNECTED; 202 203 if (next_state_ == STATE_CLOSED && read_buffer_queue_.IsEmpty()) { 204 return 0; 205 } 206 207 DCHECK(next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED); 208 DCHECK(buf); 209 size_t result = PopulateUserReadBuffer(buf->data(), buf_len); 210 if (result == 0) { 211 user_buffer_ = buf; 212 user_buffer_len_ = static_cast<size_t>(buf_len); 213 DCHECK(!callback.is_null()); 214 read_callback_ = callback; 215 return ERR_IO_PENDING; 216 } 217 user_buffer_ = NULL; 218 return result; 219 } 220 221 size_t SpdyProxyClientSocket::PopulateUserReadBuffer(char* data, size_t len) { 222 return read_buffer_queue_.Dequeue(data, len); 223 } 224 225 int SpdyProxyClientSocket::Write(IOBuffer* buf, int buf_len, 226 const CompletionCallback& callback) { 227 DCHECK(write_callback_.is_null()); 228 if (next_state_ != STATE_OPEN) 229 return ERR_SOCKET_NOT_CONNECTED; 230 231 DCHECK(spdy_stream_.get()); 232 spdy_stream_->SendData(buf, buf_len, MORE_DATA_TO_SEND); 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 bool SpdyProxyClientSocket::SetReceiveBufferSize(int32 size) { 241 // Since this StreamSocket sits on top of a shared SpdySession, it 242 // is not safe for callers to set change this underlying socket. 243 return false; 244 } 245 246 bool SpdyProxyClientSocket::SetSendBufferSize(int32 size) { 247 // Since this StreamSocket sits on top of a shared SpdySession, it 248 // is not safe for callers to set change this underlying socket. 249 return false; 250 } 251 252 int SpdyProxyClientSocket::GetPeerAddress(IPEndPoint* address) const { 253 if (!IsConnected()) 254 return ERR_SOCKET_NOT_CONNECTED; 255 return spdy_stream_->GetPeerAddress(address); 256 } 257 258 int SpdyProxyClientSocket::GetLocalAddress(IPEndPoint* address) const { 259 if (!IsConnected()) 260 return ERR_SOCKET_NOT_CONNECTED; 261 return spdy_stream_->GetLocalAddress(address); 262 } 263 264 void SpdyProxyClientSocket::LogBlockedTunnelResponse() const { 265 ProxyClientSocket::LogBlockedTunnelResponse( 266 response_.headers->response_code(), 267 request_.url, 268 /* is_https_proxy = */ true); 269 } 270 271 void SpdyProxyClientSocket::OnIOComplete(int result) { 272 DCHECK_NE(STATE_DISCONNECTED, next_state_); 273 int rv = DoLoop(result); 274 if (rv != ERR_IO_PENDING) { 275 CompletionCallback c = read_callback_; 276 read_callback_.Reset(); 277 c.Run(rv); 278 } 279 } 280 281 int SpdyProxyClientSocket::DoLoop(int last_io_result) { 282 DCHECK_NE(next_state_, STATE_DISCONNECTED); 283 int rv = last_io_result; 284 do { 285 State state = next_state_; 286 next_state_ = STATE_DISCONNECTED; 287 switch (state) { 288 case STATE_GENERATE_AUTH_TOKEN: 289 DCHECK_EQ(OK, rv); 290 rv = DoGenerateAuthToken(); 291 break; 292 case STATE_GENERATE_AUTH_TOKEN_COMPLETE: 293 rv = DoGenerateAuthTokenComplete(rv); 294 break; 295 case STATE_SEND_REQUEST: 296 DCHECK_EQ(OK, rv); 297 net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST); 298 rv = DoSendRequest(); 299 break; 300 case STATE_SEND_REQUEST_COMPLETE: 301 net_log_.EndEventWithNetErrorCode( 302 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv); 303 rv = DoSendRequestComplete(rv); 304 if (rv >= 0 || rv == ERR_IO_PENDING) { 305 // Emit extra event so can use the same events as 306 // HttpProxyClientSocket. 307 net_log_.BeginEvent( 308 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS); 309 } 310 break; 311 case STATE_READ_REPLY_COMPLETE: 312 rv = DoReadReplyComplete(rv); 313 net_log_.EndEventWithNetErrorCode( 314 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv); 315 break; 316 default: 317 NOTREACHED() << "bad state"; 318 rv = ERR_UNEXPECTED; 319 break; 320 } 321 } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED && 322 next_state_ != STATE_OPEN); 323 return rv; 324 } 325 326 int SpdyProxyClientSocket::DoGenerateAuthToken() { 327 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE; 328 return auth_->MaybeGenerateAuthToken( 329 &request_, 330 base::Bind(&SpdyProxyClientSocket::OnIOComplete, 331 weak_factory_.GetWeakPtr()), 332 net_log_); 333 } 334 335 int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) { 336 DCHECK_NE(ERR_IO_PENDING, result); 337 if (result == OK) 338 next_state_ = STATE_SEND_REQUEST; 339 return result; 340 } 341 342 int SpdyProxyClientSocket::DoSendRequest() { 343 next_state_ = STATE_SEND_REQUEST_COMPLETE; 344 345 // Add Proxy-Authentication header if necessary. 346 HttpRequestHeaders authorization_headers; 347 if (auth_->HaveAuth()) { 348 auth_->AddAuthorizationHeader(&authorization_headers); 349 } 350 351 std::string request_line; 352 HttpRequestHeaders request_headers; 353 BuildTunnelRequest(request_, authorization_headers, endpoint_, &request_line, 354 &request_headers); 355 356 net_log_.AddEvent( 357 NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS, 358 base::Bind(&HttpRequestHeaders::NetLogCallback, 359 base::Unretained(&request_headers), 360 &request_line)); 361 362 request_.extra_headers.MergeFrom(request_headers); 363 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock()); 364 CreateSpdyHeadersFromHttpRequest(request_, request_headers, headers.get(), 365 spdy_stream_->GetProtocolVersion(), true); 366 // Reset the URL to be the endpoint of the connection 367 if (spdy_stream_->GetProtocolVersion() > 2) { 368 (*headers)[":path"] = endpoint_.ToString(); 369 headers->erase(":scheme"); 370 } else { 371 (*headers)["url"] = endpoint_.ToString(); 372 headers->erase("scheme"); 373 } 374 375 return spdy_stream_->SendRequestHeaders(headers.Pass(), MORE_DATA_TO_SEND); 376 } 377 378 int SpdyProxyClientSocket::DoSendRequestComplete(int result) { 379 if (result < 0) 380 return result; 381 382 // Wait for SYN_REPLY frame from the server 383 next_state_ = STATE_READ_REPLY_COMPLETE; 384 return ERR_IO_PENDING; 385 } 386 387 int SpdyProxyClientSocket::DoReadReplyComplete(int result) { 388 // We enter this method directly from DoSendRequestComplete, since 389 // we are notified by a callback when the SYN_REPLY frame arrives 390 391 if (result < 0) 392 return result; 393 394 // Require the "HTTP/1.x" status line for SSL CONNECT. 395 if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0)) 396 return ERR_TUNNEL_CONNECTION_FAILED; 397 398 net_log_.AddEvent( 399 NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS, 400 base::Bind(&HttpResponseHeaders::NetLogCallback, response_.headers)); 401 402 switch (response_.headers->response_code()) { 403 case 200: // OK 404 next_state_ = STATE_OPEN; 405 return OK; 406 407 case 302: // Found / Moved Temporarily 408 // Try to return a sanitized response so we can follow auth redirects. 409 // If we can't, fail the tunnel connection. 410 if (SanitizeProxyRedirect(&response_, request_.url)) { 411 redirect_has_load_timing_info_ = 412 spdy_stream_->GetLoadTimingInfo(&redirect_load_timing_info_); 413 spdy_stream_->DetachDelegate(); 414 next_state_ = STATE_DISCONNECTED; 415 return ERR_HTTPS_PROXY_TUNNEL_RESPONSE; 416 } else { 417 LogBlockedTunnelResponse(); 418 return ERR_TUNNEL_CONNECTION_FAILED; 419 } 420 421 case 407: // Proxy Authentication Required 422 next_state_ = STATE_OPEN; 423 return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_); 424 425 default: 426 // Ignore response to avoid letting the proxy impersonate the target 427 // server. (See http://crbug.com/137891.) 428 LogBlockedTunnelResponse(); 429 return ERR_TUNNEL_CONNECTION_FAILED; 430 } 431 } 432 433 // SpdyStream::Delegate methods: 434 // Called when SYN frame has been sent. 435 // Returns true if no more data to be sent after SYN frame. 436 void SpdyProxyClientSocket::OnRequestHeadersSent() { 437 DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE); 438 439 OnIOComplete(OK); 440 } 441 442 SpdyResponseHeadersStatus SpdyProxyClientSocket::OnResponseHeadersUpdated( 443 const SpdyHeaderBlock& response_headers) { 444 // If we've already received the reply, existing headers are too late. 445 // TODO(mbelshe): figure out a way to make HEADERS frames useful after the 446 // initial response. 447 if (next_state_ != STATE_READ_REPLY_COMPLETE) 448 return RESPONSE_HEADERS_ARE_COMPLETE; 449 450 // Save the response 451 if (!SpdyHeadersToHttpResponse( 452 response_headers, spdy_stream_->GetProtocolVersion(), &response_)) 453 return RESPONSE_HEADERS_ARE_INCOMPLETE; 454 455 OnIOComplete(OK); 456 return RESPONSE_HEADERS_ARE_COMPLETE; 457 } 458 459 // Called when data is received or on EOF (if |buffer| is NULL). 460 void SpdyProxyClientSocket::OnDataReceived(scoped_ptr<SpdyBuffer> buffer) { 461 if (buffer) { 462 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, 463 buffer->GetRemainingSize(), 464 buffer->GetRemainingData()); 465 read_buffer_queue_.Enqueue(buffer.Pass()); 466 } else { 467 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, 0, NULL); 468 } 469 470 if (!read_callback_.is_null()) { 471 int rv = PopulateUserReadBuffer(user_buffer_->data(), user_buffer_len_); 472 CompletionCallback c = read_callback_; 473 read_callback_.Reset(); 474 user_buffer_ = NULL; 475 user_buffer_len_ = 0; 476 c.Run(rv); 477 } 478 } 479 480 void SpdyProxyClientSocket::OnDataSent() { 481 DCHECK(!write_callback_.is_null()); 482 483 int rv = write_buffer_len_; 484 write_buffer_len_ = 0; 485 ResetAndReturn(&write_callback_).Run(rv); 486 } 487 488 void SpdyProxyClientSocket::OnClose(int status) { 489 was_ever_used_ = spdy_stream_->WasEverUsed(); 490 spdy_stream_.reset(); 491 492 bool connecting = next_state_ != STATE_DISCONNECTED && 493 next_state_ < STATE_OPEN; 494 if (next_state_ == STATE_OPEN) 495 next_state_ = STATE_CLOSED; 496 else 497 next_state_ = STATE_DISCONNECTED; 498 499 base::WeakPtr<SpdyProxyClientSocket> weak_ptr = weak_factory_.GetWeakPtr(); 500 CompletionCallback write_callback = write_callback_; 501 write_callback_.Reset(); 502 write_buffer_len_ = 0; 503 504 // If we're in the middle of connecting, we need to make sure 505 // we invoke the connect callback. 506 if (connecting) { 507 DCHECK(!read_callback_.is_null()); 508 CompletionCallback read_callback = read_callback_; 509 read_callback_.Reset(); 510 read_callback.Run(status); 511 } else if (!read_callback_.is_null()) { 512 // If we have a read_callback_, the we need to make sure we call it back. 513 OnDataReceived(scoped_ptr<SpdyBuffer>()); 514 } 515 // This may have been deleted by read_callback_, so check first. 516 if (weak_ptr.get() && !write_callback.is_null()) 517 write_callback.Run(ERR_CONNECTION_CLOSED); 518 } 519 520 } // namespace net 521