Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Changeset 820

Show
Ignore:
Timestamp:
11/13/05 20:54:18
Author:
fumanchu
Message:

Improved EncodingFilter?. As before, if "encodingFilter.encoding" is provided, that encoding is forced. But if not provided, the filter now inspects the Accept-Charset request header. Use an "encodingFilter.defaultEncoding" entry (default = utf-8) to handle "Accept-Charset: *".

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/cherrypy/filters/encodingfilter.py

    r814 r820  
    22from basefilter import BaseFilter 
    33 
     4 
    45class EncodingFilter(BaseFilter): 
    56    """Filter that automatically encodes the response.""" 
     7     
     8    def find_acceptable_charset(self): 
     9        conf = cherrypy.config.get 
     10        response = cherrypy.response 
     11         
     12        stream = conf("streamResponse", False) 
     13        if stream: 
     14            # Use a generator wrapper, and just pray it works as the 
     15            # stream is being written out. 
     16            def encode_body(encoding): 
     17                def encoder(body): 
     18                    for line in body: 
     19                        yield line.encode(encoding) 
     20                response.body = encoder(response.body) 
     21                return True 
     22        else: 
     23            response.body = ''.join([chunk for chunk in response.body]) 
     24            def encode_body(encoding): 
     25                try: 
     26                    response.body = response.body.encode(encoding) 
     27                except UnicodeError: 
     28                    # Try the next encoding 
     29                    return False 
     30                else: 
     31                    return True 
     32         
     33        failmsg = "The response could not be encoded with %s" 
     34         
     35        enc = conf('encodingFilter.encoding', None) 
     36        if enc is not None: 
     37            # If specified, force this encoding to be used, or fail. 
     38            if encode_body(enc): 
     39                return enc 
     40            else: 
     41                raise cherrypy.HTTPError(500, failmsg % enc) 
     42         
     43        # Parse the Accept_Charset request header, and try to provide one 
     44        # of the requested charsets (in order of user preference). 
     45        default_enc = conf('encodingFilter.defaultEncoding', 'utf-8') 
     46         
     47        encs = cherrypy.request.headerMap.elements('Accept-Charset') 
     48        if encs is None: 
     49            # Any character-set is acceptable. 
     50            if encode_body(default_enc): 
     51                return default_enc 
     52            else: 
     53                raise cherrypy.HTTPError(500, failmsg % default_enc) 
     54        else: 
     55            newbody = None 
     56            charsets = [enc.value.lower() for enc in encs] 
     57            if "*" not in charsets: 
     58                # If no "*" is present in an Accept-Charset field, then all 
     59                # character sets not explicitly mentioned get a quality 
     60                # value of 0, except for ISO-8859-1, which gets a quality 
     61                # value of 1 if not explicitly mentioned. 
     62                iso = 'iso-8859-1' 
     63                if iso not in charsets: 
     64                    if encode_body(iso): 
     65                        return iso 
     66             
     67            for element in encs: 
     68                if element.qvalue > 0: 
     69                    if element.value == "*": 
     70                        # Matches any charset. Try our default. 
     71                        if encode_body(default_enc): 
     72                            return default_enc 
     73                    else: 
     74                        if encode_body(element.value): 
     75                            return element.value 
     76         
     77        # No suitable encoding found. 
     78        raise cherrypy.HTTPError(406) 
    679     
    780    def beforeFinalize(self): 
     
    1083            return 
    1184         
    12         contentType = cherrypy.response.headerMap.get("Content-Type") 
    13         if contentType: 
    14             ctlist = contentType.split(';')[0] 
    15             if (ctlist in conf('encodingFilter.mimeTypeList', ['text/html'])): 
    16                 enc = conf('encodingFilter.encoding', 'utf-8') 
    17                  
    18                 # Add "charset=..." to response Content-Type header 
    19                 if contentType and 'charset' not in contentType: 
    20                     cherrypy.response.headerMap["Content-Type"] += ";charset=%s" % enc 
    21                  
    22                 # Return a generator that encodes the sequence 
    23                 def encode_body(body): 
    24                     for line in body: 
    25                         yield line.encode(enc) 
    26                 cherrypy.response.body = encode_body(cherrypy.response.body) 
     85        charset = self.find_acceptable_charset() 
     86         
     87        # Set "charset=..." param on response Content-Type header 
     88        ct = cherrypy.response.headerMap.elements("Content-Type") 
     89        if ct is not None: 
     90            ct = ct[0] 
     91            ct.params['charset'] = charset 
     92            cherrypy.response.headerMap["Content-Type"] = str(ct) 
     93 
  • trunk/cherrypy/test/test_decodingencoding_filter.py

    r778 r820  
    1010        yield europoundUnicode 
    1111    index.exposed = True 
     12     
     13    def mao_zedong(self): 
     14        return u"\u6bdb\u6cfd\u4e1c: Sing, Little Birdie?" 
     15    mao_zedong.exposed = True 
    1216 
    1317cherrypy.root = Root() 
     
    2226import helper 
    2327 
    24 europoundUtf8 = europoundUnicode.encode('utf-8') 
    2528 
    2629class DecodingEncodingFilterTest(helper.CPWebCase): 
    2730     
    2831    def testDecodingEncodingFilter(self): 
     32        europoundUtf8 = europoundUnicode.encode('utf-8') 
    2933        self.getPage('/?param=%s' % europoundUtf8) 
    3034        self.assertBody(europoundUtf8) 
     35         
     36        # Default encoding should be utf-8 
     37        self.getPage('/mao_zedong') 
     38        self.assertBody("\xe6\xaf\x9b\xe6\xb3\xbd\xe4\xb8\x9c: " 
     39                        "Sing, Little Birdie?") 
     40         
     41        # Ask for utf-16. 
     42        sing16 = ('\xff\xfe\xdbk\xfdl\x1cN:\x00 \x00S\x00i\x00n\x00g\x00,\x00 ' 
     43                  '\x00L\x00i\x00t\x00t\x00l\x00e\x00 ' 
     44                  '\x00B\x00i\x00r\x00d\x00i\x00e\x00?\x00') 
     45        self.getPage('/mao_zedong', [('Accept-Charset', 'utf-16')]) 
     46        self.assertBody(sing16) 
     47         
     48        # Ask for multiple encodings. ISO-8859-1 should fail, and utf-16 
     49        # should be produced. 
     50        self.getPage('/mao_zedong', [('Accept-Charset', 
     51                                      'iso-8859-1;q=1, utf-16;q=0.5')]) 
     52        self.assertBody(sing16) 
     53         
     54        # The "*" value should default to our defaultEncoding, utf-8 
     55        self.getPage('/mao_zedong', [('Accept-Charset', '*;q=1, utf-7;q=.2')]) 
     56        self.assertBody("\xe6\xaf\x9b\xe6\xb3\xbd\xe4\xb8\x9c: " 
     57                        "Sing, Little Birdie?") 
     58         
     59        # Only allow iso-8859-1, which should fail and raise 406. 
     60        self.getPage('/mao_zedong', [('Accept-Charset', 'iso-8859-1, *;q=0')]) 
     61        self.assertStatus("406 Not Acceptable") 
     62        self.assertErrorPage(406) 
    3163 
    3264 

Hosted by WebFaction

Log in as guest/cpguest to create tickets