| OLD | NEW |
| (Empty) |
| 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, | |
| 371 spdy_stream_->GetProtocolVersion(), true, | |
| 372 headers.get()); | |
| 373 // Reset the URL to be the endpoint of the connection | |
| 374 if (spdy_stream_->GetProtocolVersion() > 2) { | |
| 375 (*headers)[":path"] = endpoint_.ToString(); | |
| 376 headers->erase(":scheme"); | |
| 377 } else { | |
| 378 (*headers)["url"] = endpoint_.ToString(); | |
| 379 headers->erase("scheme"); | |
| 380 } | |
| 381 | |
| 382 return spdy_stream_->SendRequestHeaders(headers.Pass(), MORE_DATA_TO_SEND); | |
| 383 } | |
| 384 | |
| 385 int SpdyProxyClientSocket::DoSendRequestComplete(int result) { | |
| 386 if (result < 0) | |
| 387 return result; | |
| 388 | |
| 389 // Wait for SYN_REPLY frame from the server | |
| 390 next_state_ = STATE_READ_REPLY_COMPLETE; | |
| 391 return ERR_IO_PENDING; | |
| 392 } | |
| 393 | |
| 394 int SpdyProxyClientSocket::DoReadReplyComplete(int result) { | |
| 395 // We enter this method directly from DoSendRequestComplete, since | |
| 396 // we are notified by a callback when the SYN_REPLY frame arrives | |
| 397 | |
| 398 if (result < 0) | |
| 399 return result; | |
| 400 | |
| 401 // Require the "HTTP/1.x" status line for SSL CONNECT. | |
| 402 if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0)) | |
| 403 return ERR_TUNNEL_CONNECTION_FAILED; | |
| 404 | |
| 405 net_log_.AddEvent( | |
| 406 NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS, | |
| 407 base::Bind(&HttpResponseHeaders::NetLogCallback, response_.headers)); | |
| 408 | |
| 409 switch (response_.headers->response_code()) { | |
| 410 case 200: // OK | |
| 411 next_state_ = STATE_OPEN; | |
| 412 return OK; | |
| 413 | |
| 414 case 302: // Found / Moved Temporarily | |
| 415 // Try to return a sanitized response so we can follow auth redirects. | |
| 416 // If we can't, fail the tunnel connection. | |
| 417 if (!SanitizeProxyRedirect(&response_)) { | |
| 418 LogBlockedTunnelResponse(); | |
| 419 return ERR_TUNNEL_CONNECTION_FAILED; | |
| 420 } | |
| 421 | |
| 422 redirect_has_load_timing_info_ = | |
| 423 spdy_stream_->GetLoadTimingInfo(&redirect_load_timing_info_); | |
| 424 // Note that this triggers a RST_STREAM_CANCEL. | |
| 425 spdy_stream_->DetachDelegate(); | |
| 426 next_state_ = STATE_DISCONNECTED; | |
| 427 return ERR_HTTPS_PROXY_TUNNEL_RESPONSE; | |
| 428 | |
| 429 case 407: // Proxy Authentication Required | |
| 430 next_state_ = STATE_OPEN; | |
| 431 if (!SanitizeProxyAuth(&response_)) { | |
| 432 LogBlockedTunnelResponse(); | |
| 433 return ERR_TUNNEL_CONNECTION_FAILED; | |
| 434 } | |
| 435 return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_); | |
| 436 | |
| 437 default: | |
| 438 // Ignore response to avoid letting the proxy impersonate the target | |
| 439 // server. (See http://crbug.com/137891.) | |
| 440 LogBlockedTunnelResponse(); | |
| 441 return ERR_TUNNEL_CONNECTION_FAILED; | |
| 442 } | |
| 443 } | |
| 444 | |
| 445 // SpdyStream::Delegate methods: | |
| 446 // Called when SYN frame has been sent. | |
| 447 // Returns true if no more data to be sent after SYN frame. | |
| 448 void SpdyProxyClientSocket::OnRequestHeadersSent() { | |
| 449 DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE); | |
| 450 | |
| 451 OnIOComplete(OK); | |
| 452 } | |
| 453 | |
| 454 SpdyResponseHeadersStatus SpdyProxyClientSocket::OnResponseHeadersUpdated( | |
| 455 const SpdyHeaderBlock& response_headers) { | |
| 456 // If we've already received the reply, existing headers are too late. | |
| 457 // TODO(mbelshe): figure out a way to make HEADERS frames useful after the | |
| 458 // initial response. | |
| 459 if (next_state_ != STATE_READ_REPLY_COMPLETE) | |
| 460 return RESPONSE_HEADERS_ARE_COMPLETE; | |
| 461 | |
| 462 // Save the response | |
| 463 if (!SpdyHeadersToHttpResponse( | |
| 464 response_headers, spdy_stream_->GetProtocolVersion(), &response_)) | |
| 465 return RESPONSE_HEADERS_ARE_INCOMPLETE; | |
| 466 | |
| 467 OnIOComplete(OK); | |
| 468 return RESPONSE_HEADERS_ARE_COMPLETE; | |
| 469 } | |
| 470 | |
| 471 // Called when data is received or on EOF (if |buffer| is NULL). | |
| 472 void SpdyProxyClientSocket::OnDataReceived(scoped_ptr<SpdyBuffer> buffer) { | |
| 473 if (buffer) { | |
| 474 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, | |
| 475 buffer->GetRemainingSize(), | |
| 476 buffer->GetRemainingData()); | |
| 477 read_buffer_queue_.Enqueue(buffer.Pass()); | |
| 478 } else { | |
| 479 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, 0, NULL); | |
| 480 } | |
| 481 | |
| 482 if (!read_callback_.is_null()) { | |
| 483 int rv = PopulateUserReadBuffer(user_buffer_->data(), user_buffer_len_); | |
| 484 CompletionCallback c = read_callback_; | |
| 485 read_callback_.Reset(); | |
| 486 user_buffer_ = NULL; | |
| 487 user_buffer_len_ = 0; | |
| 488 c.Run(rv); | |
| 489 } | |
| 490 } | |
| 491 | |
| 492 void SpdyProxyClientSocket::OnDataSent() { | |
| 493 DCHECK(!write_callback_.is_null()); | |
| 494 | |
| 495 int rv = write_buffer_len_; | |
| 496 write_buffer_len_ = 0; | |
| 497 | |
| 498 // Proxy write callbacks result in deep callback chains. Post to allow the | |
| 499 // stream's write callback chain to unwind (see crbug.com/355511). | |
| 500 base::MessageLoop::current()->PostTask( | |
| 501 FROM_HERE, | |
| 502 base::Bind(&SpdyProxyClientSocket::RunCallback, | |
| 503 write_callback_weak_factory_.GetWeakPtr(), | |
| 504 ResetAndReturn(&write_callback_), | |
| 505 rv)); | |
| 506 } | |
| 507 | |
| 508 void SpdyProxyClientSocket::OnClose(int status) { | |
| 509 was_ever_used_ = spdy_stream_->WasEverUsed(); | |
| 510 spdy_stream_.reset(); | |
| 511 | |
| 512 bool connecting = next_state_ != STATE_DISCONNECTED && | |
| 513 next_state_ < STATE_OPEN; | |
| 514 if (next_state_ == STATE_OPEN) | |
| 515 next_state_ = STATE_CLOSED; | |
| 516 else | |
| 517 next_state_ = STATE_DISCONNECTED; | |
| 518 | |
| 519 base::WeakPtr<SpdyProxyClientSocket> weak_ptr = weak_factory_.GetWeakPtr(); | |
| 520 CompletionCallback write_callback = write_callback_; | |
| 521 write_callback_.Reset(); | |
| 522 write_buffer_len_ = 0; | |
| 523 | |
| 524 // If we're in the middle of connecting, we need to make sure | |
| 525 // we invoke the connect callback. | |
| 526 if (connecting) { | |
| 527 DCHECK(!read_callback_.is_null()); | |
| 528 CompletionCallback read_callback = read_callback_; | |
| 529 read_callback_.Reset(); | |
| 530 read_callback.Run(status); | |
| 531 } else if (!read_callback_.is_null()) { | |
| 532 // If we have a read_callback_, the we need to make sure we call it back. | |
| 533 OnDataReceived(scoped_ptr<SpdyBuffer>()); | |
| 534 } | |
| 535 // This may have been deleted by read_callback_, so check first. | |
| 536 if (weak_ptr.get() && !write_callback.is_null()) | |
| 537 write_callback.Run(ERR_CONNECTION_CLOSED); | |
| 538 } | |
| 539 | |
| 540 } // namespace net | |
| OLD | NEW |