WebSocket over H2: Detect stream close correctly

Previously WebSocket over HTTP/2 didn't handle the case when a server
half-closes the stream after sending the Close frame correctly. It would
fail to detect the half-close and timeout after 2 seconds, considering
the close to be unclean.

Modify WebSocketSpdyStreamAdapter::OnDataReceived to close the stream
when a half-close is detected.

Also modify SpdyStream to notify its delegate when a half close is
detected. This was already the documented behaviour.

BUG=1151393

Change-Id: I668d6ae0dc5a3926efade1c28f16ca379f5b1566
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2721519
Auto-Submit: Adam Rice <[email protected]>
Commit-Queue: Bence Béky <[email protected]>
Reviewed-by: Bence Béky <[email protected]>
Cr-Commit-Position: refs/heads/master@{#858542}
diff --git a/net/spdy/spdy_stream_unittest.cc b/net/spdy/spdy_stream_unittest.cc
index ffbb1591..bba60fa 100644
--- a/net/spdy/spdy_stream_unittest.cc
+++ b/net/spdy/spdy_stream_unittest.cc
@@ -1856,6 +1856,57 @@
   EXPECT_TRUE(data.AllWriteDataConsumed());
 }
 
+TEST_F(SpdyStreamTest, DelegateIsInformedOfEOF) {
+  spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyPost(
+      kDefaultUrl, 1, kPostBodyLength, LOWEST, nullptr, 0));
+  AddWrite(req);
+
+  spdy::Http2HeaderBlock response_headers;
+  response_headers[spdy::kHttp2StatusHeader] = "200";
+  spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyResponseHeaders(
+      1, std::move(response_headers), /* fin = */ true));
+  AddRead(resp);
+
+  spdy::SpdySerializedFrame data_frame(
+      spdy_util_.ConstructSpdyDataFrame(1, kPostBodyStringPiece, true));
+  AddRead(data_frame);
+
+  spdy::SpdySerializedFrame rst(
+      spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_STREAM_CLOSED));
+  AddWrite(rst);
+
+  AddReadEOF();
+
+  SequencedSocketData data(GetReads(), GetWrites());
+  MockConnect connect_data(SYNCHRONOUS, OK);
+  data.set_connect_data(connect_data);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+  AddSSLSocketData();
+
+  base::WeakPtr<SpdySession> session(CreateDefaultSpdySession());
+
+  base::WeakPtr<SpdyStream> stream = CreateStreamSynchronously(
+      SPDY_BIDIRECTIONAL_STREAM, session, url_, LOWEST, NetLogWithSource());
+  ASSERT_TRUE(stream);
+  EXPECT_EQ(kDefaultUrl, stream->url().spec());
+
+  StreamDelegateDetectEOF delegate(stream);
+  stream->SetDelegate(&delegate);
+
+  spdy::Http2HeaderBlock headers(
+      spdy_util_.ConstructPostHeaderBlock(kDefaultUrl, kPostBodyLength));
+  EXPECT_THAT(stream->SendRequestHeaders(std::move(headers), MORE_DATA_TO_SEND),
+              IsError(ERR_IO_PENDING));
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(delegate.eof_detected());
+
+  EXPECT_TRUE(data.AllReadDataConsumed());
+  EXPECT_TRUE(data.AllWriteDataConsumed());
+}
+
 }  // namespace test
 
 }  // namespace net