From 1684b70942da8330b55bbdd8da25002bdae9e55a Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 8 Dec 2013 21:17:06 -0800 Subject: [PATCH] gallery.py: Rework headers to use an OrderedDict This makes it easier to manipulate and access headers (e.g. to extract the charset). Also: * Use CRLF line endings for headers [1], instead of our old LF. * Encode text/ content to bytes (other content should already be in bytes) [2]. The encoding used for text/ content is configurable. [1]: http://tools.ietf.org/html/rfc2616#section-6.1 [2]: http://www.python.org/dev/peps/pep-0333/#unicode-issues --- posts/gallery/gallery.py | 59 +++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/posts/gallery/gallery.py b/posts/gallery/gallery.py index 5799641..ac62c36 100755 --- a/posts/gallery/gallery.py +++ b/posts/gallery/gallery.py @@ -42,6 +42,7 @@ This script can also be run as a Simple Common Gateway Interface (SCGI) with the ``--scgi`` option. """ +import collections as _collections import logging as _logging import logging.handlers as _logging_handlers import math as _math @@ -157,38 +158,58 @@ class CGIGalleryServer (object): self._base_url = base_url self._cache_path = cache_path self._serve_originals = serve_originals + self._text_charset = 'UTF-8' self._url_regexp = _re.compile('^[a-zA-Z0-9._/-]*$') self._rows = 3 self._columns = 3 self.header = [] self.footer = [] - def _http_header(self, mime='text/html', status=200): + def _get_charset(self, headers): + content_type = headers.get('Content-type', '') + if 'charset=' in content_type: + return content_type.split('charset=', 1)[-1] + + def _http_headers(self, mime='text/html', status=200): msg = RESPONSES[status] - header = ['Status: {:d} {}'.format(status, msg)] + headers = _collections.OrderedDict() + headers['Status'] = '{:d} {}'.format(status, msg) if mime.startswith('text/'): - charset = '; charset=UTF-8' + charset = '; charset={}'.format(self._text_charset) else: charset = '' - header.append('Content-type: {}{}'.format(mime, charset)) - return '\n'.join(header) + headers['Content-type'] = '{}{}'.format(mime, charset) + return headers + + def _add_header(self, headers=None, stream=None): + if headers and self._write_http_headers: + for key, value in headers.items(): + stream.write( + '{}: {}\r\n'.format(key, value).encode('US-ASCII')) + stream.write('\r\n'.encode('US-ASCII')) - def _response(self, header=None, content='

It works!

', + def _response(self, headers=None, content='

It works!

', stream=None): - if header is None: - header = self._http_header() - stream.write(header) - stream.write('\n\n') + if headers is None: + headers = self._http_headers() + self._add_header(headers=headers, stream=stream) + charset = self._get_charset(headers=headers) + if charset: + content = content.encode(charset) stream.write(content) raise ProcessingComplete() - def _response_stream(self, header=None, content=None, stream=None, + def _response_stream(self, headers=None, content=None, stream=None, chunk_size=1024): LOG.debug('streaming response') - if header is None: - header = self._http_header() - stream.write(header) - stream.write('\n\n') + if headers is None: + headers = self._http_headers() + charset = self._get_charset(headers=headers) + if charset: + raise HTTPError( + 500, + content='charset {} set for streamed response'.format(charset)) + self._add_header(headers=headers, stream=stream) stream.flush() # flush headers while True: chunk = content.read(chunk_size) @@ -198,10 +219,10 @@ class CGIGalleryServer (object): raise ProcessingComplete() def _error(self, status=404, content=None, stream=None): - header = self._http_header(status=status) + headers = self._http_headers(status=status) if content is None: content = RESPONSES[status] - self._response(header=header, content=content, stream=stream) + self._response(headers=headers, content=content, stream=stream) def validate_url(self, url, exists=True, directory=False): LOG.debug('validating {} (exists={}, directory={})'.format( @@ -568,9 +589,9 @@ class CGIGalleryServer (object): headers = self._http_headers(mime=mime) if mime in STREAMING_TYPES: self._response_stream( - header=header, content=content, stream=stream) + headers=headers, content=content, stream=stream) content = content.read() - self._response(header=header, content=content, stream=stream) + self._response(headers=headers, content=content, stream=stream) def page(self, url, page=0, stream=None): LOG.debug('HTML page {} {}'.format(url, page)) -- 2.26.2