Skip to content

Commit

Permalink
wsgi: capitalize_response_headers option
Browse files Browse the repository at this point in the history
Setting this to False provides compatibility with broken clients
which expect response header names in particular case, such as
ETag by AWS Java SDK.
#80
  • Loading branch information
temoto committed Apr 22, 2014
1 parent 0cefaea commit e4dedf7
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 89 deletions.
34 changes: 28 additions & 6 deletions eventlet/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ def readline(self, size=-1):
class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
minimum_chunk_size = MINIMUM_CHUNK_SIZE
capitalize_response_headers = True

def setup(self):
# overriding SocketServer.setup to correctly handle SSL.Connection objects
Expand Down Expand Up @@ -378,11 +379,16 @@ def start_response(status, response_headers, exc_info=None):
# Avoid dangling circular ref
exc_info = None

capitalized_headers = [('-'.join([x.capitalize()
for x in key.split('-')]), value)
for key, value in response_headers]
# Response headers capitalization
# CONTent-TYpe: TExt/PlaiN -> Content-Type: TExt/PlaiN
# Per HTTP RFC standard, header name is case-insensitive.
# Please, fix your client to ignore header case if possible.
if self.capitalize_response_headers:
response_headers = [
('-'.join([x.capitalize() for x in key.split('-')]), value)
for key, value in response_headers]

headers_set[:] = [status, capitalized_headers]
headers_set[:] = [status, response_headers]
return write

try:
Expand All @@ -392,9 +398,12 @@ def start_response(status, response_headers, exc_info=None):
or isinstance(getattr(result, '_obj', None), _AlreadyHandled)):
self.close_connection = 1
return

# Set content-length if possible
if not headers_sent and hasattr(result, '__len__') and \
'Content-Length' not in [h for h, _v in headers_set[1]]:
headers_set[1].append(('Content-Length', str(sum(map(len, result)))))

towrite = []
towrite_size = 0
just_written_size = 0
Expand Down Expand Up @@ -544,7 +553,8 @@ def __init__(self,
log_format=DEFAULT_LOG_FORMAT,
url_length_limit=MAX_REQUEST_LINE,
debug=True,
socket_timeout=None):
socket_timeout=None,
capitalize_response_headers=True):

self.outstanding_requests = 0
self.socket = socket
Expand All @@ -566,6 +576,14 @@ def __init__(self,
self.url_length_limit = url_length_limit
self.debug = debug
self.socket_timeout = socket_timeout
self.capitalize_response_headers = capitalize_response_headers

if not self.capitalize_response_headers:
warnings.warn("""capitalize_response_headers is disabled.
Please, make sure you know what you are doing.
HTTP headers names are case-insensitive per RFC standard.
Most likely, you need to fix HTTP parsing in your client software.""",
DeprecationWarning, stacklevel=3)

def get_environ(self):
d = {
Expand All @@ -592,6 +610,7 @@ def process_request(self, sock_params):
proto = types.InstanceType(self.protocol)
if self.minimum_chunk_size is not None:
proto.minimum_chunk_size = self.minimum_chunk_size
proto.capitalize_response_headers = self.capitalize_response_headers
try:
proto.__init__(sock, address, self)
except socket.timeout:
Expand Down Expand Up @@ -630,7 +649,8 @@ def server(sock, site,
log_format=DEFAULT_LOG_FORMAT,
url_length_limit=MAX_REQUEST_LINE,
debug=True,
socket_timeout=None):
socket_timeout=None,
capitalize_response_headers=True):
"""Start up a WSGI server handling requests from the supplied server
socket. This function loops forever. The *sock* object will be closed after server exits,
but the underlying file descriptor will remain open, so if you have a dup() of *sock*,
Expand All @@ -653,6 +673,7 @@ def server(sock, site,
:param url_length_limit: A maximum allowed length of the request url. If exceeded, 414 error is returned.
:param debug: True if the server should send exception tracebacks to the clients on 500 errors. If False, the server will respond with empty bodies.
:param socket_timeout: Timeout for client connections' socket operations. Default None means wait forever.
:param capitalize_response_headers: Normalize response headers' names to Foo-Bar. Default is True.
"""
serv = Server(sock, sock.getsockname(),
site, log,
Expand All @@ -667,6 +688,7 @@ def server(sock, site,
url_length_limit=url_length_limit,
debug=debug,
socket_timeout=socket_timeout,
capitalize_response_headers=capitalize_response_headers,
)
if server_event is not None:
server_event.send(serv)
Expand Down

0 comments on commit e4dedf7

Please sign in to comment.