Repository: falconry/falcon
Issue: 416 Range Not Satisfiable - Does not need a media type

I checked a couple RFCs and it appears that this response doesn't have a body, so the media_type param should be removed.


Changed files:
falcon/http_error.py, falcon/exceptions.py, falcon/api_helpers.py, falcon/response.py, tests/test_httperror.py

diff --git a/falcon/api_helpers.py b/falcon/api_helpers.py
index 1318a94..df7a103 100644
--- a/falcon/api_helpers.py
+++ b/falcon/api_helpers.py
@@ -143,34 +143,28 @@ def get_body(resp, wsgi_file_wrapper=None):
 
 
 def compose_error_response(req, resp, ex):
-    resp.status = ex.status
-
-    content_type_override = None
-
-    if ex.headers is not None:
-        resp.set_headers(ex.headers)
-
-        # PERF(kgriffs): Only call resp.content_type if
-        # we have some headers from the exception that
-        # could have caused it to be set.
-        content_type_override = resp.content_type
 
-    if content_type_override is not None:
-        # Application is forcing the response content-type
-        preferred = content_type_override
-    else:
-        preferred = req.client_prefers(('application/xml',
-                                        'text/xml',
-                                        'application/json'))
+    preferred = req.client_prefers(('application/xml',
+                                    'text/xml',
+                                    'application/json'))
 
     if preferred is not None:
-        resp.content_type = preferred
-
         if preferred == 'application/json':
             resp.body = ex.json()
-        elif preferred.endswith('/xml'):
+        else:
             resp.body = ex.xml()
 
+    resp.status = ex.status
+
+    if ex.headers is not None:
+        resp.set_headers(ex.headers)
+
+    # NOTE(kgriffs): Do this after setting headers from ex.headers,
+    # so that we will override Content-Type if it was mistakenly set
+    # by the app.
+    if resp.body is not None:
+        resp.content_type = preferred
+
 
 def compile_uri_template(template):
     """Compile the given URI template string into a pattern matcher.
diff --git a/falcon/exceptions.py b/falcon/exceptions.py
index 1d32f78..7d26c0c 100644
--- a/falcon/exceptions.py
+++ b/falcon/exceptions.py
@@ -258,18 +258,11 @@ class HTTPRangeNotSatisfiable(HTTPError):
     Args:
         resource_length: The maximum value for the last-byte-pos of a range
             request. Used to set the Content-Range header.
-        media_type: Media type to use as the value of the Content-Type
-            header, or *None* to use the default passed to the API
-            initializer.
-
     """
 
-    def __init__(self, resource_length, media_type=None):
+    def __init__(self, resource_length):
         headers = {'Content-Range': 'bytes */' + str(resource_length)}
-        if media_type is not None:
-            headers['Content-Type'] = media_type
-
-        HTTPError.__init__(self, status.HTTP_416, None, None, headers=headers)
+        HTTPError.__init__(self, status.HTTP_416, None, headers=headers)
 
 
 class HTTPInternalServerError(HTTPError):
diff --git a/falcon/http_error.py b/falcon/http_error.py
index 3ffc4b3..07199b0 100644
--- a/falcon/http_error.py
+++ b/falcon/http_error.py
@@ -54,6 +54,23 @@ class HTTPError(Exception):
     Keyword Args:
         description (str): Human-friendly description of the error, along with
             a helpful suggestion or two (default *None*).
+        headers (dict or list): A dictionary of header names and values
+            to set, or list of (name, value) tuples. Both names and
+            values must be of type str or StringType, and only character
+            values 0x00 through 0xFF may be used on platforms that use
+            wide characters.
+
+            Note:
+                The Content-Type header, if present, will be overriden. If
+                you wish to return custom error messages, you can create
+                your own HTTP error class, and install an error handler
+                to convert it into an appropriate HTTP response for the
+                client
+
+            Note:
+                Falcon can process a list of tuples slightly faster
+                than a dict.
+
         headers (dict): Extra headers to return in the
             response to the client (default *None*).
         href (str): A URL someone can visit to find out more information
diff --git a/falcon/response.py b/falcon/response.py
index f11ea94..bf783f7 100644
--- a/falcon/response.py
+++ b/falcon/response.py
@@ -139,9 +139,9 @@ class Response(object):
         Args:
             headers (dict or list): A dictionary of header names and values
                 to set, or list of (name, value) tuples. Both names and
-                values must be of type
-                str or StringType, and only character values 0x00 through
-                0xFF may be used on platforms that use wide characters.
+                values must be of type str or StringType, and only character
+                values 0x00 through 0xFF may be used on platforms that use
+                wide characters.
 
                 Note:
                     Falcon can process a list of tuples slightly faster
diff --git a/tests/test_httperror.py b/tests/test_httperror.py
index 405225f..27c1b47 100644
--- a/tests/test_httperror.py
+++ b/tests/test_httperror.py
@@ -108,9 +108,6 @@ class RangeNotSatisfiableResource:
     def on_get(self, req, resp):
         raise falcon.HTTPRangeNotSatisfiable(123456)
 
-    def on_put(self, req, resp):
-        raise falcon.HTTPRangeNotSatisfiable(123456, 'x-falcon/peregrine')
-
 
 class ServiceUnavailableResource:
 
@@ -370,7 +367,7 @@ class TestHTTPError(testing.TestBase):
         self.assertEqual(parsed_body['title'], 'title')
         self.assertEqual(parsed_body['description'], 'description')
 
-    def test_416_default_media_type(self):
+    def test_416(self):
         self.api = falcon.API()
         self.api.add_route('/416', RangeNotSatisfiableResource())
         body = self.simulate_request('/416', headers={'accept': 'text/xml'})
@@ -378,19 +375,7 @@ class TestHTTPError(testing.TestBase):
         self.assertEqual(self.srmock.status, falcon.HTTP_416)
         self.assertEqual(body, [])
         self.assertIn(('content-range', 'bytes */123456'), self.srmock.headers)
-        self.assertIn(('content-type', 'text/xml'), self.srmock.headers)
-        self.assertNotIn(('content-length', '0'), self.srmock.headers)
-
-    def test_416_custom_media_type(self):
-        self.api.add_route('/416', RangeNotSatisfiableResource())
-        body = self.simulate_request('/416', method='PUT')
-
-        self.assertEqual(self.srmock.status, falcon.HTTP_416)
-        self.assertEqual(body, [])
-        self.assertIn(('content-range', 'bytes */123456'),
-                      self.srmock.headers)
-        self.assertIn(('content-type', 'x-falcon/peregrine'),
-                      self.srmock.headers)
+        self.assertNotIn('content-length', self.srmock.headers_dict)
 
     def test_503(self):
         self.api.add_route('/503', ServiceUnavailableResource())