blob: ade487ca22e9c969f8162c4a3b5fd8dc70fd56e6 [file] [log] [blame]
[email protected]e92fa7e12012-02-16 23:31:221// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]fe2f62a2010-10-01 03:34:072// 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
[email protected]49639fa2011-12-20 23:22:419#include "base/bind.h"
10#include "base/bind_helpers.h"
[email protected]fe2f62a2010-10-01 03:34:0711#include "base/logging.h"
12#include "base/string_util.h"
[email protected]f6c63db52013-02-02 00:35:2213#include "base/values.h"
[email protected]fe2f62a2010-10-01 03:34:0714#include "googleurl/src/gurl.h"
15#include "net/base/auth.h"
16#include "net/base/io_buffer.h"
17#include "net/base/net_util.h"
[email protected]fe3b7dc2012-02-03 19:52:0918#include "net/http/http_auth_cache.h"
19#include "net/http/http_auth_handler_factory.h"
[email protected]b104b502010-10-18 20:21:3120#include "net/http/http_response_headers.h"
[email protected]fe2f62a2010-10-01 03:34:0721#include "net/spdy/spdy_http_utils.h"
22
23namespace net {
24
25SpdyProxyClientSocket::SpdyProxyClientSocket(
26 SpdyStream* spdy_stream,
27 const std::string& user_agent,
28 const HostPortPair& endpoint,
29 const GURL& url,
30 const HostPortPair& proxy_server,
[email protected]f6c63db52013-02-02 00:35:2231 const BoundNetLog& source_net_log,
[email protected]fe3b7dc2012-02-03 19:52:0932 HttpAuthCache* auth_cache,
33 HttpAuthHandlerFactory* auth_handler_factory)
[email protected]49639fa2011-12-20 23:22:4134 : next_state_(STATE_DISCONNECTED),
[email protected]fe2f62a2010-10-01 03:34:0735 spdy_stream_(spdy_stream),
[email protected]fe2f62a2010-10-01 03:34:0736 endpoint_(endpoint),
[email protected]fe3b7dc2012-02-03 19:52:0937 auth_(
38 new HttpAuthController(HttpAuth::AUTH_PROXY,
39 GURL("https://" + proxy_server.ToString()),
40 auth_cache,
41 auth_handler_factory)),
[email protected]fe2f62a2010-10-01 03:34:0742 user_buffer_(NULL),
43 write_buffer_len_(0),
44 write_bytes_outstanding_(0),
[email protected]6af4e412011-11-29 23:39:1845 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
[email protected]f6c63db52013-02-02 00:35:2246 net_log_(BoundNetLog::Make(spdy_stream->net_log().net_log(),
47 NetLog::SOURCE_PROXY_CLIENT_SOCKET)) {
[email protected]fe2f62a2010-10-01 03:34:0748 request_.method = "CONNECT";
49 request_.url = url;
50 if (!user_agent.empty())
51 request_.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent,
52 user_agent);
[email protected]f6c63db52013-02-02 00:35:2253
54 net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE,
55 source_net_log.source().ToEventParametersCallback());
56 net_log_.AddEvent(
57 NetLog::TYPE_SPDY_PROXY_CLIENT_SESSION,
58 spdy_stream->net_log().source().ToEventParametersCallback());
59
[email protected]fe2f62a2010-10-01 03:34:0760 spdy_stream_->SetDelegate(this);
61 was_ever_used_ = spdy_stream_->WasEverUsed();
62}
63
64SpdyProxyClientSocket::~SpdyProxyClientSocket() {
65 Disconnect();
[email protected]f6c63db52013-02-02 00:35:2266 net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE);
[email protected]fe2f62a2010-10-01 03:34:0767}
68
[email protected]be1a48b2011-01-20 00:12:1369const HttpResponseInfo* SpdyProxyClientSocket::GetConnectResponseInfo() const {
70 return response_.headers ? &response_ : NULL;
71}
72
[email protected]c0fe941d2012-02-25 00:15:3273const scoped_refptr<HttpAuthController>&
74SpdyProxyClientSocket::GetAuthController() const {
75 return auth_;
76}
77
78int SpdyProxyClientSocket::RestartWithAuth(const CompletionCallback& callback) {
79 // A SPDY Stream can only handle a single request, so the underlying
80 // stream may not be reused and a new SpdyProxyClientSocket must be
81 // created (possibly on top of the same SPDY Session).
82 next_state_ = STATE_DISCONNECTED;
[email protected]0c5fb722012-02-28 11:50:3583 return OK;
[email protected]c0fe941d2012-02-25 00:15:3284}
85
86bool SpdyProxyClientSocket::IsUsingSpdy() const {
87 return true;
88}
89
[email protected]8e3c78cb2012-03-31 03:58:4690NextProto SpdyProxyClientSocket::GetProtocolNegotiated() const {
[email protected]c0fe941d2012-02-25 00:15:3291 // Save the negotiated protocol
92 SSLInfo ssl_info;
93 bool was_npn_negotiated;
[email protected]8e3c78cb2012-03-31 03:58:4694 NextProto protocol_negotiated;
[email protected]c0fe941d2012-02-25 00:15:3295 spdy_stream_->GetSSLInfo(&ssl_info, &was_npn_negotiated,
96 &protocol_negotiated);
97 return protocol_negotiated;
98}
99
[email protected]511f6f52010-12-17 03:58:29100HttpStream* SpdyProxyClientSocket::CreateConnectResponseStream() {
101 DCHECK(response_stream_.get());
102 return response_stream_.release();
103}
104
[email protected]fe2f62a2010-10-01 03:34:07105// Sends a SYN_STREAM frame to the proxy with a CONNECT request
106// for the specified endpoint. Waits for the server to send back
107// a SYN_REPLY frame. OK will be returned if the status is 200.
108// ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status.
109// In any of these cases, Read() may be called to retrieve the HTTP
110// response body. Any other return values should be considered fatal.
[email protected]fe3b7dc2012-02-03 19:52:09111// TODO(rch): handle 407 proxy auth requested correctly, perhaps
112// by creating a new stream for the subsequent request.
[email protected]fe2f62a2010-10-01 03:34:07113// TODO(rch): create a more appropriate error code to disambiguate
114// the HTTPS Proxy tunnel failure from an HTTP Proxy tunnel failure.
[email protected]dbf036f2011-12-06 23:33:24115int SpdyProxyClientSocket::Connect(const CompletionCallback& callback) {
[email protected]83039bb2011-12-09 18:43:55116 DCHECK(read_callback_.is_null());
[email protected]d9da5fe2010-10-13 22:37:16117 if (next_state_ == STATE_OPEN)
[email protected]fe2f62a2010-10-01 03:34:07118 return OK;
119
[email protected]d9da5fe2010-10-13 22:37:16120 DCHECK_EQ(STATE_DISCONNECTED, next_state_);
[email protected]fe2f62a2010-10-01 03:34:07121 next_state_ = STATE_GENERATE_AUTH_TOKEN;
122
123 int rv = DoLoop(OK);
124 if (rv == ERR_IO_PENDING)
125 read_callback_ = callback;
126 return rv;
127}
128
129void SpdyProxyClientSocket::Disconnect() {
[email protected]d9da5fe2010-10-13 22:37:16130 read_buffer_.clear();
131 user_buffer_ = NULL;
[email protected]dbf036f2011-12-06 23:33:24132 read_callback_.Reset();
[email protected]d9da5fe2010-10-13 22:37:16133
134 write_buffer_len_ = 0;
135 write_bytes_outstanding_ = 0;
[email protected]83039bb2011-12-09 18:43:55136 write_callback_.Reset();
[email protected]d9da5fe2010-10-13 22:37:16137
138 next_state_ = STATE_DISCONNECTED;
139
[email protected]fe2f62a2010-10-01 03:34:07140 if (spdy_stream_)
141 // This will cause OnClose to be invoked, which takes care of
142 // cleaning up all the internal state.
143 spdy_stream_->Cancel();
144}
145
146bool SpdyProxyClientSocket::IsConnected() const {
[email protected]194c7a92011-12-03 04:54:18147 return next_state_ == STATE_OPEN;
[email protected]fe2f62a2010-10-01 03:34:07148}
149
150bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
[email protected]194c7a92011-12-03 04:54:18151 return IsConnected() && read_buffer_.empty() && spdy_stream_->is_idle();
[email protected]fe2f62a2010-10-01 03:34:07152}
153
[email protected]e4be2dd2010-12-14 00:44:39154const BoundNetLog& SpdyProxyClientSocket::NetLog() const {
155 return net_log_;
156}
157
[email protected]fe2f62a2010-10-01 03:34:07158void SpdyProxyClientSocket::SetSubresourceSpeculation() {
159 // TODO(rch): what should this implementation be?
160}
161
162void SpdyProxyClientSocket::SetOmniboxSpeculation() {
163 // TODO(rch): what should this implementation be?
164}
165
166bool SpdyProxyClientSocket::WasEverUsed() const {
167 return was_ever_used_ || (spdy_stream_ && spdy_stream_->WasEverUsed());
168}
169
[email protected]7f7e92392010-10-26 18:29:29170bool SpdyProxyClientSocket::UsingTCPFastOpen() const {
171 return false;
172}
173
[email protected]5e6efa52011-06-27 17:26:41174int64 SpdyProxyClientSocket::NumBytesRead() const {
175 return -1;
176}
177
178base::TimeDelta SpdyProxyClientSocket::GetConnectTimeMicros() const {
179 return base::TimeDelta::FromMicroseconds(-1);
180}
181
[email protected]2d88e7d2012-07-19 17:55:17182bool SpdyProxyClientSocket::WasNpnNegotiated() const {
183 return false;
184}
185
[email protected]33661e482012-04-03 16:16:26186NextProto SpdyProxyClientSocket::GetNegotiatedProtocol() const {
187 return kProtoUnknown;
188}
189
[email protected]2d88e7d2012-07-19 17:55:17190bool SpdyProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
191 bool was_npn_negotiated;
192 NextProto protocol_negotiated;
193 return spdy_stream_->GetSSLInfo(ssl_info, &was_npn_negotiated,
194 &protocol_negotiated);
195}
196
[email protected]fe2f62a2010-10-01 03:34:07197int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len,
[email protected]3f55aa12011-12-07 02:03:33198 const CompletionCallback& callback) {
[email protected]83039bb2011-12-09 18:43:55199 DCHECK(read_callback_.is_null());
[email protected]3f55aa12011-12-07 02:03:33200 DCHECK(!user_buffer_);
201
202 if (next_state_ == STATE_DISCONNECTED)
203 return ERR_SOCKET_NOT_CONNECTED;
204
205 if (next_state_ == STATE_CLOSED && read_buffer_.empty()) {
206 return 0;
207 }
208
209 DCHECK(next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED);
210 DCHECK(buf);
211 user_buffer_ = new DrainableIOBuffer(buf, buf_len);
212 int result = PopulateUserReadBuffer();
213 if (result == 0) {
214 DCHECK(!callback.is_null());
215 read_callback_ = callback;
216 return ERR_IO_PENDING;
217 }
218 user_buffer_ = NULL;
219 return result;
220}
[email protected]fe2f62a2010-10-01 03:34:07221
222int SpdyProxyClientSocket::PopulateUserReadBuffer() {
223 if (!user_buffer_)
224 return ERR_IO_PENDING;
225
[email protected]e92fa7e12012-02-16 23:31:22226 int bytes_read = 0;
[email protected]fe2f62a2010-10-01 03:34:07227 while (!read_buffer_.empty() && user_buffer_->BytesRemaining() > 0) {
228 scoped_refptr<DrainableIOBuffer> data = read_buffer_.front();
229 const int bytes_to_copy = std::min(user_buffer_->BytesRemaining(),
[email protected]87d3c4a2010-10-07 03:00:42230 data->BytesRemaining());
[email protected]fe2f62a2010-10-01 03:34:07231 memcpy(user_buffer_->data(), data->data(), bytes_to_copy);
232 user_buffer_->DidConsume(bytes_to_copy);
[email protected]e92fa7e12012-02-16 23:31:22233 bytes_read += bytes_to_copy;
[email protected]d9da5fe2010-10-13 22:37:16234 if (data->BytesRemaining() == bytes_to_copy) {
[email protected]fe2f62a2010-10-01 03:34:07235 // Consumed all data from this buffer
236 read_buffer_.pop_front();
237 } else {
238 data->DidConsume(bytes_to_copy);
239 }
240 }
241
[email protected]e92fa7e12012-02-16 23:31:22242 if (bytes_read > 0 && spdy_stream_)
243 spdy_stream_->IncreaseRecvWindowSize(bytes_read);
244
[email protected]fe2f62a2010-10-01 03:34:07245 return user_buffer_->BytesConsumed();
246}
247
248int SpdyProxyClientSocket::Write(IOBuffer* buf, int buf_len,
[email protected]83039bb2011-12-09 18:43:55249 const CompletionCallback& callback) {
250 DCHECK(write_callback_.is_null());
[email protected]194c7a92011-12-03 04:54:18251 if (next_state_ != STATE_OPEN)
[email protected]d9da5fe2010-10-13 22:37:16252 return ERR_SOCKET_NOT_CONNECTED;
253
[email protected]194c7a92011-12-03 04:54:18254 DCHECK(spdy_stream_);
[email protected]fe2f62a2010-10-01 03:34:07255 write_bytes_outstanding_= buf_len;
256 if (buf_len <= kMaxSpdyFrameChunkSize) {
[email protected]ff98d7f02012-03-22 21:44:19257 int rv = spdy_stream_->WriteStreamData(buf, buf_len, DATA_FLAG_NONE);
[email protected]f6c63db52013-02-02 00:35:22258
259 // If there's an error, log the error. Otherwise, log the number of bytes
260 // regardless of whether or not they were actually written.
261 if (rv < 0 && rv != ERR_IO_PENDING) {
262 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, rv, NULL);
263 } else {
264 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT,
265 buf_len, buf->data());
266 }
[email protected]fe2f62a2010-10-01 03:34:07267 if (rv == ERR_IO_PENDING) {
268 write_callback_ = callback;
269 write_buffer_len_ = buf_len;
270 }
271 return rv;
272 }
273
274 // Since a SPDY Data frame can only include kMaxSpdyFrameChunkSize bytes
275 // we need to send multiple data frames
276 for (int i = 0; i < buf_len; i += kMaxSpdyFrameChunkSize) {
277 int len = std::min(kMaxSpdyFrameChunkSize, buf_len - i);
278 scoped_refptr<DrainableIOBuffer> iobuf(new DrainableIOBuffer(buf, i + len));
279 iobuf->SetOffset(i);
[email protected]ff98d7f02012-03-22 21:44:19280 int rv = spdy_stream_->WriteStreamData(iobuf, len, DATA_FLAG_NONE);
[email protected]f6c63db52013-02-02 00:35:22281
282 // If there's an error, log the error. Otherwise, log the number of bytes
283 // regardless of whether or not they were actually written.
284 if (rv < 0 && rv != ERR_IO_PENDING) {
285 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT, rv, NULL);
286 } else {
287 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_SENT,
288 len, buf->data());
289 }
290
[email protected]fe2f62a2010-10-01 03:34:07291 if (rv > 0) {
292 write_bytes_outstanding_ -= rv;
293 } else if (rv != ERR_IO_PENDING) {
294 return rv;
295 }
296 }
297 if (write_bytes_outstanding_ > 0) {
298 write_callback_ = callback;
299 write_buffer_len_ = buf_len;
300 return ERR_IO_PENDING;
301 } else {
302 return buf_len;
303 }
304}
305
306bool SpdyProxyClientSocket::SetReceiveBufferSize(int32 size) {
[email protected]3268023f2011-05-05 00:08:10307 // Since this StreamSocket sits on top of a shared SpdySession, it
[email protected]fe2f62a2010-10-01 03:34:07308 // is not safe for callers to set change this underlying socket.
309 return false;
310}
311
312bool SpdyProxyClientSocket::SetSendBufferSize(int32 size) {
[email protected]3268023f2011-05-05 00:08:10313 // Since this StreamSocket sits on top of a shared SpdySession, it
[email protected]fe2f62a2010-10-01 03:34:07314 // is not safe for callers to set change this underlying socket.
315 return false;
316}
317
[email protected]a3528692012-06-08 00:11:42318int SpdyProxyClientSocket::GetPeerAddress(IPEndPoint* address) const {
[email protected]fe2f62a2010-10-01 03:34:07319 if (!IsConnected())
[email protected]88e03fa2010-10-05 03:09:04320 return ERR_SOCKET_NOT_CONNECTED;
[email protected]fe2f62a2010-10-01 03:34:07321 return spdy_stream_->GetPeerAddress(address);
322}
323
[email protected]e7f74da2011-04-19 23:49:35324int SpdyProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
325 if (!IsConnected())
326 return ERR_SOCKET_NOT_CONNECTED;
327 return spdy_stream_->GetLocalAddress(address);
328}
329
[email protected]4eddbc732012-08-09 05:40:17330void SpdyProxyClientSocket::LogBlockedTunnelResponse() const {
331 ProxyClientSocket::LogBlockedTunnelResponse(
332 response_.headers->response_code(),
333 request_.url,
334 /* is_https_proxy = */ true);
335}
336
[email protected]fe2f62a2010-10-01 03:34:07337void SpdyProxyClientSocket::OnIOComplete(int result) {
[email protected]d9da5fe2010-10-13 22:37:16338 DCHECK_NE(STATE_DISCONNECTED, next_state_);
[email protected]fe2f62a2010-10-01 03:34:07339 int rv = DoLoop(result);
340 if (rv != ERR_IO_PENDING) {
[email protected]83039bb2011-12-09 18:43:55341 CompletionCallback c = read_callback_;
342 read_callback_.Reset();
343 c.Run(rv);
[email protected]fe2f62a2010-10-01 03:34:07344 }
345}
346
347int SpdyProxyClientSocket::DoLoop(int last_io_result) {
[email protected]d9da5fe2010-10-13 22:37:16348 DCHECK_NE(next_state_, STATE_DISCONNECTED);
[email protected]fe2f62a2010-10-01 03:34:07349 int rv = last_io_result;
350 do {
351 State state = next_state_;
[email protected]d9da5fe2010-10-13 22:37:16352 next_state_ = STATE_DISCONNECTED;
[email protected]fe2f62a2010-10-01 03:34:07353 switch (state) {
354 case STATE_GENERATE_AUTH_TOKEN:
355 DCHECK_EQ(OK, rv);
356 rv = DoGenerateAuthToken();
357 break;
358 case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
359 rv = DoGenerateAuthTokenComplete(rv);
360 break;
361 case STATE_SEND_REQUEST:
362 DCHECK_EQ(OK, rv);
[email protected]f6c63db52013-02-02 00:35:22363 net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST);
[email protected]fe2f62a2010-10-01 03:34:07364 rv = DoSendRequest();
365 break;
366 case STATE_SEND_REQUEST_COMPLETE:
[email protected]d7fd1782011-02-08 19:16:43367 net_log_.EndEventWithNetErrorCode(
368 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
[email protected]fe2f62a2010-10-01 03:34:07369 rv = DoSendRequestComplete(rv);
[email protected]f6c63db52013-02-02 00:35:22370 if (rv >= 0 || rv == ERR_IO_PENDING) {
371 // Emit extra event so can use the same events as
372 // HttpProxyClientSocket.
373 net_log_.BeginEvent(
374 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS);
375 }
[email protected]fe2f62a2010-10-01 03:34:07376 break;
377 case STATE_READ_REPLY_COMPLETE:
378 rv = DoReadReplyComplete(rv);
[email protected]d7fd1782011-02-08 19:16:43379 net_log_.EndEventWithNetErrorCode(
380 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
[email protected]fe2f62a2010-10-01 03:34:07381 break;
382 default:
383 NOTREACHED() << "bad state";
384 rv = ERR_UNEXPECTED;
385 break;
386 }
[email protected]d9da5fe2010-10-13 22:37:16387 } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
388 next_state_ != STATE_OPEN);
[email protected]fe2f62a2010-10-01 03:34:07389 return rv;
390}
391
392int SpdyProxyClientSocket::DoGenerateAuthToken() {
393 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
[email protected]49639fa2011-12-20 23:22:41394 return auth_->MaybeGenerateAuthToken(
395 &request_,
396 base::Bind(&SpdyProxyClientSocket::OnIOComplete, base::Unretained(this)),
397 net_log_);
[email protected]fe2f62a2010-10-01 03:34:07398}
399
400int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
401 DCHECK_NE(ERR_IO_PENDING, result);
402 if (result == OK)
403 next_state_ = STATE_SEND_REQUEST;
404 return result;
405}
406
407int SpdyProxyClientSocket::DoSendRequest() {
408 next_state_ = STATE_SEND_REQUEST_COMPLETE;
409
410 // Add Proxy-Authentication header if necessary.
411 HttpRequestHeaders authorization_headers;
412 if (auth_->HaveAuth()) {
413 auth_->AddAuthorizationHeader(&authorization_headers);
414 }
415
416 std::string request_line;
417 HttpRequestHeaders request_headers;
418 BuildTunnelRequest(request_, authorization_headers, endpoint_, &request_line,
419 &request_headers);
[email protected]3abacd62012-06-10 20:20:32420
421 net_log_.AddEvent(
422 NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
423 base::Bind(&HttpRequestHeaders::NetLogCallback,
424 base::Unretained(&request_headers),
425 &request_line));
[email protected]fe2f62a2010-10-01 03:34:07426
427 request_.extra_headers.MergeFrom(request_headers);
[email protected]0a01d890fc2012-07-18 17:24:05428 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock());
[email protected]7213e7c2010-10-20 15:33:52429 CreateSpdyHeadersFromHttpRequest(request_, request_headers, headers.get(),
[email protected]0e861e92012-03-15 18:42:19430 spdy_stream_->GetProtocolVersion(), true);
[email protected]fe2f62a2010-10-01 03:34:07431 // Reset the URL to be the endpoint of the connection
[email protected]0e861e92012-03-15 18:42:19432 if (spdy_stream_->GetProtocolVersion() > 2) {
433 (*headers)[":path"] = endpoint_.ToString();
434 headers->erase(":scheme");
435 } else {
436 (*headers)["url"] = endpoint_.ToString();
437 headers->erase("scheme");
438 }
[email protected]0a01d890fc2012-07-18 17:24:05439 spdy_stream_->set_spdy_headers(headers.Pass());
[email protected]fe2f62a2010-10-01 03:34:07440
441 return spdy_stream_->SendRequest(true);
442}
443
444int SpdyProxyClientSocket::DoSendRequestComplete(int result) {
445 if (result < 0)
446 return result;
447
448 // Wait for SYN_REPLY frame from the server
449 next_state_ = STATE_READ_REPLY_COMPLETE;
450 return ERR_IO_PENDING;
451}
452
453int SpdyProxyClientSocket::DoReadReplyComplete(int result) {
454 // We enter this method directly from DoSendRequestComplete, since
455 // we are notified by a callback when the SYN_REPLY frame arrives
456
457 if (result < 0)
458 return result;
459
[email protected]fe2f62a2010-10-01 03:34:07460 // Require the "HTTP/1.x" status line for SSL CONNECT.
461 if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0))
462 return ERR_TUNNEL_CONNECTION_FAILED;
463
[email protected]3abacd62012-06-10 20:20:32464 net_log_.AddEvent(
465 NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
466 base::Bind(&HttpResponseHeaders::NetLogCallback, response_.headers));
[email protected]fe2f62a2010-10-01 03:34:07467
[email protected]4eddbc732012-08-09 05:40:17468 switch (response_.headers->response_code()) {
469 case 200: // OK
470 next_state_ = STATE_OPEN;
471 return OK;
472
473 case 302: // Found / Moved Temporarily
474 // Try to return a sanitized response so we can follow auth redirects.
475 // If we can't, fail the tunnel connection.
476 if (SanitizeProxyRedirect(&response_, request_.url)) {
477 // Immediately hand off our SpdyStream to a newly created
478 // SpdyHttpStream so that any subsequent SpdyFrames are processed in
479 // the context of the HttpStream, not the socket.
480 DCHECK(spdy_stream_);
481 SpdyStream* stream = spdy_stream_;
482 spdy_stream_ = NULL;
483 response_stream_.reset(new SpdyHttpStream(NULL, false));
484 response_stream_->InitializeWithExistingStream(stream);
485 next_state_ = STATE_DISCONNECTED;
486 return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
487 } else {
488 LogBlockedTunnelResponse();
489 return ERR_TUNNEL_CONNECTION_FAILED;
490 }
491
492 case 407: // Proxy Authentication Required
493 next_state_ = STATE_OPEN;
494 return HandleProxyAuthChallenge(auth_, &response_, net_log_);
495
496 default:
497 // Ignore response to avoid letting the proxy impersonate the target
498 // server. (See http://crbug.com/137891.)
499 LogBlockedTunnelResponse();
500 return ERR_TUNNEL_CONNECTION_FAILED;
[email protected]511f6f52010-12-17 03:58:29501 }
[email protected]fe2f62a2010-10-01 03:34:07502}
503
504// SpdyStream::Delegate methods:
505// Called when SYN frame has been sent.
506// Returns true if no more data to be sent after SYN frame.
507bool SpdyProxyClientSocket::OnSendHeadersComplete(int status) {
508 DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE);
509
510 OnIOComplete(status);
511
512 // We return true here so that we send |spdy_stream_| into
513 // STATE_OPEN (ala WebSockets).
514 return true;
515}
516
517int SpdyProxyClientSocket::OnSendBody() {
518 // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets)
519 // OnSendBody() should never be called.
520 NOTREACHED();
521 return ERR_UNEXPECTED;
522}
523
[email protected]0c9bf872011-03-04 17:53:22524int SpdyProxyClientSocket::OnSendBodyComplete(int /*status*/, bool* /*eof*/) {
[email protected]fe2f62a2010-10-01 03:34:07525 // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets)
526 // OnSendBodyComplete() should never be called.
527 NOTREACHED();
[email protected]0c9bf872011-03-04 17:53:22528 return ERR_UNEXPECTED;
[email protected]fe2f62a2010-10-01 03:34:07529}
530
531int SpdyProxyClientSocket::OnResponseReceived(
[email protected]ff98d7f02012-03-22 21:44:19532 const SpdyHeaderBlock& response,
[email protected]fe2f62a2010-10-01 03:34:07533 base::Time response_time,
534 int status) {
[email protected]d08358502010-12-03 22:04:03535 // If we've already received the reply, existing headers are too late.
536 // TODO(mbelshe): figure out a way to make HEADERS frames useful after the
537 // initial response.
538 if (next_state_ != STATE_READ_REPLY_COMPLETE)
539 return OK;
[email protected]fe2f62a2010-10-01 03:34:07540
[email protected]d08358502010-12-03 22:04:03541 // Save the response
[email protected]d42dedd02012-04-03 19:42:06542 if (!SpdyHeadersToHttpResponse(
543 response, spdy_stream_->GetProtocolVersion(), &response_))
544 return ERR_INCOMPLETE_SPDY_HEADERS;
[email protected]fe2f62a2010-10-01 03:34:07545
546 OnIOComplete(status);
[email protected]fe2f62a2010-10-01 03:34:07547 return OK;
548}
549
[email protected]050b3602012-08-15 04:10:49550void SpdyProxyClientSocket::OnHeadersSent() {
551 // Proxy client sockets don't send any HEADERS frame.
552 NOTREACHED();
553}
554
[email protected]fe2f62a2010-10-01 03:34:07555// Called when data is received.
[email protected]5c6908e2012-08-06 18:53:47556int SpdyProxyClientSocket::OnDataReceived(const char* data, int length) {
[email protected]f6c63db52013-02-02 00:35:22557 net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED,
558 length, data);
[email protected]fe2f62a2010-10-01 03:34:07559 if (length > 0) {
560 // Save the received data.
[email protected]ad8e04a2010-11-01 04:16:27561 scoped_refptr<IOBuffer> io_buffer(new IOBuffer(length));
[email protected]fe2f62a2010-10-01 03:34:07562 memcpy(io_buffer->data(), data, length);
[email protected]00cd9c42010-11-02 20:15:57563 read_buffer_.push_back(
564 make_scoped_refptr(new DrainableIOBuffer(io_buffer, length)));
[email protected]fe2f62a2010-10-01 03:34:07565 }
566
[email protected]83039bb2011-12-09 18:43:55567 if (!read_callback_.is_null()) {
[email protected]dbf036f2011-12-06 23:33:24568 int rv = PopulateUserReadBuffer();
569 CompletionCallback c = read_callback_;
570 read_callback_.Reset();
571 user_buffer_ = NULL;
572 c.Run(rv);
[email protected]fe2f62a2010-10-01 03:34:07573 }
[email protected]5c6908e2012-08-06 18:53:47574 return OK;
[email protected]fe2f62a2010-10-01 03:34:07575}
576
577void SpdyProxyClientSocket::OnDataSent(int length) {
[email protected]83039bb2011-12-09 18:43:55578 DCHECK(!write_callback_.is_null());
[email protected]fe2f62a2010-10-01 03:34:07579
580 write_bytes_outstanding_ -= length;
581
582 DCHECK_GE(write_bytes_outstanding_, 0);
583
584 if (write_bytes_outstanding_ == 0) {
585 int rv = write_buffer_len_;
586 write_buffer_len_ = 0;
587 write_bytes_outstanding_ = 0;
[email protected]83039bb2011-12-09 18:43:55588 CompletionCallback c = write_callback_;
589 write_callback_.Reset();
590 c.Run(rv);
[email protected]fe2f62a2010-10-01 03:34:07591 }
592}
593
594void SpdyProxyClientSocket::OnClose(int status) {
595 DCHECK(spdy_stream_);
[email protected]fe2f62a2010-10-01 03:34:07596 was_ever_used_ = spdy_stream_->WasEverUsed();
597 spdy_stream_ = NULL;
[email protected]d9da5fe2010-10-13 22:37:16598
599 bool connecting = next_state_ != STATE_DISCONNECTED &&
600 next_state_ < STATE_OPEN;
601 if (next_state_ == STATE_OPEN)
602 next_state_ = STATE_CLOSED;
603 else
604 next_state_ = STATE_DISCONNECTED;
605
[email protected]6af4e412011-11-29 23:39:18606 base::WeakPtr<SpdyProxyClientSocket> weak_ptr = weak_factory_.GetWeakPtr();
[email protected]83039bb2011-12-09 18:43:55607 CompletionCallback write_callback = write_callback_;
608 write_callback_.Reset();
[email protected]d9da5fe2010-10-13 22:37:16609 write_buffer_len_ = 0;
610 write_bytes_outstanding_ = 0;
611
612 // If we're in the middle of connecting, we need to make sure
613 // we invoke the connect callback.
614 if (connecting) {
[email protected]83039bb2011-12-09 18:43:55615 DCHECK(!read_callback_.is_null());
616 CompletionCallback read_callback = read_callback_;
617 read_callback_.Reset();
618 read_callback.Run(status);
619 } else if (!read_callback_.is_null()) {
[email protected]6af4e412011-11-29 23:39:18620 // If we have a read_callback_, the we need to make sure we call it back.
[email protected]d9da5fe2010-10-13 22:37:16621 OnDataReceived(NULL, 0);
622 }
[email protected]6af4e412011-11-29 23:39:18623 // This may have been deleted by read_callback_, so check first.
[email protected]83039bb2011-12-09 18:43:55624 if (weak_ptr && !write_callback.is_null())
625 write_callback.Run(ERR_CONNECTION_CLOSED);
[email protected]fe2f62a2010-10-01 03:34:07626}
627
[email protected]fe2f62a2010-10-01 03:34:07628} // namespace net