blob: d36b63066c22cb1bd85f6da011525623f1b601a7 [file] [log] [blame]
[email protected]fe2f62a2010-10-01 03:34:071// 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/spdy/spdy_http_utils.h"
20
21namespace net {
22
23SpdyProxyClientSocket::SpdyProxyClientSocket(
24 SpdyStream* spdy_stream,
25 const std::string& user_agent,
26 const HostPortPair& endpoint,
27 const GURL& url,
28 const HostPortPair& proxy_server,
29 HttpAuthCache* auth_cache,
30 HttpAuthHandlerFactory* auth_handler_factory)
31 : ALLOW_THIS_IN_INITIALIZER_LIST(
32 io_callback_(this, &SpdyProxyClientSocket::OnIOComplete)),
[email protected]d9da5fe2010-10-13 22:37:1633 next_state_(STATE_DISCONNECTED),
[email protected]fe2f62a2010-10-01 03:34:0734 spdy_stream_(spdy_stream),
35 read_callback_(NULL),
36 write_callback_(NULL),
37 endpoint_(endpoint),
38 auth_(
39 new HttpAuthController(HttpAuth::AUTH_PROXY,
40 GURL("http://" + proxy_server.ToString()),
41 auth_cache,
42 auth_handler_factory)),
43 user_buffer_(NULL),
44 write_buffer_len_(0),
45 write_bytes_outstanding_(0),
46 eof_has_been_read_(false),
47 net_log_(spdy_stream->net_log()) {
48 request_.method = "CONNECT";
49 request_.url = url;
50 if (!user_agent.empty())
51 request_.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent,
52 user_agent);
53 spdy_stream_->SetDelegate(this);
54 was_ever_used_ = spdy_stream_->WasEverUsed();
55}
56
57SpdyProxyClientSocket::~SpdyProxyClientSocket() {
58 Disconnect();
59}
60
61// Sends a SYN_STREAM frame to the proxy with a CONNECT request
62// for the specified endpoint. Waits for the server to send back
63// a SYN_REPLY frame. OK will be returned if the status is 200.
64// ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status.
65// In any of these cases, Read() may be called to retrieve the HTTP
66// response body. Any other return values should be considered fatal.
67// TODO(rch): handle 407 proxy auth requested correctly, perhaps
68// by creating a new stream for the subsequent request.
69// TODO(rch): create a more appropriate error code to disambiguate
70// the HTTPS Proxy tunnel failure from an HTTP Proxy tunnel failure.
71int SpdyProxyClientSocket::Connect(CompletionCallback* callback) {
72 DCHECK(!read_callback_);
[email protected]d9da5fe2010-10-13 22:37:1673 if (next_state_ == STATE_OPEN)
[email protected]fe2f62a2010-10-01 03:34:0774 return OK;
75
[email protected]d9da5fe2010-10-13 22:37:1676 DCHECK_EQ(STATE_DISCONNECTED, next_state_);
[email protected]fe2f62a2010-10-01 03:34:0777 next_state_ = STATE_GENERATE_AUTH_TOKEN;
78
79 int rv = DoLoop(OK);
80 if (rv == ERR_IO_PENDING)
81 read_callback_ = callback;
82 return rv;
83}
84
85void SpdyProxyClientSocket::Disconnect() {
[email protected]d9da5fe2010-10-13 22:37:1686 read_buffer_.clear();
87 user_buffer_ = NULL;
88 read_callback_ = NULL;
89
90 write_buffer_len_ = 0;
91 write_bytes_outstanding_ = 0;
92 write_callback_ = NULL;
93
94 next_state_ = STATE_DISCONNECTED;
95
[email protected]fe2f62a2010-10-01 03:34:0796 if (spdy_stream_)
97 // This will cause OnClose to be invoked, which takes care of
98 // cleaning up all the internal state.
99 spdy_stream_->Cancel();
100}
101
102bool SpdyProxyClientSocket::IsConnected() const {
[email protected]d9da5fe2010-10-13 22:37:16103 return next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED;
[email protected]fe2f62a2010-10-01 03:34:07104}
105
106bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
107 return IsConnected() && !spdy_stream_->is_idle();
108}
109
110void SpdyProxyClientSocket::SetSubresourceSpeculation() {
111 // TODO(rch): what should this implementation be?
112}
113
114void SpdyProxyClientSocket::SetOmniboxSpeculation() {
115 // TODO(rch): what should this implementation be?
116}
117
118bool SpdyProxyClientSocket::WasEverUsed() const {
119 return was_ever_used_ || (spdy_stream_ && spdy_stream_->WasEverUsed());
120}
121
122int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len,
123 CompletionCallback* callback) {
124 DCHECK(!read_callback_);
125 DCHECK(!user_buffer_);
126
[email protected]d9da5fe2010-10-13 22:37:16127 if (next_state_ == STATE_DISCONNECTED)
128 return ERR_SOCKET_NOT_CONNECTED;
129
130 if (!spdy_stream_ && read_buffer_.empty()) {
[email protected]fe2f62a2010-10-01 03:34:07131 if (eof_has_been_read_)
132 return ERR_CONNECTION_CLOSED;
133 eof_has_been_read_ = true;
134 return 0;
135 }
136
[email protected]d9da5fe2010-10-13 22:37:16137 DCHECK(next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED);
[email protected]fe2f62a2010-10-01 03:34:07138 DCHECK(buf);
139 user_buffer_ = new DrainableIOBuffer(buf, buf_len);
140 int result = PopulateUserReadBuffer();
141 if (result == 0) {
142 DCHECK(callback);
143 read_callback_ = callback;
144 return ERR_IO_PENDING;
145 }
146 user_buffer_ = NULL;
147 return result;
148}
149
150int SpdyProxyClientSocket::PopulateUserReadBuffer() {
151 if (!user_buffer_)
152 return ERR_IO_PENDING;
153
154 while (!read_buffer_.empty() && user_buffer_->BytesRemaining() > 0) {
155 scoped_refptr<DrainableIOBuffer> data = read_buffer_.front();
156 const int bytes_to_copy = std::min(user_buffer_->BytesRemaining(),
[email protected]87d3c4a2010-10-07 03:00:42157 data->BytesRemaining());
[email protected]fe2f62a2010-10-01 03:34:07158 memcpy(user_buffer_->data(), data->data(), bytes_to_copy);
159 user_buffer_->DidConsume(bytes_to_copy);
[email protected]d9da5fe2010-10-13 22:37:16160 if (data->BytesRemaining() == bytes_to_copy) {
[email protected]fe2f62a2010-10-01 03:34:07161 // Consumed all data from this buffer
162 read_buffer_.pop_front();
163 } else {
164 data->DidConsume(bytes_to_copy);
165 }
166 }
167
168 return user_buffer_->BytesConsumed();
169}
170
171int SpdyProxyClientSocket::Write(IOBuffer* buf, int buf_len,
172 CompletionCallback* callback) {
173 DCHECK(!write_callback_);
[email protected]d9da5fe2010-10-13 22:37:16174 if (next_state_ == STATE_DISCONNECTED)
175 return ERR_SOCKET_NOT_CONNECTED;
176
[email protected]fe2f62a2010-10-01 03:34:07177 if (!spdy_stream_)
178 return ERR_CONNECTION_CLOSED;
179
180 write_bytes_outstanding_= buf_len;
181 if (buf_len <= kMaxSpdyFrameChunkSize) {
182 int rv = spdy_stream_->WriteStreamData(buf, buf_len, spdy::DATA_FLAG_NONE);
183 if (rv == ERR_IO_PENDING) {
184 write_callback_ = callback;
185 write_buffer_len_ = buf_len;
186 }
187 return rv;
188 }
189
190 // Since a SPDY Data frame can only include kMaxSpdyFrameChunkSize bytes
191 // we need to send multiple data frames
192 for (int i = 0; i < buf_len; i += kMaxSpdyFrameChunkSize) {
193 int len = std::min(kMaxSpdyFrameChunkSize, buf_len - i);
194 scoped_refptr<DrainableIOBuffer> iobuf(new DrainableIOBuffer(buf, i + len));
195 iobuf->SetOffset(i);
196 int rv = spdy_stream_->WriteStreamData(iobuf, len, spdy::DATA_FLAG_NONE);
197 if (rv > 0) {
198 write_bytes_outstanding_ -= rv;
199 } else if (rv != ERR_IO_PENDING) {
200 return rv;
201 }
202 }
203 if (write_bytes_outstanding_ > 0) {
204 write_callback_ = callback;
205 write_buffer_len_ = buf_len;
206 return ERR_IO_PENDING;
207 } else {
208 return buf_len;
209 }
210}
211
212bool SpdyProxyClientSocket::SetReceiveBufferSize(int32 size) {
213 // Since this ClientSocket sits on top of a shared SpdySession, it
214 // is not safe for callers to set change this underlying socket.
215 return false;
216}
217
218bool SpdyProxyClientSocket::SetSendBufferSize(int32 size) {
219 // Since this ClientSocket sits on top of a shared SpdySession, it
220 // is not safe for callers to set change this underlying socket.
221 return false;
222}
223
224int SpdyProxyClientSocket::GetPeerAddress(AddressList* address) const {
225 if (!IsConnected())
[email protected]88e03fa2010-10-05 03:09:04226 return ERR_SOCKET_NOT_CONNECTED;
[email protected]fe2f62a2010-10-01 03:34:07227 return spdy_stream_->GetPeerAddress(address);
228}
229
230void SpdyProxyClientSocket::OnIOComplete(int result) {
[email protected]d9da5fe2010-10-13 22:37:16231 DCHECK_NE(STATE_DISCONNECTED, next_state_);
[email protected]fe2f62a2010-10-01 03:34:07232 int rv = DoLoop(result);
233 if (rv != ERR_IO_PENDING) {
234 CompletionCallback* c = read_callback_;
235 read_callback_ = NULL;
236 c->Run(rv);
237 }
238}
239
240int SpdyProxyClientSocket::DoLoop(int last_io_result) {
[email protected]d9da5fe2010-10-13 22:37:16241 DCHECK_NE(next_state_, STATE_DISCONNECTED);
[email protected]fe2f62a2010-10-01 03:34:07242 int rv = last_io_result;
243 do {
244 State state = next_state_;
[email protected]d9da5fe2010-10-13 22:37:16245 next_state_ = STATE_DISCONNECTED;
[email protected]fe2f62a2010-10-01 03:34:07246 switch (state) {
247 case STATE_GENERATE_AUTH_TOKEN:
248 DCHECK_EQ(OK, rv);
249 rv = DoGenerateAuthToken();
250 break;
251 case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
252 rv = DoGenerateAuthTokenComplete(rv);
253 break;
254 case STATE_SEND_REQUEST:
255 DCHECK_EQ(OK, rv);
256 net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST,
257 NULL);
258 rv = DoSendRequest();
259 break;
260 case STATE_SEND_REQUEST_COMPLETE:
261 rv = DoSendRequestComplete(rv);
262 net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST,
263 NULL);
264 break;
265 case STATE_READ_REPLY_COMPLETE:
266 rv = DoReadReplyComplete(rv);
267 net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS,
268 NULL);
269 break;
270 default:
271 NOTREACHED() << "bad state";
272 rv = ERR_UNEXPECTED;
273 break;
274 }
[email protected]d9da5fe2010-10-13 22:37:16275 } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
276 next_state_ != STATE_OPEN);
[email protected]fe2f62a2010-10-01 03:34:07277 return rv;
278}
279
280int SpdyProxyClientSocket::DoGenerateAuthToken() {
281 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
282 return auth_->MaybeGenerateAuthToken(&request_, &io_callback_, net_log_);
283}
284
285int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
286 DCHECK_NE(ERR_IO_PENDING, result);
287 if (result == OK)
288 next_state_ = STATE_SEND_REQUEST;
289 return result;
290}
291
292int SpdyProxyClientSocket::DoSendRequest() {
293 next_state_ = STATE_SEND_REQUEST_COMPLETE;
294
295 // Add Proxy-Authentication header if necessary.
296 HttpRequestHeaders authorization_headers;
297 if (auth_->HaveAuth()) {
298 auth_->AddAuthorizationHeader(&authorization_headers);
299 }
300
301 std::string request_line;
302 HttpRequestHeaders request_headers;
303 BuildTunnelRequest(request_, authorization_headers, endpoint_, &request_line,
304 &request_headers);
305 if (net_log_.IsLoggingAll()) {
306 net_log_.AddEvent(
307 NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
308 new NetLogHttpRequestParameter(
309 request_line, request_headers));
310 }
311
312 request_.extra_headers.MergeFrom(request_headers);
313 linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock());
314 CreateSpdyHeadersFromHttpRequest(request_, headers.get(), true);
315 // Reset the URL to be the endpoint of the connection
316 (*headers)["url"] = endpoint_.ToString();
317 headers->erase("scheme");
318 spdy_stream_->set_spdy_headers(headers);
319
320 return spdy_stream_->SendRequest(true);
321}
322
323int SpdyProxyClientSocket::DoSendRequestComplete(int result) {
324 if (result < 0)
325 return result;
326
327 // Wait for SYN_REPLY frame from the server
328 next_state_ = STATE_READ_REPLY_COMPLETE;
329 return ERR_IO_PENDING;
330}
331
332int SpdyProxyClientSocket::DoReadReplyComplete(int result) {
333 // We enter this method directly from DoSendRequestComplete, since
334 // we are notified by a callback when the SYN_REPLY frame arrives
335
336 if (result < 0)
337 return result;
338
[email protected]fe2f62a2010-10-01 03:34:07339 // Require the "HTTP/1.x" status line for SSL CONNECT.
340 if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0))
341 return ERR_TUNNEL_CONNECTION_FAILED;
342
[email protected]d9da5fe2010-10-13 22:37:16343 next_state_ = STATE_OPEN;
[email protected]fe2f62a2010-10-01 03:34:07344 if (net_log_.IsLoggingAll()) {
345 net_log_.AddEvent(
346 NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
347 new NetLogHttpResponseParameter(response_.headers));
348 }
349
350 if (response_.headers->response_code() == 200)
351 return OK;
352 else
353 return ERR_TUNNEL_CONNECTION_FAILED;
354}
355
356// SpdyStream::Delegate methods:
357// Called when SYN frame has been sent.
358// Returns true if no more data to be sent after SYN frame.
359bool SpdyProxyClientSocket::OnSendHeadersComplete(int status) {
360 DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE);
361
362 OnIOComplete(status);
363
364 // We return true here so that we send |spdy_stream_| into
365 // STATE_OPEN (ala WebSockets).
366 return true;
367}
368
369int SpdyProxyClientSocket::OnSendBody() {
370 // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets)
371 // OnSendBody() should never be called.
372 NOTREACHED();
373 return ERR_UNEXPECTED;
374}
375
376bool SpdyProxyClientSocket::OnSendBodyComplete(int status) {
377 // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets)
378 // OnSendBodyComplete() should never be called.
379 NOTREACHED();
380 return false;
381}
382
383int SpdyProxyClientSocket::OnResponseReceived(
384 const spdy::SpdyHeaderBlock& response,
385 base::Time response_time,
386 int status) {
387 // Save the response
388 SpdyHeadersToHttpResponse(response, &response_);
389
390 DCHECK_EQ(next_state_, STATE_READ_REPLY_COMPLETE);
391
392 OnIOComplete(status);
393
394 return OK;
395}
396
397// Called when data is received.
398void SpdyProxyClientSocket::OnDataReceived(const char* data, int length) {
399 if (length > 0) {
400 // Save the received data.
401 scoped_refptr<IOBuffer> io_buffer = new IOBuffer(length);
402 memcpy(io_buffer->data(), data, length);
403 read_buffer_.push_back(new DrainableIOBuffer(io_buffer, length));
404 }
405
406 if (read_callback_) {
407 int rv = PopulateUserReadBuffer();
408 CompletionCallback* c = read_callback_;
409 read_callback_ = NULL;
410 user_buffer_ = NULL;
411 c->Run(rv);
412 }
413}
414
415void SpdyProxyClientSocket::OnDataSent(int length) {
416 DCHECK(write_callback_);
417
418 write_bytes_outstanding_ -= length;
419
420 DCHECK_GE(write_bytes_outstanding_, 0);
421
422 if (write_bytes_outstanding_ == 0) {
423 int rv = write_buffer_len_;
424 write_buffer_len_ = 0;
425 write_bytes_outstanding_ = 0;
426 CompletionCallback* c = write_callback_;
427 write_callback_ = NULL;
428 c->Run(rv);
429 }
430}
431
432void SpdyProxyClientSocket::OnClose(int status) {
433 DCHECK(spdy_stream_);
[email protected]fe2f62a2010-10-01 03:34:07434 was_ever_used_ = spdy_stream_->WasEverUsed();
435 spdy_stream_ = NULL;
[email protected]d9da5fe2010-10-13 22:37:16436
437 bool connecting = next_state_ != STATE_DISCONNECTED &&
438 next_state_ < STATE_OPEN;
439 if (next_state_ == STATE_OPEN)
440 next_state_ = STATE_CLOSED;
441 else
442 next_state_ = STATE_DISCONNECTED;
443
444 CompletionCallback* write_callback = write_callback_;
[email protected]fe2f62a2010-10-01 03:34:07445 write_callback_ = NULL;
[email protected]d9da5fe2010-10-13 22:37:16446 write_buffer_len_ = 0;
447 write_bytes_outstanding_ = 0;
448
449 // If we're in the middle of connecting, we need to make sure
450 // we invoke the connect callback.
451 if (connecting) {
452 DCHECK(read_callback_);
453 CompletionCallback* read_callback = read_callback_;
454 read_callback_ = NULL;
455 read_callback->Run(status);
456 } else if (read_callback_) {
457 // If we have a read_callback, the we need to make sure we call it back
458 OnDataReceived(NULL, 0);
459 }
460 if (write_callback)
461 write_callback->Run(ERR_CONNECTION_CLOSED);
[email protected]fe2f62a2010-10-01 03:34:07462}
463
464} // namespace net