Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(726)

Side by Side Diff: net/spdy/spdy_proxy_client_socket.cc

Issue 3391029: Revert 60747 - Add a new class SpdyProxyClientSocket which implements ClientS... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/spdy/spdy_proxy_client_socket.h ('k') | net/spdy/spdy_proxy_client_socket_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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/spdy/spdy_http_utils.h"
20
21 namespace net {
22
23 SpdyProxyClientSocket::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)),
33 next_state_(STATE_NONE),
34 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
57 SpdyProxyClientSocket::~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.
71 int SpdyProxyClientSocket::Connect(CompletionCallback* callback) {
72 DCHECK(!read_callback_);
73 if (next_state_ == STATE_DONE)
74 return OK;
75
76 DCHECK_EQ(STATE_NONE, next_state_);
77 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
85 void SpdyProxyClientSocket::Disconnect() {
86 next_state_ = STATE_NONE;
87 if (spdy_stream_)
88 // This will cause OnClose to be invoked, which takes care of
89 // cleaning up all the internal state.
90 spdy_stream_->Cancel();
91 }
92
93 bool SpdyProxyClientSocket::IsConnected() const {
94 return next_state_ == STATE_DONE && spdy_stream_ != NULL &&
95 !spdy_stream_->closed();
96 }
97
98 bool SpdyProxyClientSocket::IsConnectedAndIdle() const {
99 return IsConnected() && !spdy_stream_->is_idle();
100 }
101
102 void SpdyProxyClientSocket::SetSubresourceSpeculation() {
103 // TODO(rch): what should this implementation be?
104 }
105
106 void SpdyProxyClientSocket::SetOmniboxSpeculation() {
107 // TODO(rch): what should this implementation be?
108 }
109
110 bool SpdyProxyClientSocket::WasEverUsed() const {
111 return was_ever_used_ || (spdy_stream_ && spdy_stream_->WasEverUsed());
112 }
113
114 int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len,
115 CompletionCallback* callback) {
116 DCHECK(!read_callback_);
117 DCHECK(!user_buffer_);
118
119 if (!spdy_stream_) {
120 if (eof_has_been_read_)
121 return ERR_CONNECTION_CLOSED;
122 eof_has_been_read_ = true;
123 return 0;
124 }
125
126 DCHECK(next_state_ == STATE_DONE);
127 DCHECK(buf);
128 user_buffer_ = new DrainableIOBuffer(buf, buf_len);
129 int result = PopulateUserReadBuffer();
130 if (result == 0) {
131 DCHECK(callback);
132 read_callback_ = callback;
133 return ERR_IO_PENDING;
134 }
135 user_buffer_ = NULL;
136 return result;
137 }
138
139 int SpdyProxyClientSocket::PopulateUserReadBuffer() {
140 if (!user_buffer_)
141 return ERR_IO_PENDING;
142
143 while (!read_buffer_.empty() && user_buffer_->BytesRemaining() > 0) {
144 scoped_refptr<DrainableIOBuffer> data = read_buffer_.front();
145 const int bytes_to_copy = std::min(user_buffer_->BytesRemaining(),
146 data->size());
147 memcpy(user_buffer_->data(), data->data(), bytes_to_copy);
148 user_buffer_->DidConsume(bytes_to_copy);
149 if (bytes_to_copy == data->size()) {
150 // Consumed all data from this buffer
151 read_buffer_.pop_front();
152 } else {
153 data->DidConsume(bytes_to_copy);
154 }
155 }
156
157 return user_buffer_->BytesConsumed();
158 }
159
160 int SpdyProxyClientSocket::Write(IOBuffer* buf, int buf_len,
161 CompletionCallback* callback) {
162 DCHECK(!write_callback_);
163 if (!spdy_stream_)
164 return ERR_CONNECTION_CLOSED;
165
166 write_bytes_outstanding_= buf_len;
167 if (buf_len <= kMaxSpdyFrameChunkSize) {
168 int rv = spdy_stream_->WriteStreamData(buf, buf_len, spdy::DATA_FLAG_NONE);
169 if (rv == ERR_IO_PENDING) {
170 write_callback_ = callback;
171 write_buffer_len_ = buf_len;
172 }
173 return rv;
174 }
175
176 // Since a SPDY Data frame can only include kMaxSpdyFrameChunkSize bytes
177 // we need to send multiple data frames
178 for (int i = 0; i < buf_len; i += kMaxSpdyFrameChunkSize) {
179 int len = std::min(kMaxSpdyFrameChunkSize, buf_len - i);
180 scoped_refptr<DrainableIOBuffer> iobuf(new DrainableIOBuffer(buf, i + len));
181 iobuf->SetOffset(i);
182 int rv = spdy_stream_->WriteStreamData(iobuf, len, spdy::DATA_FLAG_NONE);
183 if (rv > 0) {
184 write_bytes_outstanding_ -= rv;
185 } else if (rv != ERR_IO_PENDING) {
186 return rv;
187 }
188 }
189 if (write_bytes_outstanding_ > 0) {
190 write_callback_ = callback;
191 write_buffer_len_ = buf_len;
192 return ERR_IO_PENDING;
193 } else {
194 return buf_len;
195 }
196 }
197
198 bool SpdyProxyClientSocket::SetReceiveBufferSize(int32 size) {
199 // Since this ClientSocket sits on top of a shared SpdySession, it
200 // is not safe for callers to set change this underlying socket.
201 return false;
202 }
203
204 bool SpdyProxyClientSocket::SetSendBufferSize(int32 size) {
205 // Since this ClientSocket sits on top of a shared SpdySession, it
206 // is not safe for callers to set change this underlying socket.
207 return false;
208 }
209
210 int SpdyProxyClientSocket::GetPeerAddress(AddressList* address) const {
211 if (!IsConnected())
212 return ERR_UNEXPECTED;
213 return spdy_stream_->GetPeerAddress(address);
214 }
215
216 void SpdyProxyClientSocket::OnIOComplete(int result) {
217 DCHECK_NE(STATE_NONE, next_state_);
218 int rv = DoLoop(result);
219 if (rv != ERR_IO_PENDING) {
220 CompletionCallback* c = read_callback_;
221 read_callback_ = NULL;
222 c->Run(rv);
223 }
224 }
225
226 int SpdyProxyClientSocket::DoLoop(int last_io_result) {
227 DCHECK_NE(next_state_, STATE_NONE);
228 int rv = last_io_result;
229 do {
230 State state = next_state_;
231 next_state_ = STATE_NONE;
232 switch (state) {
233 case STATE_GENERATE_AUTH_TOKEN:
234 DCHECK_EQ(OK, rv);
235 rv = DoGenerateAuthToken();
236 break;
237 case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
238 rv = DoGenerateAuthTokenComplete(rv);
239 break;
240 case STATE_SEND_REQUEST:
241 DCHECK_EQ(OK, rv);
242 net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST,
243 NULL);
244 rv = DoSendRequest();
245 break;
246 case STATE_SEND_REQUEST_COMPLETE:
247 rv = DoSendRequestComplete(rv);
248 net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST,
249 NULL);
250 break;
251 case STATE_READ_REPLY_COMPLETE:
252 rv = DoReadReplyComplete(rv);
253 net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS,
254 NULL);
255 break;
256 default:
257 NOTREACHED() << "bad state";
258 rv = ERR_UNEXPECTED;
259 break;
260 }
261 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE &&
262 next_state_ != STATE_DONE);
263 return rv;
264 }
265
266 int SpdyProxyClientSocket::DoGenerateAuthToken() {
267 next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
268 return auth_->MaybeGenerateAuthToken(&request_, &io_callback_, net_log_);
269 }
270
271 int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
272 DCHECK_NE(ERR_IO_PENDING, result);
273 if (result == OK)
274 next_state_ = STATE_SEND_REQUEST;
275 return result;
276 }
277
278 int SpdyProxyClientSocket::DoSendRequest() {
279 next_state_ = STATE_SEND_REQUEST_COMPLETE;
280
281 // Add Proxy-Authentication header if necessary.
282 HttpRequestHeaders authorization_headers;
283 if (auth_->HaveAuth()) {
284 auth_->AddAuthorizationHeader(&authorization_headers);
285 }
286
287 std::string request_line;
288 HttpRequestHeaders request_headers;
289 BuildTunnelRequest(request_, authorization_headers, endpoint_, &request_line,
290 &request_headers);
291 if (net_log_.IsLoggingAll()) {
292 net_log_.AddEvent(
293 NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
294 new NetLogHttpRequestParameter(
295 request_line, request_headers));
296 }
297
298 request_.extra_headers.MergeFrom(request_headers);
299 linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock());
300 CreateSpdyHeadersFromHttpRequest(request_, headers.get(), true);
301 // Reset the URL to be the endpoint of the connection
302 (*headers)["url"] = endpoint_.ToString();
303 headers->erase("scheme");
304 spdy_stream_->set_spdy_headers(headers);
305
306 return spdy_stream_->SendRequest(true);
307 }
308
309 int SpdyProxyClientSocket::DoSendRequestComplete(int result) {
310 if (result < 0)
311 return result;
312
313 // Wait for SYN_REPLY frame from the server
314 next_state_ = STATE_READ_REPLY_COMPLETE;
315 return ERR_IO_PENDING;
316 }
317
318 int SpdyProxyClientSocket::DoReadReplyComplete(int result) {
319 // We enter this method directly from DoSendRequestComplete, since
320 // we are notified by a callback when the SYN_REPLY frame arrives
321
322 if (result < 0)
323 return result;
324
325 next_state_ = STATE_DONE;
326 // Require the "HTTP/1.x" status line for SSL CONNECT.
327 if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0))
328 return ERR_TUNNEL_CONNECTION_FAILED;
329
330 if (net_log_.IsLoggingAll()) {
331 net_log_.AddEvent(
332 NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
333 new NetLogHttpResponseParameter(response_.headers));
334 }
335
336 if (response_.headers->response_code() == 200)
337 return OK;
338 else
339 return ERR_TUNNEL_CONNECTION_FAILED;
340 }
341
342 // SpdyStream::Delegate methods:
343 // Called when SYN frame has been sent.
344 // Returns true if no more data to be sent after SYN frame.
345 bool SpdyProxyClientSocket::OnSendHeadersComplete(int status) {
346 DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE);
347
348 OnIOComplete(status);
349
350 // We return true here so that we send |spdy_stream_| into
351 // STATE_OPEN (ala WebSockets).
352 return true;
353 }
354
355 int SpdyProxyClientSocket::OnSendBody() {
356 // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets)
357 // OnSendBody() should never be called.
358 NOTREACHED();
359 return ERR_UNEXPECTED;
360 }
361
362 bool SpdyProxyClientSocket::OnSendBodyComplete(int status) {
363 // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets)
364 // OnSendBodyComplete() should never be called.
365 NOTREACHED();
366 return false;
367 }
368
369 int SpdyProxyClientSocket::OnResponseReceived(
370 const spdy::SpdyHeaderBlock& response,
371 base::Time response_time,
372 int status) {
373 // Save the response
374 SpdyHeadersToHttpResponse(response, &response_);
375
376 DCHECK_EQ(next_state_, STATE_READ_REPLY_COMPLETE);
377
378 OnIOComplete(status);
379
380 return OK;
381 }
382
383 // Called when data is received.
384 void SpdyProxyClientSocket::OnDataReceived(const char* data, int length) {
385 if (length > 0) {
386 // Save the received data.
387 scoped_refptr<IOBuffer> io_buffer = new IOBuffer(length);
388 memcpy(io_buffer->data(), data, length);
389 read_buffer_.push_back(new DrainableIOBuffer(io_buffer, length));
390 }
391
392 if (read_callback_) {
393 int rv = PopulateUserReadBuffer();
394 CompletionCallback* c = read_callback_;
395 read_callback_ = NULL;
396 user_buffer_ = NULL;
397 c->Run(rv);
398 }
399 }
400
401 void SpdyProxyClientSocket::OnDataSent(int length) {
402 DCHECK(write_callback_);
403
404 write_bytes_outstanding_ -= length;
405
406 DCHECK_GE(write_bytes_outstanding_, 0);
407
408 if (write_bytes_outstanding_ == 0) {
409 int rv = write_buffer_len_;
410 write_buffer_len_ = 0;
411 write_bytes_outstanding_ = 0;
412 CompletionCallback* c = write_callback_;
413 write_callback_ = NULL;
414 c->Run(rv);
415 }
416 }
417
418 void SpdyProxyClientSocket::OnClose(int status) {
419 DCHECK(spdy_stream_);
420 // If we're in the middle of connecting, we need to make sure
421 // we invoke the connect callback.
422 CompletionCallback* connect_callback = NULL;
423 if (next_state_ != STATE_NONE && next_state_ != STATE_DONE) {
424 DCHECK(read_callback_);
425 connect_callback = read_callback_;
426 }
427 was_ever_used_ = spdy_stream_->WasEverUsed();
428 spdy_stream_ = NULL;
429 read_callback_ = NULL;
430 write_callback_ = NULL;
431 user_buffer_ = NULL;
432 read_buffer_.empty();
433 if (connect_callback)
434 connect_callback->Run(status);
435 }
436
437 } // namespace net
OLDNEW
« no previous file with comments | « net/spdy/spdy_proxy_client_socket.h ('k') | net/spdy/spdy_proxy_client_socket_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698