OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/quic/quic_http_stream.h" | |
6 | |
7 #include "base/callback_helpers.h" | |
8 #include "base/metrics/histogram.h" | |
9 #include "base/strings/stringprintf.h" | |
10 #include "net/base/io_buffer.h" | |
11 #include "net/base/net_errors.h" | |
12 #include "net/http/http_response_headers.h" | |
13 #include "net/http/http_util.h" | |
14 #include "net/quic/quic_client_session.h" | |
15 #include "net/quic/quic_http_utils.h" | |
16 #include "net/quic/quic_reliable_client_stream.h" | |
17 #include "net/quic/quic_utils.h" | |
18 #include "net/socket/next_proto.h" | |
19 #include "net/spdy/spdy_frame_builder.h" | |
20 #include "net/spdy/spdy_framer.h" | |
21 #include "net/spdy/spdy_http_utils.h" | |
22 #include "net/ssl/ssl_info.h" | |
23 | |
24 namespace net { | |
25 | |
26 static const size_t kHeaderBufInitialSize = 4096; | |
27 | |
28 QuicHttpStream::QuicHttpStream(const base::WeakPtr<QuicClientSession>& session) | |
29 : next_state_(STATE_NONE), | |
30 session_(session), | |
31 session_error_(OK), | |
32 was_handshake_confirmed_(session->IsCryptoHandshakeConfirmed()), | |
33 stream_(nullptr), | |
34 request_info_(nullptr), | |
35 request_body_stream_(nullptr), | |
36 priority_(MINIMUM_PRIORITY), | |
37 response_info_(nullptr), | |
38 response_status_(OK), | |
39 response_headers_received_(false), | |
40 read_buf_(new GrowableIOBuffer()), | |
41 closed_stream_received_bytes_(0), | |
42 user_buffer_len_(0), | |
43 weak_factory_(this) { | |
44 DCHECK(session_); | |
45 session_->AddObserver(this); | |
46 } | |
47 | |
48 QuicHttpStream::~QuicHttpStream() { | |
49 Close(false); | |
50 if (session_) | |
51 session_->RemoveObserver(this); | |
52 } | |
53 | |
54 int QuicHttpStream::InitializeStream(const HttpRequestInfo* request_info, | |
55 RequestPriority priority, | |
56 const BoundNetLog& stream_net_log, | |
57 const CompletionCallback& callback) { | |
58 DCHECK(!stream_); | |
59 if (!session_) | |
60 return was_handshake_confirmed_ ? ERR_CONNECTION_CLOSED : | |
61 ERR_QUIC_HANDSHAKE_FAILED; | |
62 | |
63 if (request_info->url.SchemeIsSecure()) { | |
64 SSLInfo ssl_info; | |
65 bool secure_session = | |
66 session_->GetSSLInfo(&ssl_info) && ssl_info.cert.get(); | |
67 UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.SecureResourceSecureSession", | |
68 secure_session); | |
69 if (!secure_session) | |
70 return ERR_REQUEST_FOR_SECURE_RESOURCE_OVER_INSECURE_QUIC; | |
71 } | |
72 | |
73 stream_net_log_ = stream_net_log; | |
74 request_info_ = request_info; | |
75 request_time_ = base::Time::Now(); | |
76 priority_ = priority; | |
77 | |
78 int rv = stream_request_.StartRequest( | |
79 session_, &stream_, base::Bind(&QuicHttpStream::OnStreamReady, | |
80 weak_factory_.GetWeakPtr())); | |
81 if (rv == ERR_IO_PENDING) { | |
82 callback_ = callback; | |
83 } else if (rv == OK) { | |
84 stream_->SetDelegate(this); | |
85 } else if (!was_handshake_confirmed_) { | |
86 rv = ERR_QUIC_HANDSHAKE_FAILED; | |
87 } | |
88 | |
89 return rv; | |
90 } | |
91 | |
92 void QuicHttpStream::OnStreamReady(int rv) { | |
93 DCHECK(rv == OK || !stream_); | |
94 if (rv == OK) { | |
95 stream_->SetDelegate(this); | |
96 } else if (!was_handshake_confirmed_) { | |
97 rv = ERR_QUIC_HANDSHAKE_FAILED; | |
98 } | |
99 | |
100 ResetAndReturn(&callback_).Run(rv); | |
101 } | |
102 | |
103 int QuicHttpStream::SendRequest(const HttpRequestHeaders& request_headers, | |
104 HttpResponseInfo* response, | |
105 const CompletionCallback& callback) { | |
106 CHECK(!request_body_stream_); | |
107 CHECK(!response_info_); | |
108 CHECK(!callback.is_null()); | |
109 CHECK(response); | |
110 | |
111 // TODO(rch): remove this once we figure out why channel ID is not being | |
112 // sent when it should be. | |
113 HostPortPair origin = HostPortPair::FromURL(request_info_->url); | |
114 if (origin.Equals(HostPortPair("accounts.google.com", 443)) && | |
115 request_headers.HasHeader(HttpRequestHeaders::kCookie)) { | |
116 SSLInfo ssl_info; | |
117 bool secure_session = | |
118 session_->GetSSLInfo(&ssl_info) && ssl_info.cert.get(); | |
119 DCHECK(secure_session); | |
120 UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.CookieSentToAccountsOverChannelId", | |
121 ssl_info.channel_id_sent); | |
122 } | |
123 if (!stream_) { | |
124 return ERR_CONNECTION_CLOSED; | |
125 } | |
126 | |
127 QuicPriority priority = ConvertRequestPriorityToQuicPriority(priority_); | |
128 stream_->set_priority(priority); | |
129 // Store the serialized request headers. | |
130 CreateSpdyHeadersFromHttpRequest(*request_info_, request_headers, | |
131 SPDY3, /*direct=*/true, &request_headers_); | |
132 | |
133 // Store the request body. | |
134 request_body_stream_ = request_info_->upload_data_stream; | |
135 if (request_body_stream_) { | |
136 // TODO(rch): Can we be more precise about when to allocate | |
137 // raw_request_body_buf_. Removed the following check. DoReadRequestBody() | |
138 // was being called even if we didn't yet allocate raw_request_body_buf_. | |
139 // && (request_body_stream_->size() || | |
140 // request_body_stream_->is_chunked())) | |
141 // Use 10 packets as the body buffer size to give enough space to | |
142 // help ensure we don't often send out partial packets. | |
143 raw_request_body_buf_ = new IOBufferWithSize(10 * kMaxPacketSize); | |
144 // The request body buffer is empty at first. | |
145 request_body_buf_ = new DrainableIOBuffer(raw_request_body_buf_.get(), 0); | |
146 } | |
147 | |
148 // Store the response info. | |
149 response_info_ = response; | |
150 | |
151 next_state_ = STATE_SEND_HEADERS; | |
152 int rv = DoLoop(OK); | |
153 if (rv == ERR_IO_PENDING) | |
154 callback_ = callback; | |
155 | |
156 return rv > 0 ? OK : rv; | |
157 } | |
158 | |
159 UploadProgress QuicHttpStream::GetUploadProgress() const { | |
160 if (!request_body_stream_) | |
161 return UploadProgress(); | |
162 | |
163 return UploadProgress(request_body_stream_->position(), | |
164 request_body_stream_->size()); | |
165 } | |
166 | |
167 int QuicHttpStream::ReadResponseHeaders(const CompletionCallback& callback) { | |
168 CHECK(!callback.is_null()); | |
169 | |
170 if (stream_ == nullptr) | |
171 return response_status_; | |
172 | |
173 // Check if we already have the response headers. If so, return synchronously. | |
174 if (response_headers_received_) | |
175 return OK; | |
176 | |
177 // Still waiting for the response, return IO_PENDING. | |
178 CHECK(callback_.is_null()); | |
179 callback_ = callback; | |
180 return ERR_IO_PENDING; | |
181 } | |
182 | |
183 int QuicHttpStream::ReadResponseBody( | |
184 IOBuffer* buf, int buf_len, const CompletionCallback& callback) { | |
185 CHECK(buf); | |
186 CHECK(buf_len); | |
187 CHECK(!callback.is_null()); | |
188 | |
189 // If we have data buffered, complete the IO immediately. | |
190 if (!response_body_.empty()) { | |
191 int bytes_read = 0; | |
192 while (!response_body_.empty() && buf_len > 0) { | |
193 scoped_refptr<IOBufferWithSize> data = response_body_.front(); | |
194 const int bytes_to_copy = std::min(buf_len, data->size()); | |
195 memcpy(&(buf->data()[bytes_read]), data->data(), bytes_to_copy); | |
196 buf_len -= bytes_to_copy; | |
197 if (bytes_to_copy == data->size()) { | |
198 response_body_.pop_front(); | |
199 } else { | |
200 const int bytes_remaining = data->size() - bytes_to_copy; | |
201 IOBufferWithSize* new_buffer = new IOBufferWithSize(bytes_remaining); | |
202 memcpy(new_buffer->data(), &(data->data()[bytes_to_copy]), | |
203 bytes_remaining); | |
204 response_body_.pop_front(); | |
205 response_body_.push_front(make_scoped_refptr(new_buffer)); | |
206 } | |
207 bytes_read += bytes_to_copy; | |
208 } | |
209 return bytes_read; | |
210 } | |
211 | |
212 if (!stream_) { | |
213 // If the stream is already closed, there is no body to read. | |
214 return response_status_; | |
215 } | |
216 | |
217 CHECK(callback_.is_null()); | |
218 CHECK(!user_buffer_.get()); | |
219 CHECK_EQ(0, user_buffer_len_); | |
220 | |
221 callback_ = callback; | |
222 user_buffer_ = buf; | |
223 user_buffer_len_ = buf_len; | |
224 return ERR_IO_PENDING; | |
225 } | |
226 | |
227 void QuicHttpStream::Close(bool not_reusable) { | |
228 // Note: the not_reusable flag has no meaning for SPDY streams. | |
229 if (stream_) { | |
230 closed_stream_received_bytes_ = stream_->stream_bytes_read(); | |
231 stream_->SetDelegate(nullptr); | |
232 stream_->Reset(QUIC_STREAM_CANCELLED); | |
233 stream_ = nullptr; | |
234 response_status_ = was_handshake_confirmed_ ? | |
235 ERR_CONNECTION_CLOSED : ERR_QUIC_HANDSHAKE_FAILED; | |
236 } | |
237 } | |
238 | |
239 HttpStream* QuicHttpStream::RenewStreamForAuth() { | |
240 return nullptr; | |
241 } | |
242 | |
243 bool QuicHttpStream::IsResponseBodyComplete() const { | |
244 return next_state_ == STATE_OPEN && !stream_; | |
245 } | |
246 | |
247 bool QuicHttpStream::CanFindEndOfResponse() const { | |
248 return true; | |
249 } | |
250 | |
251 bool QuicHttpStream::IsConnectionReused() const { | |
252 // TODO(rch): do something smarter here. | |
253 return stream_ && stream_->id() > 1; | |
254 } | |
255 | |
256 void QuicHttpStream::SetConnectionReused() { | |
257 // QUIC doesn't need an indicator here. | |
258 } | |
259 | |
260 bool QuicHttpStream::IsConnectionReusable() const { | |
261 // QUIC streams aren't considered reusable. | |
262 return false; | |
263 } | |
264 | |
265 int64 QuicHttpStream::GetTotalReceivedBytes() const { | |
266 if (stream_) { | |
267 return stream_->stream_bytes_read(); | |
268 } | |
269 | |
270 return closed_stream_received_bytes_; | |
271 } | |
272 | |
273 bool QuicHttpStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const { | |
274 // TODO(mmenke): Figure out what to do here. | |
275 return true; | |
276 } | |
277 | |
278 void QuicHttpStream::GetSSLInfo(SSLInfo* ssl_info) { | |
279 DCHECK(stream_); | |
280 stream_->GetSSLInfo(ssl_info); | |
281 } | |
282 | |
283 void QuicHttpStream::GetSSLCertRequestInfo( | |
284 SSLCertRequestInfo* cert_request_info) { | |
285 DCHECK(stream_); | |
286 NOTIMPLEMENTED(); | |
287 } | |
288 | |
289 bool QuicHttpStream::IsSpdyHttpStream() const { | |
290 return false; | |
291 } | |
292 | |
293 void QuicHttpStream::Drain(HttpNetworkSession* session) { | |
294 Close(false); | |
295 delete this; | |
296 } | |
297 | |
298 void QuicHttpStream::SetPriority(RequestPriority priority) { | |
299 priority_ = priority; | |
300 } | |
301 | |
302 int QuicHttpStream::OnDataReceived(const char* data, int length) { | |
303 DCHECK_NE(0, length); | |
304 // Are we still reading the response headers. | |
305 if (!response_headers_received_) { | |
306 // Grow the read buffer if necessary. | |
307 if (read_buf_->RemainingCapacity() < length) { | |
308 size_t additional_capacity = length - read_buf_->RemainingCapacity(); | |
309 if (additional_capacity < kHeaderBufInitialSize) | |
310 additional_capacity = kHeaderBufInitialSize; | |
311 read_buf_->SetCapacity(read_buf_->capacity() + additional_capacity); | |
312 } | |
313 memcpy(read_buf_->data(), data, length); | |
314 read_buf_->set_offset(read_buf_->offset() + length); | |
315 int rv = ParseResponseHeaders(); | |
316 if (rv != ERR_IO_PENDING && !callback_.is_null()) { | |
317 DoCallback(rv); | |
318 } | |
319 return OK; | |
320 } | |
321 | |
322 if (callback_.is_null()) { | |
323 BufferResponseBody(data, length); | |
324 return OK; | |
325 } | |
326 | |
327 if (length <= user_buffer_len_) { | |
328 memcpy(user_buffer_->data(), data, length); | |
329 } else { | |
330 memcpy(user_buffer_->data(), data, user_buffer_len_); | |
331 int delta = length - user_buffer_len_; | |
332 BufferResponseBody(data + user_buffer_len_, delta); | |
333 length = user_buffer_len_; | |
334 } | |
335 | |
336 user_buffer_ = nullptr; | |
337 user_buffer_len_ = 0; | |
338 DoCallback(length); | |
339 return OK; | |
340 } | |
341 | |
342 void QuicHttpStream::OnClose(QuicErrorCode error) { | |
343 if (error != QUIC_NO_ERROR) { | |
344 response_status_ = was_handshake_confirmed_ ? | |
345 ERR_QUIC_PROTOCOL_ERROR : ERR_QUIC_HANDSHAKE_FAILED; | |
346 } else if (!response_headers_received_) { | |
347 response_status_ = ERR_ABORTED; | |
348 } | |
349 | |
350 closed_stream_received_bytes_ = stream_->stream_bytes_read(); | |
351 stream_ = nullptr; | |
352 if (!callback_.is_null()) | |
353 DoCallback(response_status_); | |
354 } | |
355 | |
356 void QuicHttpStream::OnError(int error) { | |
357 stream_ = nullptr; | |
358 response_status_ = was_handshake_confirmed_ ? | |
359 error : ERR_QUIC_HANDSHAKE_FAILED; | |
360 if (!callback_.is_null()) | |
361 DoCallback(response_status_); | |
362 } | |
363 | |
364 bool QuicHttpStream::HasSendHeadersComplete() { | |
365 return next_state_ > STATE_SEND_HEADERS_COMPLETE; | |
366 } | |
367 | |
368 void QuicHttpStream::OnCryptoHandshakeConfirmed() { | |
369 was_handshake_confirmed_ = true; | |
370 } | |
371 | |
372 void QuicHttpStream::OnSessionClosed(int error) { | |
373 Close(false); | |
374 session_error_ = error; | |
375 session_.reset(); | |
376 } | |
377 | |
378 void QuicHttpStream::OnIOComplete(int rv) { | |
379 rv = DoLoop(rv); | |
380 | |
381 if (rv != ERR_IO_PENDING && !callback_.is_null()) { | |
382 DoCallback(rv); | |
383 } | |
384 } | |
385 | |
386 void QuicHttpStream::DoCallback(int rv) { | |
387 CHECK_NE(rv, ERR_IO_PENDING); | |
388 CHECK(!callback_.is_null()); | |
389 | |
390 // The client callback can do anything, including destroying this class, | |
391 // so any pending callback must be issued after everything else is done. | |
392 base::ResetAndReturn(&callback_).Run(rv); | |
393 } | |
394 | |
395 int QuicHttpStream::DoLoop(int rv) { | |
396 do { | |
397 State state = next_state_; | |
398 next_state_ = STATE_NONE; | |
399 switch (state) { | |
400 case STATE_SEND_HEADERS: | |
401 CHECK_EQ(OK, rv); | |
402 rv = DoSendHeaders(); | |
403 break; | |
404 case STATE_SEND_HEADERS_COMPLETE: | |
405 rv = DoSendHeadersComplete(rv); | |
406 break; | |
407 case STATE_READ_REQUEST_BODY: | |
408 CHECK_EQ(OK, rv); | |
409 rv = DoReadRequestBody(); | |
410 break; | |
411 case STATE_READ_REQUEST_BODY_COMPLETE: | |
412 rv = DoReadRequestBodyComplete(rv); | |
413 break; | |
414 case STATE_SEND_BODY: | |
415 CHECK_EQ(OK, rv); | |
416 rv = DoSendBody(); | |
417 break; | |
418 case STATE_SEND_BODY_COMPLETE: | |
419 rv = DoSendBodyComplete(rv); | |
420 break; | |
421 case STATE_OPEN: | |
422 CHECK_EQ(OK, rv); | |
423 break; | |
424 default: | |
425 NOTREACHED() << "next_state_: " << next_state_; | |
426 break; | |
427 } | |
428 } while (next_state_ != STATE_NONE && next_state_ != STATE_OPEN && | |
429 rv != ERR_IO_PENDING); | |
430 | |
431 return rv; | |
432 } | |
433 | |
434 int QuicHttpStream::DoSendHeaders() { | |
435 if (!stream_) | |
436 return ERR_UNEXPECTED; | |
437 | |
438 // Log the actual request with the URL Request's net log. | |
439 stream_net_log_.AddEvent( | |
440 NetLog::TYPE_HTTP_TRANSACTION_QUIC_SEND_REQUEST_HEADERS, | |
441 base::Bind(&QuicRequestNetLogCallback, stream_->id(), &request_headers_, | |
442 priority_)); | |
443 // Also log to the QuicSession's net log. | |
444 stream_->net_log().AddEvent( | |
445 NetLog::TYPE_QUIC_HTTP_STREAM_SEND_REQUEST_HEADERS, | |
446 base::Bind(&QuicRequestNetLogCallback, stream_->id(), &request_headers_, | |
447 priority_)); | |
448 | |
449 bool has_upload_data = request_body_stream_ != nullptr; | |
450 | |
451 next_state_ = STATE_SEND_HEADERS_COMPLETE; | |
452 int rv = stream_->WriteHeaders(request_headers_, !has_upload_data, nullptr); | |
453 request_headers_.clear(); | |
454 return rv; | |
455 } | |
456 | |
457 int QuicHttpStream::DoSendHeadersComplete(int rv) { | |
458 if (rv < 0) | |
459 return rv; | |
460 | |
461 next_state_ = request_body_stream_ ? | |
462 STATE_READ_REQUEST_BODY : STATE_OPEN; | |
463 | |
464 return OK; | |
465 } | |
466 | |
467 int QuicHttpStream::DoReadRequestBody() { | |
468 next_state_ = STATE_READ_REQUEST_BODY_COMPLETE; | |
469 return request_body_stream_->Read( | |
470 raw_request_body_buf_.get(), | |
471 raw_request_body_buf_->size(), | |
472 base::Bind(&QuicHttpStream::OnIOComplete, weak_factory_.GetWeakPtr())); | |
473 } | |
474 | |
475 int QuicHttpStream::DoReadRequestBodyComplete(int rv) { | |
476 // |rv| is the result of read from the request body from the last call to | |
477 // DoSendBody(). | |
478 if (rv < 0) | |
479 return rv; | |
480 | |
481 request_body_buf_ = new DrainableIOBuffer(raw_request_body_buf_.get(), rv); | |
482 if (rv == 0) { // Reached the end. | |
483 DCHECK(request_body_stream_->IsEOF()); | |
484 } | |
485 | |
486 next_state_ = STATE_SEND_BODY; | |
487 return OK; | |
488 } | |
489 | |
490 int QuicHttpStream::DoSendBody() { | |
491 if (!stream_) | |
492 return ERR_UNEXPECTED; | |
493 | |
494 CHECK(request_body_stream_); | |
495 CHECK(request_body_buf_.get()); | |
496 const bool eof = request_body_stream_->IsEOF(); | |
497 int len = request_body_buf_->BytesRemaining(); | |
498 if (len > 0 || eof) { | |
499 next_state_ = STATE_SEND_BODY_COMPLETE; | |
500 base::StringPiece data(request_body_buf_->data(), len); | |
501 return stream_->WriteStreamData( | |
502 data, eof, | |
503 base::Bind(&QuicHttpStream::OnIOComplete, weak_factory_.GetWeakPtr())); | |
504 } | |
505 | |
506 next_state_ = STATE_OPEN; | |
507 return OK; | |
508 } | |
509 | |
510 int QuicHttpStream::DoSendBodyComplete(int rv) { | |
511 if (rv < 0) | |
512 return rv; | |
513 | |
514 request_body_buf_->DidConsume(request_body_buf_->BytesRemaining()); | |
515 | |
516 if (!request_body_stream_->IsEOF()) { | |
517 next_state_ = STATE_READ_REQUEST_BODY; | |
518 return OK; | |
519 } | |
520 | |
521 next_state_ = STATE_OPEN; | |
522 return OK; | |
523 } | |
524 | |
525 int QuicHttpStream::ParseResponseHeaders() { | |
526 size_t read_buf_len = static_cast<size_t>(read_buf_->offset()); | |
527 SpdyFramer framer(SPDY3); | |
528 SpdyHeaderBlock headers; | |
529 char* data = read_buf_->StartOfBuffer(); | |
530 size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(), | |
531 &headers); | |
532 | |
533 if (len == 0) { | |
534 return ERR_IO_PENDING; | |
535 } | |
536 | |
537 // Save the remaining received data. | |
538 size_t delta = read_buf_len - len; | |
539 if (delta > 0) { | |
540 BufferResponseBody(data + len, delta); | |
541 } | |
542 | |
543 // The URLRequest logs these headers, so only log to the QuicSession's | |
544 // net log. | |
545 stream_->net_log().AddEvent( | |
546 NetLog::TYPE_QUIC_HTTP_STREAM_READ_RESPONSE_HEADERS, | |
547 base::Bind(&SpdyHeaderBlockNetLogCallback, &headers)); | |
548 | |
549 if (!SpdyHeadersToHttpResponse(headers, SPDY3, response_info_)) { | |
550 DLOG(WARNING) << "Invalid headers"; | |
551 return ERR_QUIC_PROTOCOL_ERROR; | |
552 } | |
553 // Put the peer's IP address and port into the response. | |
554 IPEndPoint address = stream_->GetPeerAddress(); | |
555 response_info_->socket_address = HostPortPair::FromIPEndPoint(address); | |
556 response_info_->connection_info = | |
557 HttpResponseInfo::CONNECTION_INFO_QUIC1_SPDY3; | |
558 response_info_->vary_data | |
559 .Init(*request_info_, *response_info_->headers.get()); | |
560 response_info_->was_npn_negotiated = true; | |
561 response_info_->npn_negotiated_protocol = "quic/1+spdy/3"; | |
562 response_info_->response_time = base::Time::Now(); | |
563 response_info_->request_time = request_time_; | |
564 response_headers_received_ = true; | |
565 | |
566 return OK; | |
567 } | |
568 | |
569 void QuicHttpStream::BufferResponseBody(const char* data, int length) { | |
570 if (length == 0) | |
571 return; | |
572 IOBufferWithSize* io_buffer = new IOBufferWithSize(length); | |
573 memcpy(io_buffer->data(), data, length); | |
574 response_body_.push_back(make_scoped_refptr(io_buffer)); | |
575 } | |
576 | |
577 } // namespace net | |
OLD | NEW |