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.
5 #include "net/spdy/spdy_stream.h"
10 #include "base/compiler_specific.h"
11 #include "base/logging.h"
12 #include "base/message_loop.h"
13 #include "base/stringprintf.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/values.h"
16 #include "net/spdy/spdy_buffer_producer.h"
17 #include "net/spdy/spdy_http_utils.h"
18 #include "net/spdy/spdy_session.h"
24 Value
* NetLogSpdyStreamErrorCallback(SpdyStreamId stream_id
,
26 const std::string
* description
,
27 NetLog::LogLevel
/* log_level */) {
28 DictionaryValue
* dict
= new DictionaryValue();
29 dict
->SetInteger("stream_id", static_cast<int>(stream_id
));
30 dict
->SetInteger("status", status
);
31 dict
->SetString("description", *description
);
35 Value
* NetLogSpdyStreamWindowUpdateCallback(SpdyStreamId stream_id
,
38 NetLog::LogLevel
/* log_level */) {
39 DictionaryValue
* dict
= new DictionaryValue();
40 dict
->SetInteger("stream_id", stream_id
);
41 dict
->SetInteger("delta", delta
);
42 dict
->SetInteger("window_size", window_size
);
46 bool ContainsUpperAscii(const std::string
& str
) {
47 for (std::string::const_iterator
i(str
.begin()); i
!= str
.end(); ++i
) {
48 if (*i
>= 'A' && *i
<= 'Z') {
57 // A wrapper around a stream that calls into ProduceSynStreamFrame().
58 class SpdyStream::SynStreamBufferProducer
: public SpdyBufferProducer
{
60 SynStreamBufferProducer(const base::WeakPtr
<SpdyStream
>& stream
)
65 virtual ~SynStreamBufferProducer() {}
67 virtual scoped_ptr
<SpdyBuffer
> ProduceBuffer() OVERRIDE
{
70 return scoped_ptr
<SpdyBuffer
>();
72 DCHECK_GT(stream_
->stream_id(), 0u);
73 return scoped_ptr
<SpdyBuffer
>(
74 new SpdyBuffer(stream_
->ProduceSynStreamFrame()));
78 const base::WeakPtr
<SpdyStream
> stream_
;
81 // A wrapper around a stream that calls into ProduceHeaderFrame() with
82 // a given header block.
83 class SpdyStream::HeaderBufferProducer
: public SpdyBufferProducer
{
85 HeaderBufferProducer(const base::WeakPtr
<SpdyStream
>& stream
,
86 scoped_ptr
<SpdyHeaderBlock
> headers
)
88 headers_(headers
.Pass()) {
93 virtual ~HeaderBufferProducer() {}
95 virtual scoped_ptr
<SpdyBuffer
> ProduceBuffer() OVERRIDE
{
98 return scoped_ptr
<SpdyBuffer
>();
100 DCHECK_GT(stream_
->stream_id(), 0u);
101 return scoped_ptr
<SpdyBuffer
>(
102 new SpdyBuffer(stream_
->ProduceHeaderFrame(headers_
.Pass())));
106 const base::WeakPtr
<SpdyStream
> stream_
;
107 scoped_ptr
<SpdyHeaderBlock
> headers_
;
110 SpdyStream::SpdyStream(SpdySession
* session
,
111 const std::string
& path
,
112 RequestPriority priority
,
113 int32 initial_send_window_size
,
114 int32 initial_recv_window_size
,
116 const BoundNetLog
& net_log
)
117 : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
118 continue_buffering_data_(true),
123 send_stalled_by_flow_control_(false),
124 send_window_size_(initial_send_window_size
),
125 recv_window_size_(initial_recv_window_size
),
126 unacked_recv_window_bytes_(0),
128 response_received_(false),
131 request_time_(base::Time::Now()),
132 response_(new SpdyHeaderBlock
),
133 io_state_(STATE_NONE
),
134 response_status_(OK
),
136 has_upload_data_(false),
140 domain_bound_cert_type_(CLIENT_CERT_INVALID_TYPE
),
141 just_completed_frame_type_(DATA
),
142 just_completed_frame_size_(0) {
145 SpdyStream::~SpdyStream() {
149 void SpdyStream::SetDelegate(Delegate
* delegate
) {
151 delegate_
= delegate
;
154 CHECK(response_received());
155 MessageLoop::current()->PostTask(
156 FROM_HERE
, base::Bind(&SpdyStream::PushedStreamReplayData
, this));
158 continue_buffering_data_
= false;
162 void SpdyStream::PushedStreamReplayData() {
163 if (cancelled_
|| !delegate_
)
166 continue_buffering_data_
= false;
168 int rv
= delegate_
->OnResponseReceived(*response_
, response_time_
, OK
);
169 if (rv
== ERR_INCOMPLETE_SPDY_HEADERS
) {
170 // We don't have complete headers. Assume we're waiting for another
171 // HEADERS frame. Since we don't have headers, we had better not have
172 // any pending data frames.
173 if (pending_buffers_
.size() != 0U) {
174 LogStreamError(ERR_SPDY_PROTOCOL_ERROR
,
175 "HEADERS incomplete headers, but pending data frames.");
176 session_
->CloseStream(stream_id_
, ERR_SPDY_PROTOCOL_ERROR
);
181 std::vector
<SpdyBuffer
*> buffers
;
182 pending_buffers_
.release(&buffers
);
183 for (size_t i
= 0; i
< buffers
.size(); ++i
) {
184 // It is always possible that a callback to the delegate results in
185 // the delegate no longer being available.
189 delegate_
->OnDataReceived(scoped_ptr
<SpdyBuffer
>(buffers
[i
]));
191 delegate_
->OnDataReceived(scoped_ptr
<SpdyBuffer
>());
192 session_
->CloseStream(stream_id_
, OK
);
193 // Note: |this| may be deleted after calling CloseStream.
194 DCHECK_EQ(buffers
.size() - 1, i
);
199 scoped_ptr
<SpdyFrame
> SpdyStream::ProduceSynStreamFrame() {
200 CHECK_EQ(io_state_
, STATE_SEND_HEADERS_COMPLETE
);
201 CHECK(request_
.get());
202 CHECK_GT(stream_id_
, 0u);
204 SpdyControlFlags flags
=
205 has_upload_data_
? CONTROL_FLAG_NONE
: CONTROL_FLAG_FIN
;
206 scoped_ptr
<SpdyFrame
> frame(session_
->CreateSynStream(
207 stream_id_
, priority_
, slot_
, flags
, *request_
));
208 send_time_
= base::TimeTicks::Now();
212 scoped_ptr
<SpdyFrame
> SpdyStream::ProduceHeaderFrame(
213 scoped_ptr
<SpdyHeaderBlock
> header_block
) {
215 // We must need to write stream data.
216 // Until the headers have been completely sent, we can not be sure
217 // that our stream_id is correct.
218 DCHECK_GT(io_state_
, STATE_SEND_HEADERS_COMPLETE
);
219 DCHECK_GT(stream_id_
, 0u);
221 // Create actual HEADERS frame just in time because it depends on
222 // compression context and should not be reordered after the creation.
223 scoped_ptr
<SpdyFrame
> header_frame(session_
->CreateHeadersFrame(
224 stream_id_
, *header_block
, SpdyControlFlags()));
225 return header_frame
.Pass();
228 void SpdyStream::DetachDelegate() {
234 const SpdyHeaderBlock
& SpdyStream::spdy_headers() const {
235 DCHECK(request_
!= NULL
);
236 return *request_
.get();
239 void SpdyStream::set_spdy_headers(scoped_ptr
<SpdyHeaderBlock
> headers
) {
240 request_
.reset(headers
.release());
243 void SpdyStream::AdjustSendWindowSize(int32 delta_window_size
) {
244 DCHECK_GE(session_
->flow_control_state(), SpdySession::FLOW_CONTROL_STREAM
);
249 // Check for wraparound.
250 if (send_window_size_
> 0) {
251 DCHECK_LE(delta_window_size
, kint32max
- send_window_size_
);
253 if (send_window_size_
< 0) {
254 DCHECK_GE(delta_window_size
, kint32min
- send_window_size_
);
256 send_window_size_
+= delta_window_size
;
257 PossiblyResumeIfSendStalled();
260 void SpdyStream::OnWriteBufferConsumed(
261 size_t frame_payload_size
,
263 SpdyBuffer::ConsumeSource consume_source
) {
264 DCHECK_GE(session_
->flow_control_state(), SpdySession::FLOW_CONTROL_STREAM
);
265 if (consume_source
== SpdyBuffer::DISCARD
) {
266 // If we're discarding a frame or part of it, increase the send
267 // window by the number of discarded bytes. (Although if we're
268 // discarding part of a frame, it's probably because of a write
269 // error and we'll be tearing down the stream soon.)
270 size_t remaining_payload_bytes
= std::min(consume_size
, frame_payload_size
);
271 DCHECK_GT(remaining_payload_bytes
, 0u);
272 IncreaseSendWindowSize(static_cast<int32
>(remaining_payload_bytes
));
274 // For consumed bytes, the send window is increased when we receive
275 // a WINDOW_UPDATE frame.
278 void SpdyStream::IncreaseSendWindowSize(int32 delta_window_size
) {
279 DCHECK_GE(session_
->flow_control_state(), SpdySession::FLOW_CONTROL_STREAM
);
280 DCHECK_GE(delta_window_size
, 1);
282 // Ignore late WINDOW_UPDATEs.
286 if (send_window_size_
> 0) {
287 // Check for overflow.
288 int32 max_delta_window_size
= kint32max
- send_window_size_
;
289 if (delta_window_size
> max_delta_window_size
) {
290 std::string desc
= base::StringPrintf(
291 "Received WINDOW_UPDATE [delta: %d] for stream %d overflows "
292 "send_window_size_ [current: %d]", delta_window_size
, stream_id_
,
294 session_
->ResetStream(stream_id_
, RST_STREAM_FLOW_CONTROL_ERROR
, desc
);
299 send_window_size_
+= delta_window_size
;
302 NetLog::TYPE_SPDY_STREAM_UPDATE_SEND_WINDOW
,
303 base::Bind(&NetLogSpdyStreamWindowUpdateCallback
,
304 stream_id_
, delta_window_size
, send_window_size_
));
306 PossiblyResumeIfSendStalled();
309 void SpdyStream::DecreaseSendWindowSize(int32 delta_window_size
) {
310 DCHECK_GE(session_
->flow_control_state(), SpdySession::FLOW_CONTROL_STREAM
);
315 // We only call this method when sending a frame. Therefore,
316 // |delta_window_size| should be within the valid frame size range.
317 DCHECK_GE(delta_window_size
, 1);
318 DCHECK_LE(delta_window_size
, kMaxSpdyFrameChunkSize
);
320 // |send_window_size_| should have been at least |delta_window_size| for
321 // this call to happen.
322 DCHECK_GE(send_window_size_
, delta_window_size
);
324 send_window_size_
-= delta_window_size
;
327 NetLog::TYPE_SPDY_STREAM_UPDATE_SEND_WINDOW
,
328 base::Bind(&NetLogSpdyStreamWindowUpdateCallback
,
329 stream_id_
, -delta_window_size
, send_window_size_
));
332 void SpdyStream::OnReadBufferConsumed(
334 SpdyBuffer::ConsumeSource consume_source
) {
335 DCHECK_GE(session_
->flow_control_state(), SpdySession::FLOW_CONTROL_STREAM
);
336 DCHECK_GE(consume_size
, 1u);
337 DCHECK_LE(consume_size
, static_cast<size_t>(kint32max
));
338 IncreaseRecvWindowSize(static_cast<int32
>(consume_size
));
341 void SpdyStream::IncreaseRecvWindowSize(int32 delta_window_size
) {
342 DCHECK_GE(session_
->flow_control_state(), SpdySession::FLOW_CONTROL_STREAM
);
344 // By the time a read is processed by the delegate, this stream may
345 // already be inactive.
346 if (!session_
->IsStreamActive(stream_id_
))
349 DCHECK_GE(unacked_recv_window_bytes_
, 0);
350 DCHECK_GE(recv_window_size_
, unacked_recv_window_bytes_
);
351 DCHECK_GE(delta_window_size
, 1);
352 // Check for overflow.
353 DCHECK_LE(delta_window_size
, kint32max
- recv_window_size_
);
355 recv_window_size_
+= delta_window_size
;
357 NetLog::TYPE_SPDY_STREAM_UPDATE_RECV_WINDOW
,
358 base::Bind(&NetLogSpdyStreamWindowUpdateCallback
,
359 stream_id_
, delta_window_size
, recv_window_size_
));
361 unacked_recv_window_bytes_
+= delta_window_size
;
362 if (unacked_recv_window_bytes_
>
363 session_
->stream_initial_recv_window_size() / 2) {
364 session_
->SendStreamWindowUpdate(
365 stream_id_
, static_cast<uint32
>(unacked_recv_window_bytes_
));
366 unacked_recv_window_bytes_
= 0;
370 void SpdyStream::DecreaseRecvWindowSize(int32 delta_window_size
) {
371 DCHECK(session_
->IsStreamActive(stream_id_
));
372 DCHECK_GE(session_
->flow_control_state(), SpdySession::FLOW_CONTROL_STREAM
);
373 DCHECK_GE(delta_window_size
, 1);
375 // Since we never decrease the initial receive window size,
376 // |delta_window_size| should never cause |recv_window_size_| to go
377 // negative. If we do, the receive window isn't being respected.
378 if (delta_window_size
> recv_window_size_
) {
379 session_
->ResetStream(
380 stream_id_
, RST_STREAM_PROTOCOL_ERROR
,
381 "delta_window_size is " + base::IntToString(delta_window_size
) +
382 " in DecreaseRecvWindowSize, which is larger than the receive " +
383 "window size of " + base::IntToString(recv_window_size_
));
387 recv_window_size_
-= delta_window_size
;
389 NetLog::TYPE_SPDY_STREAM_UPDATE_RECV_WINDOW
,
390 base::Bind(&NetLogSpdyStreamWindowUpdateCallback
,
391 stream_id_
, -delta_window_size
, recv_window_size_
));
394 int SpdyStream::GetPeerAddress(IPEndPoint
* address
) const {
395 return session_
->GetPeerAddress(address
);
398 int SpdyStream::GetLocalAddress(IPEndPoint
* address
) const {
399 return session_
->GetLocalAddress(address
);
402 bool SpdyStream::WasEverUsed() const {
403 return session_
->WasEverUsed();
406 base::Time
SpdyStream::GetRequestTime() const {
407 return request_time_
;
410 void SpdyStream::SetRequestTime(base::Time t
) {
414 int SpdyStream::OnResponseReceived(const SpdyHeaderBlock
& response
) {
417 metrics_
.StartStream();
419 DCHECK(response_
->empty());
420 *response_
= response
; // TODO(ukai): avoid copy.
422 recv_first_byte_time_
= base::TimeTicks::Now();
423 response_time_
= base::Time::Now();
425 // If we receive a response before we are in STATE_WAITING_FOR_RESPONSE, then
426 // the server has sent the SYN_REPLY too early.
427 if (!pushed_
&& io_state_
!= STATE_WAITING_FOR_RESPONSE
)
428 return ERR_SPDY_PROTOCOL_ERROR
;
430 CHECK(io_state_
== STATE_NONE
);
431 io_state_
= STATE_OPEN
;
433 // Append all the headers into the response header block.
434 for (SpdyHeaderBlock::const_iterator it
= response
.begin();
435 it
!= response
.end(); ++it
) {
436 // Disallow uppercase headers.
437 if (ContainsUpperAscii(it
->first
)) {
438 session_
->ResetStream(stream_id_
, RST_STREAM_PROTOCOL_ERROR
,
439 "Upper case characters in header: " + it
->first
);
440 response_status_
= ERR_SPDY_PROTOCOL_ERROR
;
441 return ERR_SPDY_PROTOCOL_ERROR
;
445 if ((*response_
).find("transfer-encoding") != (*response_
).end()) {
446 session_
->ResetStream(stream_id_
, RST_STREAM_PROTOCOL_ERROR
,
447 "Received transfer-encoding header");
448 return ERR_SPDY_PROTOCOL_ERROR
;
452 rv
= delegate_
->OnResponseReceived(*response_
, response_time_
, rv
);
453 // If delegate_ is not yet attached, we'll call OnResponseReceived after the
454 // delegate gets attached to the stream.
459 int SpdyStream::OnHeaders(const SpdyHeaderBlock
& headers
) {
460 DCHECK(!response_
->empty());
462 // Append all the headers into the response header block.
463 for (SpdyHeaderBlock::const_iterator it
= headers
.begin();
464 it
!= headers
.end(); ++it
) {
465 // Disallow duplicate headers. This is just to be conservative.
466 if ((*response_
).find(it
->first
) != (*response_
).end()) {
467 LogStreamError(ERR_SPDY_PROTOCOL_ERROR
, "HEADERS duplicate header");
468 response_status_
= ERR_SPDY_PROTOCOL_ERROR
;
469 return ERR_SPDY_PROTOCOL_ERROR
;
472 // Disallow uppercase headers.
473 if (ContainsUpperAscii(it
->first
)) {
474 session_
->ResetStream(stream_id_
, RST_STREAM_PROTOCOL_ERROR
,
475 "Upper case characters in header: " + it
->first
);
476 response_status_
= ERR_SPDY_PROTOCOL_ERROR
;
477 return ERR_SPDY_PROTOCOL_ERROR
;
480 (*response_
)[it
->first
] = it
->second
;
483 if ((*response_
).find("transfer-encoding") != (*response_
).end()) {
484 session_
->ResetStream(stream_id_
, RST_STREAM_PROTOCOL_ERROR
,
485 "Received transfer-encoding header");
486 return ERR_SPDY_PROTOCOL_ERROR
;
491 rv
= delegate_
->OnResponseReceived(*response_
, response_time_
, rv
);
492 // ERR_INCOMPLETE_SPDY_HEADERS means that we are waiting for more
493 // headers before the response header block is complete.
494 if (rv
== ERR_INCOMPLETE_SPDY_HEADERS
)
500 void SpdyStream::OnDataReceived(scoped_ptr
<SpdyBuffer
> buffer
) {
501 DCHECK(session_
->IsStreamActive(stream_id_
));
502 // If we don't have a response, then the SYN_REPLY did not come through.
503 // We cannot pass data up to the caller unless the reply headers have been
505 if (!response_received()) {
506 LogStreamError(ERR_SYN_REPLY_NOT_RECEIVED
, "Didn't receive a response.");
507 session_
->CloseStream(stream_id_
, ERR_SYN_REPLY_NOT_RECEIVED
);
511 if (!delegate_
|| continue_buffering_data_
) {
512 // It should be valid for this to happen in the server push case.
513 // We'll return received data when delegate gets attached to the stream.
515 pending_buffers_
.push_back(buffer
.release());
517 pending_buffers_
.push_back(NULL
);
518 metrics_
.StopStream();
519 // Note: we leave the stream open in the session until the stream
528 metrics_
.StopStream();
529 session_
->CloseStream(stream_id_
, OK
);
530 // Note: |this| may be deleted after calling CloseStream.
534 size_t length
= buffer
->GetRemainingSize();
535 DCHECK_LE(length
, session_
->GetDataFrameMaximumPayload());
536 if (session_
->flow_control_state() >= SpdySession::FLOW_CONTROL_STREAM
) {
537 DecreaseRecvWindowSize(static_cast<int32
>(length
));
538 buffer
->AddConsumeCallback(
539 base::Bind(&SpdyStream::OnReadBufferConsumed
,
540 weak_ptr_factory_
.GetWeakPtr()));
543 // Track our bandwidth.
544 metrics_
.RecordBytes(length
);
545 recv_bytes_
+= length
;
546 recv_last_byte_time_
= base::TimeTicks::Now();
548 if (delegate_
->OnDataReceived(buffer
.Pass()) != OK
) {
549 // |delegate_| rejected the data.
550 LogStreamError(ERR_SPDY_PROTOCOL_ERROR
, "Delegate rejected the data");
551 session_
->CloseStream(stream_id_
, ERR_SPDY_PROTOCOL_ERROR
);
556 void SpdyStream::OnFrameWriteComplete(SpdyFrameType frame_type
,
558 if (frame_size
< session_
->GetFrameMinimumSize() ||
559 frame_size
> session_
->GetFrameMaximumSize()) {
563 if (cancelled() || closed())
565 just_completed_frame_type_
= frame_type
;
566 just_completed_frame_size_
= frame_size
;
570 int SpdyStream::GetProtocolVersion() const {
571 return session_
->GetProtocolVersion();
574 void SpdyStream::LogStreamError(int status
, const std::string
& description
) {
575 net_log_
.AddEvent(NetLog::TYPE_SPDY_STREAM_ERROR
,
576 base::Bind(&NetLogSpdyStreamErrorCallback
,
577 stream_id_
, status
, &description
));
580 void SpdyStream::OnClose(int status
) {
581 io_state_
= STATE_DONE
;
582 response_status_
= status
;
583 Delegate
* delegate
= delegate_
;
586 delegate
->OnClose(status
);
589 void SpdyStream::Cancel() {
594 if (session_
->IsStreamActive(stream_id_
))
595 session_
->ResetStream(stream_id_
, RST_STREAM_CANCEL
, std::string());
596 else if (stream_id_
== 0)
597 session_
->CloseCreatedStream(this, RST_STREAM_CANCEL
);
600 void SpdyStream::Close() {
602 session_
->CloseStream(stream_id_
, OK
);
604 session_
->CloseCreatedStream(this, OK
);
607 int SpdyStream::SendRequest(bool has_upload_data
) {
608 // Pushed streams do not send any data, and should always be
609 // idle. However, we still want to return IO_PENDING to mimic
610 // non-push behavior.
611 has_upload_data_
= has_upload_data
;
614 DCHECK(!has_upload_data_
);
615 DCHECK(response_received());
616 send_time_
= base::TimeTicks::Now();
617 return ERR_IO_PENDING
;
619 CHECK_EQ(STATE_NONE
, io_state_
);
620 io_state_
= STATE_GET_DOMAIN_BOUND_CERT
;
624 void SpdyStream::QueueHeaders(scoped_ptr
<SpdyHeaderBlock
> headers
) {
625 // Until the first headers by SYN_STREAM have been completely sent, we can
626 // not be sure that our stream_id is correct.
627 DCHECK_GT(io_state_
, STATE_SEND_HEADERS_COMPLETE
);
628 CHECK_GT(stream_id_
, 0u);
630 session_
->EnqueueStreamWrite(
632 scoped_ptr
<SpdyBufferProducer
>(
633 new HeaderBufferProducer(
634 weak_ptr_factory_
.GetWeakPtr(), headers
.Pass())));
637 void SpdyStream::QueueStreamData(IOBuffer
* data
,
639 SpdyDataFlags flags
) {
640 // Until the headers have been completely sent, we can not be sure
641 // that our stream_id is correct.
642 DCHECK_GT(io_state_
, STATE_SEND_HEADERS_COMPLETE
);
643 CHECK_GT(stream_id_
, 0u);
646 scoped_ptr
<SpdyBuffer
> data_buffer(session_
->CreateDataBuffer(
647 stream_id_
, data
, length
, flags
));
648 // We'll get called again by PossiblyResumeIfSendStalled().
652 if (session_
->flow_control_state() >= SpdySession::FLOW_CONTROL_STREAM
) {
653 DCHECK_GE(data_buffer
->GetRemainingSize(),
654 session_
->GetDataFrameMinimumSize());
655 size_t payload_size
=
656 data_buffer
->GetRemainingSize() - session_
->GetDataFrameMinimumSize();
657 DCHECK_LE(payload_size
, session_
->GetDataFrameMaximumPayload());
658 DecreaseSendWindowSize(static_cast<int32
>(payload_size
));
659 // This currently isn't strictly needed, since write frames are
660 // discarded only if the stream is about to be closed. But have it
661 // here anyway just in case this changes.
662 data_buffer
->AddConsumeCallback(
663 base::Bind(&SpdyStream::OnWriteBufferConsumed
,
664 weak_ptr_factory_
.GetWeakPtr(),
668 session_
->EnqueueStreamWrite(
670 scoped_ptr
<SpdyBufferProducer
>(
671 new SimpleBufferProducer(data_buffer
.Pass())));
674 bool SpdyStream::GetSSLInfo(SSLInfo
* ssl_info
,
675 bool* was_npn_negotiated
,
676 NextProto
* protocol_negotiated
) {
677 return session_
->GetSSLInfo(
678 ssl_info
, was_npn_negotiated
, protocol_negotiated
);
681 bool SpdyStream::GetSSLCertRequestInfo(SSLCertRequestInfo
* cert_request_info
) {
682 return session_
->GetSSLCertRequestInfo(cert_request_info
);
685 void SpdyStream::PossiblyResumeIfSendStalled() {
688 if (send_stalled_by_flow_control_
&& !session_
->IsSendStalled() &&
689 send_window_size_
> 0) {
691 NetLog::TYPE_SPDY_STREAM_FLOW_CONTROL_UNSTALLED
,
692 NetLog::IntegerCallback("stream_id", stream_id_
));
693 send_stalled_by_flow_control_
= false;
694 io_state_
= STATE_SEND_BODY
;
699 bool SpdyStream::HasUrl() const {
701 return response_received();
702 return request_
.get() != NULL
;
705 GURL
SpdyStream::GetUrl() const {
708 const SpdyHeaderBlock
& headers
= (pushed_
) ? *response_
: *request_
;
709 return GetUrlFromHeaderBlock(headers
, GetProtocolVersion(), pushed_
);
712 void SpdyStream::OnGetDomainBoundCertComplete(int result
) {
713 DCHECK_EQ(STATE_GET_DOMAIN_BOUND_CERT_COMPLETE
, io_state_
);
717 int SpdyStream::DoLoop(int result
) {
719 State state
= io_state_
;
720 io_state_
= STATE_NONE
;
722 // State machine 1: Send headers and body.
723 case STATE_GET_DOMAIN_BOUND_CERT
:
724 CHECK_EQ(result
, OK
);
725 result
= DoGetDomainBoundCert();
727 case STATE_GET_DOMAIN_BOUND_CERT_COMPLETE
:
728 result
= DoGetDomainBoundCertComplete(result
);
730 case STATE_SEND_DOMAIN_BOUND_CERT
:
731 CHECK_EQ(result
, OK
);
732 result
= DoSendDomainBoundCert();
734 case STATE_SEND_DOMAIN_BOUND_CERT_COMPLETE
:
735 CHECK_EQ(result
, OK
);
736 result
= DoSendDomainBoundCertComplete();
738 case STATE_SEND_HEADERS
:
739 CHECK_EQ(result
, OK
);
740 result
= DoSendHeaders();
742 case STATE_SEND_HEADERS_COMPLETE
:
743 CHECK_EQ(result
, OK
);
744 result
= DoSendHeadersComplete();
746 case STATE_SEND_BODY
:
747 CHECK_EQ(result
, OK
);
748 result
= DoSendBody();
750 case STATE_SEND_BODY_COMPLETE
:
751 CHECK_EQ(result
, OK
);
752 result
= DoSendBodyComplete();
754 // This is an intermediary waiting state. This state is reached when all
755 // data has been sent, but no data has been received.
756 case STATE_WAITING_FOR_RESPONSE
:
757 io_state_
= STATE_WAITING_FOR_RESPONSE
;
758 result
= ERR_IO_PENDING
;
760 // State machine 2: connection is established.
761 // In STATE_OPEN, OnResponseReceived has already been called.
762 // OnDataReceived, OnClose and OnFrameWriteComplete can be called.
763 // Only OnFrameWriteComplete calls DoLoop().
765 // For HTTP streams, no data is sent from the client while in the OPEN
766 // state, so OnFrameWriteComplete is never called here. The HTTP body is
767 // handled in the OnDataReceived callback, which does not call into
770 // For WebSocket streams, which are bi-directional, we'll send and
771 // receive data once the connection is established. Received data is
772 // handled in OnDataReceived. Sent data is handled in
773 // OnFrameWriteComplete, which calls DoOpen().
775 CHECK_EQ(result
, OK
);
780 DCHECK(result
!= ERR_IO_PENDING
);
783 NOTREACHED() << io_state_
;
786 } while (result
!= ERR_IO_PENDING
&& io_state_
!= STATE_NONE
&&
787 io_state_
!= STATE_OPEN
);
792 int SpdyStream::DoGetDomainBoundCert() {
793 CHECK(request_
.get());
794 if (!session_
->NeedsCredentials()) {
795 // Proceed directly to sending headers
796 io_state_
= STATE_SEND_HEADERS
;
800 slot_
= session_
->credential_state()->FindCredentialSlot(GetUrl());
801 if (slot_
!= SpdyCredentialState::kNoEntry
) {
802 // Proceed directly to sending headers
803 io_state_
= STATE_SEND_HEADERS
;
807 io_state_
= STATE_GET_DOMAIN_BOUND_CERT_COMPLETE
;
808 ServerBoundCertService
* sbc_service
= session_
->GetServerBoundCertService();
809 DCHECK(sbc_service
!= NULL
);
810 std::vector
<uint8
> requested_cert_types
;
811 requested_cert_types
.push_back(CLIENT_CERT_ECDSA_SIGN
);
812 int rv
= sbc_service
->GetDomainBoundCert(
813 GetUrl().GetOrigin().host(), requested_cert_types
,
814 &domain_bound_cert_type_
, &domain_bound_private_key_
, &domain_bound_cert_
,
815 base::Bind(&SpdyStream::OnGetDomainBoundCertComplete
,
816 weak_ptr_factory_
.GetWeakPtr()),
817 &domain_bound_cert_request_handle_
);
821 int SpdyStream::DoGetDomainBoundCertComplete(int result
) {
825 io_state_
= STATE_SEND_DOMAIN_BOUND_CERT
;
826 slot_
= session_
->credential_state()->SetHasCredential(GetUrl());
830 int SpdyStream::DoSendDomainBoundCert() {
831 io_state_
= STATE_SEND_DOMAIN_BOUND_CERT_COMPLETE
;
832 CHECK(request_
.get());
834 std::string origin
= GetUrl().GetOrigin().spec();
835 DCHECK(origin
[origin
.length() - 1] == '/');
836 origin
.erase(origin
.length() - 1); // Trim trailing slash.
837 scoped_ptr
<SpdyFrame
> frame
;
838 int rv
= session_
->CreateCredentialFrame(
839 origin
, domain_bound_cert_type_
, domain_bound_private_key_
,
840 domain_bound_cert_
, priority_
, &frame
);
842 DCHECK_NE(rv
, ERR_IO_PENDING
);
847 // TODO(akalin): Fix the following race condition:
849 // Since this is decoupled from sending the SYN_STREAM frame, it is
850 // possible that other domain-bound cert frames will clobber ours
851 // before our SYN_STREAM frame gets sent. This can be solved by
852 // immediately enqueueing the SYN_STREAM frame here and adjusting
853 // the state machine appropriately.
854 session_
->EnqueueStreamWrite(
856 scoped_ptr
<SpdyBufferProducer
>(
857 new SimpleBufferProducer(
858 scoped_ptr
<SpdyBuffer
>(new SpdyBuffer(frame
.Pass())))));
859 return ERR_IO_PENDING
;
862 int SpdyStream::DoSendDomainBoundCertComplete() {
863 DCHECK_EQ(just_completed_frame_type_
, CREDENTIAL
);
864 io_state_
= STATE_SEND_HEADERS
;
868 int SpdyStream::DoSendHeaders() {
870 io_state_
= STATE_SEND_HEADERS_COMPLETE
;
872 session_
->EnqueueStreamWrite(
874 scoped_ptr
<SpdyBufferProducer
>(
875 new SynStreamBufferProducer(weak_ptr_factory_
.GetWeakPtr())));
876 return ERR_IO_PENDING
;
879 int SpdyStream::DoSendHeadersComplete() {
880 DCHECK_EQ(just_completed_frame_type_
, SYN_STREAM
);
881 DCHECK_NE(stream_id_
, 0u);
883 return ERR_UNEXPECTED
;
886 (delegate_
->OnSendHeadersComplete() == MORE_DATA_TO_SEND
) ?
887 STATE_SEND_BODY
: STATE_WAITING_FOR_RESPONSE
;
892 // DoSendBody is called to send the optional body for the request. This call
893 // will also be called as each write of a chunk of the body completes.
894 int SpdyStream::DoSendBody() {
895 // If we're already in the STATE_SEND_BODY state, then we've already
896 // sent a portion of the body. In that case, we need to first consume
897 // the bytes written in the body stream. Note that the bytes written is
898 // the number of bytes in the frame that were written, only consume the
899 // data portion, of course.
900 io_state_
= STATE_SEND_BODY_COMPLETE
;
902 return ERR_UNEXPECTED
;
903 return delegate_
->OnSendBody();
906 int SpdyStream::DoSendBodyComplete() {
907 if (just_completed_frame_type_
!= DATA
) {
909 return ERR_UNEXPECTED
;
912 if (just_completed_frame_size_
< session_
->GetDataFrameMinimumSize()) {
914 return ERR_UNEXPECTED
;
917 size_t frame_payload_size
=
918 just_completed_frame_size_
- session_
->GetDataFrameMinimumSize();
919 if (frame_payload_size
> session_
->GetDataFrameMaximumPayload()) {
921 return ERR_UNEXPECTED
;
926 return ERR_UNEXPECTED
;
929 send_bytes_
+= frame_payload_size
;
932 (delegate_
->OnSendBodyComplete(frame_payload_size
) == MORE_DATA_TO_SEND
) ?
933 STATE_SEND_BODY
: STATE_WAITING_FOR_RESPONSE
;
938 int SpdyStream::DoOpen() {
939 io_state_
= STATE_OPEN
;
941 switch (just_completed_frame_type_
) {
943 if (just_completed_frame_size_
< session_
->GetDataFrameMinimumSize()) {
945 return ERR_UNEXPECTED
;
948 size_t frame_payload_size
=
949 just_completed_frame_size_
- session_
->GetDataFrameMinimumSize();
950 if (frame_payload_size
> session_
->GetDataFrameMaximumPayload()) {
952 return ERR_UNEXPECTED
;
955 send_bytes_
+= frame_payload_size
;
957 delegate_
->OnDataSent(frame_payload_size
);
963 delegate_
->OnHeadersSent();
968 return ERR_UNEXPECTED
;
974 void SpdyStream::UpdateHistograms() {
975 // We need all timers to be filled in, otherwise metrics can be bogus.
976 if (send_time_
.is_null() || recv_first_byte_time_
.is_null() ||
977 recv_last_byte_time_
.is_null())
980 UMA_HISTOGRAM_TIMES("Net.SpdyStreamTimeToFirstByte",
981 recv_first_byte_time_
- send_time_
);
982 UMA_HISTOGRAM_TIMES("Net.SpdyStreamDownloadTime",
983 recv_last_byte_time_
- recv_first_byte_time_
);
984 UMA_HISTOGRAM_TIMES("Net.SpdyStreamTime",
985 recv_last_byte_time_
- send_time_
);
987 UMA_HISTOGRAM_COUNTS("Net.SpdySendBytes", send_bytes_
);
988 UMA_HISTOGRAM_COUNTS("Net.SpdyRecvBytes", recv_bytes_
);