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