[SPDY] Make sure final SpdyStream references are held by SpdySession

This will make it much easier to avoid use-after-free bugs when we
make SpdyStream not ref-counted.

Expose only WeakPtr<SpdyStream> objects outside of SpdySession
and SpdyWriteQueue.

Introduce SpdySession::DeleteStreamRefs, which takes a pointer to the last
scoped_refptr to that stream and serves as its destructor.

Fix a lot of dangling SpdyStream references in tests.

Remove SpdyStream::cancelled(), as a stream now guaranteed to be
deleted when cancelled.

Split SpdyStreamRequest::OnRequestComplete() into
OnRequestComplete{Success,Failure}().

BUG=178943

Review URL: https://chromiumcodereview.appspot.com/14812007

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@199688 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc
index 3c38c155..03b43ed3 100644
--- a/net/http/http_proxy_client_socket_pool.cc
+++ b/net/http/http_proxy_client_socket_pool.cc
@@ -330,7 +330,7 @@
     return result;
 
   next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
-  scoped_refptr<SpdyStream> stream = spdy_stream_request_.ReleaseStream();
+  base::WeakPtr<SpdyStream> stream = spdy_stream_request_.ReleaseStream();
   DCHECK(stream);
   // |transport_socket_| will set itself as |stream|'s delegate.
   transport_socket_.reset(
diff --git a/net/spdy/spdy_http_stream.cc b/net/spdy/spdy_http_stream.cc
index 33fe47d..84e30b5f 100644
--- a/net/spdy/spdy_http_stream.cc
+++ b/net/spdy/spdy_http_stream.cc
@@ -43,15 +43,18 @@
       more_read_data_pending_(false),
       direct_(direct) {}
 
-void SpdyHttpStream::InitializeWithExistingStream(SpdyStream* spdy_stream) {
+void SpdyHttpStream::InitializeWithExistingStream(
+    const base::WeakPtr<SpdyStream>& spdy_stream) {
   stream_ = spdy_stream;
   stream_->SetDelegate(this);
   response_headers_received_ = true;
 }
 
 SpdyHttpStream::~SpdyHttpStream() {
-  if (stream_)
+  if (stream_) {
     stream_->DetachDelegate();
+    DCHECK(!stream_);
+  }
 }
 
 int SpdyHttpStream::InitializeStream(const HttpRequestInfo* request_info,
@@ -104,9 +107,6 @@
 
 int SpdyHttpStream::ReadResponseHeaders(const CompletionCallback& callback) {
   CHECK(!callback.is_null());
-  if (stream_)
-    CHECK(!stream_->cancelled());
-
   if (stream_closed_)
     return closed_stream_status_;
 
@@ -241,7 +241,6 @@
   }
 
   CHECK(!callback.is_null());
-  CHECK(!stream_->cancelled());
   CHECK(response);
 
   // SendRequest can be called in two cases.
@@ -276,9 +275,10 @@
 
 void SpdyHttpStream::Cancel() {
   callback_.Reset();
-  if (stream_)
+  if (stream_) {
     stream_->Cancel();
-  DCHECK(!stream_.get());
+    DCHECK(!stream_);
+  }
 }
 
 void SpdyHttpStream::OnStreamCreated(
@@ -451,7 +451,7 @@
     closed_stream_status_ = status;
     closed_stream_id_ = stream_->stream_id();
   }
-  stream_ = NULL;
+  stream_.reset();
   bool invoked_callback = false;
   if (status == net::OK) {
     // We need to complete any pending buffered read now.
@@ -504,9 +504,6 @@
   if (stream_status != OK)
     return false;
 
-  if (stream_)
-    DCHECK(!stream_->cancelled());
-
   // When more_read_data_pending_ is true, it means that more data has
   // arrived since we started waiting.  Wait a little longer and continue
   // to buffer.
diff --git a/net/spdy/spdy_http_stream.h b/net/spdy/spdy_http_stream.h
index 2af82f0..bfb5076 100644
--- a/net/spdy/spdy_http_stream.h
+++ b/net/spdy/spdy_http_stream.h
@@ -38,7 +38,8 @@
   // SpdyStream. In particular, this must be called instead of
   // InitializeStream() if a NULL SpdySession was passed into the
   // constructor.
-  void InitializeWithExistingStream(SpdyStream* spdy_stream);
+  void InitializeWithExistingStream(
+      const base::WeakPtr<SpdyStream>& spdy_stream);
 
   SpdyStream* stream() { return stream_.get(); }
 
@@ -116,7 +117,7 @@
 
   const scoped_refptr<SpdySession> spdy_session_;
   SpdyStreamRequest stream_request_;
-  scoped_refptr<SpdyStream> stream_;
+  base::WeakPtr<SpdyStream> stream_;
 
   bool stream_closed_;
 
diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc
index 84552d2..2fcb899 100644
--- a/net/spdy/spdy_proxy_client_socket.cc
+++ b/net/spdy/spdy_proxy_client_socket.cc
@@ -23,7 +23,7 @@
 namespace net {
 
 SpdyProxyClientSocket::SpdyProxyClientSocket(
-    SpdyStream* spdy_stream,
+    const base::WeakPtr<SpdyStream>& spdy_stream,
     const std::string& user_agent,
     const HostPortPair& endpoint,
     const GURL& url,
@@ -142,7 +142,7 @@
     // This will cause OnClose to be invoked, which takes care of
     // cleaning up all the internal state.
     spdy_stream_->Cancel();
-    DCHECK(!spdy_stream_.get());
+    DCHECK(!spdy_stream_);
   }
 }
 
@@ -432,8 +432,8 @@
         // SpdyHttpStream so that any subsequent SpdyFrames are processed in
         // the context of the HttpStream, not the socket.
         DCHECK(spdy_stream_);
-        SpdyStream* stream = spdy_stream_;
-        spdy_stream_ = NULL;
+        base::WeakPtr<SpdyStream> stream = spdy_stream_;
+        spdy_stream_.reset();
         response_stream_.reset(new SpdyHttpStream(NULL, false));
         response_stream_->InitializeWithExistingStream(stream);
         next_state_ = STATE_DISCONNECTED;
@@ -546,9 +546,8 @@
 }
 
 void SpdyProxyClientSocket::OnClose(int status)  {
-  DCHECK(spdy_stream_);
   was_ever_used_ = spdy_stream_->WasEverUsed();
-  spdy_stream_ = NULL;
+  spdy_stream_.reset();
 
   bool connecting = next_state_ != STATE_DISCONNECTED &&
       next_state_ < STATE_OPEN;
diff --git a/net/spdy/spdy_proxy_client_socket.h b/net/spdy/spdy_proxy_client_socket.h
index e25e1e3..f5b1baa 100644
--- a/net/spdy/spdy_proxy_client_socket.h
+++ b/net/spdy/spdy_proxy_client_socket.h
@@ -42,7 +42,7 @@
   // CONNECT frame for |endpoint|.  After the SYN_REPLY is received,
   // any data read/written to the socket will be transferred in data
   // frames. This object will set itself as |spdy_stream|'s delegate.
-  SpdyProxyClientSocket(SpdyStream* spdy_stream,
+  SpdyProxyClientSocket(const base::WeakPtr<SpdyStream>& spdy_stream,
                         const std::string& user_agent,
                         const HostPortPair& endpoint,
                         const GURL& url,
@@ -132,7 +132,7 @@
   State next_state_;
 
   // Pointer to the SPDY Stream that this sits on top of.
-  scoped_refptr<SpdyStream> spdy_stream_;
+  base::WeakPtr<SpdyStream> spdy_stream_;
 
   // Stores the callback to the layer above, called on completing Read() or
   // Connect().
diff --git a/net/spdy/spdy_proxy_client_socket_spdy2_unittest.cc b/net/spdy/spdy_proxy_client_socket_spdy2_unittest.cc
index 0e2d2b1d..509838a 100644
--- a/net/spdy/spdy_proxy_client_socket_spdy2_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_spdy2_unittest.cc
@@ -122,7 +122,6 @@
   SpdySessionDependencies session_deps_;
   MockConnect connect_data_;
   scoped_refptr<SpdySession> spdy_session_;
-  scoped_refptr<SpdyStream> spdy_stream_;
   BufferedSpdyFramer framer_;
 
   std::string user_agent_;
@@ -145,7 +144,6 @@
       session_deps_(kProtoSPDY2),
       connect_data_(SYNCHRONOUS, OK),
       spdy_session_(NULL),
-      spdy_stream_(NULL),
       framer_(2, false),
       user_agent_(kUserAgent),
       url_(kRequestUrl),
@@ -203,13 +201,14 @@
   spdy_session_->InitializeWithSocket(connection.release(), false, OK);
 
   // Create the SPDY Stream.
-  spdy_stream_ =
-      CreateStreamSynchronously(spdy_session_, url_, LOWEST, net_log_.bound());
-  ASSERT_TRUE(spdy_stream_.get() != NULL);
+  base::WeakPtr<SpdyStream> spdy_stream(
+      CreateStreamSynchronously(
+          spdy_session_, url_, LOWEST, net_log_.bound()));
+  ASSERT_TRUE(spdy_stream.get() != NULL);
 
   // Create the SpdyProxyClientSocket.
   sock_.reset(
-      new SpdyProxyClientSocket(spdy_stream_, user_agent_,
+      new SpdyProxyClientSocket(spdy_stream, user_agent_,
                                 endpoint_host_port_pair_, url_,
                                 proxy_host_port_, net_log_.bound(),
                                 session_->http_auth_cache(),
diff --git a/net/spdy/spdy_proxy_client_socket_spdy3_unittest.cc b/net/spdy/spdy_proxy_client_socket_spdy3_unittest.cc
index 0251f81..0ce54b7 100644
--- a/net/spdy/spdy_proxy_client_socket_spdy3_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_spdy3_unittest.cc
@@ -122,7 +122,6 @@
   SpdySessionDependencies session_deps_;
   MockConnect connect_data_;
   scoped_refptr<SpdySession> spdy_session_;
-  scoped_refptr<SpdyStream> spdy_stream_;
   BufferedSpdyFramer framer_;
 
   std::string user_agent_;
@@ -145,7 +144,6 @@
       session_deps_(kProtoSPDY3),
       connect_data_(SYNCHRONOUS, OK),
       spdy_session_(NULL),
-      spdy_stream_(NULL),
       framer_(3, false),
       user_agent_(kUserAgent),
       url_(kRequestUrl),
@@ -203,13 +201,14 @@
   spdy_session_->InitializeWithSocket(connection.release(), false, OK);
 
   // Create the SPDY Stream.
-  spdy_stream_ =
-      CreateStreamSynchronously(spdy_session_, url_, LOWEST, net_log_.bound());
-  ASSERT_TRUE(spdy_stream_.get() != NULL);
+  base::WeakPtr<SpdyStream> spdy_stream(
+      CreateStreamSynchronously(
+          spdy_session_, url_, LOWEST, net_log_.bound()));
+  ASSERT_TRUE(spdy_stream.get() != NULL);
 
   // Create the SpdyProxyClientSocket.
   sock_.reset(
-      new SpdyProxyClientSocket(spdy_stream_, user_agent_,
+      new SpdyProxyClientSocket(spdy_stream, user_agent_,
                                 endpoint_host_port_pair_, url_,
                                 proxy_host_port_,  net_log_.bound(),
                                 session_->http_auth_cache(),
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index e0b48cf..cf697bf 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -254,7 +254,7 @@
   net_log_ = net_log;
   callback_ = callback;
 
-  scoped_refptr<SpdyStream> stream;
+  base::WeakPtr<SpdyStream> stream;
   int rv = session->TryCreateStream(this, &stream);
   if (rv == OK) {
     Reset();
@@ -269,28 +269,39 @@
   Reset();
 }
 
-scoped_refptr<SpdyStream> SpdyStreamRequest::ReleaseStream() {
+base::WeakPtr<SpdyStream> SpdyStreamRequest::ReleaseStream() {
   DCHECK(!session_.get());
-  scoped_refptr<SpdyStream> stream = stream_;
-  DCHECK(stream.get());
+  base::WeakPtr<SpdyStream> stream = stream_;
+  DCHECK(stream);
   Reset();
   return stream;
 }
 
-void SpdyStreamRequest::OnRequestComplete(
-    const scoped_refptr<SpdyStream>& stream,
-    int rv) {
-  DCHECK(session_.get());
+void SpdyStreamRequest::OnRequestCompleteSuccess(
+    base::WeakPtr<SpdyStream>* stream) {
+  DCHECK(session_);
+  DCHECK(!stream_);
   DCHECK(!callback_.is_null());
   CompletionCallback callback = callback_;
   Reset();
-  stream_ = stream;
+  DCHECK(*stream);
+  stream_ = *stream;
+  callback.Run(OK);
+}
+
+void SpdyStreamRequest::OnRequestCompleteFailure(int rv) {
+  DCHECK(session_);
+  DCHECK(!stream_);
+  DCHECK(!callback_.is_null());
+  CompletionCallback callback = callback_;
+  Reset();
+  DCHECK_NE(rv, OK);
   callback.Run(rv);
 }
 
 void SpdyStreamRequest::Reset() {
   session_ = NULL;
-  stream_ = NULL;
+  stream_.reset();
   url_ = GURL();
   priority_ = MINIMUM_PRIORITY;
   net_log_ = BoundNetLog();
@@ -496,11 +507,11 @@
 
 int SpdySession::GetPushStream(
     const GURL& url,
-    scoped_refptr<SpdyStream>* stream,
+    base::WeakPtr<SpdyStream>* stream,
     const BoundNetLog& stream_net_log) {
   CHECK_NE(state_, STATE_CLOSED);
 
-  *stream = NULL;
+  stream->reset();
 
   // Don't allow access to secure push streams over an unauthenticated, but
   // encrypted SSL socket.
@@ -525,7 +536,7 @@
 }
 
 int SpdySession::TryCreateStream(SpdyStreamRequest* request,
-                                 scoped_refptr<SpdyStream>* stream) {
+                                 base::WeakPtr<SpdyStream>* stream) {
   if (!max_concurrent_streams_ ||
       (active_streams_.size() + created_streams_.size() <
        max_concurrent_streams_)) {
@@ -539,7 +550,7 @@
 }
 
 int SpdySession::CreateStream(const SpdyStreamRequest& request,
-                              scoped_refptr<SpdyStream>* stream) {
+                              base::WeakPtr<SpdyStream>* stream) {
   DCHECK_GE(request.priority(), MINIMUM_PRIORITY);
   DCHECK_LT(request.priority(), NUM_PRIORITIES);
 
@@ -572,11 +583,14 @@
   }
 
   const std::string& path = request.url().PathForRequest();
-  *stream = new SpdyStream(this, path, request.priority(),
-                           stream_initial_send_window_size_,
-                           stream_initial_recv_window_size_,
-                           false, request.net_log());
-  created_streams_.insert(*stream);
+  scoped_refptr<SpdyStream> new_stream(
+      new SpdyStream(this, path, request.priority(),
+                     stream_initial_send_window_size_,
+                     stream_initial_recv_window_size_,
+                     false, request.net_log()));
+  created_streams_.insert(new_stream);
+
+  *stream = new_stream->GetWeakPtr();
 
   UMA_HISTOGRAM_CUSTOM_COUNTS(
       "Net.SpdyPriorityCount",
@@ -896,11 +910,13 @@
   DeleteStream(stream_id, status);
 }
 
-void SpdySession::CloseCreatedStream(SpdyStream* stream, int status) {
+void SpdySession::CloseCreatedStream(
+    const base::WeakPtr<SpdyStream>& stream, int status) {
   DCHECK_EQ(0u, stream->stream_id());
-  created_streams_.erase(scoped_refptr<SpdyStream>(stream));
-  stream->OnClose(status);
-  ProcessPendingStreamRequests();
+  scoped_refptr<SpdyStream> last_ref(stream.get());
+  created_streams_.erase(last_ref);
+
+  DeleteStreamRefs(&last_ref, status);
 }
 
 void SpdySession::ResetStream(SpdyStreamId stream_id,
@@ -1075,7 +1091,7 @@
     if (in_flight_write_->GetRemainingSize() == 0) {
       // It is possible that the stream was cancelled while we were
       // writing to the socket.
-      if (in_flight_write_stream_ && !in_flight_write_stream_->cancelled()) {
+      if (in_flight_write_stream_) {
         DCHECK_GT(in_flight_write_frame_size_, 0u);
         in_flight_write_stream_->OnFrameWriteComplete(
             in_flight_write_frame_type_,
@@ -1137,19 +1153,14 @@
       if (!write_queue_.Dequeue(&frame_type, &producer, &stream))
         break;
 
-      // It is possible that a stream had data to write, but a
-      // WINDOW_UPDATE frame has been received which made that
-      // stream no longer writable.
-      // TODO(rch): consider handling that case by removing the
-      // stream from the writable queue?
-      if (stream.get() && stream->cancelled())
-        continue;
+      if (stream)
+        DCHECK(!stream->closed());
 
       // Activate the stream only when sending the SYN_STREAM frame to
       // guarantee monotonically-increasing stream IDs.
       if (frame_type == SYN_STREAM) {
-        if (stream.get() && stream->stream_id() == 0) {
-          ActivateStream(stream);
+        if (stream && stream->stream_id() == 0) {
+          ActivateStream(stream.get());
         } else {
           NOTREACHED();
           continue;
@@ -1204,26 +1215,24 @@
     queue.swap(pending_create_stream_queues_[i]);
     for (PendingStreamRequestQueue::const_iterator it = queue.begin();
          it != queue.end(); ++it) {
-      (*it)->OnRequestComplete(NULL, ERR_ABORTED);
+      (*it)->OnRequestCompleteFailure(ERR_ABORTED);
     }
   }
 
   ActiveStreamMap::iterator it =
       active_streams_.lower_bound(last_good_stream_id + 1);
   while (it != active_streams_.end()) {
-    const scoped_refptr<SpdyStream>& stream = it->second;
-    ++it;
-    LogAbandonedStream(stream, status);
-    DeleteStream(stream->stream_id(), status);
+    SpdyStreamId stream_id = it->first;
     streams_abandoned_count_++;
+    LogAbandonedStream(it->second, status);
+    ++it;
+    DeleteStream(stream_id, status);
   }
 
   while (!created_streams_.empty()) {
     CreatedStreamSet::iterator it = created_streams_.begin();
-    const scoped_refptr<SpdyStream> stream = *it;
-    created_streams_.erase(it);
-    LogAbandonedStream(stream, status);
-    stream->OnClose(status);
+    LogAbandonedStream(*it, status);
+    CloseCreatedStream((*it)->GetWeakPtr(), status);
   }
 
   write_queue_.RemovePendingWritesForStreamsAfter(last_good_stream_id);
@@ -1411,8 +1420,7 @@
   if (status != OK) {
     for (PushedStreamMap::iterator it = unclaimed_pushed_streams_.begin();
          it != unclaimed_pushed_streams_.end(); ++it) {
-      scoped_refptr<SpdyStream> curr = it->second.first;
-      if (id == curr->stream_id()) {
+      if (id == it->second.first->stream_id()) {
         unclaimed_pushed_streams_.erase(it);
         break;
       }
@@ -1424,15 +1432,36 @@
   if (it == active_streams_.end())
     return;
 
-  const scoped_refptr<SpdyStream> stream(it->second);
+  scoped_refptr<SpdyStream> last_ref;
+  last_ref.swap(it->second);
   active_streams_.erase(it);
-  DCHECK(stream);
+  DCHECK(last_ref);
 
-  write_queue_.RemovePendingWritesForStream(stream);
+  DeleteStreamRefs(&last_ref, status);
+}
 
-  // If this is an active stream, call the callback.
-  stream->OnClose(status);
+void SpdySession::DeleteStreamRefs(scoped_refptr<SpdyStream>* last_ref,
+                                   int status) {
+  if (in_flight_write_stream_ == *last_ref) {
+    // If we're deleting the stream for the in-flight write, we still
+    // need to let the write complete, so we clear
+    // |in_flight_write_stream_| and let the write finish on its own
+    // without notifying |in_flight_write_stream_|.
+    in_flight_write_stream_ = NULL;
+  }
+
+  write_queue_.RemovePendingWritesForStream(*last_ref);
+
+  (*last_ref)->OnClose(status);
+
   ProcessPendingStreamRequests();
+
+  // Nothing else should be holding a reference to |stream| after this
+  // point.
+  CHECK((*last_ref)->HasOneRef());
+
+  // May release the last reference to |this|.
+  *last_ref = NULL;
 }
 
 void SpdySession::RemoveFromPool() {
@@ -1443,19 +1472,19 @@
   }
 }
 
-scoped_refptr<SpdyStream> SpdySession::GetActivePushStream(
+base::WeakPtr<SpdyStream> SpdySession::GetActivePushStream(
     const std::string& path) {
   base::StatsCounter used_push_streams("spdy.claimed_push_streams");
 
   PushedStreamMap::iterator it = unclaimed_pushed_streams_.find(path);
   if (it != unclaimed_pushed_streams_.end()) {
     net_log_.AddEvent(NetLog::TYPE_SPDY_STREAM_ADOPTED_PUSH_STREAM);
-    scoped_refptr<SpdyStream> stream = it->second.first;
+    base::WeakPtr<SpdyStream> stream = it->second.first->GetWeakPtr();
     unclaimed_pushed_streams_.erase(it);
     used_push_streams.Increment();
     return stream;
   }
-  return NULL;
+  return base::WeakPtr<SpdyStream>();
 }
 
 bool SpdySession::GetSSLInfo(SSLInfo* ssl_info,
@@ -1491,10 +1520,11 @@
 void SpdySession::OnStreamError(SpdyStreamId stream_id,
                                 const std::string& description) {
   ActiveStreamMap::const_iterator it = active_streams_.find(stream_id);
-  if (it != active_streams_.end()) {
-    ResetStream(stream_id, it->second->priority(),
-                RST_STREAM_PROTOCOL_ERROR, description);
-  }
+  // We still want to reset the stream even if we don't know anything
+  // about it.
+  RequestPriority priority =
+      (it == active_streams_.end()) ? IDLE : it->second->priority();
+  ResetStream(stream_id, priority, RST_STREAM_PROTOCOL_ERROR, description);
 }
 
 void SpdySession::OnStreamFrameData(SpdyStreamId stream_id,
@@ -1572,12 +1602,13 @@
 
 
 bool SpdySession::Respond(const SpdyHeaderBlock& headers,
-                          const scoped_refptr<SpdyStream> stream) {
+                          const scoped_refptr<SpdyStream>& stream) {
   int rv = OK;
+  SpdyStreamId stream_id = stream->stream_id();
+  // May invalidate |stream|.
   rv = stream->OnResponseReceived(headers);
   if (rv < 0) {
     DCHECK_NE(rv, ERR_IO_PENDING);
-    const SpdyStreamId stream_id = stream->stream_id();
     DeleteStream(stream_id, rv);
     return false;
   }
@@ -1636,7 +1667,9 @@
   const std::string& url = gurl.spec();
 
   // Verify we have a valid stream association.
-  if (!IsStreamActive(associated_stream_id)) {
+  ActiveStreamMap::iterator associated_it =
+      active_streams_.find(associated_stream_id);
+  if (associated_it == active_streams_.end()) {
     ResetStream(stream_id, request_priority, RST_STREAM_INVALID_STREAM,
                 base::StringPrintf(
                     "Received OnSyn with inactive associated stream %d",
@@ -1656,9 +1689,7 @@
                       associated_stream_id));
     }
   } else {
-    scoped_refptr<SpdyStream> associated_stream =
-        active_streams_[associated_stream_id];
-    GURL associated_url(associated_stream->GetUrl());
+    GURL associated_url(associated_it->second->GetUrl());
     if (associated_url.GetOrigin() != gurl.GetOrigin()) {
       ResetStream(stream_id, request_priority, RST_STREAM_REFUSED_STREAM,
                   base::StringPrintf(
@@ -1669,8 +1700,7 @@
   }
 
   // There should not be an existing pushed stream with the same path.
-  PushedStreamMap::iterator it = unclaimed_pushed_streams_.find(url);
-  if (it != unclaimed_pushed_streams_.end()) {
+  if (unclaimed_pushed_streams_.find(url) != unclaimed_pushed_streams_.end()) {
     ResetStream(stream_id, request_priority, RST_STREAM_PROTOCOL_ERROR,
                 "Received duplicate pushed stream with url: " + url);
     return;
@@ -1688,12 +1718,18 @@
       std::pair<scoped_refptr<SpdyStream>, base::TimeTicks> (
           stream, time_func_());
 
-
-  ActivateStream(stream);
+  ActivateStream(stream.get());
   stream->set_response_received();
+  stream = NULL;
+
+  ActiveStreamMap::iterator it = active_streams_.find(stream_id);
+  if (it == active_streams_.end()) {
+    NOTREACHED();
+    return;
+  }
 
   // Parse the headers.
-  if (!Respond(headers, stream))
+  if (!Respond(headers, it->second))
     return;
 
   base::StatsCounter push_requests("spdy.pushed_streams");
@@ -1719,13 +1755,13 @@
     // DeleteStream() will invalidate the current iterator, so move to next.
     ++it;
     if (minimum_freshness > creation_time) {
-      DeleteStream(stream->stream_id(), ERR_INVALID_SPDY_STREAM);
       base::StatsCounter abandoned_push_streams(
           "spdy.abandoned_push_streams");
       base::StatsCounter abandoned_streams("spdy.abandoned_streams");
       abandoned_push_streams.Increment();
       abandoned_streams.Increment();
       streams_abandoned_count_++;
+      DeleteStream(stream->stream_id(), ERR_INVALID_SPDY_STREAM);
     }
   }
   next_unclaimed_push_stream_sweep_time_ = time_func_() +
@@ -1743,14 +1779,14 @@
                    stream_id, 0));
   }
 
-  if (!IsStreamActive(stream_id)) {
+  ActiveStreamMap::iterator it = active_streams_.find(stream_id);
+  if (it == active_streams_.end()) {
     // NOTE:  it may just be that the stream was cancelled.
     return;
   }
 
-  scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
+  const scoped_refptr<SpdyStream>& stream = it->second;
   CHECK_EQ(stream->stream_id(), stream_id);
-  CHECK(!stream->cancelled());
 
   if (stream->response_received()) {
     stream->LogStreamError(ERR_SYN_REPLY_NOT_RECEIVED,
@@ -1775,20 +1811,18 @@
                    stream_id, 0));
   }
 
-  if (!IsStreamActive(stream_id)) {
+  ActiveStreamMap::iterator it = active_streams_.find(stream_id);
+  if (it == active_streams_.end()) {
     // NOTE:  it may just be that the stream was cancelled.
     LOG(WARNING) << "Received HEADERS for invalid stream " << stream_id;
     return;
   }
 
-  scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
-  CHECK_EQ(stream->stream_id(), stream_id);
-  CHECK(!stream->cancelled());
+  CHECK_EQ(it->second->stream_id(), stream_id);
 
-  int rv = stream->OnHeaders(headers);
+  int rv = it->second->OnHeaders(headers);
   if (rv < 0) {
     DCHECK_NE(rv, ERR_IO_PENDING);
-    const SpdyStreamId stream_id = stream->stream_id();
     DeleteStream(stream_id, rv);
   }
 }
@@ -1801,23 +1835,23 @@
       base::Bind(&NetLogSpdyRstCallback,
                  stream_id, status, &description));
 
-  if (!IsStreamActive(stream_id)) {
+  ActiveStreamMap::iterator it = active_streams_.find(stream_id);
+  if (it == active_streams_.end()) {
     // NOTE:  it may just be that the stream was cancelled.
     LOG(WARNING) << "Received RST for invalid stream" << stream_id;
     return;
   }
-  scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
-  CHECK_EQ(stream->stream_id(), stream_id);
-  CHECK(!stream->cancelled());
+
+  CHECK_EQ(it->second->stream_id(), stream_id);
 
   if (status == 0) {
-    stream->OnDataReceived(scoped_ptr<SpdyBuffer>());
+    it->second->OnDataReceived(scoped_ptr<SpdyBuffer>());
   } else if (status == RST_STREAM_REFUSED_STREAM) {
     DeleteStream(stream_id, ERR_SPDY_SERVER_REFUSED_STREAM);
   } else {
     RecordProtocolErrorHistogram(
         PROTOCOL_ERROR_RST_STREAM_FOR_NON_ACTIVE_STREAM);
-    stream->LogStreamError(
+    it->second->LogStreamError(
         ERR_SPDY_PROTOCOL_ERROR,
         base::StringPrintf("SPDY stream closed with status: %d", status));
     // TODO(mbelshe): Map from Spdy-protocol errors to something sensical.
@@ -1921,7 +1955,6 @@
     }
 
     CHECK_EQ(it->second->stream_id(), stream_id);
-    CHECK(!it->second->cancelled());
     it->second->IncreaseSendWindowSize(static_cast<int32>(delta_window_size));
   }
 }
@@ -2231,11 +2264,17 @@
   if (it == pending_stream_request_completions_.end())
     return;
 
-  scoped_refptr<SpdyStream> stream;
+  base::WeakPtr<SpdyStream> stream;
   int rv = CreateStream(*pending_request, &stream);
   pending_stream_request_completions_.erase(it);
 
-  pending_request->OnRequestComplete(stream, rv);
+  if (rv == OK) {
+    DCHECK(stream);
+    pending_request->OnRequestCompleteSuccess(&stream);
+  } else {
+    DCHECK(!stream);
+    pending_request->OnRequestCompleteFailure(rv);
+  }
 }
 
 SSLClientSocket* SpdySession::GetSSLClientSocket() const {
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 8995371..5e22074 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -142,16 +142,19 @@
   // caller. Must be called at most once after StartRequest() returns
   // OK or |callback| is called with OK. The caller must immediately
   // set a delegate for the returned stream (except for test code).
-  scoped_refptr<SpdyStream> ReleaseStream();
+  base::WeakPtr<SpdyStream> ReleaseStream();
 
  private:
   friend class SpdySession;
 
-  // Called by |session_| when the stream attempt is
-  // finished. |stream| is non-NULL exactly when |rv| is OK. Also
-  // called with a NULL stream and ERR_ABORTED if |session_| is
-  // destroyed while the stream attempt is still pending.
-  void OnRequestComplete(const scoped_refptr<SpdyStream>& stream, int rv);
+  // Called by |session_| when the stream attempt has finished
+  // successfully.
+  void OnRequestCompleteSuccess(base::WeakPtr<SpdyStream>* stream);
+
+  // Called by |session_| when the stream attempt has finished with an
+  // error. Also called with ERR_ABORTED if |session_| is destroyed
+  // while the stream attempt is still pending.
+  void OnRequestCompleteFailure(int rv);
 
   // Accessors called by |session_|.
   const GURL& url() const { return url_; }
@@ -161,7 +164,7 @@
   void Reset();
 
   scoped_refptr<SpdySession> session_;
-  scoped_refptr<SpdyStream> stream_;
+  base::WeakPtr<SpdyStream> stream_;
   GURL url_;
   RequestPriority priority_;
   BoundNetLog net_log_;
@@ -222,7 +225,7 @@
   // ERR_IO_PENDING) otherwise.
   int GetPushStream(
       const GURL& url,
-      scoped_refptr<SpdyStream>* spdy_stream,
+      base::WeakPtr<SpdyStream>* spdy_stream,
       const BoundNetLog& stream_net_log);
 
   // Used by SpdySessionPool to initialize with a pre-existing SSL socket. For
@@ -282,17 +285,20 @@
                                           int len,
                                           SpdyDataFlags flags);
 
-  // Close a stream.
+  // If there is an active stream with the given ID, close it. There
+  // must be no external references to that active stream.
   void CloseStream(SpdyStreamId stream_id, int status);
 
   // Close a stream that has been created but is not yet active.
-  void CloseCreatedStream(SpdyStream* stream, int status);
+  // There must be no external references to that active stream.
+  void CloseCreatedStream(const base::WeakPtr<SpdyStream>& stream, int status);
 
-  // Reset a stream by sending a RST_STREAM frame with the given
-  // status code at the given priority, which should be the stream's
-  // priority.  Also closes the stream.  Was not piggybacked to
-  // CloseStream since not all of the calls to CloseStream necessitate
-  // sending a RST_STREAM.
+  // If there is an active stream with the given ID, reset it by by
+  // sending a RST_STREAM frame with the given status code at the
+  // given priority, which should be the stream's priority, and
+  // deleting it.  There must be no external references to that active
+  // stream.  Was not piggybacked to CloseStream since not all of the
+  // calls to CloseStream necessitate sending a RST_STREAM.
   void ResetStream(SpdyStreamId stream_id,
                    RequestPriority priority,
                    SpdyRstStreamStatus status,
@@ -480,7 +486,7 @@
   typedef std::deque<SpdyStreamRequest*> PendingStreamRequestQueue;
   typedef std::set<SpdyStreamRequest*> PendingStreamRequestCompletionSet;
 
-  typedef std::map<int, scoped_refptr<SpdyStream> > ActiveStreamMap;
+  typedef std::map<SpdyStreamId, scoped_refptr<SpdyStream> > ActiveStreamMap;
   typedef std::map<std::string,
       std::pair<scoped_refptr<SpdyStream>, base::TimeTicks> > PushedStreamMap;
 
@@ -499,16 +505,16 @@
   // Called by SpdyStreamRequest to start a request to create a
   // stream. If OK is returned, then |stream| will be filled in with a
   // valid stream. If ERR_IO_PENDING is returned, then
-  // |request->OnRequestComplete()| will be called when the stream is
-  // created (unless it is cancelled). Otherwise, no stream is created
-  // and the error is returned.
+  // |request->OnRequestComplete{Success,Failure}()| will be called
+  // when the stream is created (unless it is cancelled). Otherwise,
+  // no stream is created and the error is returned.
   int TryCreateStream(SpdyStreamRequest* request,
-                      scoped_refptr<SpdyStream>* stream);
+                      base::WeakPtr<SpdyStream>* stream);
 
   // Actually create a stream into |stream|. Returns OK if successful;
   // otherwise, returns an error and |stream| is not filled.
   int CreateStream(const SpdyStreamRequest& request,
-                   scoped_refptr<SpdyStream>* stream);
+                   base::WeakPtr<SpdyStream>* stream);
 
   // Called by SpdyStreamRequest to remove |request| from the stream
   // creation queue.
@@ -594,20 +600,31 @@
 
   // Track active streams in the active stream list.
   void ActivateStream(SpdyStream* stream);
+
+  // If there is an active stream with the given ID, delete it. There
+  // must be no external references to that active stream. Also note
+  // that that active stream may hold the last reference to this
+  // object.
   void DeleteStream(SpdyStreamId id, int status);
 
+  // Remove all internal references to the stream pointed to by
+  // |last_ref|, which must be the last reference to that stream and
+  // is set to NULL. Also note that the given stream may hold the last
+  // reference to this object.
+  void DeleteStreamRefs(scoped_refptr<SpdyStream>* last_ref, int status);
+
   // Removes this session from the session pool.
   void RemoveFromPool();
 
   // Check if we have a pending pushed-stream for this url
   // Returns the stream if found (and returns it from the pending
   // list), returns NULL otherwise.
-  scoped_refptr<SpdyStream> GetActivePushStream(const std::string& url);
+  base::WeakPtr<SpdyStream> GetActivePushStream(const std::string& url);
 
   // Calls OnResponseReceived().
   // Returns true if successful.
   bool Respond(const SpdyHeaderBlock& headers,
-               const scoped_refptr<SpdyStream> stream);
+               const scoped_refptr<SpdyStream>& stream);
 
   void RecordPingRTTHistogram(base::TimeDelta duration);
   void RecordHistograms();
diff --git a/net/spdy/spdy_session_spdy2_unittest.cc b/net/spdy/spdy_session_spdy2_unittest.cc
index af0af3d..686949b3 100644
--- a/net/spdy/spdy_session_spdy2_unittest.cc
+++ b/net/spdy/spdy_session_spdy2_unittest.cc
@@ -139,10 +139,10 @@
   EXPECT_EQ(2, session->GetProtocolVersion());
 
   GURL url("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url, MEDIUM, BoundNetLog());
 
-  scoped_refptr<SpdyStream> spdy_stream2 =
+  base::WeakPtr<SpdyStream> spdy_stream2 =
       CreateStreamSynchronously(session, url, MEDIUM, BoundNetLog());
 
   scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock);
@@ -180,8 +180,7 @@
   scoped_refptr<SpdySession> session2 = GetSession(pair_);
 
   spdy_stream1->Close();
-  spdy_stream1 = NULL;
-  spdy_stream2 = NULL;
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
   // Delete the first session.
   session = NULL;
@@ -189,6 +188,7 @@
   // Delete the second session.
   spdy_session_pool_->Remove(session2);
   session2 = NULL;
+  EXPECT_EQ(NULL, spdy_stream2.get());
 }
 
 TEST_F(SpdySessionSpdy2Test, ClientPing) {
@@ -217,7 +217,7 @@
 
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   test::StreamDelegateSendImmediate delegate(
@@ -272,7 +272,7 @@
 
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   test::StreamDelegateSendImmediate delegate(
@@ -286,6 +286,7 @@
 
   // Delete the session.
   session = NULL;
+  EXPECT_EQ(NULL, spdy_stream1.get());
 }
 
 TEST_F(SpdySessionSpdy2Test, DeleteExpiredPushStreams) {
@@ -315,6 +316,7 @@
                      false, session->net_log_));
   stream->set_spdy_headers(request_headers.Pass());
   session->ActivateStream(stream);
+  stream = NULL;
 
   SpdyHeaderBlock headers;
   headers["url"] = "http://www.google.com/a.dat";
@@ -366,7 +368,7 @@
 
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   test::StreamDelegateSendImmediate delegate(
@@ -400,6 +402,7 @@
 
   // Delete the first session.
   session = NULL;
+  EXPECT_EQ(NULL, spdy_stream1.get());
 }
 
 TEST_F(SpdySessionSpdy2Test, CloseIdleSessions) {
@@ -424,7 +427,7 @@
       InitializeSession(
           http_session_.get(), session1.get(), test_host_port_pair1));
   GURL url1(kTestHost1);
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session1, url1, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
 
@@ -439,7 +442,7 @@
       InitializeSession(
           http_session_.get(), session2.get(), test_host_port_pair2));
   GURL url2(kTestHost2);
-  scoped_refptr<SpdyStream> spdy_stream2 =
+  base::WeakPtr<SpdyStream> spdy_stream2 =
       CreateStreamSynchronously(session2, url2, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream2.get() != NULL);
 
@@ -454,7 +457,7 @@
       InitializeSession(
           http_session_.get(), session3.get(), test_host_port_pair3));
   GURL url3(kTestHost3);
-  scoped_refptr<SpdyStream> spdy_stream3 =
+  base::WeakPtr<SpdyStream> spdy_stream3 =
       CreateStreamSynchronously(session3, url3, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream3.get() != NULL);
 
@@ -478,7 +481,9 @@
   // Make sessions 1 and 3 inactive, but keep them open.
   // Session 2 still open and active
   session1->CloseCreatedStream(spdy_stream1, OK);
+  EXPECT_EQ(NULL, spdy_stream1.get());
   session3->CloseCreatedStream(spdy_stream3, OK);
+  EXPECT_EQ(NULL, spdy_stream3.get());
   EXPECT_FALSE(session1->is_active());
   EXPECT_FALSE(session1->IsClosed());
   EXPECT_TRUE(session2->is_active());
@@ -502,6 +507,7 @@
 
   // Make 2 not active
   session2->CloseCreatedStream(spdy_stream2, OK);
+  EXPECT_EQ(NULL, spdy_stream2.get());
   EXPECT_FALSE(session2->is_active());
   EXPECT_FALSE(session2->IsClosed());
 
@@ -553,22 +559,17 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   // Create 2 streams.  First will succeed.  Second will be pending.
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
 
-  StreamReleaserCallback stream_releaser(session, spdy_stream1);
-
+  StreamReleaserCallback stream_releaser;
   SpdyStreamRequest request;
   ASSERT_EQ(ERR_IO_PENDING,
             request.StartRequest(session, test_url_, MEDIUM,
                                  BoundNetLog(),
                                  stream_releaser.MakeCallback(&request)));
 
-  // Make sure |stream_releaser| holds the last refs.
-  session = NULL;
-  spdy_stream1 = NULL;
-
   EXPECT_EQ(OK, stream_releaser.WaitForResult());
 }
 
@@ -617,19 +618,17 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   // Create 2 streams.  First will succeed.  Second will be pending.
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
 
-  StreamReleaserCallback stream_releaser(session, spdy_stream1);
-
+  StreamReleaserCallback stream_releaser;
   SpdyStreamRequest request;
   ASSERT_EQ(ERR_IO_PENDING,
             request.StartRequest(session, test_url_, MEDIUM,
                                  BoundNetLog(),
                                  stream_releaser.MakeCallback(&request)));
 
-  spdy_stream1 = NULL;
   EXPECT_EQ(OK, stream_releaser.WaitForResult());
 
   // Make sure that persisted data is cleared.
@@ -674,7 +673,7 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   // Create 2 streams.  First will succeed.  Second will be pending.
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
 
@@ -690,7 +689,7 @@
 
   // Release the first one, this will allow the second to be created.
   spdy_stream1->Cancel();
-  spdy_stream1 = NULL;
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
   request.CancelRequest();
   callback.reset();
@@ -955,18 +954,20 @@
       break;
     case SPDY_POOL_CLOSE_IDLE_SESSIONS:
       GURL url(test_hosts[0].url);
-      scoped_refptr<SpdyStream> spdy_stream =
+      base::WeakPtr<SpdyStream> spdy_stream =
           CreateStreamSynchronously(session, url, MEDIUM, BoundNetLog());
       GURL url1(test_hosts[1].url);
-      scoped_refptr<SpdyStream> spdy_stream1 =
+      base::WeakPtr<SpdyStream> spdy_stream1 =
           CreateStreamSynchronously(session1, url1, MEDIUM, BoundNetLog());
       GURL url2(test_hosts[2].url);
-      scoped_refptr<SpdyStream> spdy_stream2 =
+      base::WeakPtr<SpdyStream> spdy_stream2 =
           CreateStreamSynchronously(session2, url2, MEDIUM, BoundNetLog());
 
       // Close streams to make spdy_session and spdy_session1 inactive.
       session->CloseCreatedStream(spdy_stream, OK);
+      EXPECT_EQ(NULL, spdy_stream.get());
       session1->CloseCreatedStream(spdy_stream1, OK);
+      EXPECT_EQ(NULL, spdy_stream1.get());
 
       // Check spdy_session and spdy_session1 are not closed.
       EXPECT_FALSE(session->is_active());
@@ -988,10 +989,8 @@
       EXPECT_TRUE(session2->is_active());
       EXPECT_FALSE(session2->IsClosed());
 
-      spdy_stream = NULL;
-      spdy_stream1 = NULL;
       spdy_stream2->Cancel();
-      spdy_stream2 = NULL;
+      EXPECT_EQ(NULL, spdy_stream2.get());
       spdy_session_pool->Remove(session2);
       session2 = NULL;
       break;
@@ -1121,15 +1120,19 @@
 
   GURL url("http://www.google.com");
 
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url, LOWEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
+  spdy_stream1->SetDelegate(&delegate1);
 
-  scoped_refptr<SpdyStream> spdy_stream2 =
+  base::WeakPtr<SpdyStream> spdy_stream2 =
       CreateStreamSynchronously(session, url, HIGHEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream2.get() != NULL);
   EXPECT_EQ(0u, spdy_stream2->stream_id());
+  test::StreamDelegateDoNothing delegate2(spdy_stream2);
+  spdy_stream2->SetDelegate(&delegate2);
 
   scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock);
   (*headers)["method"] = "GET";
@@ -1150,14 +1153,10 @@
   spdy_stream2->SendRequest(false);
   MessageLoop::current()->RunUntilIdle();
 
-  EXPECT_EQ(3u, spdy_stream1->stream_id());
-  EXPECT_EQ(1u, spdy_stream2->stream_id());
-
-  spdy_stream1->Cancel();
-  spdy_stream1 = NULL;
-
-  spdy_stream2->Cancel();
-  spdy_stream2 = NULL;
+  EXPECT_EQ(NULL, spdy_stream1.get());
+  EXPECT_EQ(NULL, spdy_stream2.get());
+  EXPECT_EQ(3u, delegate1.stream_id());
+  EXPECT_EQ(1u, delegate2.stream_id());
 }
 
 TEST_F(SpdySessionSpdy2Test, CancelStream) {
@@ -1192,16 +1191,20 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, HIGHEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
+  spdy_stream1->SetDelegate(&delegate1);
 
   GURL url2("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream2 =
+  base::WeakPtr<SpdyStream> spdy_stream2 =
       CreateStreamSynchronously(session, url2, LOWEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream2.get() != NULL);
   EXPECT_EQ(0u, spdy_stream2->stream_id());
+  test::StreamDelegateDoNothing delegate2(spdy_stream2);
+  spdy_stream2->SetDelegate(&delegate2);
 
   scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock);
   (*headers)["method"] = "GET";
@@ -1224,17 +1227,17 @@
   EXPECT_EQ(0u, spdy_stream1->stream_id());
 
   spdy_stream1->Cancel();
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
-  EXPECT_EQ(0u, spdy_stream1->stream_id());
+  EXPECT_EQ(0u, delegate1.stream_id());
 
   data.RunFor(1);
 
-  EXPECT_EQ(0u, spdy_stream1->stream_id());
-  EXPECT_EQ(1u, spdy_stream2->stream_id());
+  EXPECT_EQ(0u, delegate1.stream_id());
+  EXPECT_EQ(1u, delegate2.stream_id());
 
-  spdy_stream1 = NULL;
   spdy_stream2->Cancel();
-  spdy_stream2 = NULL;
+  EXPECT_EQ(NULL, spdy_stream2.get());
 }
 
 TEST_F(SpdySessionSpdy2Test, CloseSessionWithTwoCreatedStreams) {
@@ -1265,13 +1268,13 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, HIGHEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
 
   GURL url2("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream2 =
+  base::WeakPtr<SpdyStream> spdy_stream2 =
       CreateStreamSynchronously(session, url2, LOWEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream2.get() != NULL);
   EXPECT_EQ(0u, spdy_stream2->stream_id());
@@ -1287,12 +1290,12 @@
 
   spdy_stream1->set_spdy_headers(headers.Pass());
   EXPECT_TRUE(spdy_stream1->HasUrl());
-  test::ClosingDelegate delegate1(spdy_stream1.get());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
   spdy_stream1->SetDelegate(&delegate1);
 
   spdy_stream2->set_spdy_headers(headers2.Pass());
   EXPECT_TRUE(spdy_stream2->HasUrl());
-  test::ClosingDelegate delegate2(spdy_stream2.get());
+  test::StreamDelegateDoNothing delegate2(spdy_stream2);
   spdy_stream2->SetDelegate(&delegate2);
 
   spdy_stream1->SendRequest(false);
@@ -1305,11 +1308,11 @@
   // Ensure we don't crash while closing the session.
   session->CloseSessionOnError(ERR_ABORTED, true, std::string());
 
-  EXPECT_TRUE(spdy_stream1->closed());
-  EXPECT_TRUE(spdy_stream2->closed());
+  EXPECT_EQ(NULL, spdy_stream1.get());
+  EXPECT_EQ(NULL, spdy_stream2.get());
 
-  spdy_stream1 = NULL;
-  spdy_stream2 = NULL;
+  EXPECT_TRUE(delegate1.StreamIsClosed());
+  EXPECT_TRUE(delegate2.StreamIsClosed());
 }
 
 TEST_F(SpdySessionSpdy2Test, VerifyDomainAuthentication) {
@@ -1509,10 +1512,12 @@
   data.RunFor(1);
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, LOWEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
+  spdy_stream1->SetDelegate(&delegate1);
 
   TestCompletionCallback callback2;
   GURL url2("http://www.google.com");
@@ -1549,33 +1554,42 @@
   spdy_stream1->SendRequest(false);
 
   // Run until 1st stream is closed and 2nd one is opened.
-  EXPECT_EQ(0u, spdy_stream1->stream_id());
+  EXPECT_EQ(0u, delegate1.stream_id());
   data.RunFor(3);
-  EXPECT_EQ(1u, spdy_stream1->stream_id());
+  // Pump loop for SpdySession::ProcessPendingStreamRequests().
+  MessageLoop::current()->RunUntilIdle();
+  EXPECT_EQ(NULL, spdy_stream1.get());
+  EXPECT_EQ(1u, delegate1.stream_id());
   EXPECT_EQ(2u, session->num_active_streams() + session->num_created_streams());
   EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST));
 
-  scoped_refptr<SpdyStream> stream2 = request2.ReleaseStream();
+  base::WeakPtr<SpdyStream> stream2 = request2.ReleaseStream();
+  test::StreamDelegateDoNothing delegate2(stream2);
+  stream2->SetDelegate(&delegate2);
   stream2->set_spdy_headers(headers2.Pass());
   EXPECT_TRUE(stream2->HasUrl());
   stream2->SendRequest(false);
 
   // Run until 2nd stream is closed.
-  EXPECT_EQ(0u, stream2->stream_id());
+  EXPECT_EQ(0u, delegate2.stream_id());
   data.RunFor(3);
-  EXPECT_EQ(3u, stream2->stream_id());
+  EXPECT_EQ(NULL, stream2.get());
+  EXPECT_EQ(3u, delegate2.stream_id());
   EXPECT_EQ(1u, session->num_active_streams() + session->num_created_streams());
   EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST));
 
-  scoped_refptr<SpdyStream> stream3 = request3.ReleaseStream();
+  base::WeakPtr<SpdyStream> stream3 = request3.ReleaseStream();
   ASSERT_TRUE(stream3.get() != NULL);
+  test::StreamDelegateDoNothing delegate3(stream3);
+  stream3->SetDelegate(&delegate3);
   stream3->set_spdy_headers(headers3.Pass());
   EXPECT_TRUE(stream3->HasUrl());
   stream3->SendRequest(false);
 
-  EXPECT_EQ(0u, stream3->stream_id());
+  EXPECT_EQ(0u, delegate3.stream_id());
   data.RunFor(4);
-  EXPECT_EQ(5u, stream3->stream_id());
+  EXPECT_EQ(NULL, stream3.get());
+  EXPECT_EQ(5u, delegate3.stream_id());
   EXPECT_EQ(0u, session->num_active_streams() + session->num_created_streams());
   EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST));
 }
@@ -1608,7 +1622,7 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, LOWEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
@@ -1635,19 +1649,23 @@
   // Cancel the first stream, this will allow the second stream to be created.
   EXPECT_TRUE(spdy_stream1.get() != NULL);
   spdy_stream1->Cancel();
-  spdy_stream1 = NULL;
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
   callback2.WaitForResult();
   EXPECT_EQ(2u, session->num_active_streams() + session->num_created_streams());
   EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST));
 
   // Cancel the second stream, this will allow the third stream to be created.
-  request2.ReleaseStream()->Cancel();
+  base::WeakPtr<SpdyStream> spdy_stream2 = request2.ReleaseStream();
+  spdy_stream2->Cancel();
+  EXPECT_EQ(NULL, spdy_stream2.get());
   EXPECT_EQ(1u, session->num_active_streams() + session->num_created_streams());
   EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST));
 
   // Cancel the third stream.
-  request3.ReleaseStream()->Cancel();
+  base::WeakPtr<SpdyStream> spdy_stream3 = request3.ReleaseStream();
+  spdy_stream3->Cancel();
+  EXPECT_EQ(NULL, spdy_stream3.get());
   EXPECT_EQ(0u, session->num_active_streams() + session->num_created_streams());
   EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST));
 }
@@ -1765,10 +1783,12 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
+  spdy_stream1->SetDelegate(&delegate1);
 
   scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock);
   (*headers)["method"] = "GET";
@@ -1785,13 +1805,14 @@
   SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead");
 
   // Run until 1st read.
-  EXPECT_EQ(0u, spdy_stream1->stream_id());
+  EXPECT_EQ(0u, delegate1.stream_id());
   data.RunFor(2);
-  EXPECT_EQ(1u, spdy_stream1->stream_id());
+  EXPECT_EQ(1u, delegate1.stream_id());
   EXPECT_EQ(0u, observer.executed_count());
 
   // Read all the data and verify SpdySession::DoRead has not posted a task.
   data.RunFor(4);
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
   // Verify task observer's executed_count is zero, which indicates DoRead read
   // all the available data.
@@ -1855,10 +1876,12 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
+  spdy_stream1->SetDelegate(&delegate1);
 
   scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock);
   (*headers)["method"] = "GET";
@@ -1875,13 +1898,14 @@
   SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead");
 
   // Run until 1st read.
-  EXPECT_EQ(0u, spdy_stream1->stream_id());
+  EXPECT_EQ(0u, delegate1.stream_id());
   data.RunFor(2);
-  EXPECT_EQ(1u, spdy_stream1->stream_id());
+  EXPECT_EQ(1u, delegate1.stream_id());
   EXPECT_EQ(0u, observer.executed_count());
 
   // Read all the data and verify SpdySession::DoRead has posted a task.
   data.RunFor(6);
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
   // Verify task observer's executed_count is 1, which indicates DoRead has
   // posted only one task and thus yielded though there is data available for it
@@ -1968,10 +1992,12 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
+  spdy_stream1->SetDelegate(&delegate1);
 
   scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock);
   (*headers)["method"] = "GET";
@@ -1988,13 +2014,14 @@
   SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead");
 
   // Run until 1st read.
-  EXPECT_EQ(0u, spdy_stream1->stream_id());
+  EXPECT_EQ(0u, delegate1.stream_id());
   data.RunFor(2);
-  EXPECT_EQ(1u, spdy_stream1->stream_id());
+  EXPECT_EQ(1u, delegate1.stream_id());
   EXPECT_EQ(0u, observer.executed_count());
 
   // Read all the data and verify SpdySession::DoRead has posted a task.
   data.RunFor(12);
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
   // Verify task observer's executed_count is 1, which indicates DoRead has
   // posted only one task and thus yielded though there is data available for
@@ -2043,8 +2070,9 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog());
+  session = NULL;
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
 
@@ -2064,19 +2092,13 @@
   data.RunFor(1);
   EXPECT_EQ(1u, spdy_stream1->stream_id());
 
-  // Drop the reference to the session.
-  session = NULL;
+  // Only references to SpdySession are held by DoLoop and
+  // SpdySessionPool. If DoLoop doesn't hold the reference, we get a
+  // crash if SpdySession is deleted from the SpdySessionPool.
 
   // Run until GoAway.
-  data.RunFor(2);
-
-  // Drop the reference to the stream which deletes its reference to the
-  // SpdySession. Only references to SpdySession are held by DoLoop and
-  // SpdySessionPool. If DoLoop doesn't hold the reference, we get a crash if
-  // SpdySession is deleted from the SpdySessionPool.
-  spdy_stream1 = NULL;
-
-  data.RunFor(2);
+  data.RunFor(4);
+  EXPECT_EQ(NULL, spdy_stream1.get());
   EXPECT_TRUE(data.at_write_eof());
   EXPECT_TRUE(data.at_read_eof());
 }
diff --git a/net/spdy/spdy_session_spdy3_unittest.cc b/net/spdy/spdy_session_spdy3_unittest.cc
index 05190f1..6c7313a 100644
--- a/net/spdy/spdy_session_spdy3_unittest.cc
+++ b/net/spdy/spdy_session_spdy3_unittest.cc
@@ -223,10 +223,10 @@
   EXPECT_EQ(3, session->GetProtocolVersion());
 
   GURL url("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url, MEDIUM, BoundNetLog());
 
-  scoped_refptr<SpdyStream> spdy_stream2 =
+  base::WeakPtr<SpdyStream> spdy_stream2 =
       CreateStreamSynchronously(session, url, MEDIUM, BoundNetLog());
 
   scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock);
@@ -264,8 +264,7 @@
   scoped_refptr<SpdySession> session2 = GetSession(pair_);
 
   spdy_stream1->Close();
-  spdy_stream1 = NULL;
-  spdy_stream2 = NULL;
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
   // Delete the first session.
   session = NULL;
@@ -273,6 +272,7 @@
   // Delete the second session.
   spdy_session_pool_->Remove(session2);
   session2 = NULL;
+  EXPECT_EQ(NULL, spdy_stream2.get());
 }
 
 TEST_F(SpdySessionSpdy3Test, ClientPing) {
@@ -301,7 +301,7 @@
 
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   test::StreamDelegateSendImmediate delegate(
@@ -356,7 +356,7 @@
 
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   test::StreamDelegateSendImmediate delegate(
@@ -370,6 +370,7 @@
 
   // Delete the session.
   session = NULL;
+  EXPECT_EQ(NULL, spdy_stream1.get());
 }
 
 TEST_F(SpdySessionSpdy3Test, DeleteExpiredPushStreams) {
@@ -399,6 +400,7 @@
                      false, session->net_log_));
   stream->set_spdy_headers(request_headers.Pass());
   session->ActivateStream(stream);
+  stream = NULL;
 
   SpdyHeaderBlock headers;
   headers[":scheme"] = "http";
@@ -454,7 +456,7 @@
 
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   test::StreamDelegateSendImmediate delegate(
@@ -488,6 +490,7 @@
 
   // Delete the first session.
   session = NULL;
+  EXPECT_EQ(NULL, spdy_stream1.get());
 }
 
 TEST_F(SpdySessionSpdy3Test, CloseIdleSessions) {
@@ -512,7 +515,7 @@
       InitializeSession(
           http_session_.get(), session1.get(), test_host_port_pair1));
   GURL url1(kTestHost1);
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session1, url1, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
 
@@ -527,7 +530,7 @@
       InitializeSession(
           http_session_.get(), session2.get(), test_host_port_pair2));
   GURL url2(kTestHost2);
-  scoped_refptr<SpdyStream> spdy_stream2 =
+  base::WeakPtr<SpdyStream> spdy_stream2 =
       CreateStreamSynchronously(session2, url2, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream2.get() != NULL);
 
@@ -542,7 +545,7 @@
       InitializeSession(
           http_session_.get(), session3.get(), test_host_port_pair3));
   GURL url3(kTestHost3);
-  scoped_refptr<SpdyStream> spdy_stream3 =
+  base::WeakPtr<SpdyStream> spdy_stream3 =
       CreateStreamSynchronously(session3, url3, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream3.get() != NULL);
 
@@ -566,7 +569,9 @@
   // Make sessions 1 and 3 inactive, but keep them open.
   // Session 2 still open and active
   session1->CloseCreatedStream(spdy_stream1, OK);
+  EXPECT_EQ(NULL, spdy_stream1.get());
   session3->CloseCreatedStream(spdy_stream3, OK);
+  EXPECT_EQ(NULL, spdy_stream3.get());
   EXPECT_FALSE(session1->is_active());
   EXPECT_FALSE(session1->IsClosed());
   EXPECT_TRUE(session2->is_active());
@@ -590,6 +595,7 @@
 
   // Make 2 not active
   session2->CloseCreatedStream(spdy_stream2, OK);
+  EXPECT_EQ(NULL, spdy_stream2.get());
   EXPECT_FALSE(session2->is_active());
   EXPECT_FALSE(session2->IsClosed());
 
@@ -641,21 +647,17 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   // Create 2 streams.  First will succeed.  Second will be pending.
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
 
-  StreamReleaserCallback stream_releaser(session, spdy_stream1);
-
+  StreamReleaserCallback stream_releaser;
   SpdyStreamRequest request;
   ASSERT_EQ(ERR_IO_PENDING,
             request.StartRequest(session, test_url_, MEDIUM,
                                  BoundNetLog(),
                                  stream_releaser.MakeCallback(&request)));
-
-  // Make sure |stream_releaser| holds the last refs.
   session = NULL;
-  spdy_stream1 = NULL;
 
   EXPECT_EQ(OK, stream_releaser.WaitForResult());
 }
@@ -705,11 +707,11 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   // Create 2 streams.  First will succeed.  Second will be pending.
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
 
-  StreamReleaserCallback stream_releaser(session, spdy_stream1);
+  StreamReleaserCallback stream_releaser;
 
   SpdyStreamRequest request;
   ASSERT_EQ(ERR_IO_PENDING,
@@ -717,7 +719,6 @@
                                  BoundNetLog(),
                                  stream_releaser.MakeCallback(&request)));
 
