| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/spdy/spdy_websocket_stream.h" |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "googleurl/src/gurl.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/io_buffer.h" |
| #include "net/spdy/spdy_framer.h" |
| #include "net/spdy/spdy_protocol.h" |
| #include "net/spdy/spdy_session.h" |
| #include "net/spdy/spdy_stream.h" |
| |
| namespace net { |
| |
| SpdyWebSocketStream::SpdyWebSocketStream( |
| SpdySession* spdy_session, Delegate* delegate) |
| : stream_(NULL), |
| spdy_session_(spdy_session), |
| delegate_(delegate) { |
| DCHECK(spdy_session_); |
| DCHECK(delegate_); |
| } |
| |
| SpdyWebSocketStream::~SpdyWebSocketStream() { |
| if (stream_) { |
| // If Close() has not already been called, DetachDelegate() will send a |
| // SPDY RST_STREAM. Deleting SpdyWebSocketStream is good enough to initiate |
| // graceful shutdown, so we call Close() to avoid sending a RST_STREAM. |
| // For safe, we should eliminate |delegate_| for OnClose() calback. |
| delegate_ = NULL; |
| stream_->Close(); |
| } |
| } |
| |
| int SpdyWebSocketStream::InitializeStream(const GURL& url, |
| RequestPriority request_priority, |
| const BoundNetLog& net_log) { |
| if (spdy_session_->IsClosed()) |
| return ERR_SOCKET_NOT_CONNECTED; |
| |
| int result = spdy_session_->CreateStream( |
| url, request_priority, &stream_, net_log, |
| base::Bind(&SpdyWebSocketStream::OnSpdyStreamCreated, |
| base::Unretained(this))); |
| |
| if (result == OK) { |
| DCHECK(stream_); |
| stream_->SetDelegate(this); |
| } |
| return result; |
| } |
| |
| int SpdyWebSocketStream::SendRequest(scoped_ptr<SpdyHeaderBlock> headers) { |
| if (!stream_) { |
| NOTREACHED(); |
| return ERR_UNEXPECTED; |
| } |
| stream_->set_spdy_headers(headers.Pass()); |
| int result = stream_->SendRequest(true); |
| if (result < OK && result != ERR_IO_PENDING) |
| Close(); |
| return result; |
| } |
| |
| int SpdyWebSocketStream::SendData(const char* data, int length) { |
| if (!stream_) { |
| NOTREACHED(); |
| return ERR_UNEXPECTED; |
| } |
| scoped_refptr<IOBuffer> buf(new IOBuffer(length)); |
| memcpy(buf->data(), data, length); |
| return stream_->WriteStreamData(buf.get(), length, DATA_FLAG_NONE); |
| } |
| |
| void SpdyWebSocketStream::Close() { |
| if (spdy_session_) |
| spdy_session_->CancelPendingCreateStreams(&stream_); |
| if (stream_) |
| stream_->Close(); |
| } |
| |
| bool SpdyWebSocketStream::OnSendHeadersComplete(int status) { |
| DCHECK(delegate_); |
| delegate_->OnSentSpdyHeaders(status); |
| return true; |
| } |
| |
| int SpdyWebSocketStream::OnSendBody() { |
| NOTREACHED(); |
| return ERR_UNEXPECTED; |
| } |
| |
| int SpdyWebSocketStream::OnSendBodyComplete(int status, bool* eof) { |
| NOTREACHED(); |
| *eof = true; |
| return ERR_UNEXPECTED; |
| } |
| |
| int SpdyWebSocketStream::OnResponseReceived( |
| const SpdyHeaderBlock& response, |
| base::Time response_time, int status) { |
| DCHECK(delegate_); |
| return delegate_->OnReceivedSpdyResponseHeader(response, status); |
| } |
| |
| void SpdyWebSocketStream::OnHeadersSent() { |
| // This will be called when WebSocket over SPDY supports new framing. |
| NOTREACHED(); |
| } |
| |
| int SpdyWebSocketStream::OnDataReceived(const char* data, int length) { |
| DCHECK(delegate_); |
| delegate_->OnReceivedSpdyData(data, length); |
| return OK; |
| } |
| |
| void SpdyWebSocketStream::OnDataSent(int length) { |
| DCHECK(delegate_); |
| delegate_->OnSentSpdyData(length); |
| } |
| |
| void SpdyWebSocketStream::OnClose(int status) { |
| stream_ = NULL; |
| |
| // Destruction without Close() call OnClose() with delegate_ being NULL. |
| if (!delegate_) |
| return; |
| Delegate* delegate = delegate_; |
| delegate_ = NULL; |
| delegate->OnCloseSpdyStream(); |
| } |
| |
| void SpdyWebSocketStream::OnSpdyStreamCreated(int result) { |
| DCHECK_NE(ERR_IO_PENDING, result); |
| if (result == OK) { |
| DCHECK(stream_); |
| stream_->SetDelegate(this); |
| } |
| DCHECK(delegate_); |
| delegate_->OnCreatedSpdyStream(result); |
| } |
| |
| } // namespace net |