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
22namespace net {
23
24SpdyProxyClientSocket::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
58SpdyProxyClientSocket::~SpdyProxyClientSocket() {
59  Disconnect();
60}
61
62const HttpResponseInfo* SpdyProxyClientSocket::GetConnectResponseInfo() const {
63  return response_.headers ? &response_ : NULL;
64}
65
66HttpStream* 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
85int 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
105void 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
122bool SpdyProxyClientSocket::IsConnected() const {
123  return next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED;
124}
125
126bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
127  return IsConnected() && !spdy_stream_->is_idle();
128}
129
130const BoundNetLog& SpdyProxyClientSocket::NetLog() const {
131  return net_log_;
132}
133
134void SpdyProxyClientSocket::SetSubresourceSpeculation() {
135  // TODO(rch): what should this implementation be?
136}
137
138void SpdyProxyClientSocket::SetOmniboxSpeculation() {
139  // TODO(rch): what should this implementation be?
140}
141
142bool SpdyProxyClientSocket::WasEverUsed() const {
143  return was_ever_used_ || (spdy_stream_ && spdy_stream_->WasEverUsed());
144}
145
146bool SpdyProxyClientSocket::UsingTCPFastOpen() const {
147  return false;
148}
149
150int 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
178int 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
199int 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
240bool 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
246bool 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
252int SpdyProxyClientSocket::GetPeerAddress(AddressList* address) const {
253  if (!IsConnected())
254    return ERR_SOCKET_NOT_CONNECTED;
255  return spdy_stream_->GetPeerAddress(address);
256}
257
258int SpdyProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
259  if (!IsConnected())
260    return ERR_SOCKET_NOT_CONNECTED;
261  return spdy_stream_->GetLocalAddress(address);
262}
263
264void 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
274int 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
314int SpdyProxyClientSocket::DoGenerateAuthToken() {
315  next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
316  return auth_->MaybeGenerateAuthToken(&request_, &io_callback_, net_log_);
317}
318
319int 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
326int 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
358int 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
367int 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.
406bool 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
416int 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
423int 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
430int 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.
450void 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
468void 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
485void 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
517void SpdyProxyClientSocket::set_chunk_callback(ChunkCallback* /*callback*/) {
518}
519
520}  // namespace net
521