-  spdy_stream1 = NULL;
   EXPECT_EQ(OK, stream_releaser.WaitForResult());
 
   // Make sure that persisted data is cleared.
@@ -726,8 +727,6 @@
 
   // Make sure session's max_concurrent_streams is 2.
   EXPECT_EQ(2u, session->max_concurrent_streams());
-
-  session = NULL;
 }
 
 // Start with max concurrent streams set to 1.  Request two streams.  When the
@@ -762,7 +761,7 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   // Create 2 streams.  First will succeed.  Second will be pending.
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
 
@@ -778,7 +777,7 @@
 
   // Release the first one, this will allow the second to be created.
   spdy_stream1->Cancel();
-  spdy_stream1 = NULL;
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
   request.CancelRequest();
   callback.reset();
@@ -1048,18 +1047,20 @@
       break;
     case SPDY_POOL_CLOSE_IDLE_SESSIONS:
       GURL url(test_hosts[0].url);
-      scoped_refptr<SpdyStream> spdy_stream =
+      base::WeakPtr<SpdyStream> spdy_stream =
           CreateStreamSynchronously(session, url, MEDIUM, BoundNetLog());
       GURL url1(test_hosts[1].url);
-      scoped_refptr<SpdyStream> spdy_stream1 =
+      base::WeakPtr<SpdyStream> spdy_stream1 =
           CreateStreamSynchronously(session1, url1, MEDIUM, BoundNetLog());
       GURL url2(test_hosts[2].url);
