Implement ECP in Ipsilon
[cascardo/ipsilon.git] / ipsilon / util / http.py
1 # Copyright (C) 2015 Ipsilon project Contributors, for license see COPYING
2
3 import cherrypy
4 import fnmatch
5
6
7 def require_content_type(required=None, absent_ok=True, debug=False):
8     '''CherryPy Tool that validates request Content-Type.
9
10     This is a CherryPy Tool that checks the Content-Type in a request and
11     raises HTTP Error 415 "Unsupported Media Type" if it does not match.
12
13     The tool accepts a glob style pattern or list of patterns (see fnmatch)
14     and verifies the Content-Type in the request matches at least one of
15     the patterns, if not a HTTP Error 415 "Unsupported Media Type" is raised.
16
17     If absent_ok is False and if the request does not contain a
18     Content-Type header a HTTP Error 415 "Unsupported Media Type" is
19     raised.
20
21     The tool may be deployed use any of the standard methods for
22     invoking CherryPy tools, for example as a decorator:
23
24     @cherrypy.tools.require_content_type(required='text/xml')
25     def POST(self, *args, **kwargs):
26         pass
27
28     :param required: May be a single string or a list of strings. Each
29            string is interpreted as a glob style pattern (see fnmatch).
30            The Content-Type must match at least one pattern.
31
32     :param absent_ok: Boolean specifying if the Content-Type header
33            must be present or if it is OK to be absent.
34
35     '''
36     if required is None:
37         return
38
39     if isinstance(required, basestring):
40         required = [required]
41
42     content_type = cherrypy.request.body.content_type.value
43     pattern = None
44     match = False
45     if content_type:
46         for pattern in required:
47             if fnmatch.fnmatch(content_type, pattern):
48                 match = True
49                 break
50     else:
51         if absent_ok:
52             return
53
54     if debug:
55         cherrypy.log('require_content_type: required=%s, absent_ok=%s '
56                      'content_type=%s match=%s pattern=%s' %
57                      required, absent_ok, content_type, match, pattern)
58
59     if not match:
60         acceptable = ', '.join(['"%s"' % x for x in required])
61         if content_type:
62             content_type = '"%s"' % content_type
63         else:
64             content_type = 'not specified'
65         message = ('Content-Type must match one of following patterns [%s], '
66                    'but the Content-Type was %s' %
67                    (acceptable, content_type))
68         raise cherrypy.HTTPError(415, message=message)