• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SpdyProxyClientSocket(SpdyStream * spdy_stream,const std::string & user_agent,const HostPortPair & endpoint,const GURL & url,const HostPortPair & proxy_server,HttpAuthCache * auth_cache,HttpAuthHandlerFactory * auth_handler_factory)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 
~SpdyProxyClientSocket()58 SpdyProxyClientSocket::~SpdyProxyClientSocket() {
59   Disconnect();
60 }
61 
GetConnectResponseInfo() const62 const HttpResponseInfo* SpdyProxyClientSocket::GetConnectResponseInfo() const {
63   return response_.headers ? &response_ : NULL;
64 }
65 
CreateConnectResponseStream()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
Connect(CompletionCallback * callback,bool wait_for_connect,bool valid_uid,uid_t calling_uid)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 
Disconnect()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 
IsConnected() const122 bool SpdyProxyClientSocket::IsConnected() const {
123   return next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED;
124 }
125 
IsConnectedAndIdle() const126 bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
127   return IsConnected() && !spdy_stream_->is_idle();
128 }
129 
NetLog() const130 const BoundNetLog& SpdyProxyClientSocket::NetLog() const {
131   return net_log_;
132 }
133 
SetSubresourceSpeculation()134 void SpdyProxyClientSocket::SetSubresourceSpeculation() {
135   // TODO(rch): what should this implementation be?
136 }
137 
SetOmniboxSpeculation()138 void SpdyProxyClientSocket::SetOmniboxSpeculation() {
139   // TODO(rch): what should this implementation be?
140 }
141 
WasEverUsed() const142 bool SpdyProxyClientSocket::WasEverUsed() const {
143   return was_ever_used_ || (spdy_stream_ && spdy_stream_->WasEverUsed());
144 }
145 
UsingTCPFastOpen() const146 bool SpdyProxyClientSocket::UsingTCPFastOpen() const {
147   return false;
148 }
149 
Read(IOBuffer * buf,int buf_len,CompletionCallback * callback)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 
PopulateUserReadBuffer()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 
Write(IOBuffer * buf,int buf_len,CompletionCallback * callback)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 
SetReceiveBufferSize(int32 size)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 
SetSendBufferSize(int32 size)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 
GetPeerAddress(AddressList * address) const252 int SpdyProxyClientSocket::GetPeerAddress(AddressList* address) const {
253   if (!IsConnected())
254     return ERR_SOCKET_NOT_CONNECTED;
255   return spdy_stream_->GetPeerAddress(address);
256 }
257 
GetLocalAddress(IPEndPoint * address) const258 int SpdyProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
259   if (!IsConnected())
260     return ERR_SOCKET_NOT_CONNECTED;
261   return spdy_stream_->GetLocalAddress(address);
262 }
263 
OnIOComplete(int result)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 
DoLoop(int last_io_result)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 
DoGenerateAuthToken()314 int SpdyProxyClientSocket::DoGenerateAuthToken() {
315   next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
316   return auth_->MaybeGenerateAuthToken(&request_, &io_callback_, net_log_);
317 }
318 
DoGenerateAuthTokenComplete(int result)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 
DoSendRequest()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 
DoSendRequestComplete(int result)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 
DoReadReplyComplete(int result)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.
OnSendHeadersComplete(int status)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 
OnSendBody()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 
OnSendBodyComplete(int,bool *)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 
OnResponseReceived(const spdy::SpdyHeaderBlock & response,base::Time response_time,int status)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.
OnDataReceived(const char * data,int length)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 
OnDataSent(int length)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 
OnClose(int status)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 
set_chunk_callback(ChunkCallback *)517 void SpdyProxyClientSocket::set_chunk_callback(ChunkCallback* /*callback*/) {
518 }
519 
520 }  // namespace net
521