-      scoped_refptr<SpdyStream> spdy_stream2 =
+      base::WeakPtr<SpdyStream> spdy_stream2 =
           CreateStreamSynchronously(session2, url2, MEDIUM, BoundNetLog());
 
       // Close streams to make spdy_session and spdy_session1 inactive.
       session->CloseCreatedStream(spdy_stream, OK);
+      EXPECT_EQ(NULL, spdy_stream.get());
       session1->CloseCreatedStream(spdy_stream1, OK);
+      EXPECT_EQ(NULL, spdy_stream1.get());
 
       // Check spdy_session and spdy_session1 are not closed.
       EXPECT_FALSE(session->is_active());
@@ -1081,10 +1082,10 @@
       EXPECT_TRUE(session2->is_active());
       EXPECT_FALSE(session2->IsClosed());
 
-      spdy_stream = NULL;
-      spdy_stream1 = NULL;
       spdy_stream2->Cancel();
-      spdy_stream2 = NULL;
+      EXPECT_EQ(NULL, spdy_stream.get());
+      EXPECT_EQ(NULL, spdy_stream1.get());
+      EXPECT_EQ(NULL, spdy_stream2.get());
       spdy_session_pool->Remove(session2);
       session2 = NULL;
       break;
@@ -1265,15 +1266,19 @@
 
   GURL url("http://www.google.com");
 
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url, LOWEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
+  spdy_stream1->SetDelegate(&delegate1);
 
