1 // Copyright (c) 2010 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/logging.h" 10 #include "base/string_util.h" 11 #include "googleurl/src/gurl.h" 12 #include "net/base/auth.h" 13 #include "net/base/io_buffer.h" 14 #include "net/base/net_util.h" 15 #include "net/http/http_auth_cache.h" 16 #include "net/http/http_auth_handler_factory.h" 17 #include "net/http/http_net_log_params.h" 18 #include "net/http/http_proxy_utils.h" 19 #include "net/http/http_response_headers.h" 20 #include "net/spdy/spdy_http_utils.h" 21 22 namespace net { 23 24 SpdyProxyClientSocket::SpdyProxyClientSocket( 25 SpdyStream* spdy_stream, 26 const std::string& user_agent, 27 const HostPortPair& endpoint, 28 const GURL& url, 29 const HostPortPair& proxy_server, 30 HttpAuthCache* auth_cache, 31 HttpAuthHandlerFactory* auth_handler_factory) 32 : ALLOW_THIS_IN_INITIALIZER_LIST( 33 io_callback_(this, &SpdyProxyClientSocket::OnIOComplete)), 34 next_state_(STATE_DISCONNECTED), 35 spdy_stream_(spdy_stream), 36 read_callback_(NULL), 37 write_callback_(NULL), 38 endpoint_(endpoint), 39 auth_( 40 new HttpAuthController(HttpAuth::AUTH_PROXY, 41 GURL("http://" + proxy_server.ToString()), 42 auth_cache, 43 auth_handler_factory)), 44 user_buffer_(NULL), 45 write_buffer_len_(0), 46 write_bytes_outstanding_(0), 47 eof_has_been_read_(false), 48 net_log_(spdy_stream->net_log()) { 49 request_.method = "CONNECT"; 50 request_.url = url; 51 if (!user_agent.empty()) 52 request_.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, 53 user_agent); 54 spdy_stream_->SetDelegate(this); 55 was_ever_used_ = spdy_stream_->WasEverUsed(); 56 } 57 58 SpdyProxyClientSocket::~SpdyProxyClientSocket() { 59 Disconnect(); 60 } 61 62 const HttpResponseInfo* SpdyProxyClientSocket::GetConnectResponseInfo() const { 63 return response_.headers ? &response_ : NULL; 64 } 65 66 HttpStream* SpdyProxyClientSocket::CreateConnectResponseStream() { 67 DCHECK(response_stream_.get()); 68 return response_stream_.release(); 69 } 70 71 // Sends a SYN_STREAM frame to the proxy with a CONNECT request 72 // for the specified endpoint. Waits for the server to send back 73 // a SYN_REPLY frame. OK will be returned if the status is 200. 74 // ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status. 75 // In any of these cases, Read() may be called to retrieve the HTTP 76 // response body. Any other return values should be considered fatal. 77 // TODO(rch): handle 407 proxy auth requested correctly, perhaps 78 // by creating a new stream for the subsequent request. 79 // TODO(rch): create a more appropriate error code to disambiguate 80 // the HTTPS Proxy tunnel failure from an HTTP Proxy tunnel failure. 81 #ifdef ANDROID 82 // TODO(kristianm): handle the case when wait_for_connect is true 83 // (sync requests) 84 #endif 85 int SpdyProxyClientSocket::Connect(CompletionCallback* callback 86 #ifdef ANDROID 87 , bool wait_for_connect 88 , bool valid_uid 89 , uid_t calling_uid 90 #endif 91 ) { 92 DCHECK(!read_callback_); 93 if (next_state_ == STATE_OPEN) 94 return OK; 95 96 DCHECK_EQ(STATE_DISCONNECTED, next_state_); 97 next_state_ = STATE_GENERATE_AUTH_TOKEN; 98 99 int rv = DoLoop(OK); 100 if (rv == ERR_IO_PENDING) 101 read_callback_ = callback; 102 return rv; 103 } 104 105 void SpdyProxyClientSocket::Disconnect() { 106 read_buffer_.clear(); 107 user_buffer_ = NULL; 108 read_callback_ = NULL; 109 110 write_buffer_len_ = 0; 111 write_bytes_outstanding_ = 0; 112 write_callback_ = NULL; 113 114 next_state_ = STATE_DISCONNECTED; 115 116 if (spdy_stream_) 117 // This will cause OnClose to be invoked, which takes care of 118 // cleaning up all the internal state. 119 spdy_stream_->Cancel(); 120 } 121 122 bool SpdyProxyClientSocket::IsConnected() const { 123 return next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED; 124 } 125 126 bool SpdyProxyClientSocket::IsConnectedAndIdle() const { 127 return IsConnected() && !spdy_stream_->is_idle(); 128 } 129 130 const BoundNetLog& SpdyProxyClientSocket::NetLog() const { 131 return net_log_; 132 } 133 134 void SpdyProxyClientSocket::SetSubresourceSpeculation() { 135 // TODO(rch): what should this implementation be? 136 } 137 138 void SpdyProxyClientSocket::SetOmniboxSpeculation() { 139 // TODO(rch): what should this implementation be? 140 } 141 142 bool SpdyProxyClientSocket::WasEverUsed() const { 143 return was_ever_used_ || (spdy_stream_ && spdy_stream_->WasEverUsed()); 144 } 145 146 bool SpdyProxyClientSocket::UsingTCPFastOpen() const { 147 return false; 148 } 149 150 int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len, 151 CompletionCallback* callback) { 152 DCHECK(!read_callback_); 153 DCHECK(!user_buffer_); 154 155 if (next_state_ == STATE_DISCONNECTED) 156 return ERR_SOCKET_NOT_CONNECTED; 157 158 if (!spdy_stream_ && read_buffer_.empty()) { 159 if (eof_has_been_read_) 160 return ERR_CONNECTION_CLOSED; 161 eof_has_been_read_ = true; 162 return 0; 163 } 164 165 DCHECK(next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED); 166 DCHECK(buf); 167 user_buffer_ = new DrainableIOBuffer(buf, buf_len); 168 int result = PopulateUserReadBuffer(); 169 if (result == 0) { 170 DCHECK(callback); 171 read_callback_ = callback; 172 return ERR_IO_PENDING; 173 } 174 user_buffer_ = NULL; 175 return result; 176 } 177 178 int SpdyProxyClientSocket::PopulateUserReadBuffer() { 179 if (!user_buffer_) 180 return ERR_IO_PENDING; 181 182 while (!read_buffer_.empty() && user_buffer_->BytesRemaining() > 0) { 183 scoped_refptr<DrainableIOBuffer> data = read_buffer_.front(); 184 const int bytes_to_copy = std::min(user_buffer_->BytesRemaining(), 185 data->BytesRemaining()); 186 memcpy(user_buffer_->data(), data->data(), bytes_to_copy); 187 user_buffer_->DidConsume(bytes_to_copy); 188 if (data->BytesRemaining() == bytes_to_copy) { 189 // Consumed all data from this buffer 190 read_buffer_.pop_front(); 191 } else { 192 data->DidConsume(bytes_to_copy); 193 } 194 } 195 196 return user_buffer_->BytesConsumed(); 197 } 198 199 int SpdyProxyClientSocket::Write(IOBuffer* buf, int buf_len, 200 CompletionCallback* callback) { 201 DCHECK(!write_callback_); 202 if (next_state_ == STATE_DISCONNECTED) 203 return ERR_SOCKET_NOT_CONNECTED; 204 205 if (!spdy_stream_) 206 return ERR_CONNECTION_CLOSED; 207 208 write_bytes_outstanding_= buf_len; 209 if (buf_len <= kMaxSpdyFrameChunkSize) { 210 int rv = spdy_stream_->WriteStreamData(buf, buf_len, spdy::DATA_FLAG_NONE); 211 if (rv == ERR_IO_PENDING) { 212 write_callback_ = callback; 213 write_buffer_len_ = buf_len; 214 } 215 return rv; 216 } 217 218 // Since a SPDY Data frame can only include kMaxSpdyFrameChunkSize bytes 219 // we need to send multiple data frames 220 for (int i = 0; i < buf_len; i += kMaxSpdyFrameChunkSize) { 221 int len = std::min(kMaxSpdyFrameChunkSize, buf_len - i); 222 scoped_refptr<DrainableIOBuffer> iobuf(new DrainableIOBuffer(buf, i + len)); 223 iobuf->SetOffset(i); 224 int rv = spdy_stream_->WriteStreamData(iobuf, len, spdy::DATA_FLAG_NONE); 225 if (rv > 0) { 226 write_bytes_outstanding_ -= rv; 227 } else if (rv != ERR_IO_PENDING) { 228 return rv; 229 } 230 } 231 if (write_bytes_outstanding_ > 0) { 232 write_callback_ = callback; 233 write_buffer_len_ = buf_len; 234 return ERR_IO_PENDING; 235 } else { 236 return buf_len; 237 } 238 } 239 240 bool SpdyProxyClientSocket::SetReceiveBufferSize(int32 size) { 241 // Since this ClientSocket 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 ClientSocket 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(AddressList* 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::OnIOComplete(int result) { 265 DCHECK_NE(STATE_DISCONNECTED, next_state_); 266 int rv = DoLoop(result); 267 if (rv != ERR_IO_PENDING) { 268 CompletionCallback* c = read_callback_; 269 read_callback_ = NULL; 270 c->Run(rv); 271 } 272 } 273 274 int SpdyProxyClientSocket::DoLoop(int last_io_result) { 275 DCHECK_NE(next_state_, STATE_DISCONNECTED); 276 int rv = last_io_result; 277 do { 278 State state = next_state_; 279 next_state_ = STATE_DISCONNECTED; 280 switch (state) { 281 case STATE_GENERATE_AUTH_TOKEN: 282 DCHECK_EQ(OK, rv); 283 rv = DoGenerateAuthToken(); 284 break; 285 case STATE_GENERATE_AUTH_TOKEN_COMPLETE: 286 rv = DoGenerateAuthTokenComplete(rv); 287 break; 288 case STATE_SEND_REQUEST: 289 DCHECK_EQ(OK, rv); 290 net_log_.BeginEvent( 291 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, NULL); 292 rv = DoSendRequest(); 293 break; 294 case STATE_SEND_REQUEST_COMPLETE: 295 net_log_.EndEventWithNetErrorCode( 296 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv); 297 rv = DoSendRequestComplete(rv); 298 break; 299 case STATE_READ_REPLY_COMPLETE: 300 rv = DoReadReplyComplete(rv); 301 net_log_.EndEventWithNetErrorCode( 302 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv); 303 break; 304 default: 305 NOTREACHED() << "bad state"; 306 rv = ERR_UNEXPECTED; 307 break; 308 } 309 } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED && 310 next_state_ != STATE_OPEN); 311 return rv; 312 } 313 314 int SpdyProxyClientSocket::DoGenerateAuthToken() { 315 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE; 316 return auth_->MaybeGenerateAuthToken(&request_, &io_callback_, net_log_); 317 } 318 319 int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) { 320 DCHECK_NE(ERR_IO_PENDING, result); 321 if (result == OK) 322 next_state_ = STATE_SEND_REQUEST; 323 return result; 324 } 325 326 int SpdyProxyClientSocket::DoSendRequest() { 327 next_state_ = STATE_SEND_REQUEST_COMPLETE; 328 329 // Add Proxy-Authentication header if necessary. 330 HttpRequestHeaders authorization_headers; 331 if (auth_->HaveAuth()) { 332 auth_->AddAuthorizationHeader(&authorization_headers); 333 } 334 335 std::string request_line; 336 HttpRequestHeaders request_headers; 337 BuildTunnelRequest(request_, authorization_headers, endpoint_, &request_line, 338 &request_headers); 339 if (net_log_.IsLoggingAllEvents()) { 340 net_log_.AddEvent( 341 NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS, 342 make_scoped_refptr(new NetLogHttpRequestParameter( 343 request_line, request_headers))); 344 } 345 346 request_.extra_headers.MergeFrom(request_headers); 347 linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock()); 348 CreateSpdyHeadersFromHttpRequest(request_, request_headers, headers.get(), 349 true); 350 // Reset the URL to be the endpoint of the connection 351 (*headers)["url"] = endpoint_.ToString(); 352 headers->erase("scheme"); 353 spdy_stream_->set_spdy_headers(headers); 354 355 return spdy_stream_->SendRequest(true); 356 } 357 358 int SpdyProxyClientSocket::DoSendRequestComplete(int result) { 359 if (result < 0) 360 return result; 361 362 // Wait for SYN_REPLY frame from the server 363 next_state_ = STATE_READ_REPLY_COMPLETE; 364 return ERR_IO_PENDING; 365 } 366 367 int SpdyProxyClientSocket::DoReadReplyComplete(int result) { 368 // We enter this method directly from DoSendRequestComplete, since 369 // we are notified by a callback when the SYN_REPLY frame arrives 370 371 if (result < 0) 372 return result; 373 374 // Require the "HTTP/1.x" status line for SSL CONNECT. 375 if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0)) 376 return ERR_TUNNEL_CONNECTION_FAILED; 377 378 next_state_ = STATE_OPEN; 379 if (net_log_.IsLoggingAllEvents()) { 380 net_log_.AddEvent( 381 NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS, 382 make_scoped_refptr(new NetLogHttpResponseParameter(response_.headers))); 383 } 384 385 if (response_.headers->response_code() == 200) { 386 return OK; 387 } else if (response_.headers->response_code() == 407) { 388 return ERR_TUNNEL_CONNECTION_FAILED; 389 } else { 390 // Immediately hand off our SpdyStream to a newly created SpdyHttpStream 391 // so that any subsequent SpdyFrames are processed in the context of 392 // the HttpStream, not the socket. 393 DCHECK(spdy_stream_); 394 SpdyStream* stream = spdy_stream_; 395 spdy_stream_ = NULL; 396 response_stream_.reset(new SpdyHttpStream(NULL, false)); 397 response_stream_->InitializeWithExistingStream(stream); 398 next_state_ = STATE_DISCONNECTED; 399 return ERR_HTTPS_PROXY_TUNNEL_RESPONSE; 400 } 401 } 402 403 // SpdyStream::Delegate methods: 404 // Called when SYN frame has been sent. 405 // Returns true if no more data to be sent after SYN frame. 406 bool SpdyProxyClientSocket::OnSendHeadersComplete(int status) { 407 DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE); 408 409 OnIOComplete(status); 410 411 // We return true here so that we send |spdy_stream_| into 412 // STATE_OPEN (ala WebSockets). 413 return true; 414 } 415 416 int SpdyProxyClientSocket::OnSendBody() { 417 // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets) 418 // OnSendBody() should never be called. 419 NOTREACHED(); 420 return ERR_UNEXPECTED; 421 } 422 423 int SpdyProxyClientSocket::OnSendBodyComplete(int /*status*/, bool* /*eof*/) { 424 // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets) 425 // OnSendBodyComplete() should never be called. 426 NOTREACHED(); 427 return ERR_UNEXPECTED; 428 } 429 430 int SpdyProxyClientSocket::OnResponseReceived( 431 const spdy::SpdyHeaderBlock& response, 432 base::Time response_time, 433 int status) { 434 // If we've already received the reply, existing headers are too late. 435 // TODO(mbelshe): figure out a way to make HEADERS frames useful after the 436 // initial response. 437 if (next_state_ != STATE_READ_REPLY_COMPLETE) 438 return OK; 439 440 // Save the response 441 int rv = SpdyHeadersToHttpResponse(response, &response_); 442 if (rv == ERR_INCOMPLETE_SPDY_HEADERS) 443 return rv; // More headers are coming. 444 445 OnIOComplete(status); 446 return OK; 447 } 448 449 // Called when data is received. 450 void SpdyProxyClientSocket::OnDataReceived(const char* data, int length) { 451 if (length > 0) { 452 // Save the received data. 453 scoped_refptr<IOBuffer> io_buffer(new IOBuffer(length)); 454 memcpy(io_buffer->data(), data, length); 455 read_buffer_.push_back( 456 make_scoped_refptr(new DrainableIOBuffer(io_buffer, length))); 457 } 458 459 if (read_callback_) { 460 int rv = PopulateUserReadBuffer(); 461 CompletionCallback* c = read_callback_; 462 read_callback_ = NULL; 463 user_buffer_ = NULL; 464 c->Run(rv); 465 } 466 } 467 468 void SpdyProxyClientSocket::OnDataSent(int length) { 469 DCHECK(write_callback_); 470 471 write_bytes_outstanding_ -= length; 472 473 DCHECK_GE(write_bytes_outstanding_, 0); 474 475 if (write_bytes_outstanding_ == 0) { 476 int rv = write_buffer_len_; 477 write_buffer_len_ = 0; 478 write_bytes_outstanding_ = 0; 479 CompletionCallback* c = write_callback_; 480 write_callback_ = NULL; 481 c->Run(rv); 482 } 483 } 484 485 void SpdyProxyClientSocket::OnClose(int status) { 486 DCHECK(spdy_stream_); 487 was_ever_used_ = spdy_stream_->WasEverUsed(); 488 spdy_stream_ = NULL; 489 490 bool connecting = next_state_ != STATE_DISCONNECTED && 491 next_state_ < STATE_OPEN; 492 if (next_state_ == STATE_OPEN) 493 next_state_ = STATE_CLOSED; 494 else 495 next_state_ = STATE_DISCONNECTED; 496 497 CompletionCallback* write_callback = write_callback_; 498 write_callback_ = NULL; 499 write_buffer_len_ = 0; 500 write_bytes_outstanding_ = 0; 501 502 // If we're in the middle of connecting, we need to make sure 503 // we invoke the connect callback. 504 if (connecting) { 505 DCHECK(read_callback_); 506 CompletionCallback* read_callback = read_callback_; 507 read_callback_ = NULL; 508 read_callback->Run(status); 509 } else if (read_callback_) { 510 // If we have a read_callback, the we need to make sure we call it back 511 OnDataReceived(NULL, 0); 512 } 513 if (write_callback) 514 write_callback->Run(ERR_CONNECTION_CLOSED); 515 } 516 517 void SpdyProxyClientSocket::set_chunk_callback(ChunkCallback* /*callback*/) { 518 } 519 520 } // namespace net 521