blob: 6323b71cc8ba3640d98581374a6684b7ffcee915 [file] [log] [blame]
// 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