Repository: encode/uvicorn

Issue:
Drop response body on HEAD requests

Discussion:

Patch:
diff --git a/uvicorn/protocols/http/h11_impl.py b/uvicorn/protocols/http/h11_impl.py
index ab9cdaf..598b5e0 100644
--- a/uvicorn/protocols/http/h11_impl.py
+++ b/uvicorn/protocols/http/h11_impl.py
@@ -465,7 +465,10 @@ class RequestResponseCycle:
             more_body = message.get("more_body", False)
 
             # Write response body
-            event = h11.Data(data=body)
+            if self.scope['method'] == "HEAD":
+                event = h11.Data()
+            else:
+                event = h11.Data(data=body)
             output = self.conn.send(event)
             self.transport.write(output)
 
diff --git a/uvicorn/protocols/http/httptools_impl.py b/uvicorn/protocols/http/httptools_impl.py
index cfd7887..8a593fc 100644
--- a/uvicorn/protocols/http/httptools_impl.py
+++ b/uvicorn/protocols/http/httptools_impl.py
@@ -467,7 +467,9 @@ class RequestResponseCycle:
             more_body = message.get("more_body", False)
 
             # Write response body
-            if self.chunked_encoding:
+            if self.scope['method'] == "HEAD":
+                self.expected_content_length = 0
+            elif self.chunked_encoding:
                 content = [b"%x\r\n" % len(body), body, b"\r\n"]
                 if not more_body:
                     content.append(b"0\r\n\r\n")


Test patch:
diff --git a/tests/protocols/test_http.py b/tests/protocols/test_http.py
index dd4b62c..4ec0a9b 100644
--- a/tests/protocols/test_http.py
+++ b/tests/protocols/test_http.py
@@ -48,6 +48,8 @@ class Response:
 
 SIMPLE_GET_REQUEST = b"\r\n".join([b"GET / HTTP/1.1", b"Host: example.org", b"", b""])
 
+SIMPLE_HEAD_REQUEST = b"\r\n".join([b"HEAD / HTTP/1.1", b"Host: example.org", b"", b""])
+
 SIMPLE_POST_REQUEST = b"\r\n".join(
     [
         b"POST / HTTP/1.1",
@@ -175,6 +177,18 @@ def test_get_request(protocol_cls):
     assert b"Hello, world" in protocol.transport.buffer
 
 
+@pytest.mark.parametrize("protocol_cls", [HttpToolsProtocol, H11Protocol])
+def test_head_request(protocol_cls):
+    def app(scope):
+        return Response("Hello, world", media_type="text/plain")
+
+    protocol = get_connected_protocol(app, protocol_cls)
+    protocol.data_received(SIMPLE_HEAD_REQUEST)
+    protocol.loop.run_one()
+    assert b"HTTP/1.1 200 OK" in protocol.transport.buffer
+    assert b"Hello, world" not in protocol.transport.buffer
+
+
 @pytest.mark.parametrize("protocol_cls", [HttpToolsProtocol, H11Protocol])
 def test_post_request(protocol_cls):
     class App: