We started redesigning GpuMemoryBuffer interface to handle multiple buffers [0].
[chromium-blink-merge.git] / net / spdy / spdy_proxy_client_socket.cc
blob3e1feab8add3c77dc7110d55fbf1170f6695c059
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/callback_helpers.h"
12 #include "base/logging.h"
13 #include "base/strings/string_util.h"
14 #include "base/values.h"
15 #include "net/base/auth.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_util.h"
18 #include "net/http/http_auth_cache.h"
19 #include "net/http/http_auth_handler_factory.h"
20 #include "net/http/http_response_headers.h"
21 #include "net/http/proxy_connect_redirect_http_stream.h"
22 #include "net/spdy/spdy_http_utils.h"
23 #include "url/gurl.h"
25 namespace net {
27 SpdyProxyClientSocket::SpdyProxyClientSocket(
28 const base::WeakPtr<SpdyStream>& spdy_stream,
29 const std::string& user_agent,
30 const HostPortPair& endpoint,
31 const HostPortPair& proxy_server,
32 const BoundNetLog& source_net_log,
33 HttpAuthCache* auth_cache,
34 HttpAuthHandlerFactory* auth_handler_factory)
35 : next_state_(STATE_DISCONNECTED),
36 spdy_stream_(spdy_stream),
37 endpoint_(endpoint),
38 auth_(new HttpAuthController(HttpAuth::AUTH_PROXY,
39 GURL("https://" + proxy_server.ToString()),
40 auth_cache,
41 auth_handler_factory)),
42 user_agent_(user_agent),
43 user_buffer_len_(0),
44 write_buffer_len_(0),
45 was_ever_used_(false),
46 redirect_has_load_timing_info_(false),
47 net_log_(BoundNetLog::Make(spdy_stream->net_log().net_log(),
48 NetLog::SOURCE_PROXY_CLIENT_SOCKET)),
49 weak_factory_(this),
50 write_callback_weak_factory_(this) {
51 request_.method = "CONNECT";
52 request_.url = GURL("https://" + endpoint.ToString());
53 net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE,
54 source_net_log.source().ToEventParametersCallback());
55 net_log_.AddEvent(
56 NetLog::TYPE_HTTP2_PROXY_CLIENT_SESSION,
57 spdy_stream->net_log().source().ToEventParametersCallback());
59 spdy_stream_->SetDelegate(this);
60 was_ever_used_ = spdy_stream_->WasEverUsed();
63 SpdyProxyClientSocket::~SpdyProxyClientSocket() {
64 Disconnect();
65 net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE);
68 const HttpResponseInfo* SpdyProxyClientSocket::GetConnectResponseInfo() const {
69 return response_.headers.get() ? &response_ : NULL;
72 const scoped_refptr<HttpAuthController>&
73 SpdyProxyClientSocket::GetAuthController() const {
74 return auth_;
77 int SpdyProxyClientSocket::RestartWithAuth(const CompletionCallback& callback) {
78 // A SPDY Stream can only handle a single request, so the underlying
79 // stream may not be reused and a new SpdyProxyClientSocket must be
80 // created (possibly on top of the same SPDY Session).
81 next_state_ = STATE_DISCONNECTED;
82 return OK;
85 bool SpdyProxyClientSocket::IsUsingSpdy() const {
86 return true;
89 NextProto SpdyProxyClientSocket::GetProtocolNegotiated() const {
90 // Save the negotiated protocol
91 SSLInfo ssl_info;
92 bool was_npn_negotiated;
93 NextProto protocol_negotiated;
94 spdy_stream_->GetSSLInfo(&ssl_info, &was_npn_negotiated,
95 &protocol_negotiated);
96 return protocol_negotiated;
99 HttpStream* SpdyProxyClientSocket::CreateConnectResponseStream() {
100 return new ProxyConnectRedirectHttpStream(
101 redirect_has_load_timing_info_ ? &redirect_load_timing_info_ : NULL);
104 // Sends a SYN_STREAM frame to the proxy with a CONNECT request
105 // for the specified endpoint. Waits for the server to send back
106 // a SYN_REPLY frame. OK will be returned if the status is 200.
107 // ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status.
108 // In any of these cases, Read() may be called to retrieve the HTTP
109 // response body. Any other return values should be considered fatal.
110 // TODO(rch): handle 407 proxy auth requested correctly, perhaps
111 // by creating a new stream for the subsequent request.
112 // TODO(rch): create a more appropriate error code to disambiguate
113 // the HTTPS Proxy tunnel failure from an HTTP Proxy tunnel failure.
114 int SpdyProxyClientSocket::Connect(const CompletionCallback& callback) {
115 DCHECK(read_callback_.is_null());
116 if (next_state_ == STATE_OPEN)
117 return OK;
119 DCHECK_EQ(STATE_DISCONNECTED, next_state_);
120 next_state_ = STATE_GENERATE_AUTH_TOKEN;
122 int rv = DoLoop(OK);
123 if (rv == ERR_IO_PENDING)
124 read_callback_ = callback;
125 return rv;
128 void SpdyProxyClientSocket::Disconnect() {
129 read_buffer_queue_.Clear();
130 user_buffer_ = NULL;
131 user_buffer_len_ = 0;
132 read_callback_.Reset();
134 write_buffer_len_ = 0;
135 write_callback_.Reset();
136 write_callback_weak_factory_.InvalidateWeakPtrs();
138 next_state_ = STATE_DISCONNECTED;
140 if (spdy_stream_.get()) {
141 // This will cause OnClose to be invoked, which takes care of
142 // cleaning up all the internal state.
143 spdy_stream_->Cancel();
144 DCHECK(!spdy_stream_.get());
148 bool SpdyProxyClientSocket::IsConnected() const {
149 return next_state_ == STATE_OPEN;
152 bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
153 return IsConnected() && read_buffer_queue_.IsEmpty() &&
154 spdy_stream_->IsOpen();
157 const BoundNetLog& SpdyProxyClientSocket::NetLog() const {
158 return net_log_;
161 void SpdyProxyClientSocket::SetSubresourceSpeculation() {
162 // TODO(rch): what should this implementation be?
165 void SpdyProxyClientSocket::SetOmniboxSpeculation() {
166 // TODO(rch): what should this implementation be?
169 bool SpdyProxyClientSocket::WasEverUsed() const {
170 return was_ever_used_ || (spdy_stream_.get() && spdy_stream_->WasEverUsed());
173 bool SpdyProxyClientSocket::UsingTCPFastOpen() const {
174 return false;
177 bool SpdyProxyClientSocket::WasNpnNegotiated() const {
178 return false;
181 NextProto SpdyProxyClientSocket::GetNegotiatedProtocol() const {
182 return kProtoUnknown;
185 bool SpdyProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
186 bool was_npn_negotiated;
187 NextProto protocol_negotiated;
188 return spdy_stream_->GetSSLInfo(ssl_info, &was_npn_negotiated,
189 &protocol_negotiated);
192 int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len,
193 const CompletionCallback& callback) {
194 DCHECK(read_callback_.is_null());
195 DCHECK(!user_buffer_.get());
197 if (next_state_ == STATE_DISCONNECTED)
198 return ERR_SOCKET_NOT_CONNECTED;
200 if (next_state_ == STATE_CLOSED && read_buffer_queue_.IsEmpty()) {
201 return 0;
204 DCHECK(next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED);
205 DCHECK(buf);
206 size_t result = PopulateUserReadBuffer(buf->data(), buf_len);
207 if (result == 0) {
208 user_buffer_ = buf;
209 user_buffer_len_ = static_cast<size_t>(buf_len);
210 DCHECK(!callback.is_null());
211 read_callback_ = callback;
212 return ERR_IO_PENDING;
214 user_buffer_ = NULL;
215 return result;
218 size_t SpdyProxyClientSocket::PopulateUserReadBuffer(char* data, size_t len) {
219 return read_buffer_queue_.Dequeue(data, len);
222 int SpdyProxyClientSocket::Write(IOBuffer* buf, int buf_len,
223 const CompletionCallback& callback) {
224 DCHECK(write_callback_.is_null());
225 if (next_state_ != STATE_OPEN)
226 return ERR_SOCKET_NOT_CONNECTED;
228 DCHECK(spdy_stream_.get());
229 spdy_stream_->SendData(buf, buf_len, MORE_DATA_TO_SEND);
230 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT,
231 buf_len, buf->data());
232 write_callback_ = callback;
233 write_buffer_len_ = buf_len;
234 return ERR_IO_PENDING;
237 int SpdyProxyClientSocket::SetReceiveBufferSize(int32 size) {
238 // Since this StreamSocket sits on top of a shared SpdySession, it
239 // is not safe for callers to change this underlying socket.
240 return ERR_NOT_IMPLEMENTED;
243 int SpdyProxyClientSocket::SetSendBufferSize(int32 size) {
244 // Since this StreamSocket sits on top of a shared SpdySession, it
245 // is not safe for callers to change this underlying socket.
246 return ERR_NOT_IMPLEMENTED;
249 int SpdyProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
250 if (!IsConnected())
251 return ERR_SOCKET_NOT_CONNECTED;
252 return spdy_stream_->GetPeerAddress(address);
255 int SpdyProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
256 if (!IsConnected())
257 return ERR_SOCKET_NOT_CONNECTED;
258 return spdy_stream_->GetLocalAddress(address);
261 void SpdyProxyClientSocket::LogBlockedTunnelResponse() const {
262 ProxyClientSocket::LogBlockedTunnelResponse(
263 response_.headers->response_code(),
264 /* is_https_proxy = */ true);
267 void SpdyProxyClientSocket::RunCallback(const CompletionCallback& callback,
268 int result) const {
269 callback.Run(result);
272 void SpdyProxyClientSocket::OnIOComplete(int result) {
273 DCHECK_NE(STATE_DISCONNECTED, next_state_);
274 int rv = DoLoop(result);
275 if (rv != ERR_IO_PENDING) {
276 CompletionCallback c = read_callback_;
277 read_callback_.Reset();
278 c.Run(rv);
282 int SpdyProxyClientSocket::DoLoop(int last_io_result) {
283 DCHECK_NE(next_state_, STATE_DISCONNECTED);
284 int rv = last_io_result;
285 do {
286 State state = next_state_;
287 next_state_ = STATE_DISCONNECTED;
288 switch (state) {
289 case STATE_GENERATE_AUTH_TOKEN:
290 DCHECK_EQ(OK, rv);
291 rv = DoGenerateAuthToken();
292 break;
293 case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
294 rv = DoGenerateAuthTokenComplete(rv);
295 break;
296 case STATE_SEND_REQUEST:
297 DCHECK_EQ(OK, rv);
298 net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST);
299 rv = DoSendRequest();
300 break;
301 case STATE_SEND_REQUEST_COMPLETE:
302 net_log_.EndEventWithNetErrorCode(
303 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
304 rv = DoSendRequestComplete(rv);
305 if (rv >= 0 || rv == ERR_IO_PENDING) {
306 // Emit extra event so can use the same events as
307 // HttpProxyClientSocket.
308 net_log_.BeginEvent(
309 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS);
311 break;
312 case STATE_READ_REPLY_COMPLETE:
313 rv = DoReadReplyComplete(rv);
314 net_log_.EndEventWithNetErrorCode(
315 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
316 break;
317 default:
318 NOTREACHED() << "bad state";
319 rv = ERR_UNEXPECTED;
320 break;
322 } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
323 next_state_ != STATE_OPEN);
324 return rv;
327 int SpdyProxyClientSocket::DoGenerateAuthToken() {
328 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
329 return auth_->MaybeGenerateAuthToken(
330 &request_,
331 base::Bind(&SpdyProxyClientSocket::OnIOComplete,
332 weak_factory_.GetWeakPtr()),
333 net_log_);
336 int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
337 DCHECK_NE(ERR_IO_PENDING, result);
338 if (result == OK)
339 next_state_ = STATE_SEND_REQUEST;
340 return result;
343 int SpdyProxyClientSocket::DoSendRequest() {
344 next_state_ = STATE_SEND_REQUEST_COMPLETE;
346 // Add Proxy-Authentication header if necessary.
347 HttpRequestHeaders authorization_headers;
348 if (auth_->HaveAuth()) {
349 auth_->AddAuthorizationHeader(&authorization_headers);
352 std::string request_line;
353 BuildTunnelRequest(endpoint_, authorization_headers, user_agent_,
354 &request_line, &request_.extra_headers);
356 net_log_.AddEvent(
357 NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
358 base::Bind(&HttpRequestHeaders::NetLogCallback,
359 base::Unretained(&request_.extra_headers), &request_line));
361 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock());
362 CreateSpdyHeadersFromHttpRequest(request_, request_.extra_headers,
363 spdy_stream_->GetProtocolVersion(), true,
364 headers.get());
365 // Reset the URL to be the endpoint of the connection
366 if (spdy_stream_->GetProtocolVersion() > 2) {
367 (*headers)[":path"] = endpoint_.ToString();
368 headers->erase(":scheme");
369 } else {
370 (*headers)["url"] = endpoint_.ToString();
371 headers->erase("scheme");
374 return spdy_stream_->SendRequestHeaders(headers.Pass(), MORE_DATA_TO_SEND);
377 int SpdyProxyClientSocket::DoSendRequestComplete(int result) {
378 if (result < 0)
379 return result;
381 // Wait for SYN_REPLY frame from the server
382 next_state_ = STATE_READ_REPLY_COMPLETE;
383 return ERR_IO_PENDING;
386 int SpdyProxyClientSocket::DoReadReplyComplete(int result) {
387 // We enter this method directly from DoSendRequestComplete, since
388 // we are notified by a callback when the SYN_REPLY frame arrives
390 if (result < 0)
391 return result;
393 // Require the "HTTP/1.x" status line for SSL CONNECT.
394 if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0))
395 return ERR_TUNNEL_CONNECTION_FAILED;
397 net_log_.AddEvent(
398 NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
399 base::Bind(&HttpResponseHeaders::NetLogCallback, response_.headers));
401 switch (response_.headers->response_code()) {
402 case 200: // OK
403 next_state_ = STATE_OPEN;
404 return OK;
406 case 302: // Found / Moved Temporarily
407 // Try to return a sanitized response so we can follow auth redirects.
408 // If we can't, fail the tunnel connection.
409 if (!SanitizeProxyRedirect(&response_)) {
410 LogBlockedTunnelResponse();
411 return ERR_TUNNEL_CONNECTION_FAILED;
414 redirect_has_load_timing_info_ =
415 spdy_stream_->GetLoadTimingInfo(&redirect_load_timing_info_);
416 // Note that this triggers a RST_STREAM_CANCEL.
417 spdy_stream_->DetachDelegate();
418 next_state_ = STATE_DISCONNECTED;
419 return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
421 case 407: // Proxy Authentication Required
422 next_state_ = STATE_OPEN;
423 if (!SanitizeProxyAuth(&response_)) {
424 LogBlockedTunnelResponse();
425 return ERR_TUNNEL_CONNECTION_FAILED;
427 return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_);
429 default:
430 // Ignore response to avoid letting the proxy impersonate the target
431 // server. (See http://crbug.com/137891.)
432 LogBlockedTunnelResponse();
433 return ERR_TUNNEL_CONNECTION_FAILED;
437 // SpdyStream::Delegate methods:
438 // Called when SYN frame has been sent.
439 // Returns true if no more data to be sent after SYN frame.
440 void SpdyProxyClientSocket::OnRequestHeadersSent() {
441 DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE);
443 OnIOComplete(OK);
446 SpdyResponseHeadersStatus SpdyProxyClientSocket::OnResponseHeadersUpdated(
447 const SpdyHeaderBlock& response_headers) {
448 // If we've already received the reply, existing headers are too late.
449 // TODO(mbelshe): figure out a way to make HEADERS frames useful after the
450 // initial response.
451 if (next_state_ != STATE_READ_REPLY_COMPLETE)
452 return RESPONSE_HEADERS_ARE_COMPLETE;
454 // Save the response
455 if (!SpdyHeadersToHttpResponse(
456 response_headers, spdy_stream_->GetProtocolVersion(), &response_))
457 return RESPONSE_HEADERS_ARE_INCOMPLETE;
459 OnIOComplete(OK);
460 return RESPONSE_HEADERS_ARE_COMPLETE;
463 // Called when data is received or on EOF (if |buffer| is NULL).
464 void SpdyProxyClientSocket::OnDataReceived(scoped_ptr<SpdyBuffer> buffer) {
465 if (buffer) {
466 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED,
467 buffer->GetRemainingSize(),
468 buffer->GetRemainingData());
469 read_buffer_queue_.Enqueue(buffer.Pass());
470 } else {
471 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, 0, NULL);
474 if (!read_callback_.is_null()) {
475 int rv = PopulateUserReadBuffer(user_buffer_->data(), user_buffer_len_);
476 CompletionCallback c = read_callback_;
477 read_callback_.Reset();
478 user_buffer_ = NULL;
479 user_buffer_len_ = 0;
480 c.Run(rv);
484 void SpdyProxyClientSocket::OnDataSent() {
485 DCHECK(!write_callback_.is_null());
487 int rv = write_buffer_len_;
488 write_buffer_len_ = 0;
490 // Proxy write callbacks result in deep callback chains. Post to allow the
491 // stream's write callback chain to unwind (see crbug.com/355511).
492 base::MessageLoop::current()->PostTask(
493 FROM_HERE,
494 base::Bind(&SpdyProxyClientSocket::RunCallback,
495 write_callback_weak_factory_.GetWeakPtr(),
496 ResetAndReturn(&write_callback_),
497 rv));
500 void SpdyProxyClientSocket::OnClose(int status) {
501 was_ever_used_ = spdy_stream_->WasEverUsed();
502 spdy_stream_.reset();
504 bool connecting = next_state_ != STATE_DISCONNECTED &&
505 next_state_ < STATE_OPEN;
506 if (next_state_ == STATE_OPEN)
507 next_state_ = STATE_CLOSED;
508 else
509 next_state_ = STATE_DISCONNECTED;
511 base::WeakPtr<SpdyProxyClientSocket> weak_ptr = weak_factory_.GetWeakPtr();
512 CompletionCallback write_callback = write_callback_;
513 write_callback_.Reset();
514 write_buffer_len_ = 0;
516 // If we're in the middle of connecting, we need to make sure
517 // we invoke the connect callback.
518 if (connecting) {
519 DCHECK(!read_callback_.is_null());
520 CompletionCallback read_callback = read_callback_;
521 read_callback_.Reset();
522 read_callback.Run(status);
523 } else if (!read_callback_.is_null()) {
524 // If we have a read_callback_, the we need to make sure we call it back.
525 OnDataReceived(scoped_ptr<SpdyBuffer>());
527 // This may have been deleted by read_callback_, so check first.
528 if (weak_ptr.get() && !write_callback.is_null())
529 write_callback.Run(ERR_CONNECTION_CLOSED);
532 } // namespace net