diff -r 0ddba0abab49 Lib/http/server.py --- a/Lib/http/server.py Thu Jun 02 10:13:47 2016 +0000 +++ b/Lib/http/server.py Thu Jun 02 16:54:10 2016 -0700 @@ -456,13 +456,20 @@ self.send_response(code, message) self.send_header("Content-Type", self.error_content_type) self.send_header('Connection', 'close') - self.send_header('Content-Length', int(len(body))) + if code not in (HTTPStatus.NO_CONTENT, HTTPStatus.RESET_CONTENT): + # RFC7230: 3.3.2. You can't send Content-Length for 204(NO_CONTENT) + # RFC7231: 6.3.6. Content-Length is not a required field and sending + # blank line and closing connection immediately is a + # correct behavior described in the RFC. + self.send_header('Content-Length', int(len(body))) self.end_headers() if (self.command != 'HEAD' and code >= 200 and code not in ( - HTTPStatus.NO_CONTENT, HTTPStatus.NOT_MODIFIED)): + HTTPStatus.NO_CONTENT, + HTTPStatus.RESET_CONTENT, + HTTPStatus.NOT_MODIFIED)): self.wfile.write(body) def send_response(self, code, message=None): diff -r 0ddba0abab49 Lib/test/test_httpservers.py --- a/Lib/test/test_httpservers.py Thu Jun 02 10:13:47 2016 +0000 +++ b/Lib/test/test_httpservers.py Thu Jun 02 16:54:10 2016 -0700 @@ -116,6 +116,18 @@ body = self.headers['x-special-incoming'].encode('utf-8') self.wfile.write(body) + def do_NOCONTENT_VIA_SEND_ERROR(self): + # RFC7231: 6.3.5 + # In most cases, NO_CONTENT should be sent via + # send_response() as this is actually not an error. + self.send_error(HTTPStatus.NO_CONTENT) + + def do_RESETCONTENT_VIA_SEND_ERROR(self): + # RFC7231: 6.3.6 + # In most cases, RESET_CONTENT should be sent via + # send_response() as this is actually not an error. + self.send_error(HTTPStatus.RESET_CONTENT) + def setUp(self): BaseTestCase.setUp(self) self.con = http.client.HTTPConnection(self.HOST, self.PORT) @@ -237,6 +249,29 @@ data = res.read() self.assertEqual(int(res.getheader('Content-Length')), len(data)) + def test_no_content_via_send_error(self): + # RFC7230: 3.3.2 states that Content-Length must not be specified + # in response message with 204(NO_CONTENT). + self.con.request('NOCONTENT_VIA_SEND_ERROR', '/') + res = self.con.getresponse() + self.assertEqual(HTTPStatus.NO_CONTENT, res.status) + self.assertEqual(None, res.getheader('Content-Length')) + + data = res.read() + self.assertEqual(b'', data) + + def test_reset_content(self): + # RFC7231: 6.3.6 There are 3 ways to handle 205(RESET_CONTENT) + # In this implementation, we'll expect option c) in RFC where + # it'll receive empty line in the body. + self.con.request('RESETCONTENT_VIA_SEND_ERROR', '/') + res = self.con.getresponse() + self.assertEqual(HTTPStatus.RESET_CONTENT, res.status) + self.assertEqual(None, res.getheader('Content-Length')) + + data = res.read() + self.assertEqual(b'', data) + class RequestHandlerLoggingTestCase(BaseTestCase): class request_handler(BaseHTTPRequestHandler):