-  scoped_refptr<SpdyStream> spdy_stream2 =
+  base::WeakPtr<SpdyStream> spdy_stream2 =
       CreateStreamSynchronously(session, url, HIGHEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream2.get() != NULL);
   EXPECT_EQ(0u, spdy_stream2->stream_id());
+  test::StreamDelegateDoNothing delegate2(spdy_stream2);
+  spdy_stream2->SetDelegate(&delegate2);
 
   spdy_stream1->set_spdy_headers(
       spdy_util_.ConstructGetHeaderBlock(url.spec()));
@@ -1287,14 +1292,10 @@
   spdy_stream2->SendRequest(false);
   MessageLoop::current()->RunUntilIdle();
 
-  EXPECT_EQ(3u, spdy_stream1->stream_id());
-  EXPECT_EQ(1u, spdy_stream2->stream_id());
-
-  spdy_stream1->Cancel();
-  spdy_stream1 = NULL;
-
-  spdy_stream2->Cancel();
-  spdy_stream2 = NULL;
+  EXPECT_EQ(NULL, spdy_stream1.get());
+  EXPECT_EQ(NULL, spdy_stream2.get());
+  EXPECT_EQ(3u, delegate1.stream_id());
+  EXPECT_EQ(1u, delegate2.stream_id());
 }
 
 TEST_F(SpdySessionSpdy3Test, CancelStream) {
@@ -1329,16 +1330,20 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, HIGHEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
+  spdy_stream1->SetDelegate(&delegate1);
 
   GURL url2("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream2 =
+  base::WeakPtr<SpdyStream> spdy_stream2 =
       CreateStreamSynchronously(session, url2, LOWEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream2.get() != NULL);
   EXPECT_EQ(0u, spdy_stream2->stream_id());
+  test::StreamDelegateDoNothing delegate2(spdy_stream2);
+  spdy_stream2->SetDelegate(&delegate2);
 
   spdy_stream1->set_spdy_headers(
       spdy_util_.ConstructGetHeaderBlock(url1.spec()));
@@ -1354,17 +1359,17 @@
   EXPECT_EQ(0u, spdy_stream1->stream_id());
 
   spdy_stream1->Cancel();
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
-  EXPECT_EQ(0u, spdy_stream1->stream_id());
+  EXPECT_EQ(0u, delegate1.stream_id());
 
   data.RunFor(1);
 
-  EXPECT_EQ(0u, spdy_stream1->stream_id());
-  EXPECT_EQ(1u, spdy_stream2->stream_id());
+  EXPECT_EQ(0u, delegate1.stream_id());
+  EXPECT_EQ(1u, delegate2.stream_id());
 
-  spdy_stream1 = NULL;
   spdy_stream2->Cancel();
-  spdy_stream2 = NULL;
+  EXPECT_EQ(NULL, spdy_stream2.get());
 }
 
 TEST_F(SpdySessionSpdy3Test, CloseSessionWithTwoCreatedStreams) {
@@ -1395,13 +1400,13 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, HIGHEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
 
   GURL url2("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream2 =
+  base::WeakPtr<SpdyStream> spdy_stream2 =
       CreateStreamSynchronously(session, url2, LOWEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream2.get() != NULL);
   EXPECT_EQ(0u, spdy_stream2->stream_id());
@@ -1409,13 +1414,13 @@
   spdy_stream1->set_spdy_headers(
       spdy_util_.ConstructGetHeaderBlock(url1.spec()));
   EXPECT_TRUE(spdy_stream1->HasUrl());
-  test::ClosingDelegate delegate1(spdy_stream1.get());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
   spdy_stream1->SetDelegate(&delegate1);
 
   spdy_stream2->set_spdy_headers(
       spdy_util_.ConstructGetHeaderBlock(url2.spec()));
   EXPECT_TRUE(spdy_stream2->HasUrl());
-  test::ClosingDelegate delegate2(spdy_stream2.get());
+  test::StreamDelegateDoNothing delegate2(spdy_stream2);
   spdy_stream2->SetDelegate(&delegate2);
 
   spdy_stream1->SendRequest(false);
@@ -1428,11 +1433,12 @@
   // Ensure we don't crash while closing the session.
   session->CloseSessionOnError(ERR_ABORTED, true, std::string());
 
-  EXPECT_TRUE(spdy_stream1->closed());
-  EXPECT_TRUE(spdy_stream2->closed());
+  EXPECT_EQ(NULL, spdy_stream1.get());
+  EXPECT_EQ(NULL, spdy_stream2.get());
 
-  spdy_stream1 = NULL;
-  spdy_stream2 = NULL;
+  EXPECT_TRUE(delegate1.StreamIsClosed());
+  EXPECT_TRUE(delegate2.StreamIsClosed());
+
   session = NULL;
 }
 
@@ -1633,10 +1639,12 @@
   data.RunFor(1);
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, LOWEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
+  spdy_stream1->SetDelegate(&delegate1);
 
   TestCompletionCallback callback2;
   GURL url2("http://www.google.com");
@@ -1663,34 +1671,41 @@
   spdy_stream1->SendRequest(false);
 
   // Run until 1st stream is closed and 2nd one is opened.
-  EXPECT_EQ(0u, spdy_stream1->stream_id());
+  EXPECT_EQ(0u, delegate1.stream_id());
   data.RunFor(3);
-  EXPECT_EQ(1u, spdy_stream1->stream_id());
+  // Pump loop for SpdySession::ProcessPendingStreamRequests().
+  MessageLoop::current()->RunUntilIdle();
+  EXPECT_EQ(NULL, spdy_stream1.get());
+  EXPECT_EQ(1u, delegate1.stream_id());
   EXPECT_EQ(2u, session->num_active_streams() + session->num_created_streams());
   EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST));
 
-  scoped_refptr<SpdyStream> stream2 = request2.ReleaseStream();
-  stream2->set_spdy_headers(
-      spdy_util_.ConstructGetHeaderBlock(url2.spec()));
+  base::WeakPtr<SpdyStream> stream2 = request2.ReleaseStream();
+  test::StreamDelegateDoNothing delegate2(stream2);
+  stream2->SetDelegate(&delegate2);
+  stream2->set_spdy_headers(spdy_util_.ConstructGetHeaderBlock(url2.spec()));
   EXPECT_TRUE(stream2->HasUrl());
   stream2->SendRequest(false);
 
   // Run until 2nd stream is closed.
-  EXPECT_EQ(0u, stream2->stream_id());
+  EXPECT_EQ(0u, delegate2.stream_id());
   data.RunFor(3);
-  EXPECT_EQ(3u, stream2->stream_id());
+  EXPECT_EQ(NULL, stream2.get());
+  EXPECT_EQ(3u, delegate2.stream_id());
   EXPECT_EQ(1u, session->num_active_streams() + session->num_created_streams());
   EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST));
 
-  scoped_refptr<SpdyStream> stream3 = request3.ReleaseStream();
-  stream3->set_spdy_headers(
-      spdy_util_.ConstructGetHeaderBlock(url3.spec()));
+  base::WeakPtr<SpdyStream> stream3 = request3.ReleaseStream();
+  test::StreamDelegateDoNothing delegate3(stream3);
+  stream3->SetDelegate(&delegate3);
+  stream3->set_spdy_headers(spdy_util_.ConstructGetHeaderBlock(url3.spec()));
   EXPECT_TRUE(stream3->HasUrl());
   stream3->SendRequest(false);
 
-  EXPECT_EQ(0u, stream3->stream_id());
+  EXPECT_EQ(0u, delegate3.stream_id());
   data.RunFor(4);
-  EXPECT_EQ(5u, stream3->stream_id());
+  EXPECT_EQ(NULL, stream3.get());
+  EXPECT_EQ(5u, delegate3.stream_id());
   EXPECT_EQ(0u, session->num_active_streams() + session->num_created_streams());
   EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST));
 }
@@ -1723,7 +1738,7 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, LOWEST, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
@@ -1750,19 +1765,23 @@
   // Cancel the first stream, this will allow the second stream to be created.
   EXPECT_TRUE(spdy_stream1.get() != NULL);
   spdy_stream1->Cancel();
-  spdy_stream1 = NULL;
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
   callback2.WaitForResult();
   EXPECT_EQ(2u, session->num_active_streams() + session->num_created_streams());
   EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST));
 
   // Cancel the second stream, this will allow the third stream to be created.
-  request2.ReleaseStream()->Cancel();
+  base::WeakPtr<SpdyStream> spdy_stream2 = request2.ReleaseStream();
+  spdy_stream2->Cancel();
+  EXPECT_EQ(NULL, spdy_stream2.get());
   EXPECT_EQ(1u, session->num_active_streams() + session->num_created_streams());
   EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST));
 
   // Cancel the third stream.
-  request3.ReleaseStream()->Cancel();
+  base::WeakPtr<SpdyStream> spdy_stream3 = request3.ReleaseStream();
+  spdy_stream3->Cancel();
+  EXPECT_EQ(NULL, spdy_stream3.get());
   EXPECT_EQ(0u, session->num_active_streams() + session->num_created_streams());
   EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST));
 }
