diff --git a/net/spdy/chromium/spdy_proxy_client_socket.cc b/net/spdy/chromium/spdy_proxy_client_socket.cc index fb1168aa05..dcee1ae111 100644 --- a/net/spdy/chromium/spdy_proxy_client_socket.cc +++ b/net/spdy/chromium/spdy_proxy_client_socket.cc @@ -45,6 +45,10 @@ SpdyProxyClientSocket::SpdyProxyClientSocket( write_buffer_len_(0), was_ever_used_(false), redirect_has_load_timing_info_(false), + // This is a hack to avoid messing up higher APIs. + // Should be false by default officially. + use_fastopen_(true), + read_headers_pending_(false), net_log_(NetLogWithSource::Make(spdy_stream->net_log().net_log(), NetLogSourceType::PROXY_CLIENT_SOCKET)), source_dependency_(source_net_log.source()), @@ -314,6 +318,20 @@ int SpdyProxyClientSocket::DoLoop(int last_io_result) { rv = DoReadReplyComplete(rv); net_log_.EndEventWithNetErrorCode( NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv); + if (use_fastopen_ && read_headers_pending_) { + read_headers_pending_ = false; + if (rv < 0) { + // read_callback_ cannot be called. + if (!read_callback_) + rv = ERR_IO_PENDING; + // read_callback_ will be called with this error and be reset. + // Further data after that will be ignored. + next_state_ = STATE_DISCONNECTED; + } else { + // Does not call read_callback_ from here if headers are OK. + rv = ERR_IO_PENDING; + } + } break; default: NOTREACHED() << "bad state"; @@ -371,6 +389,32 @@ int SpdyProxyClientSocket::DoSendRequestComplete(int result) { if (result < 0) return result; + // This is better than TCP Fast Open style fake Connect(). + // + // Fast Open should not be used for preconnects as preconnects need actual + // connections set up. Hence there is a need to check the incoming socket for + // available bytes and configure whether a socket should be Fast Open. But + // fake Connect() make it difficult to check the incoming socket because it + // immediately returns and there is not enough time to get the first read + // from the incoming socket. + // + // The other (wrong) way that pushes the read of incoming socket as late as + // possible is to pass in an early read callback and call it and then send + // available bytes here. This way is wrong because it does not work with late + // binding, which causes sockets opened in this way to be potentially bound + // to the wrong socket requests. + // + // The current approach is to return OK in Connect() before + // STATE_READ_REPLY_COMPLETE, which is completed later. If it is completed + // during a Read and there is an error, the error is returned to the callback + // of the Read; otherwise the error is ignored and subsequent Read/Write + // should discover STATE_DISCONNECTED. + if (use_fastopen_) { + read_headers_pending_ = true; + next_state_ = STATE_OPEN; + return OK; + } + // Wait for HEADERS frame from the server next_state_ = STATE_READ_REPLY_COMPLETE; return ERR_IO_PENDING; @@ -438,6 +482,10 @@ void SpdyProxyClientSocket::OnHeadersSent() { void SpdyProxyClientSocket::OnHeadersReceived( const SpdyHeaderBlock& response_headers) { + if (use_fastopen_ && read_headers_pending_ && next_state_ == STATE_OPEN) { + next_state_ = STATE_READ_REPLY_COMPLETE; + } + // If we've already received the reply, existing headers are too late. // TODO(mbelshe): figure out a way to make HEADERS frames useful after the // initial response. diff --git a/net/spdy/chromium/spdy_proxy_client_socket.h b/net/spdy/chromium/spdy_proxy_client_socket.h index 60c095a996..e8324518a4 100644 --- a/net/spdy/chromium/spdy_proxy_client_socket.h +++ b/net/spdy/chromium/spdy_proxy_client_socket.h @@ -170,6 +170,9 @@ class NET_EXPORT_PRIVATE SpdyProxyClientSocket : public ProxyClientSocket, bool redirect_has_load_timing_info_; LoadTimingInfo redirect_load_timing_info_; + bool use_fastopen_; + bool read_headers_pending_; + const NetLogWithSource net_log_; const NetLogSource source_dependency_;