diff options
Diffstat (limited to 'ipsilon/util/http.py')
-rw-r--r-- | ipsilon/util/http.py | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/ipsilon/util/http.py b/ipsilon/util/http.py new file mode 100644 index 0000000..fa9d725 --- /dev/null +++ b/ipsilon/util/http.py @@ -0,0 +1,68 @@ +# Copyright (C) 2015 Ipsilon project Contributors, for license see COPYING + +import cherrypy +import fnmatch + + +def require_content_type(required=None, absent_ok=True, debug=False): + '''CherryPy Tool that validates request Content-Type. + + This is a CherryPy Tool that checks the Content-Type in a request and + raises HTTP Error 415 "Unsupported Media Type" if it does not match. + + The tool accepts a glob style pattern or list of patterns (see fnmatch) + and verifies the Content-Type in the request matches at least one of + the patterns, if not a HTTP Error 415 "Unsupported Media Type" is raised. + + If absent_ok is False and if the request does not contain a + Content-Type header a HTTP Error 415 "Unsupported Media Type" is + raised. + + The tool may be deployed use any of the standard methods for + invoking CherryPy tools, for example as a decorator: + + @cherrypy.tools.require_content_type(required='text/xml') + def POST(self, *args, **kwargs): + pass + + :param required: May be a single string or a list of strings. Each + string is interpreted as a glob style pattern (see fnmatch). + The Content-Type must match at least one pattern. + + :param absent_ok: Boolean specifying if the Content-Type header + must be present or if it is OK to be absent. + + ''' + if required is None: + return + + if isinstance(required, basestring): + required = [required] + + content_type = cherrypy.request.body.content_type.value + pattern = None + match = False + if content_type: + for pattern in required: + if fnmatch.fnmatch(content_type, pattern): + match = True + break + else: + if absent_ok: + return + + if debug: + cherrypy.log('require_content_type: required=%s, absent_ok=%s ' + 'content_type=%s match=%s pattern=%s' % + required, absent_ok, content_type, match, pattern) + + if not match: + acceptable = ', '.join(['"%s"' % x for x in required]) + if content_type: + content_type = '"%s"' % content_type + else: + content_type = 'not specified' + message = ('Content-Type must match one of following patterns [%s], ' + 'but the Content-Type was %s' % + (acceptable, content_type)) + raise cherrypy.HTTPError(415, message=message) |