Pylons/pyramid — pull request #620 by lmctv (2012-06-08 16:34:12+00:00)
Title: Accept header normalization

According to RFC 2616, the mimetype in the accept header should be matched in a case insensitive way.

While fixing the root case insensitivity in webob (https://github.com/Pylons/webob/pull/56), I noticed a glitch in view registration: if you run pyramid_view_accept.py with an unpatched pyramid, both the view callables get registered; which one will be called depends on the client-supplied Accept header, and the webob available version...

```
$ python pyramid_view_accept.py
Route: service, Path: /, Callable: service_HTML, Accept: text/HTML
Route: service, Path: /, Callable: service_html, Accept: text/html
Listening on port 6543
Accept: text/html
localhost.localdomain - - [08/Jun/2012 18:05:41] "GET / HTTP/1.1" 200 92
Accept: text/HTML
localhost.localdomain - - [08/Jun/2012 18:10:39] "GET / HTTP/1.0" 200 92
```

With the proposed patches, the configurator would notice the attempted shadowing and correctly raises a ConfigurationConflictError:

```
$ python pyramid_view_accept.py
Traceback (most recent call last):
File "pyramid_view_accept.py", line 38, in
app = config.make_wsgi_app()
File "/home/lorenzo/pyramid/pyramid/config/__init__.py", line 921, in make_wsgi_app
self.commit()
File "/home/lorenzo/pyramid/pyramid/config/__init__.py", line 594, in commit
self.action_state.execute_actions(introspector=self.introspector)
File "/home/lorenzo/pyramid/pyramid/config/__init__.py", line 1030, in execute_actions
for action in resolveConflicts(self.actions):
File "/home/lorenzo/pyramid/pyramid/config/__init__.py", line 1136, in resolveConflicts
raise ConfigurationConflictError(conflicts)
pyramid.exceptions.ConfigurationConflictError: Conflicting configuration actions
For: ('view', None, '', None, , None, None, ('GET', 'HEAD'), 'service', None, False, 'text/html', None, None, None)
Line 12 of file pyramid_view_accept.py:
accept="text/HTML")
Line 18 of file pyramid_view_accept.py:
accept="text/html")
```

pyramid_view_accept.py:
```python
#!python
# encoding: utf-8

from pyramid.config import Configurator
from wsgiref.simple_server import make_server
from pyramid.view import view_config
from pyramid.response import Response

import sys

@view_config(route_name='service', request_method='GET',
accept="text/HTML")
def service_HTML(request):
return Response('

HTML SERVICE

route_name=service

\n')

@view_config(route_name='service', request_method='GET',
accept="text/html")
def service_html(request):
return Response('

html service

route_name=service

\n')

if __name__ == '__main__':
# configuration settings
if len(sys.argv) > 1:
httpport = int(sys.argv[1])
else:
httpport = 6543
settings = {}
settings['reload_all'] = True
settings['debug_all'] = True
# configuration setup
config = Configurator(settings=settings)
config.add_route(name='service', pattern='/')
config.scan()
# serve app
app = config.make_wsgi_app()
# print view mappings
rt_map = config.get_routes_mapper()
views = config.introspector.get_category('views')
for view in views:
_callable = view['introspectable']['callable'].__name__
_accept = view['introspectable']['accept']
_route_nm = view['introspectable']['route_name']
_route = rt_map.get_route(_route_nm)
if _route:
print 'Route: %s, Path: %s, Callable: %s, Accept: %s' % (_route.name, _route.path, _callable, _accept)
server = make_server('', httpport, app)
print 'Listening on port %i' % httpport
server.serve_forever()
```