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/spdy/spdy_websocket_stream.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/compiler_specific.h"
10#include "net/base/io_buffer.h"
11#include "net/base/net_errors.h"
12#include "net/spdy/spdy_framer.h"
13#include "net/spdy/spdy_protocol.h"
14#include "net/spdy/spdy_session.h"
15#include "net/spdy/spdy_stream.h"
16#include "url/gurl.h"
17
18namespace net {
19
20SpdyWebSocketStream::SpdyWebSocketStream(
21    const base::WeakPtr<SpdySession>& spdy_session, Delegate* delegate)
22    : spdy_session_(spdy_session),
23      pending_send_data_length_(0),
24      delegate_(delegate),
25      weak_ptr_factory_(this) {
26  DCHECK(spdy_session_.get());
27  DCHECK(delegate_);
28}
29
30SpdyWebSocketStream::~SpdyWebSocketStream() {
31  delegate_ = NULL;
32  Close();
33}
34
35int SpdyWebSocketStream::InitializeStream(const GURL& url,
36                                          RequestPriority request_priority,
37                                          const BoundNetLog& net_log) {
38  if (!spdy_session_)
39    return ERR_SOCKET_NOT_CONNECTED;
40
41  int rv = stream_request_.StartRequest(
42      SPDY_BIDIRECTIONAL_STREAM, spdy_session_, url, request_priority, net_log,
43      base::Bind(&SpdyWebSocketStream::OnSpdyStreamCreated,
44                 weak_ptr_factory_.GetWeakPtr()));
45
46  if (rv == OK) {
47    stream_ = stream_request_.ReleaseStream();
48    DCHECK(stream_.get());
49    stream_->SetDelegate(this);
50  }
51  return rv;
52}
53
54int SpdyWebSocketStream::SendRequest(scoped_ptr<SpdyHeaderBlock> headers) {
55  if (!stream_.get()) {
56    NOTREACHED();
57    return ERR_UNEXPECTED;
58  }
59  int result = stream_->SendRequestHeaders(headers.Pass(), MORE_DATA_TO_SEND);
60  if (result < OK && result != ERR_IO_PENDING)
61    Close();
62  return result;
63}
64
65int SpdyWebSocketStream::SendData(const char* data, int length) {
66  if (!stream_.get()) {
67    NOTREACHED();
68    return ERR_UNEXPECTED;
69  }
70  DCHECK_GE(length, 0);
71  pending_send_data_length_ = static_cast<size_t>(length);
72  scoped_refptr<IOBuffer> buf(new IOBuffer(length));
73  memcpy(buf->data(), data, length);
74  stream_->SendData(buf.get(), length, MORE_DATA_TO_SEND);
75  return ERR_IO_PENDING;
76}
77
78void SpdyWebSocketStream::Close() {
79  if (stream_.get()) {
80    stream_->Close();
81    DCHECK(!stream_.get());
82  }
83}
84
85void SpdyWebSocketStream::OnRequestHeadersSent() {
86  DCHECK(delegate_);
87  delegate_->OnSentSpdyHeaders();
88}
89
90SpdyResponseHeadersStatus SpdyWebSocketStream::OnResponseHeadersUpdated(
91    const SpdyHeaderBlock& response_headers) {
92  DCHECK(delegate_);
93  delegate_->OnSpdyResponseHeadersUpdated(response_headers);
94  return RESPONSE_HEADERS_ARE_COMPLETE;
95}
96
97void SpdyWebSocketStream::OnDataReceived(scoped_ptr<SpdyBuffer> buffer) {
98  DCHECK(delegate_);
99  delegate_->OnReceivedSpdyData(buffer.Pass());
100}
101
102void SpdyWebSocketStream::OnDataSent() {
103  DCHECK(delegate_);
104  delegate_->OnSentSpdyData(pending_send_data_length_);
105  pending_send_data_length_ = 0;
106}
107
108void SpdyWebSocketStream::OnClose(int status) {
109  stream_.reset();
110
111  // Destruction without Close() call OnClose() with delegate_ being NULL.
112  if (!delegate_)
113    return;
114  Delegate* delegate = delegate_;
115  delegate_ = NULL;
116  delegate->OnCloseSpdyStream();
117}
118
119void SpdyWebSocketStream::OnSpdyStreamCreated(int result) {
120  DCHECK_NE(ERR_IO_PENDING, result);
121  if (result == OK) {
122    stream_ = stream_request_.ReleaseStream();
123    DCHECK(stream_.get());
124    stream_->SetDelegate(this);
125  }
126  DCHECK(delegate_);
127  delegate_->OnCreatedSpdyStream(result);
128}
129
130}  // namespace net
131