@@ -1919,7 +1938,7 @@
   CreateDeterministicNetworkSession();
 
   scoped_refptr<SpdySession> session = CreateInitializedSession();
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   TestCompletionCallback callback1;
@@ -1932,14 +1951,14 @@
 
   // Release the first one, this will allow the second to be created.
   spdy_stream1->Cancel();
-  spdy_stream1 = NULL;
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
-  scoped_refptr<SpdyStream> spdy_stream2 =
+  base::WeakPtr<SpdyStream> spdy_stream2 =
       CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream2.get() != NULL);
   EXPECT_EQ(spdy_stream2->send_window_size(), window_size);
   spdy_stream2->Cancel();
-  spdy_stream2 = NULL;
+  EXPECT_EQ(NULL, spdy_stream2.get());
 }
 
 // Test that SpdySession::DoRead reads data from the socket without yielding.
@@ -1996,10 +2015,12 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
+  spdy_stream1->SetDelegate(&delegate1);
 
   spdy_stream1->set_spdy_headers(
       spdy_util_.ConstructGetHeaderBlock(url1.spec()));
@@ -2010,13 +2031,14 @@
   SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead");
 
   // Run until 1st read.
-  EXPECT_EQ(0u, spdy_stream1->stream_id());
+  EXPECT_EQ(0u, delegate1.stream_id());
   data.RunFor(2);
-  EXPECT_EQ(1u, spdy_stream1->stream_id());
+  EXPECT_EQ(1u, delegate1.stream_id());
   EXPECT_EQ(0u, observer.executed_count());
 
   // Read all the data and verify SpdySession::DoRead has not posted a task.
   data.RunFor(4);
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
   // Verify task observer's executed_count is zero, which indicates DoRead read
   // all the available data.
@@ -2080,27 +2102,31 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
+  spdy_stream1->SetDelegate(&delegate1);
 
   spdy_stream1->set_spdy_headers(
       spdy_util_.ConstructGetHeaderBlock(url1.spec()));
   EXPECT_TRUE(spdy_stream1->HasUrl());
   spdy_stream1->SendRequest(false);
+  spdy_stream1.reset();
 
   // Set up the TaskObserver to verify SpdySession::DoRead posts a task.
   SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead");
 
   // Run until 1st read.
-  EXPECT_EQ(0u, spdy_stream1->stream_id());
+  EXPECT_EQ(0u, delegate1.stream_id());
   data.RunFor(2);
-  EXPECT_EQ(1u, spdy_stream1->stream_id());
+  EXPECT_EQ(1u, delegate1.stream_id());
   EXPECT_EQ(0u, observer.executed_count());
 
   // Read all the data and verify SpdySession::DoRead has posted a task.
   data.RunFor(6);
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
   // Verify task observer's executed_count is 1, which indicates DoRead has
   // posted only one task and thus yielded though there is data available for it
@@ -2187,10 +2213,12 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog());
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
+  test::StreamDelegateDoNothing delegate1(spdy_stream1);
+  spdy_stream1->SetDelegate(&delegate1);
 
   spdy_stream1->set_spdy_headers(
       spdy_util_.ConstructGetHeaderBlock(url1.spec()));
@@ -2201,13 +2229,14 @@
   SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead");
 
   // Run until 1st read.
-  EXPECT_EQ(0u, spdy_stream1->stream_id());
+  EXPECT_EQ(0u, delegate1.stream_id());
   data.RunFor(2);
-  EXPECT_EQ(1u, spdy_stream1->stream_id());
+  EXPECT_EQ(1u, delegate1.stream_id());
   EXPECT_EQ(0u, observer.executed_count());
 
   // Read all the data and verify SpdySession::DoRead has posted a task.
   data.RunFor(12);
+  EXPECT_EQ(NULL, spdy_stream1.get());
 
   // Verify task observer's executed_count is 1, which indicates DoRead has
   // posted only one task and thus yielded though there is data available for
@@ -2256,8 +2285,9 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url1("http://www.google.com");
-  scoped_refptr<SpdyStream> spdy_stream1 =
+  base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog());
+  session = NULL;
   ASSERT_TRUE(spdy_stream1.get() != NULL);
   EXPECT_EQ(0u, spdy_stream1->stream_id());
 
@@ -2271,19 +2301,13 @@
   data.RunFor(1);
   EXPECT_EQ(1u, spdy_stream1->stream_id());
 
-  // Drop the reference to the session.
-  session = NULL;
+  // Only references to SpdySession are held by DoLoop and
+  // SpdySessionPool. If DoLoop doesn't hold the reference, we get a
+  // crash if SpdySession is deleted from the SpdySessionPool.
 
   // Run until GoAway.
-  data.RunFor(2);
-
-  // Drop the reference to the stream which deletes its reference to the
-  // SpdySession. Only references to SpdySession are held by DoLoop and
-  // SpdySessionPool. If DoLoop doesn't hold the reference, we get a crash if
-  // SpdySession is deleted from the SpdySessionPool.
-  spdy_stream1 = NULL;
-
-  data.RunFor(2);
+  data.RunFor(4);
+  EXPECT_EQ(NULL, spdy_stream1.get());
   EXPECT_TRUE(data.at_write_eof());
   EXPECT_TRUE(data.at_read_eof());
 }
@@ -2540,7 +2564,7 @@
 // A delegate that drops any received data.
 class DropReceivedDataDelegate : public test::StreamDelegateSendImmediate {
  public:
-  DropReceivedDataDelegate(const scoped_refptr<SpdyStream>& stream,
+  DropReceivedDataDelegate(const base::WeakPtr<SpdyStream>& stream,
                            base::StringPiece data)
       : StreamDelegateSendImmediate(
           stream, scoped_ptr<SpdyHeaderBlock>(), data) {}
@@ -2607,7 +2631,7 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url(kStreamUrl);
-  scoped_refptr<SpdyStream> stream =
+  base::WeakPtr<SpdyStream> stream =
       CreateStreamSynchronously(session, url, MEDIUM, BoundNetLog());
   ASSERT_TRUE(stream.get() != NULL);
   EXPECT_EQ(0u, stream->stream_id());
@@ -2632,6 +2656,7 @@
   EXPECT_EQ(msg_data_size, session->session_unacked_recv_window_bytes_);
 
   stream->Close();
+  EXPECT_EQ(NULL, stream.get());
 
   EXPECT_EQ(OK, delegate.WaitForClose());
 
@@ -2684,13 +2709,13 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url(kStreamUrl);
-  scoped_refptr<SpdyStream> stream =
+  base::WeakPtr<SpdyStream> stream =
       CreateStreamSynchronously(session, url, MEDIUM, BoundNetLog());
   ASSERT_TRUE(stream.get() != NULL);
   EXPECT_EQ(0u, stream->stream_id());
 
   test::StreamDelegateSendImmediate delegate(
-      stream.get(), scoped_ptr<SpdyHeaderBlock>(), msg_data);
+      stream, scoped_ptr<SpdyHeaderBlock>(), msg_data);
   stream->SetDelegate(&delegate);
 
   stream->set_spdy_headers(
@@ -2714,6 +2739,7 @@
 
   // Closing the stream should increase the session's send window.
   stream->Close();
+  EXPECT_EQ(NULL, stream.get());
 
   EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_send_window_size_);
 
@@ -2774,13 +2800,13 @@
   scoped_refptr<SpdySession> session = CreateInitializedSession();
 
   GURL url(kStreamUrl);
-  scoped_refptr<SpdyStream> stream =
+  base::WeakPtr<SpdyStream> stream =
       CreateStreamSynchronously(session, url, MEDIUM, BoundNetLog());
   ASSERT_TRUE(stream.get() != NULL);
   EXPECT_EQ(0u, stream->stream_id());
 
   test::StreamDelegateSendImmediate delegate(
-      stream.get(), scoped_ptr<SpdyHeaderBlock>(), msg_data);
+      stream, scoped_ptr<SpdyHeaderBlock>(), msg_data);
   stream->SetDelegate(&delegate);
 
   stream->set_spdy_headers(
@@ -2839,6 +2865,7 @@
   EXPECT_EQ(msg_data_size, session->session_unacked_recv_window_bytes_);
 
   stream->Close();
+  EXPECT_EQ(NULL, stream.get());
 
   EXPECT_EQ(OK, delegate.WaitForClose());
 
@@ -2895,11 +2922,11 @@
   EXPECT_EQ(SpdySession::FLOW_CONTROL_STREAM_AND_SESSION,
             session->flow_control_state());
 
-  scoped_refptr<SpdyStream> stream =
+  base::WeakPtr<SpdyStream> stream =
       CreateStreamSynchronously(session, url, LOWEST, BoundNetLog());
   ASSERT_TRUE(stream.get() != NULL);
 
-  test::StreamDelegateWithBody delegate(stream.get(), kBodyDataStringPiece);
+  test::StreamDelegateWithBody delegate(stream, kBodyDataStringPiece);
   stream->SetDelegate(&delegate);
 
   EXPECT_FALSE(stream->HasUrl());
@@ -3051,11 +3078,11 @@
   EXPECT_EQ(SpdySession::FLOW_CONTROL_STREAM_AND_SESSION,
             session->flow_control_state());
 
-  scoped_refptr<SpdyStream> stream1 =
+  base::WeakPtr<SpdyStream> stream1 =
       CreateStreamSynchronously(session, url, LOWEST, BoundNetLog());
   ASSERT_TRUE(stream1.get() != NULL);
 
-  test::StreamDelegateWithBody delegate1(stream1.get(), kBodyDataStringPiece);
+  test::StreamDelegateWithBody delegate1(stream1, kBodyDataStringPiece);
   stream1->SetDelegate(&delegate1);
 
   EXPECT_FALSE(stream1->HasUrl());
@@ -3070,11 +3097,11 @@
   data.RunFor(3);
   EXPECT_EQ(1u, stream1->stream_id());
 
-  scoped_refptr<SpdyStream> stream2 =
+  base::WeakPtr<SpdyStream> stream2 =
       CreateStreamSynchronously(session, url, MEDIUM, BoundNetLog());
   ASSERT_TRUE(stream2.get() != NULL);
 
-  test::StreamDelegateWithBody delegate2(stream2.get(), kBodyDataStringPiece);
+  test::StreamDelegateWithBody delegate2(stream2, kBodyDataStringPiece);
   stream2->SetDelegate(&delegate2);
 
   EXPECT_FALSE(stream2->HasUrl());
@@ -3138,13 +3165,13 @@
 // Delegate that closes a given stream after sending its body.
 class StreamClosingDelegate : public test::StreamDelegateWithBody {
  public:
-  StreamClosingDelegate(const scoped_refptr<SpdyStream>& stream,
+  StreamClosingDelegate(const base::WeakPtr<SpdyStream>& stream,
                         base::StringPiece data)
       : StreamDelegateWithBody(stream, data) {}
 
   virtual ~StreamClosingDelegate() {}
 
-  void set_stream_to_close(const scoped_refptr<SpdyStream>& stream_to_close) {
+  void set_stream_to_close(const base::WeakPtr<SpdyStream>& stream_to_close) {
     stream_to_close_ = stream_to_close;
   }
 
@@ -3152,13 +3179,13 @@
     int rv = test::StreamDelegateWithBody::OnSendBody();
     if (stream_to_close_) {
       stream_to_close_->Close();
-      stream_to_close_ = NULL;
+      EXPECT_EQ(NULL, stream_to_close_.get());
     }
     return rv;
   }
 
  private:
-  scoped_refptr<SpdyStream> stream_to_close_;
+  base::WeakPtr<SpdyStream> stream_to_close_;
 };
 
 // Cause a stall by reducing the flow control send window to
@@ -3217,11 +3244,11 @@
   EXPECT_EQ(SpdySession::FLOW_CONTROL_STREAM_AND_SESSION,
             session->flow_control_state());
 
-  scoped_refptr<SpdyStream> stream1 =
+  base::WeakPtr<SpdyStream> stream1 =
       CreateStreamSynchronously(session, url, LOWEST, BoundNetLog());
   ASSERT_TRUE(stream1.get() != NULL);
 
-  test::StreamDelegateWithBody delegate1(stream1.get(), kBodyDataStringPiece);
+  test::StreamDelegateWithBody delegate1(stream1, kBodyDataStringPiece);
   stream1->SetDelegate(&delegate1);
 
   EXPECT_FALSE(stream1->HasUrl());
@@ -3236,11 +3263,11 @@
   data.RunFor(3);
   EXPECT_EQ(1u, stream1->stream_id());
 
-  scoped_refptr<SpdyStream> stream2 =
+  base::WeakPtr<SpdyStream> stream2 =
       CreateStreamSynchronously(session, url, LOWEST, BoundNetLog());
   ASSERT_TRUE(stream2.get() != NULL);
 
-  StreamClosingDelegate delegate2(stream2.get(), kBodyDataStringPiece);
+  StreamClosingDelegate delegate2(stream2, kBodyDataStringPiece);
   stream2->SetDelegate(&delegate2);
 
   EXPECT_FALSE(stream2->HasUrl());
@@ -3255,11 +3282,11 @@
   data.RunFor(2);
   EXPECT_EQ(3u, stream2->stream_id());
 
-  scoped_refptr<SpdyStream> stream3 =
+  base::WeakPtr<SpdyStream> stream3 =
       CreateStreamSynchronously(session, url, LOWEST, BoundNetLog());
   ASSERT_TRUE(stream3.get() != NULL);
 
-  test::StreamDelegateWithBody delegate3(stream3.get(), kBodyDataStringPiece);
+  test::StreamDelegateWithBody delegate3(stream3, kBodyDataStringPiece);
   stream3->SetDelegate(&delegate3);
 
   EXPECT_FALSE(stream3->HasUrl());
@@ -3293,8 +3320,8 @@
   SpdyStreamId stream_id3 = stream3->stream_id();
 
   // Close stream1 preemptively.
-  stream1 = NULL;
   session->CloseStream(stream_id1, ERR_CONNECTION_CLOSED);
+  EXPECT_EQ(NULL, stream1.get());
 
   EXPECT_FALSE(session->IsStreamActive(stream_id1));
   EXPECT_TRUE(session->IsStreamActive(stream_id2));
@@ -3302,8 +3329,8 @@
 
   // Unstall stream2, which should then close stream3.
   delegate2.set_stream_to_close(stream3);
-  stream3 = NULL;
   UnstallSessionSend(session, kBodyDataSize);
+  EXPECT_EQ(NULL, stream3.get());
 
   EXPECT_FALSE(stream2->send_stalled_by_flow_control());
   EXPECT_FALSE(session->IsStreamActive(stream_id1));
@@ -3311,6 +3338,7 @@
   EXPECT_FALSE(session->IsStreamActive(stream_id3));
 
   data.RunFor(3);
+  EXPECT_EQ(NULL, stream2.get());
 
   EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate1.WaitForClose());
   EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate2.WaitForClose());
