OLD | NEW |
| (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 | |
OLD | NEW |