Chromium Code Reviews| Index: net/spdy/spdy_stream.cc |
| diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc |
| index 7db3b8c375d66a1fa4543403475886cc3703741d..89ef4dae68df885963aca4417049c3ec92dfd683 100644 |
| --- a/net/spdy/spdy_stream.cc |
| +++ b/net/spdy/spdy_stream.cc |
| @@ -105,7 +105,7 @@ SpdyStream::SpdyStream(SpdyStreamType type, |
| NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND), |
| request_time_(base::Time::Now()), |
| response_(new SpdyHeaderBlock), |
| - io_state_(STATE_NONE), |
| + io_state_((type_ == SPDY_PUSH_STREAM) ? STATE_OPEN : STATE_NONE), |
| response_status_(OK), |
| net_log_(net_log), |
| send_bytes_(0), |
| @@ -177,7 +177,7 @@ void SpdyStream::PushedStreamReplayData() { |
| scoped_ptr<SpdyFrame> SpdyStream::ProduceSynStreamFrame() { |
| CHECK_EQ(io_state_, STATE_SEND_REQUEST_HEADERS_COMPLETE); |
| - CHECK(request_.get()); |
| + CHECK(request_); |
| CHECK_GT(stream_id_, 0u); |
| SpdyControlFlags flags = |
| @@ -373,19 +373,46 @@ int SpdyStream::OnResponseHeadersReceived(const SpdyHeaderBlock& response) { |
| metrics_.StartStream(); |
| + // TODO(akalin): This should be handled as a protocol error. |
| DCHECK(response_->empty()); |
| *response_ = response; // TODO(ukai): avoid copy. |
| recv_first_byte_time_ = base::TimeTicks::Now(); |
| response_time_ = base::Time::Now(); |
| - // If we receive a response before we are in STATE_WAITING_FOR_RESPONSE, then |
| - // the server has sent the SYN_REPLY too early. |
| - if (type_ != SPDY_PUSH_STREAM && io_state_ != STATE_WAITING_FOR_RESPONSE) |
| - return ERR_SPDY_PROTOCOL_ERROR; |
| - if (type_ == SPDY_PUSH_STREAM) |
| - CHECK_EQ(io_state_, STATE_NONE); |
| - io_state_ = STATE_OPEN; |
| + // Check to make sure that we don't receive the response headers |
| + // before we're ready for it. |
| + switch (type_) { |
| + case SPDY_BIDIRECTIONAL_STREAM: |
| + // For a bidirectional stream, we're ready for the response |
| + // headers once we've finished sending the request headers. |
| + if (io_state_ < STATE_OPEN) |
| + return ERR_SPDY_PROTOCOL_ERROR; |
| + break; |
| + |
| + case SPDY_REQUEST_RESPONSE_STREAM: |
| + // For a request/response stream, we're ready for the response |
| + // headers once we've finished sending the request headers and |
| + // the request body (if we have one). |
| + if ((io_state_ < STATE_OPEN) || |
| + (send_status_ == MORE_DATA_TO_SEND) || |
| + pending_send_data_) |
| + return ERR_SPDY_PROTOCOL_ERROR; |
| + break; |
| + |
| + case SPDY_PUSH_STREAM: |
| + // For a push stream, we're ready immediately. |
| + DCHECK_EQ(send_status_, NO_MORE_DATA_TO_SEND); |
| + DCHECK_EQ(io_state_, STATE_OPEN); |
| + break; |
| + |
| + default: |
|
Ryan Hamilton
2013/05/29 03:05:33
If there is not another value for the enum, I thin
akalin
2013/05/29 03:12:45
Unfortunately, no. since the enum has to be at lea
|
| + NOTREACHED(); |
| + } |
| + |
| + DCHECK_EQ(io_state_, STATE_OPEN); |
| + |
| + // TODO(akalin): Merge the code below with the code in OnHeaders(). |
| // Append all the headers into the response header block. |
| for (SpdyHeaderBlock::const_iterator it = response.begin(); |
| @@ -582,7 +609,7 @@ void SpdyStream::SendData(IOBuffer* data, |
| SpdySendStatus send_status) { |
| CHECK_NE(type_, SPDY_PUSH_STREAM); |
| CHECK_EQ(send_status_, MORE_DATA_TO_SEND); |
| - CHECK_GE(io_state_, STATE_SEND_BODY); |
| + CHECK_GE(io_state_, STATE_SEND_REQUEST_HEADERS_COMPLETE); |
| CHECK(!pending_send_data_); |
| pending_send_data_ = new DrainableIOBuffer(data, length); |
| send_status_ = send_status; |
| @@ -620,7 +647,7 @@ base::WeakPtr<SpdyStream> SpdyStream::GetWeakPtr() { |
| bool SpdyStream::HasUrl() const { |
| if (type_ == SPDY_PUSH_STREAM) |
| return response_received(); |
| - return request_.get() != NULL; |
| + return request_ != NULL; |
| } |
| GURL SpdyStream::GetUrl() const { |
| @@ -645,7 +672,6 @@ int SpdyStream::DoLoop(int result) { |
| State state = io_state_; |
| io_state_ = STATE_NONE; |
| switch (state) { |
| - // State machine 1: Send headers and body. |
| case STATE_GET_DOMAIN_BOUND_CERT: |
| CHECK_EQ(result, OK); |
| result = DoGetDomainBoundCert(); |
| @@ -668,35 +694,15 @@ int SpdyStream::DoLoop(int result) { |
| CHECK_EQ(result, OK); |
| result = DoSendRequestHeadersComplete(); |
| break; |
| - // TODO(akalin): Remove the states STATE_SEND_BODY through |
| - // STATE_WAITING_FOR_RESPONSE; we can infer correct behavior |
| - // from |type_| and |send_status_|. |
| - case STATE_SEND_BODY: |
| - CHECK_EQ(result, OK); |
| - result = DoSendBody(); |
| - break; |
| - case STATE_SEND_BODY_COMPLETE: |
| - result = DoSendBodyComplete(result); |
| - break; |
| - // This is an intermediary waiting state. This state is reached when all |
| - // data has been sent, but no data has been received. |
| - case STATE_WAITING_FOR_RESPONSE: |
| - io_state_ = STATE_WAITING_FOR_RESPONSE; |
| - result = ERR_IO_PENDING; |
| - break; |
| - // State machine 2: connection is established. |
| - // In STATE_OPEN, OnResponseHeadersReceived has already been called. |
| - // OnDataReceived, OnClose and OnFrameWriteComplete can be called. |
| - // Only OnFrameWriteComplete calls DoLoop(). |
| - // |
| - // For HTTP streams, no data is sent from the client while in the OPEN |
| - // state, so OnFrameWriteComplete is never called here. The HTTP body is |
| - // handled in the OnDataReceived callback, which does not call into |
| - // DoLoop. |
| + |
| + // For request/response streams, no data is sent from the client |
| + // while in the OPEN state, so OnFrameWriteComplete is never |
| + // called here. The HTTP body is handled in the OnDataReceived |
| + // callback, which does not call into DoLoop. |
| // |
| - // For WebSocket streams, which are bi-directional, we'll send and |
| - // receive data once the connection is established. Received data is |
| - // handled in OnDataReceived. Sent data is handled in |
| + // For bidirectional streams, we'll send and receive data once |
| + // the connection is established. Received data is handled in |
| + // OnDataReceived. Sent data is handled in |
| // OnFrameWriteComplete, which calls DoOpen(). |
| case STATE_OPEN: |
| CHECK_EQ(result, OK); |
| @@ -720,7 +726,7 @@ int SpdyStream::DoLoop(int result) { |
| } |
| int SpdyStream::DoGetDomainBoundCert() { |
| - CHECK(request_.get()); |
| + CHECK(request_); |
| DCHECK_NE(type_, SPDY_PUSH_STREAM); |
| GURL url = GetUrl(); |
| if (!session_->NeedsCredentials() || !url.SchemeIs("https")) { |
| @@ -760,7 +766,7 @@ int SpdyStream::DoGetDomainBoundCertComplete(int result) { |
| } |
| int SpdyStream::DoSendDomainBoundCert() { |
| - CHECK(request_.get()); |
| + CHECK(request_); |
| DCHECK_NE(type_, SPDY_PUSH_STREAM); |
| io_state_ = STATE_SEND_DOMAIN_BOUND_CERT_COMPLETE; |
| @@ -813,78 +819,60 @@ int SpdyStream::DoSendRequestHeaders() { |
| return ERR_IO_PENDING; |
| } |
| -int SpdyStream::DoSendRequestHeadersComplete() { |
| - DCHECK_NE(type_, SPDY_PUSH_STREAM); |
| - DCHECK_EQ(just_completed_frame_type_, SYN_STREAM); |
| - DCHECK_NE(stream_id_, 0u); |
| - if (!delegate_) |
| - return ERR_UNEXPECTED; |
| +namespace { |
| - switch (type_) { |
| +// Assuming we're in STATE_OPEN, maps the given type (which must not |
| +// be SPDY_PUSH_STREAM) and send status to a result to return from |
| +// DoSendRequestHeadersComplete() or DoOpen(). |
| +int GetOpenStateResult(SpdyStreamType type, SpdySendStatus send_status) { |
| + switch (type) { |
| case SPDY_BIDIRECTIONAL_STREAM: |
| - DCHECK_EQ(send_status_, MORE_DATA_TO_SEND); |
| - io_state_ = STATE_WAITING_FOR_RESPONSE; |
| - break; |
| + // For bidirectional streams, there's nothing else to do. |
| + DCHECK_EQ(send_status, MORE_DATA_TO_SEND); |
| + return OK; |
| case SPDY_REQUEST_RESPONSE_STREAM: |
| - io_state_ = |
| - (send_status_ == MORE_DATA_TO_SEND) ? |
| - STATE_SEND_BODY : STATE_WAITING_FOR_RESPONSE; |
| - break; |
| + // For request/response streams, wait for the delegate to send |
| + // data if there's request data to send; we'll get called back |
| + // when the send finishes. |
| + if (send_status == MORE_DATA_TO_SEND) |
| + return ERR_IO_PENDING; |
| + |
| + return OK; |
| case SPDY_PUSH_STREAM: |
| - // Fall through. |
| + // This should never be called for push streams. |
| + break; |
| + |
| default: |
|
Ryan Hamilton
2013/05/29 03:05:33
ditto. I think you can drop this case, right?
akalin
2013/05/29 03:12:45
ditto.
|
| - NOTREACHED(); |
| - return ERR_UNEXPECTED; |
| + break; |
| } |
| - delegate_->OnRequestHeadersSent(); |
| - |
| - return OK; |
| + CHECK(false); |
| + return ERR_UNEXPECTED; |
| } |
| -// DoSendBody is called to send the optional body for the request. This call |
| -// will also be called as each write of a chunk of the body completes. |
| -int SpdyStream::DoSendBody() { |
| - DCHECK_NE(type_, SPDY_PUSH_STREAM); |
| - io_state_ = STATE_SEND_BODY_COMPLETE; |
| - return ERR_IO_PENDING; |
| -} |
| +} // namespace |
| -int SpdyStream::DoSendBodyComplete(int result) { |
| +int SpdyStream::DoSendRequestHeadersComplete() { |
| DCHECK_NE(type_, SPDY_PUSH_STREAM); |
| - result = ProcessJustCompletedFrame(result, STATE_SEND_BODY_COMPLETE); |
| + DCHECK_EQ(just_completed_frame_type_, SYN_STREAM); |
| + DCHECK_NE(stream_id_, 0u); |
| - if (result != OK) |
| - return result; |
| + io_state_ = STATE_OPEN; |
| - io_state_ = |
| - (send_status_ == MORE_DATA_TO_SEND) ? |
| - STATE_SEND_BODY : STATE_WAITING_FOR_RESPONSE; |
| + // Do this before calling into the |delegate_| as that call may |
| + // delete us. |
| + int result = GetOpenStateResult(type_, send_status_); |
| - delegate_->OnDataSent(); |
| + CHECK(delegate_); |
| + delegate_->OnRequestHeadersSent(); |
| - return OK; |
| + return result; |
| } |
| int SpdyStream::DoOpen() { |
| - int result = ProcessJustCompletedFrame(OK, STATE_OPEN); |
| - |
| - if (result != OK) |
| - return result; |
| - |
| - // Set |io_state_| first as |delegate_| may check it. |
| - io_state_ = STATE_OPEN; |
| - |
| - delegate_->OnDataSent(); |
| - |
| - return OK; |
| -} |
| - |
| -int SpdyStream::ProcessJustCompletedFrame(int result, State io_pending_state) { |
| - if (result != OK) |
| - return result; |
| + DCHECK_NE(type_, SPDY_PUSH_STREAM); |
| if (just_completed_frame_type_ != DATA) { |
| NOTREACHED(); |
| @@ -903,23 +891,27 @@ int SpdyStream::ProcessJustCompletedFrame(int result, State io_pending_state) { |
| return ERR_UNEXPECTED; |
| } |
| + // Set |io_state_| first as |delegate_| may check it. |
| + io_state_ = STATE_OPEN; |
| + |
| send_bytes_ += frame_payload_size; |
| pending_send_data_->DidConsume(frame_payload_size); |
| if (pending_send_data_->BytesRemaining() > 0) { |
| - io_state_ = io_pending_state; |
| QueueNextDataFrame(); |
| return ERR_IO_PENDING; |
| } |
| pending_send_data_ = NULL; |
| - if (!delegate_) { |
| - NOTREACHED(); |
| - return ERR_UNEXPECTED; |
| - } |
| + // Do this before calling into the |delegate_| as that call may |
| + // delete us. |
| + int result = GetOpenStateResult(type_, send_status_); |
| - return OK; |
| + CHECK(delegate_); |
| + delegate_->OnDataSent(); |
| + |
| + return result; |
| } |
| void SpdyStream::UpdateHistograms() { |