@@ -3338,7 +3366,7 @@
 // Delegate that closes a given session after sending its body.
 class SessionClosingDelegate : public test::StreamDelegateWithBody {
  public:
-  SessionClosingDelegate(const scoped_refptr<SpdyStream>& stream,
+  SessionClosingDelegate(const base::WeakPtr<SpdyStream>& stream,
                          base::StringPiece data)
       : StreamDelegateWithBody(stream, data) {}
 
@@ -3414,11 +3442,11 @@
   EXPECT_EQ(SpdySession::FLOW_CONTROL_STREAM_AND_SESSION,
             session->flow_control_state());
 
-  scoped_refptr<SpdyStream> stream1 =
+  base::WeakPtr<SpdyStream> stream1 =
       CreateStreamSynchronously(session, url, LOWEST, BoundNetLog());
   ASSERT_TRUE(stream1.get() != NULL);
 
-  SessionClosingDelegate delegate1(stream1.get(), kBodyDataStringPiece);
+  SessionClosingDelegate delegate1(stream1, kBodyDataStringPiece);
   stream1->SetDelegate(&delegate1);
 
   EXPECT_FALSE(stream1->HasUrl());
@@ -3433,11 +3461,11 @@
   data.RunFor(3);
   EXPECT_EQ(1u, stream1->stream_id());
 
-  scoped_refptr<SpdyStream> stream2 =
+  base::WeakPtr<SpdyStream> stream2 =
       CreateStreamSynchronously(session, url, LOWEST, BoundNetLog());
   ASSERT_TRUE(stream2.get() != NULL);
 
-  test::StreamDelegateWithBody delegate2(stream2.get(), kBodyDataStringPiece);
+  test::StreamDelegateWithBody delegate2(stream2, kBodyDataStringPiece);
   stream2->SetDelegate(&delegate2);
 
   EXPECT_FALSE(stream2->HasUrl());
@@ -3467,8 +3495,6 @@
 
   // Unstall stream1, which should then close the session.
   delegate1.set_session_to_close(session);
-  stream1 = NULL;
-  stream2 = NULL;
   UnstallSessionSend(session, kBodyDataSize);
   session = NULL;
 
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc
index 5a38a89a..a4ba04c 100644
--- a/net/spdy/spdy_stream.cc
+++ b/net/spdy/spdy_stream.cc
@@ -132,7 +132,6 @@
       response_(new SpdyHeaderBlock),
       io_state_(STATE_NONE),
       response_status_(OK),
-      cancelled_(false),
       has_upload_data_(false),
       net_log_(net_log),
       send_bytes_(0),
@@ -153,14 +152,16 @@
   if (pushed_) {
     CHECK(response_received());
     MessageLoop::current()->PostTask(
-        FROM_HERE, base::Bind(&SpdyStream::PushedStreamReplayData, this));
+        FROM_HERE,
+        base::Bind(&SpdyStream::PushedStreamReplayData,
+                   weak_ptr_factory_.GetWeakPtr()));
   } else {
     continue_buffering_data_ = false;
   }
 }
 
 void SpdyStream::PushedStreamReplayData() {
-  if (cancelled_ || !delegate_)
+  if (!delegate_)
     return;
 
   continue_buffering_data_ = false;
@@ -211,7 +212,6 @@
 
 scoped_ptr<SpdyFrame> SpdyStream::ProduceHeaderFrame(
     scoped_ptr<SpdyHeaderBlock> header_block) {
-  CHECK(!cancelled());
   // We must need to write stream data.
   // Until the headers have been completely sent, we can not be sure
   // that our stream_id is correct.
@@ -226,9 +226,9 @@
 }
 
 void SpdyStream::DetachDelegate() {
+  DCHECK(!closed());
   delegate_ = NULL;
-  if (!closed())
-    Cancel();
+  Cancel();
 }
 
 const SpdyHeaderBlock& SpdyStream::spdy_headers() const {
@@ -438,7 +438,6 @@
     if (ContainsUpperAscii(it->first)) {
       session_->ResetStream(stream_id_, priority_, RST_STREAM_PROTOCOL_ERROR,
                             "Upper case characters in header: " + it->first);
-      response_status_ = ERR_SPDY_PROTOCOL_ERROR;
       return ERR_SPDY_PROTOCOL_ERROR;
     }
   }
@@ -474,7 +473,6 @@
     if (ContainsUpperAscii(it->first)) {
       session_->ResetStream(stream_id_, priority_, RST_STREAM_PROTOCOL_ERROR,
                             "Upper case characters in header: " + it->first);
-      response_status_ = ERR_SPDY_PROTOCOL_ERROR;
       return ERR_SPDY_PROTOCOL_ERROR;
     }
 
@@ -561,7 +559,7 @@
     NOTREACHED();
     return;
   }
-  if (cancelled() || closed())
+  if (closed())
     return;
   just_completed_frame_type_ = frame_type;
   just_completed_frame_size_ = frame_size;
@@ -588,22 +586,20 @@
 }
 
 void SpdyStream::Cancel() {
-  if (cancelled())
-    return;
-
-  cancelled_ = true;
-  if (session_->IsStreamActive(stream_id_))
+  if (stream_id_ != 0) {
     session_->ResetStream(stream_id_, priority_,
                           RST_STREAM_CANCEL, std::string());
-  else if (stream_id_ == 0)
-    session_->CloseCreatedStream(this, RST_STREAM_CANCEL);
+  } else {
+    session_->CloseCreatedStream(GetWeakPtr(), RST_STREAM_CANCEL);
+  }
 }
 
 void SpdyStream::Close() {
-  if (stream_id_ != 0)
+  if (stream_id_ != 0) {
     session_->CloseStream(stream_id_, OK);
-  else
-    session_->CloseCreatedStream(this, OK);
+  } else {
+    session_->CloseCreatedStream(GetWeakPtr(), OK);
+  }
 }
 
 int SpdyStream::SendRequest(bool has_upload_data) {
@@ -643,7 +639,6 @@
   // that our stream_id is correct.
   DCHECK_GT(io_state_, STATE_SEND_HEADERS_COMPLETE);
   CHECK_GT(stream_id_, 0u);
-  CHECK(!cancelled());
 
   scoped_ptr<SpdyBuffer> data_buffer(session_->CreateDataBuffer(
       stream_id_, data, length, flags));
@@ -698,6 +693,10 @@
   }
 }
 
+base::WeakPtr<SpdyStream> SpdyStream::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
 bool SpdyStream::HasUrl() const {
   if (pushed_)
     return response_received();
@@ -869,7 +868,6 @@
 }
 
 int SpdyStream::DoSendHeaders() {
-  CHECK(!cancelled_);
   io_state_ = STATE_SEND_HEADERS_COMPLETE;
 
   session_->EnqueueStreamWrite(
diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h
index d3ef69f..7e8c5758 100644
--- a/net/spdy/spdy_stream.h
+++ b/net/spdy/spdy_stream.h
@@ -119,8 +119,8 @@
   void SetDelegate(Delegate* delegate);
   Delegate* GetDelegate() { return delegate_; }
 
-  // Detach delegate from the stream. It will cancel the stream if it was not
-  // cancelled yet.  It is safe to call multiple times.
+  // Detach the delegate from the stream, which must not yet be
+  // closed, and cancel it.
   void DetachDelegate();
 
   // Is this stream a pushed stream from the server.
@@ -264,10 +264,19 @@
   // Called by the SpdySession to log stream related errors.
   void LogStreamError(int status, const std::string& description);
 
+  // If this stream is active, reset it, and close it otherwise. In
+  // either case the stream is deleted.
   void Cancel();
+
+  // Close this stream without sending a RST_STREAM and delete
+  // it.
   void Close();
-  bool cancelled() const { return cancelled_; }
+
+  // Returns whether or not this stream is closed. Note that the only
+  // time a stream is closed and not deleted is in its delegate's
+  // OnClose() method.
   bool closed() const { return io_state_ == STATE_DONE; }
+
   // TODO(satorux): This is only for testing. We should be able to remove
   // this once crbug.com/113107 is addressed.
   bool body_sent() const { return io_state_ > STATE_SEND_BODY_COMPLETE; }
@@ -302,6 +311,9 @@
   // called only when the stream is still open.
   void PossiblyResumeIfSendStalled();
 
+  // Must be used only by the SpdySession.
+  base::WeakPtr<SpdyStream> GetWeakPtr();
+
   bool is_idle() const {
     return io_state_ == STATE_OPEN || io_state_ == STATE_DONE;
   }
@@ -418,7 +430,6 @@
   // Not valid until the stream is closed.
   int response_status_;
 
-  bool cancelled_;
   bool has_upload_data_;
 
   BoundNetLog net_log_;
diff --git a/net/spdy/spdy_stream_spdy2_unittest.cc b/net/spdy/spdy_stream_spdy2_unittest.cc
index 0e14c4b8..7d5c1ac 100644
--- a/net/spdy/spdy_stream_spdy2_unittest.cc
+++ b/net/spdy/spdy_stream_spdy2_unittest.cc
@@ -116,12 +116,12 @@
 
   InitializeSpdySession(session, host_port_pair_);
 
-  scoped_refptr<SpdyStream> stream =
+  base::WeakPtr<SpdyStream> stream =
       CreateStreamSynchronously(session, url, LOWEST, BoundNetLog());
   ASSERT_TRUE(stream.get() != NULL);
 
   StreamDelegateSendImmediate delegate(
-      stream.get(), scoped_ptr<SpdyHeaderBlock>(), kPostBodyStringPiece);
+      stream, scoped_ptr<SpdyHeaderBlock>(), kPostBodyStringPiece);
   stream->SetDelegate(&delegate);
 
   EXPECT_FALSE(stream->HasUrl());
@@ -186,7 +186,7 @@
   HostPortPair host_port_pair("server.example.com", 80);
   InitializeSpdySession(session, host_port_pair);
 
-  scoped_refptr<SpdyStream> stream =
+  base::WeakPtr<SpdyStream> stream =
       CreateStreamSynchronously(session, url, HIGHEST, BoundNetLog());
   ASSERT_TRUE(stream.get() != NULL);
   scoped_ptr<SpdyHeaderBlock> message_headers(new SpdyHeaderBlock);
@@ -195,7 +195,7 @@
   (*message_headers)["fin"] = "1";
 
   StreamDelegateSendImmediate delegate(
-      stream.get(), message_headers.Pass(), base::StringPiece("hello!", 6));
+      stream, message_headers.Pass(), base::StringPiece("hello!", 6));
   stream->SetDelegate(&delegate);
 
   EXPECT_FALSE(stream->HasUrl());
@@ -308,12 +308,12 @@
 
   InitializeSpdySession(session, host_port_pair_);
 
-  scoped_refptr<SpdyStream> stream =
+  base::WeakPtr<SpdyStream> stream =
       CreateStreamSynchronously(session, url, LOWEST, log.bound());
   ASSERT_TRUE(stream.get() != NULL);
 
   StreamDelegateSendImmediate delegate(
-      stream.get(), scoped_ptr<SpdyHeaderBlock>(), kPostBodyStringPiece);
+      stream, scoped_ptr<SpdyHeaderBlock>(), kPostBodyStringPiece);
   stream->SetDelegate(&delegate);
 
   EXPECT_FALSE(stream->HasUrl());
@@ -327,7 +327,7 @@
 
   EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate.WaitForClose());
 
-  const SpdyStreamId stream_id = stream->stream_id();
+  const SpdyStreamId stream_id = delegate.stream_id();
 
   EXPECT_TRUE(delegate.send_headers_completed());
   EXPECT_EQ("200", delegate.GetResponseHeaderValue("status"));
diff --git a/net/spdy/spdy_stream_spdy3_unittest.cc b/net/spdy/spdy_stream_spdy3_unittest.cc
index 643cfd8..cb08736 100644
--- a/net/spdy/spdy_stream_spdy3_unittest.cc
+++ b/net/spdy/spdy_stream_spdy3_unittest.cc
@@ -113,12 +113,12 @@
 
   InitializeSpdySession(session, host_port_pair_);
 
-  scoped_refptr<SpdyStream> stream =
+  base::WeakPtr<SpdyStream> stream =
       CreateStreamSynchronously(session, url, LOWEST, BoundNetLog());
   ASSERT_TRUE(stream.get() != NULL);
 
   StreamDelegateSendImmediate delegate(
-      stream.get(), scoped_ptr<SpdyHeaderBlock>(), kPostBodyStringPiece);
+      stream, scoped_ptr<SpdyHeaderBlock>(), kPostBodyStringPiece);
   stream->SetDelegate(&delegate);
 
   EXPECT_FALSE(stream->HasUrl());
@@ -183,7 +183,7 @@
   HostPortPair host_port_pair("server.example.com", 80);
   InitializeSpdySession(session, host_port_pair);
 
-  scoped_refptr<SpdyStream> stream =
+  base::WeakPtr<SpdyStream> stream =
       CreateStreamSynchronously(session, url, HIGHEST, BoundNetLog());
   ASSERT_TRUE(stream.get() != NULL);
   TestCompletionCallback callback;
@@ -193,7 +193,7 @@
   (*message_headers)[":fin"] = "1";
 
   StreamDelegateSendImmediate delegate(
-      stream.get(), message_headers.Pass(), base::StringPiece("hello!", 6));
+      stream, message_headers.Pass(), base::StringPiece("hello!", 6));
   stream->SetDelegate(&delegate);
 
   EXPECT_FALSE(stream->HasUrl());
@@ -309,12 +309,12 @@
 
   InitializeSpdySession(session, host_port_pair_);
 
-  scoped_refptr<SpdyStream> stream =
+  base::WeakPtr<SpdyStream> stream =
       CreateStreamSynchronously(session, url, LOWEST, log.bound());
   ASSERT_TRUE(stream.get() != NULL);
 
   StreamDelegateSendImmediate delegate(
-      stream.get(), scoped_ptr<SpdyHeaderBlock>(), kPostBodyStringPiece);
+      stream, scoped_ptr<SpdyHeaderBlock>(), kPostBodyStringPiece);
   stream->SetDelegate(&delegate);
 
   EXPECT_FALSE(stream->HasUrl());
@@ -328,7 +328,7 @@
 
   EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate.WaitForClose());
 
