Revert 97987 - Switching NaCl IRT to be built inside the chrome build.
[chromium-blink-merge.git] / net / spdy / spdy_proxy_client_socket.cc
blob5034d44897bf0d681ce6c302f5ad4667546cf440
1 // Copyright (c) 2011 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/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"
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 : 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("https://" + 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();
58 SpdyProxyClientSocket::~SpdyProxyClientSocket() {
59 Disconnect();
62 const HttpResponseInfo* SpdyProxyClientSocket::GetConnectResponseInfo() const {
63 return response_.headers ? &response_ : NULL;
66 HttpStream* SpdyProxyClientSocket::CreateConnectResponseStream() {
67 DCHECK(response_stream_.get());
68 return response_stream_.release();
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 int SpdyProxyClientSocket::Connect(CompletionCallback* callback) {
82 DCHECK(!read_callback_);
83 if (next_state_ == STATE_OPEN)
84 return OK;
86 DCHECK_EQ(STATE_DISCONNECTED, next_state_);
87 next_state_ = STATE_GENERATE_AUTH_TOKEN;
89 int rv = DoLoop(OK);
90 if (rv == ERR_IO_PENDING)
91 read_callback_ = callback;
92 return rv;
95 void SpdyProxyClientSocket::Disconnect() {
96 read_buffer_.clear();
97 user_buffer_ = NULL;
98 read_callback_ = NULL;
100 write_buffer_len_ = 0;
101 write_bytes_outstanding_ = 0;
102 write_callback_ = NULL;
104 next_state_ = STATE_DISCONNECTED;
106 if (spdy_stream_)
107 // This will cause OnClose to be invoked, which takes care of
108 // cleaning up all the internal state.
109 spdy_stream_->Cancel();
112 bool SpdyProxyClientSocket::IsConnected() const {
113 return next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED;
116 bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
117 return IsConnected() && spdy_stream_.get() != NULL &&
118 !spdy_stream_->is_idle();
121 const BoundNetLog& SpdyProxyClientSocket::NetLog() const {
122 return net_log_;
125 void SpdyProxyClientSocket::SetSubresourceSpeculation() {
126 // TODO(rch): what should this implementation be?
129 void SpdyProxyClientSocket::SetOmniboxSpeculation() {
130 // TODO(rch): what should this implementation be?
133 bool SpdyProxyClientSocket::WasEverUsed() const {
134 return was_ever_used_ || (spdy_stream_ && spdy_stream_->WasEverUsed());
137 bool SpdyProxyClientSocket::UsingTCPFastOpen() const {
138 return false;
141 int64 SpdyProxyClientSocket::NumBytesRead() const {
142 return -1;
145 base::TimeDelta SpdyProxyClientSocket::GetConnectTimeMicros() const {
146 return base::TimeDelta::FromMicroseconds(-1);
149 int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len,
150 CompletionCallback* callback) {
151 DCHECK(!read_callback_);
152 DCHECK(!user_buffer_);
154 if (next_state_ == STATE_DISCONNECTED)
155 return ERR_SOCKET_NOT_CONNECTED;
157 if (!spdy_stream_ && read_buffer_.empty()) {
158 if (eof_has_been_read_)
159 return ERR_CONNECTION_CLOSED;
160 eof_has_been_read_ = true;
161 return 0;
164 DCHECK(next_state_ == STATE_OPEN || next_state_ == STATE_CLOSED);
165 DCHECK(buf);
166 user_buffer_ = new DrainableIOBuffer(buf, buf_len);
167 int result = PopulateUserReadBuffer();
168 if (result == 0) {
169 DCHECK(callback);
170 read_callback_ = callback;
171 return ERR_IO_PENDING;
173 user_buffer_ = NULL;
174 return result;
177 int SpdyProxyClientSocket::PopulateUserReadBuffer() {
178 if (!user_buffer_)
179 return ERR_IO_PENDING;
181 while (!read_buffer_.empty() && user_buffer_->BytesRemaining() > 0) {
182 scoped_refptr<DrainableIOBuffer> data = read_buffer_.front();
183 const int bytes_to_copy = std::min(user_buffer_->BytesRemaining(),
184 data->BytesRemaining());
185 memcpy(user_buffer_->data(), data->data(), bytes_to_copy);
186 user_buffer_->DidConsume(bytes_to_copy);
187 if (data->BytesRemaining() == bytes_to_copy) {
188 // Consumed all data from this buffer
189 read_buffer_.pop_front();
190 } else {
191 data->DidConsume(bytes_to_copy);
195 return user_buffer_->BytesConsumed();
198 int SpdyProxyClientSocket::Write(IOBuffer* buf, int buf_len,
199 CompletionCallback* callback) {
200 DCHECK(!write_callback_);
201 if (next_state_ == STATE_DISCONNECTED)
202 return ERR_SOCKET_NOT_CONNECTED;
204 if (!spdy_stream_)
205 return ERR_CONNECTION_CLOSED;
207 write_bytes_outstanding_= buf_len;
208 if (buf_len <= kMaxSpdyFrameChunkSize) {
209 int rv = spdy_stream_->WriteStreamData(buf, buf_len, spdy::DATA_FLAG_NONE);
210 if (rv == ERR_IO_PENDING) {
211 write_callback_ = callback;
212 write_buffer_len_ = buf_len;
214 return rv;
217 // Since a SPDY Data frame can only include kMaxSpdyFrameChunkSize bytes
218 // we need to send multiple data frames
219 for (int i = 0; i < buf_len; i += kMaxSpdyFrameChunkSize) {
220 int len = std::min(kMaxSpdyFrameChunkSize, buf_len - i);
221 scoped_refptr<DrainableIOBuffer> iobuf(new DrainableIOBuffer(buf, i + len));
222 iobuf->SetOffset(i);
223 int rv = spdy_stream_->WriteStreamData(iobuf, len, spdy::DATA_FLAG_NONE);
224 if (rv > 0) {
225 write_bytes_outstanding_ -= rv;
226 } else if (rv != ERR_IO_PENDING) {
227 return rv;
230 if (write_bytes_outstanding_ > 0) {
231 write_callback_ = callback;
232 write_buffer_len_ = buf_len;
233 return ERR_IO_PENDING;
234 } else {
235 return buf_len;
239 bool SpdyProxyClientSocket::SetReceiveBufferSize(int32 size) {
240 // Since this StreamSocket sits on top of a shared SpdySession, it
241 // is not safe for callers to set change this underlying socket.
242 return false;
245 bool SpdyProxyClientSocket::SetSendBufferSize(int32 size) {
246 // Since this StreamSocket sits on top of a shared SpdySession, it
247 // is not safe for callers to set change this underlying socket.
248 return false;
251 int SpdyProxyClientSocket::GetPeerAddress(AddressList* address) const {
252 if (!IsConnected())
253 return ERR_SOCKET_NOT_CONNECTED;
254 return spdy_stream_->GetPeerAddress(address);
257 int SpdyProxyClientSocket::GetLocalAddress(IPEndPoint* address) const {
258 if (!IsConnected())
259 return ERR_SOCKET_NOT_CONNECTED;
260 return spdy_stream_->GetLocalAddress(address);
263 void SpdyProxyClientSocket::OnIOComplete(int result) {
264 DCHECK_NE(STATE_DISCONNECTED, next_state_);
265 int rv = DoLoop(result);
266 if (rv != ERR_IO_PENDING) {
267 CompletionCallback* c = read_callback_;
268 read_callback_ = NULL;
269 c->Run(rv);
273 int SpdyProxyClientSocket::DoLoop(int last_io_result) {
274 DCHECK_NE(next_state_, STATE_DISCONNECTED);
275 int rv = last_io_result;
276 do {
277 State state = next_state_;
278 next_state_ = STATE_DISCONNECTED;
279 switch (state) {
280 case STATE_GENERATE_AUTH_TOKEN:
281 DCHECK_EQ(OK, rv);
282 rv = DoGenerateAuthToken();
283 break;
284 case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
285 rv = DoGenerateAuthTokenComplete(rv);
286 break;
287 case STATE_SEND_REQUEST:
288 DCHECK_EQ(OK, rv);
289 net_log_.BeginEvent(
290 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, NULL);
291 rv = DoSendRequest();
292 break;
293 case STATE_SEND_REQUEST_COMPLETE:
294 net_log_.EndEventWithNetErrorCode(
295 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv);
296 rv = DoSendRequestComplete(rv);
297 break;
298 case STATE_READ_REPLY_COMPLETE:
299 rv = DoReadReplyComplete(rv);
300 net_log_.EndEventWithNetErrorCode(
301 NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv);
302 break;
303 default:
304 NOTREACHED() << "bad state";
305 rv = ERR_UNEXPECTED;
306 break;
308 } while (rv != ERR_IO_PENDING && next_state_ != STATE_DISCONNECTED &&
309 next_state_ != STATE_OPEN);
310 return rv;
313 int SpdyProxyClientSocket::DoGenerateAuthToken() {
314 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
315 return auth_->MaybeGenerateAuthToken(&request_, &io_callback_, net_log_);
318 int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
319 DCHECK_NE(ERR_IO_PENDING, result);
320 if (result == OK)
321 next_state_ = STATE_SEND_REQUEST;
322 return result;
325 int SpdyProxyClientSocket::DoSendRequest() {
326 next_state_ = STATE_SEND_REQUEST_COMPLETE;
328 // Add Proxy-Authentication header if necessary.
329 HttpRequestHeaders authorization_headers;
330 if (auth_->HaveAuth()) {
331 auth_->AddAuthorizationHeader(&authorization_headers);
334 std::string request_line;
335 HttpRequestHeaders request_headers;
336 BuildTunnelRequest(request_, authorization_headers, endpoint_, &request_line,
337 &request_headers);
338 if (net_log_.IsLoggingAllEvents()) {
339 net_log_.AddEvent(
340 NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
341 make_scoped_refptr(new NetLogHttpRequestParameter(
342 request_line, request_headers)));
345 request_.extra_headers.MergeFrom(request_headers);
346 linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock());
347 CreateSpdyHeadersFromHttpRequest(request_, request_headers, headers.get(),
348 true);
349 // Reset the URL to be the endpoint of the connection
350 (*headers)["url"] = endpoint_.ToString();
351 headers->erase("scheme");
352 spdy_stream_->set_spdy_headers(headers);
354 return spdy_stream_->SendRequest(true);
357 int SpdyProxyClientSocket::DoSendRequestComplete(int result) {
358 if (result < 0)
359 return result;
361 // Wait for SYN_REPLY frame from the server
362 next_state_ = STATE_READ_REPLY_COMPLETE;
363 return ERR_IO_PENDING;
366 int SpdyProxyClientSocket::DoReadReplyComplete(int result) {
367 // We enter this method directly from DoSendRequestComplete, since
368 // we are notified by a callback when the SYN_REPLY frame arrives
370 if (result < 0)
371 return result;
373 // Require the "HTTP/1.x" status line for SSL CONNECT.
374 if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0))
375 return ERR_TUNNEL_CONNECTION_FAILED;
377 next_state_ = STATE_OPEN;
378 if (net_log_.IsLoggingAllEvents()) {
379 net_log_.AddEvent(
380 NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
381 make_scoped_refptr(new NetLogHttpResponseParameter(response_.headers)));
384 if (response_.headers->response_code() == 200) {
385 return OK;
386 } else if (response_.headers->response_code() == 407) {
387 return ERR_TUNNEL_CONNECTION_FAILED;
388 } else {
389 // Immediately hand off our SpdyStream to a newly created SpdyHttpStream
390 // so that any subsequent SpdyFrames are processed in the context of
391 // the HttpStream, not the socket.
392 DCHECK(spdy_stream_);
393 SpdyStream* stream = spdy_stream_;
394 spdy_stream_ = NULL;
395 response_stream_.reset(new SpdyHttpStream(NULL, false));
396 response_stream_->InitializeWithExistingStream(stream);
397 next_state_ = STATE_DISCONNECTED;
398 return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
402 // SpdyStream::Delegate methods:
403 // Called when SYN frame has been sent.
404 // Returns true if no more data to be sent after SYN frame.
405 bool SpdyProxyClientSocket::OnSendHeadersComplete(int status) {
406 DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE);
408 OnIOComplete(status);
410 // We return true here so that we send |spdy_stream_| into
411 // STATE_OPEN (ala WebSockets).
412 return true;
415 int SpdyProxyClientSocket::OnSendBody() {
416 // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets)
417 // OnSendBody() should never be called.
418 NOTREACHED();
419 return ERR_UNEXPECTED;
422 int SpdyProxyClientSocket::OnSendBodyComplete(int /*status*/, bool* /*eof*/) {
423 // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets)
424 // OnSendBodyComplete() should never be called.
425 NOTREACHED();
426 return ERR_UNEXPECTED;
429 int SpdyProxyClientSocket::OnResponseReceived(
430 const spdy::SpdyHeaderBlock& response,
431 base::Time response_time,
432 int status) {
433 // If we've already received the reply, existing headers are too late.
434 // TODO(mbelshe): figure out a way to make HEADERS frames useful after the
435 // initial response.
436 if (next_state_ != STATE_READ_REPLY_COMPLETE)
437 return OK;
439 // Save the response
440 int rv = SpdyHeadersToHttpResponse(response, &response_);
441 if (rv == ERR_INCOMPLETE_SPDY_HEADERS)
442 return rv; // More headers are coming.
444 OnIOComplete(status);
445 return OK;
448 // Called when data is received.
449 void SpdyProxyClientSocket::OnDataReceived(const char* data, int length) {
450 if (length > 0) {
451 // Save the received data.
452 scoped_refptr<IOBuffer> io_buffer(new IOBuffer(length));
453 memcpy(io_buffer->data(), data, length);
454 read_buffer_.push_back(
455 make_scoped_refptr(new DrainableIOBuffer(io_buffer, length)));
458 if (read_callback_) {
459 int rv = PopulateUserReadBuffer();
460 CompletionCallback* c = read_callback_;
461 read_callback_ = NULL;
462 user_buffer_ = NULL;
463 c->Run(rv);
467 void SpdyProxyClientSocket::OnDataSent(int length) {
468 DCHECK(write_callback_);
470 write_bytes_outstanding_ -= length;
472 DCHECK_GE(write_bytes_outstanding_, 0);
474 if (write_bytes_outstanding_ == 0) {
475 int rv = write_buffer_len_;
476 write_buffer_len_ = 0;
477 write_bytes_outstanding_ = 0;
478 CompletionCallback* c = write_callback_;
479 write_callback_ = NULL;
480 c->Run(rv);
484 void SpdyProxyClientSocket::OnClose(int status) {
485 DCHECK(spdy_stream_);
486 was_ever_used_ = spdy_stream_->WasEverUsed();
487 spdy_stream_ = NULL;
489 bool connecting = next_state_ != STATE_DISCONNECTED &&
490 next_state_ < STATE_OPEN;
491 if (next_state_ == STATE_OPEN)
492 next_state_ = STATE_CLOSED;
493 else
494 next_state_ = STATE_DISCONNECTED;
496 CompletionCallback* write_callback = write_callback_;
497 write_callback_ = NULL;
498 write_buffer_len_ = 0;
499 write_bytes_outstanding_ = 0;
501 // If we're in the middle of connecting, we need to make sure
502 // we invoke the connect callback.
503 if (connecting) {
504 DCHECK(read_callback_);
505 CompletionCallback* read_callback = read_callback_;
506 read_callback_ = NULL;
507 read_callback->Run(status);
508 } else if (read_callback_) {
509 // If we have a read_callback, the we need to make sure we call it back
510 OnDataReceived(NULL, 0);
512 if (write_callback)
513 write_callback->Run(ERR_CONNECTION_CLOSED);
516 void SpdyProxyClientSocket::set_chunk_callback(ChunkCallback* /*callback*/) {
519 } // namespace net