Enables compositing support for webview.
[chromium-blink-merge.git] / net / spdy / spdy_proxy_client_socket.cc
blob0d62ce7a500846f364ef6fa83e414370aa3140a0
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.
5 #include "net/spdy/spdy_proxy_client_socket.h"
7 #include <algorithm> // min
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/logging.h"
12 #include "base/string_util.h"
13 #include "googleurl/src/gurl.h"
14 #include "net/base/auth.h"
15 #include "net/base/io_buffer.h"
16 #include "net/base/net_util.h"
17 #include "net/http/http_auth_cache.h"
18 #include "net/http/http_auth_handler_factory.h"
19 #include "net/http/http_response_headers.h"
20 #include "net/spdy/spdy_http_utils.h"
22 namespace net {
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 : next_state_(STATE_DISCONNECTED),
33 spdy_stream_(spdy_stream),
34 endpoint_(endpoint),
35 auth_(
36 new HttpAuthController(HttpAuth::AUTH_PROXY,
37 GURL("https://" + proxy_server.ToString()),
38 auth_cache,
39 auth_handler_factory)),
40 user_buffer_(NULL),
41 write_buffer_len_(0),
42 write_bytes_outstanding_(0),
43 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
44 net_log_(spdy_stream->net_log()) {
45 request_.method = "CONNECT";
46 request_.url = url;
47 if (!user_agent.empty())
48 request_.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent,
49 user_agent);
50 spdy_stream_->SetDelegate(this);
51 was_ever_used_ = spdy_stream_->WasEverUsed();
54 SpdyProxyClientSocket::~SpdyProxyClientSocket() {
55 Disconnect();
58 const HttpResponseInfo* SpdyProxyClientSocket::GetConnectResponseInfo() const {
59 return response_.headers ? &response_ : NULL;
62 const scoped_refptr<HttpAuthController>&
63 SpdyProxyClientSocket::GetAuthController() const {
64 return auth_;
67 int SpdyProxyClientSocket::RestartWithAuth(const CompletionCallback& callback) {
68 // A SPDY Stream can only handle a single request, so the underlying
69 // stream may not be reused and a new SpdyProxyClientSocket must be
70 // created (possibly on top of the same SPDY Session).
71 next_state_ = STATE_DISCONNECTED;
72 return OK;
75 bool SpdyProxyClientSocket::IsUsingSpdy() const {
76 return true;
79 NextProto SpdyProxyClientSocket::GetProtocolNegotiated() const {
80 // Save the negotiated protocol
81 SSLInfo ssl_info;
82 bool was_npn_negotiated;
83 NextProto protocol_negotiated;
84 spdy_stream_->GetSSLInfo(&ssl_info, &was_npn_negotiated,
85 &protocol_negotiated);
86 return protocol_negotiated;
89 HttpStream* SpdyProxyClientSocket::CreateConnectResponseStream() {
90 DCHECK(response_stream_.get());
91 return response_stream_.release();
94 // Sends a SYN_STREAM frame to the proxy with a CONNECT request
95 // for the specified endpoint. Waits for the server to send back
96 // a SYN_REPLY frame. OK will be returned if the status is 200.
97 // ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status.
98 // In any of these cases, Read() may be called to retrieve the HTTP
99 // response body. Any other return values should be considered fatal.
100 // TODO(rch): handle 407 proxy auth requested correctly, perhaps
101 // by creating a new stream for the subsequent request.
102 // TODO(rch): create a more appropriate error code to disambiguate
103 // the HTTPS Proxy tunnel failure from an HTTP Proxy tunnel failure.
104 int SpdyProxyClientSocket::Connect(const CompletionCallback& callback) {
105 DCHECK(read_callback_.is_null());
106 if (next_state_ == STATE_OPEN)
107 return OK;
109 DCHECK_EQ(STATE_DISCONNECTED, next_state_);
110 next_state_ = STATE_GENERATE_AUTH_TOKEN;
112 int rv = DoLoop(OK);
113 if (rv == ERR_IO_PENDING)
114 read_callback_ = callback;
115 return rv;
118 void SpdyProxyClientSocket::Disconnect() {
119 read_buffer_.clear();
120 user_buffer_ = NULL;
121 read_callback_.Reset();
123 write_buffer_len_ = 0;
124 write_bytes_outstanding_ = 0;
125 write_callback_.Reset();
127 next_state_ = STATE_DISCONNECTED;
129 if (spdy_stream_)
130 // This will cause OnClose to be invoked, which takes care of
131 // cleaning up all the internal state.
132 spdy_stream_->Cancel();
135 bool SpdyProxyClientSocket::IsConnected() const {
136 return next_state_ == STATE_OPEN;
139 bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
140 return IsConnected() && read_buffer_.empty() && spdy_stream_->is_idle();
143 const BoundNetLog& SpdyProxyClientSocket::NetLog() const {
144 return net_log_;
147 void SpdyProxyClientSocket::SetSubresourceSpeculation() {
148 // TODO(rch): what should this implementation be?
151 void SpdyProxyClientSocket::SetOmniboxSpeculation() {
152 // TODO(rch): what should this implementation be?
155 bool SpdyProxyClientSocket::WasEverUsed() const {
156 return was_ever_used_ || (spdy_stream_ && spdy_stream_->WasEverUsed());
159 bool SpdyProxyClientSocket::UsingTCPFastOpen() const {
160 return false;
163 int64 SpdyProxyClientSocket::NumBytesRead() const {
164 return -1;
167 base::TimeDelta SpdyProxyClientSocket::GetConnectTimeMicros() const {
168 return base::TimeDelta::FromMicroseconds(-1);
171 bool SpdyProxyClientSocket::WasNpnNegotiated() const {
172 return false;
175 NextProto SpdyProxyClientSocket::GetNegotiatedProtocol() const {
176 return kProtoUnknown;
179 bool SpdyProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
180 bool was_npn_negotiated;
181 NextProto protocol_negotiated;
182 return spdy_stream_->GetSSLInfo(ssl_info, &was_npn_negotiated,
183 &protocol_negotiated);
186 int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len,
187 const CompletionCallback& callback) {
188 DCHECK(read_callback_.is_null());
189 DCHECK(!user_buffer_);
191 if (next_state_ == STATE_DISCONNECTED)
192 return ERR_SOCKET_NOT_CONNECTED;
194 if (next_state_ == STATE_CLOSED && read_buffer_.empty()) {
195 return 0;
198 DCHECK(next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED);
199 DCHECK(buf);
200 user_buffer_ = new DrainableIOBuffer(buf, buf_len);
201 int result = PopulateUserReadBuffer();
202 if (result == 0) {
203 DCHECK(!callback.is_null());
204 read_callback_ = callback;
205 return ERR_IO_PENDING;
207 user_buffer_ = NULL;
208 return result;
211 int SpdyProxyClientSocket::PopulateUserReadBuffer() {
212 if (!user_buffer_)
213 return ERR_IO_PENDING;
215 int bytes_read = 0;
216 while (!read_buffer_.empty() && user_buffer_->BytesRemaining() > 0) {
217 scoped_refptr<DrainableIOBuffer> data = read_buffer_.front();
218 const int bytes_to_copy = std::min(user_buffer_->BytesRemaining(),
219 data->BytesRemaining());
220 memcpy(user_buffer_->data(), data->data(), bytes_to_copy);
221 user_buffer_->DidConsume(bytes_to_copy);
222 bytes_read += bytes_to_copy;
223 if (data->BytesRemaining() == bytes_to_copy) {
224 // Consumed all data from this buffer
225 read_buffer_.pop_front();
226 } else {
227 data->DidConsume(bytes_to_copy);
231 if (bytes_read > 0 && spdy_stream_)
232 spdy_stream_->IncreaseRecvWindowSize(bytes_read);
234 return user_buffer_->BytesConsumed();
237 int SpdyProxyClientSocket::Write(IOBuffer* buf, int buf_len,
238 const CompletionCallback& callback) {
239 DCHECK(write_callback_.is_null());
240 if (next_state_ != STATE_OPEN)
241 return ERR_SOCKET_NOT_CONNECTED;
243 DCHECK(spdy_stream_);
244 write_bytes_outstanding_= buf_len;
245 if (buf_len <= kMaxSpdyFrameChunkSize) {
246 int rv = spdy_stream_->WriteStreamData(buf, buf_len, DATA_FLAG_NONE);
247 if (rv == ERR_IO_PENDING) {
248 write_callback_ = callback;
249 write_buffer_len_ = buf_len;
251 return rv;
254 // Since a SPDY Data frame can only include kMaxSpdyFrameChunkSize bytes
255 // we need to send multiple data frames
256 for (int i = 0; i < buf_len; i += kMaxSpdyFrameChunkSize) {
257 int len = std::min(kMaxSpdyFrameChunkSize, buf_len - i);
258 scoped_refptr<DrainableIOBuffer> iobuf(new DrainableIOBuffer(buf, i + len));
259 iobuf->SetOffset(i);
260 int rv = spdy_stream_->WriteStreamData(iobuf, len, DATA_FLAG_NONE);
261 if (rv > 0) {
262 write_bytes_outstanding_ -= rv;
263 } else if (rv != ERR_IO_PENDING) {
264 return rv;
267 if (write_bytes_outstanding_ > 0) {
268 write_callback_ = callback;
269 write_buffer_len_ = buf_len;
270 return ERR_IO_PENDING;
271 } else {
272 return buf_len;
276 bool SpdyProxyClientSocket::SetReceiveBufferSize(int32 size) {
277 // Since this StreamSocket sits on top of a shared SpdySession, it
278 // is not safe for callers to set change this underlying socket.
279 return false;
282 bool SpdyProxyClientSocket::SetSendBufferSize(int32 size) {
283 // Since this StreamSocket sits on top of a shared SpdySession, it
284 // is not safe for callers to set change this underlying socket.
285 return false;
288 int SpdyProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
289 if (!IsConnected())
290 return ERR_SOCKET_NOT_CONNECTED;
291 return spdy_stream_->GetPeerAddress(address);
294 int SpdyProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
295 if (!IsConnected())
296 return ERR_SOCKET_NOT_CONNECTED;
297 return spdy_stream_->GetLocalAddress(address);
300 void SpdyProxyClientSocket::LogBlockedTunnelResponse() const {
301 ProxyClientSocket::LogBlockedTunnelResponse(
302 response_.headers->response_code(),
303 request_.url,
304 /* is_https_proxy = */ true);
307 void SpdyProxyClientSocket::OnIOComplete(int result) {
308 DCHECK_NE(STATE_DISCONNECTED, next_state_);
309 int rv = DoLoop(result);
310 if (rv != ERR_IO_PENDING) {
311 CompletionCallback c = read_callback_;
312 read_callback_.Reset();
313 c.Run(rv);
317 int SpdyProxyClientSocket::DoLoop(int last_io_result) {
318 DCHECK_NE(next_state_, STATE_DISCONNECTED);
319 int rv = last_io_result;
320 do {
321 State state = next_state_;
322 next_state_ = STATE_DISCONNECTED;
323 switch (state) {
324 case STATE_GENERATE_AUTH_TOKEN:
325 DCHECK_EQ(OK, rv);
326 rv = DoGenerateAuthToken();
327 break;
328 case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
329 rv = DoGenerateAuthTokenComplete(rv);
330 break;
331 case STATE_SEND_REQUEST:
332 DCHECK_EQ(OK, rv);
333 net_log_.BeginEvent(
334 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST);
335 rv = DoSendRequest();
336 break;
337 case STATE_SEND_REQUEST_COMPLETE:
338 net_log_.EndEventWithNetErrorCode(
339 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
340 rv = DoSendRequestComplete(rv);
341 break;
342 case STATE_READ_REPLY_COMPLETE:
343 rv = DoReadReplyComplete(rv);
344 net_log_.EndEventWithNetErrorCode(
345 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
346 break;
347 default:
348 NOTREACHED() << "bad state";
349 rv = ERR_UNEXPECTED;
350 break;
352 } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
353 next_state_ != STATE_OPEN);
354 return rv;
357 int SpdyProxyClientSocket::DoGenerateAuthToken() {
358 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
359 return auth_->MaybeGenerateAuthToken(
360 &request_,
361 base::Bind(&SpdyProxyClientSocket::OnIOComplete, base::Unretained(this)),
362 net_log_);
365 int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
366 DCHECK_NE(ERR_IO_PENDING, result);
367 if (result == OK)
368 next_state_ = STATE_SEND_REQUEST;
369 return result;
372 int SpdyProxyClientSocket::DoSendRequest() {
373 next_state_ = STATE_SEND_REQUEST_COMPLETE;
375 // Add Proxy-Authentication header if necessary.
376 HttpRequestHeaders authorization_headers;
377 if (auth_->HaveAuth()) {
378 auth_->AddAuthorizationHeader(&authorization_headers);
381 std::string request_line;
382 HttpRequestHeaders request_headers;
383 BuildTunnelRequest(request_, authorization_headers, endpoint_, &request_line,
384 &request_headers);
386 net_log_.AddEvent(
387 NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
388 base::Bind(&HttpRequestHeaders::NetLogCallback,
389 base::Unretained(&request_headers),
390 &request_line));
392 request_.extra_headers.MergeFrom(request_headers);
393 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock());
394 CreateSpdyHeadersFromHttpRequest(request_, request_headers, headers.get(),
395 spdy_stream_->GetProtocolVersion(), true);
396 // Reset the URL to be the endpoint of the connection
397 if (spdy_stream_->GetProtocolVersion() > 2) {
398 (*headers)[":path"] = endpoint_.ToString();
399 headers->erase(":scheme");
400 } else {
401 (*headers)["url"] = endpoint_.ToString();
402 headers->erase("scheme");
404 spdy_stream_->set_spdy_headers(headers.Pass());
406 return spdy_stream_->SendRequest(true);
409 int SpdyProxyClientSocket::DoSendRequestComplete(int result) {
410 if (result < 0)
411 return result;
413 // Wait for SYN_REPLY frame from the server
414 next_state_ = STATE_READ_REPLY_COMPLETE;
415 return ERR_IO_PENDING;
418 int SpdyProxyClientSocket::DoReadReplyComplete(int result) {
419 // We enter this method directly from DoSendRequestComplete, since
420 // we are notified by a callback when the SYN_REPLY frame arrives
422 if (result < 0)
423 return result;
425 // Require the "HTTP/1.x" status line for SSL CONNECT.
426 if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0))
427 return ERR_TUNNEL_CONNECTION_FAILED;
429 net_log_.AddEvent(
430 NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
431 base::Bind(&HttpResponseHeaders::NetLogCallback, response_.headers));
433 switch (response_.headers->response_code()) {
434 case 200: // OK
435 next_state_ = STATE_OPEN;
436 return OK;
438 case 302: // Found / Moved Temporarily
439 // Try to return a sanitized response so we can follow auth redirects.
440 // If we can't, fail the tunnel connection.
441 if (SanitizeProxyRedirect(&response_, request_.url)) {
442 // Immediately hand off our SpdyStream to a newly created
443 // SpdyHttpStream so that any subsequent SpdyFrames are processed in
444 // the context of the HttpStream, not the socket.
445 DCHECK(spdy_stream_);
446 SpdyStream* stream = spdy_stream_;
447 spdy_stream_ = NULL;
448 response_stream_.reset(new SpdyHttpStream(NULL, false));
449 response_stream_->InitializeWithExistingStream(stream);
450 next_state_ = STATE_DISCONNECTED;
451 return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
452 } else {
453 LogBlockedTunnelResponse();
454 return ERR_TUNNEL_CONNECTION_FAILED;
457 case 407: // Proxy Authentication Required
458 next_state_ = STATE_OPEN;
459 return HandleProxyAuthChallenge(auth_, &response_, net_log_);
461 default:
462 // Ignore response to avoid letting the proxy impersonate the target
463 // server. (See http://crbug.com/137891.)
464 LogBlockedTunnelResponse();
465 return ERR_TUNNEL_CONNECTION_FAILED;
469 // SpdyStream::Delegate methods:
470 // Called when SYN frame has been sent.
471 // Returns true if no more data to be sent after SYN frame.
472 bool SpdyProxyClientSocket::OnSendHeadersComplete(int status) {
473 DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE);
475 OnIOComplete(status);
477 // We return true here so that we send |spdy_stream_| into
478 // STATE_OPEN (ala WebSockets).
479 return true;
482 int SpdyProxyClientSocket::OnSendBody() {
483 // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets)
484 // OnSendBody() should never be called.
485 NOTREACHED();
486 return ERR_UNEXPECTED;
489 int SpdyProxyClientSocket::OnSendBodyComplete(int /*status*/, bool* /*eof*/) {
490 // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets)
491 // OnSendBodyComplete() should never be called.
492 NOTREACHED();
493 return ERR_UNEXPECTED;
496 int SpdyProxyClientSocket::OnResponseReceived(
497 const SpdyHeaderBlock& response,
498 base::Time response_time,
499 int status) {
500 // If we've already received the reply, existing headers are too late.
501 // TODO(mbelshe): figure out a way to make HEADERS frames useful after the
502 // initial response.
503 if (next_state_ != STATE_READ_REPLY_COMPLETE)
504 return OK;
506 // Save the response
507 if (!SpdyHeadersToHttpResponse(
508 response, spdy_stream_->GetProtocolVersion(), &response_))
509 return ERR_INCOMPLETE_SPDY_HEADERS;
511 OnIOComplete(status);
512 return OK;
515 void SpdyProxyClientSocket::OnHeadersSent() {
516 // Proxy client sockets don't send any HEADERS frame.
517 NOTREACHED();
520 // Called when data is received.
521 int SpdyProxyClientSocket::OnDataReceived(const char* data, int length) {
522 if (length > 0) {
523 // Save the received data.
524 scoped_refptr<IOBuffer> io_buffer(new IOBuffer(length));
525 memcpy(io_buffer->data(), data, length);
526 read_buffer_.push_back(
527 make_scoped_refptr(new DrainableIOBuffer(io_buffer, length)));
530 if (!read_callback_.is_null()) {
531 int rv = PopulateUserReadBuffer();
532 CompletionCallback c = read_callback_;
533 read_callback_.Reset();
534 user_buffer_ = NULL;
535 c.Run(rv);
537 return OK;
540 void SpdyProxyClientSocket::OnDataSent(int length) {
541 DCHECK(!write_callback_.is_null());
543 write_bytes_outstanding_ -= length;
545 DCHECK_GE(write_bytes_outstanding_, 0);
547 if (write_bytes_outstanding_ == 0) {
548 int rv = write_buffer_len_;
549 write_buffer_len_ = 0;
550 write_bytes_outstanding_ = 0;
551 CompletionCallback c = write_callback_;
552 write_callback_.Reset();
553 c.Run(rv);
557 void SpdyProxyClientSocket::OnClose(int status) {
558 DCHECK(spdy_stream_);
559 was_ever_used_ = spdy_stream_->WasEverUsed();
560 spdy_stream_ = NULL;
562 bool connecting = next_state_ != STATE_DISCONNECTED &&
563 next_state_ < STATE_OPEN;
564 if (next_state_ == STATE_OPEN)
565 next_state_ = STATE_CLOSED;
566 else
567 next_state_ = STATE_DISCONNECTED;
569 base::WeakPtr<SpdyProxyClientSocket> weak_ptr = weak_factory_.GetWeakPtr();
570 CompletionCallback write_callback = write_callback_;
571 write_callback_.Reset();
572 write_buffer_len_ = 0;
573 write_bytes_outstanding_ = 0;
575 // If we're in the middle of connecting, we need to make sure
576 // we invoke the connect callback.
577 if (connecting) {
578 DCHECK(!read_callback_.is_null());
579 CompletionCallback read_callback = read_callback_;
580 read_callback_.Reset();
581 read_callback.Run(status);
582 } else if (!read_callback_.is_null()) {
583 // If we have a read_callback_, the we need to make sure we call it back.
584 OnDataReceived(NULL, 0);
586 // This may have been deleted by read_callback_, so check first.
587 if (weak_ptr && !write_callback.is_null())
588 write_callback.Run(ERR_CONNECTION_CLOSED);
591 } // namespace net