-  const SpdyStreamId stream_id = stream->stream_id();
+  const SpdyStreamId stream_id = delegate.stream_id();
 
   EXPECT_TRUE(delegate.send_headers_completed());
   EXPECT_EQ("200", delegate.GetResponseHeaderValue(":status"));
@@ -385,12 +385,12 @@
 
   InitializeSpdySession(session, host_port_pair_);
 
-  scoped_refptr<SpdyStream> stream =
+  base::WeakPtr<SpdyStream> stream =
       CreateStreamSynchronously(session, url, LOWEST, log.bound());
   ASSERT_TRUE(stream.get() != NULL);
 
   StreamDelegateSendImmediate delegate(
-      stream.get(), scoped_ptr<SpdyHeaderBlock>(), kPostBodyStringPiece);
+      stream, scoped_ptr<SpdyHeaderBlock>(), kPostBodyStringPiece);
   stream->SetDelegate(&delegate);
 
   EXPECT_FALSE(stream->HasUrl());
@@ -443,11 +443,11 @@
 
   InitializeSpdySession(session, host_port_pair_);
 
-  scoped_refptr<SpdyStream> stream =
+  base::WeakPtr<SpdyStream> stream =
       CreateStreamSynchronously(session, url, LOWEST, BoundNetLog());
   ASSERT_TRUE(stream.get() != NULL);
 
-  StreamDelegateWithBody delegate(stream.get(), kPostBodyStringPiece);
+  StreamDelegateWithBody delegate(stream, kPostBodyStringPiece);
   stream->SetDelegate(&delegate);
 
   EXPECT_FALSE(stream->HasUrl());
@@ -527,11 +527,11 @@
 
   InitializeSpdySession(session, host_port_pair_);
 
-  scoped_refptr<SpdyStream> stream =
+  base::WeakPtr<SpdyStream> stream =
       CreateStreamSynchronously(session, url, LOWEST, BoundNetLog());
   ASSERT_TRUE(stream.get() != NULL);
 
-  StreamDelegateWithBody delegate(stream.get(), kPostBodyStringPiece);
+  StreamDelegateWithBody delegate(stream, kPostBodyStringPiece);
   stream->SetDelegate(&delegate);
 
   EXPECT_FALSE(stream->HasUrl());
diff --git a/net/spdy/spdy_stream_test_util.cc b/net/spdy/spdy_stream_test_util.cc
index 4af3382..76597ab2 100644
--- a/net/spdy/spdy_stream_test_util.cc
+++ b/net/spdy/spdy_stream_test_util.cc
@@ -16,7 +16,7 @@
 namespace test {
 
 ClosingDelegate::ClosingDelegate(
-    const scoped_refptr<SpdyStream>& stream) : stream_(stream) {}
+    const base::WeakPtr<SpdyStream>& stream) : stream_(stream) {}
 
 ClosingDelegate::~ClosingDelegate() {}
 
@@ -47,14 +47,15 @@
 void ClosingDelegate::OnDataSent(size_t bytes_sent) {}
 
 void ClosingDelegate::OnClose(int status) {
-  if (stream_)
-    stream_->Close();
-  stream_ = NULL;
+  DCHECK(stream_);
+  stream_->Close();
+  DCHECK(!stream_);
 }
 
 StreamDelegateBase::StreamDelegateBase(
-    const scoped_refptr<SpdyStream>& stream)
+    const base::WeakPtr<SpdyStream>& stream)
     : stream_(stream),
+      stream_id_(0),
       send_headers_completed_(false),
       headers_sent_(0),
       data_sent_(0) {
@@ -64,6 +65,8 @@
 }
 
 SpdySendStatus StreamDelegateBase::OnSendHeadersComplete() {
+  stream_id_ = stream_->stream_id();
+  EXPECT_NE(stream_id_, 0u);
   send_headers_completed_ = true;
   return NO_MORE_DATA_TO_SEND;
 }
@@ -93,7 +96,8 @@
 void StreamDelegateBase::OnClose(int status) {
   if (!stream_)
     return;
-  stream_ = NULL;
+  stream_id_ = stream_->stream_id();
+  stream_.reset();
   callback_.callback().Run(status);
 }
 
@@ -120,8 +124,23 @@
   return (it == response_.end()) ? std::string() : it->second;
 }
 
+StreamDelegateDoNothing::StreamDelegateDoNothing(
+    const base::WeakPtr<SpdyStream>& stream)
+    : StreamDelegateBase(stream) {}
+
+StreamDelegateDoNothing::~StreamDelegateDoNothing() {
+}
+
+int StreamDelegateDoNothing::OnSendBody() {
+  return OK;
+}
+SpdySendStatus StreamDelegateDoNothing::OnSendBodyComplete(
+    size_t /*bytes_sent*/) {
+  return NO_MORE_DATA_TO_SEND;
+}
+
 StreamDelegateSendImmediate::StreamDelegateSendImmediate(
-    const scoped_refptr<SpdyStream>& stream,
+    const base::WeakPtr<SpdyStream>& stream,
     scoped_ptr<SpdyHeaderBlock> headers,
     base::StringPiece data)
     : StreamDelegateBase(stream),
@@ -158,7 +177,7 @@
 }
 
 StreamDelegateWithBody::StreamDelegateWithBody(
-    const scoped_refptr<SpdyStream>& stream,
+    const base::WeakPtr<SpdyStream>& stream,
     base::StringPiece data)
     : StreamDelegateBase(stream),
       buf_(new DrainableIOBuffer(new StringIOBuffer(data.as_string()),
diff --git a/net/spdy/spdy_stream_test_util.h b/net/spdy/spdy_stream_test_util.h
index 1e59ad8..baf92a1 100644
--- a/net/spdy/spdy_stream_test_util.h
+++ b/net/spdy/spdy_stream_test_util.h
@@ -22,7 +22,7 @@
 // to make sure that such an action is harmless.
 class ClosingDelegate : public SpdyStream::Delegate {
  public:
-  explicit ClosingDelegate(const scoped_refptr<SpdyStream>& stream);
+  explicit ClosingDelegate(const base::WeakPtr<SpdyStream>& stream);
   virtual ~ClosingDelegate();
 
   // SpdyStream::Delegate implementation.
@@ -37,15 +37,18 @@
   virtual void OnDataSent(size_t bytes_sent) OVERRIDE;
   virtual void OnClose(int status) OVERRIDE;
 
+  // Returns whether or not the stream is closed.
+  bool StreamIsClosed() const { return !stream_; }
+
  private:
-  scoped_refptr<SpdyStream> stream_;
+  base::WeakPtr<SpdyStream> stream_;
 };
 
 // Base class with shared functionality for test delegate
 // implementations below.
 class StreamDelegateBase : public SpdyStream::Delegate {
  public:
-  explicit StreamDelegateBase(const scoped_refptr<SpdyStream>& stream);
+  explicit StreamDelegateBase(const base::WeakPtr<SpdyStream>& stream);
   virtual ~StreamDelegateBase();
 
   virtual SpdySendStatus OnSendHeadersComplete() OVERRIDE;
@@ -67,16 +70,24 @@
   // a string.
   std::string TakeReceivedData();
 
+  // Returns whether or not the stream is closed.
+  bool StreamIsClosed() const { return !stream_; }
+
+  // Returns the stream's ID. If called when the stream is closed,
+  // returns the stream's ID when it was open.
+  SpdyStreamId stream_id() const { return stream_id_; }
+
   std::string GetResponseHeaderValue(const std::string& name) const;
   bool send_headers_completed() const { return send_headers_completed_; }
   int headers_sent() const { return headers_sent_; }
   int data_sent() const { return data_sent_; }
 
  protected:
-  const scoped_refptr<SpdyStream>& stream() { return stream_; }
+  const base::WeakPtr<SpdyStream>& stream() { return stream_; }
 
  private:
-  scoped_refptr<SpdyStream> stream_;
+  base::WeakPtr<SpdyStream> stream_;
+  SpdyStreamId stream_id_;
   TestCompletionCallback callback_;
   bool send_headers_completed_;
   SpdyHeaderBlock response_;
@@ -85,11 +96,22 @@
   int data_sent_;
 };
 
+// Test delegate that does nothing. Used to capture data about the
+// stream, e.g. its id when it was open.
+class StreamDelegateDoNothing : public StreamDelegateBase {
+ public:
+  StreamDelegateDoNothing(const base::WeakPtr<SpdyStream>& stream);
+  virtual ~StreamDelegateDoNothing();
+
+  virtual int OnSendBody() OVERRIDE;
+  virtual SpdySendStatus OnSendBodyComplete(size_t bytes_sent) OVERRIDE;
+};
+
 // Test delegate that sends data immediately in OnResponseReceived().
 class StreamDelegateSendImmediate : public StreamDelegateBase {
  public:
   // Both |headers| and |buf| can be NULL.
-  StreamDelegateSendImmediate(const scoped_refptr<SpdyStream>& stream,
+  StreamDelegateSendImmediate(const base::WeakPtr<SpdyStream>& stream,
                               scoped_ptr<SpdyHeaderBlock> headers,
                               base::StringPiece data);
   virtual ~StreamDelegateSendImmediate();
@@ -108,7 +130,7 @@
 // Test delegate that sends body data.
 class StreamDelegateWithBody : public StreamDelegateBase {
  public:
-  StreamDelegateWithBody(const scoped_refptr<SpdyStream>& stream,
+  StreamDelegateWithBody(const base::WeakPtr<SpdyStream>& stream,
                          base::StringPiece data);
   virtual ~StreamDelegateWithBody();
 
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 4f17b65..047f264 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -276,7 +276,7 @@
   return true;
 }
 
-scoped_refptr<SpdyStream> CreateStreamSynchronously(
+base::WeakPtr<SpdyStream> CreateStreamSynchronously(
     const scoped_refptr<SpdySession>& session,
     const GURL& url,
     RequestPriority priority,
@@ -284,14 +284,11 @@
   SpdyStreamRequest stream_request;
   int rv = stream_request.StartRequest(session, url, priority, net_log,
                                        CompletionCallback());
-  return (rv == OK) ? stream_request.ReleaseStream() : NULL;
+  return
+      (rv == OK) ? stream_request.ReleaseStream() : base::WeakPtr<SpdyStream>();
 }
 
-StreamReleaserCallback::StreamReleaserCallback(
-    SpdySession* session,
-    SpdyStream* first_stream)
-    : session_(session),
-      first_stream_(first_stream) {}
+StreamReleaserCallback::StreamReleaserCallback() {}
 
 StreamReleaserCallback::~StreamReleaserCallback() {}
 
@@ -304,10 +301,6 @@
 
 void StreamReleaserCallback::OnComplete(
     SpdyStreamRequest* request, int result) {
-  session_->CloseSessionOnError(ERR_FAILED, false, "On complete.");
-  session_ = NULL;
-  first_stream_->Cancel();
-  first_stream_ = NULL;
   request->ReleaseStream()->Cancel();
   SetResult(result);
 }
diff --git a/net/spdy/spdy_test_util_common.h b/net/spdy/spdy_test_util_common.h
index 5531a85f..1af36c81 100644
--- a/net/spdy/spdy_test_util_common.h
+++ b/net/spdy/spdy_test_util_common.h
@@ -130,30 +130,25 @@
 
 // Tries to create a stream in |session| synchronously. Returns NULL
 // on failure.
-scoped_refptr<SpdyStream> CreateStreamSynchronously(
+base::WeakPtr<SpdyStream> CreateStreamSynchronously(
     const scoped_refptr<SpdySession>& session,
     const GURL& url,
     RequestPriority priority,
     const BoundNetLog& net_log);
 
-// Helper class used by some tests to release two streams as soon as
-// one is created.
+// Helper class used by some tests to release a stream as soon as it's
+// created.
 class StreamReleaserCallback : public TestCompletionCallbackBase {
  public:
-  StreamReleaserCallback(SpdySession* session,
-                         SpdyStream* first_stream);
+  StreamReleaserCallback();
 
   virtual ~StreamReleaserCallback();
 
-  // Returns a callback that releases |request|'s stream as well as
-  // |first_stream|.
+  // Returns a callback that releases |request|'s stream.
   CompletionCallback MakeCallback(SpdyStreamRequest* request);
 
  private:
   void OnComplete(SpdyStreamRequest* request, int result);
-
-  scoped_refptr<SpdySession> session_;
-  scoped_refptr<SpdyStream> first_stream_;
 };
 
 const size_t kSpdyCredentialSlotUnused = 0;
diff --git a/net/spdy/spdy_websocket_stream.cc b/net/spdy/spdy_websocket_stream.cc
index 3440af7..d503fa2 100644
--- a/net/spdy/spdy_websocket_stream.cc
+++ b/net/spdy/spdy_websocket_stream.cc
@@ -20,7 +20,6 @@
 SpdyWebSocketStream::SpdyWebSocketStream(
     SpdySession* spdy_session, Delegate* delegate)
     : weak_ptr_factory_(this),
-      stream_(NULL),
       spdy_session_(spdy_session),
       delegate_(delegate) {
   DCHECK(spdy_session_);
@@ -28,17 +27,8 @@
 }
 
 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();
-    // The call to Close() should call into OnClose(), which should
-    // set |stream_| to NULL.
-    DCHECK(!stream_.get());
-  }
+  delegate_ = NULL;
+  Close();
 }
 
 int SpdyWebSocketStream::InitializeStream(const GURL& url,
@@ -84,8 +74,10 @@
 }
 
 void SpdyWebSocketStream::Close() {
-  if (stream_)
+  if (stream_) {
     stream_->Close();
+    DCHECK(!stream_);
+  }
 }
 
 SpdySendStatus SpdyWebSocketStream::OnSendHeadersComplete() {
@@ -128,7 +120,7 @@
 }
 
 void SpdyWebSocketStream::OnClose(int status) {
-  stream_ = NULL;
+  stream_.reset();
 
   // Destruction without Close() call OnClose() with delegate_ being NULL.
   if (!delegate_)
diff --git a/net/spdy/spdy_websocket_stream.h b/net/spdy/spdy_websocket_stream.h
index 02f6c7c..a589ae0 100644
--- a/net/spdy/spdy_websocket_stream.h
+++ b/net/spdy/spdy_websocket_stream.h
@@ -95,7 +95,7 @@
 
   base::WeakPtrFactory<SpdyWebSocketStream> weak_ptr_factory_;
   SpdyStreamRequest stream_request_;
-  scoped_refptr<SpdyStream> stream_;
+  base::WeakPtr<SpdyStream> stream_;
   scoped_refptr<SpdySession> spdy_session_;
   Delegate* delegate_;