Repository: tornadoweb/tornado

Issue:
When HttpResponse body is empty it returns a ValueError when it should not
In checkin https://github.com/tornadoweb/tornado/pull/2514/commits/7b846ea56bff1892a4d4d05206210b4d234e292b the code for httpclient.HttpResponse.body was changed to throw a ValueError when the body is empty.

But since the message body is optional throwing an ValueError seems not right because it is not an error. Can it be reverted back to the 5 behavior of just returning a None when the body is empty.


Discussion:

Resolving patch:
diff --git a/tornado/__init__.py b/tornado/__init__.py
--- a/tornado/__init__.py
+++ b/tornado/__init__.py
@@ -22,5 +22,5 @@
 # is zero for an official release, positive for a development branch,
 # or negative for a release candidate or beta (after the base version
 # number has been incremented)
-version = "6.0.1"
-version_info = (6, 0, 1, 0)
+version = "6.0.2"
+version_info = (6, 0, 2, 0)
diff --git a/tornado/httpclient.py b/tornado/httpclient.py
--- a/tornado/httpclient.py
+++ b/tornado/httpclient.py
@@ -665,7 +665,7 @@ def __init__(
     @property
     def body(self) -> bytes:
         if self.buffer is None:
-            raise ValueError("body not set")
+            return b""
         elif self._body is None:
             self._body = self.buffer.getvalue()
 
diff --git a/tornado/websocket.py b/tornado/websocket.py
--- a/tornado/websocket.py
+++ b/tornado/websocket.py
@@ -558,8 +558,8 @@ def set_nodelay(self, value: bool) -> None:
 
         .. versionadded:: 3.1
         """
-        assert self.stream is not None
-        self.stream.set_nodelay(value)
+        assert self.ws_connection is not None
+        self.ws_connection.set_nodelay(value)
 
     def on_connection_close(self) -> None:
         if self.ws_connection:
@@ -714,6 +714,10 @@ def start_pinging(self) -> None:
     async def _receive_frame_loop(self) -> None:
         raise NotImplementedError()
 
+    @abc.abstractmethod
+    def set_nodelay(self, x: bool) -> None:
+        raise NotImplementedError()
+
 
 class _PerMessageDeflateCompressor(object):
     def __init__(
@@ -1345,6 +1349,9 @@ def periodic_ping(self) -> None:
         self.write_ping(b"")
         self.last_ping = now
 
+    def set_nodelay(self, x: bool) -> None:
+        self.stream.set_nodelay(x)
+
 
 class WebSocketClientConnection(simple_httpclient._HTTPConnection):
     """WebSocket client connection.


Test patch:
diff --git a/tornado/test/websocket_test.py b/tornado/test/websocket_test.py
--- a/tornado/test/websocket_test.py
+++ b/tornado/test/websocket_test.py
@@ -198,6 +198,12 @@ async def open(self):
         raise Exception("boom")
 
 
+class NoDelayHandler(TestWebSocketHandler):
+    def open(self):
+        self.set_nodelay(True)
+        self.write_message("hello")
+
+
 class WebSocketBaseTestCase(AsyncHTTPTestCase):
     @gen.coroutine
     def ws_connect(self, path, **kwargs):
@@ -258,6 +264,7 @@ def get_app(self):
                 ),
                 ("/error_in_open", ErrorInOpenHandler),
                 ("/error_in_async_open", ErrorInAsyncOpenHandler),
+                ("/nodelay", NoDelayHandler),
             ],
             template_loader=DictLoader({"message.html": "<b>{{ message }}</b>"}),
         )
@@ -562,6 +569,12 @@ def test_error_in_async_open(self):
             res = yield ws.read_message()
         self.assertIsNone(res)
 
+    @gen_test
+    def test_nodelay(self):
+        ws = yield self.ws_connect("/nodelay")
+        res = yield ws.read_message()
+        self.assertEqual(res, "hello")
+
 
 class NativeCoroutineOnMessageHandler(TestWebSocketHandler):
     def initialize(self, **kwargs):