Home | History | Annotate | Download | only in spdy
      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