# -*- coding: utf-8 -*-
"""
Middlewares for request / response handling.
"""
# Copyright (C) 2015 ZetaOps Inc.
#
# This file is licensed under the GNU General Public License v3
# (GPLv3). See LICENSE.txt for details.
import json
import falcon
import sys
from zengine.config import settings
from zengine.log import log
[docs]class CORS(object):
"""
Sets required headers to allow origins for the hosts listed in
:attr:`~zengine.settings.ALLOWED_ORIGINS`
Note:
When :attr:`~zengine.settings.DEBUG` set to ``True``
all hosts are allowed
"""
[docs] def process_response(self, request, response, resource):
"""
Do response processing
"""
origin = request.get_header('Origin')
if not settings.DEBUG:
if origin in settings.ALLOWED_ORIGINS or not origin:
response.set_header('Access-Control-Allow-Origin', origin)
else:
log.debug("CORS ERROR: %s not allowed, allowed hosts: %s" % (origin,
settings.ALLOWED_ORIGINS))
raise falcon.HTTPForbidden("Denied", "Origin not in ALLOWED_ORIGINS: %s" % origin)
# response.status = falcon.HTTP_403
else:
response.set_header('Access-Control-Allow-Origin', origin or '*')
response.set_header('Access-Control-Allow-Credentials', "true")
response.set_header('Access-Control-Allow-Headers', 'Content-Type')
# This could be overridden in the resource level
response.set_header('Access-Control-Allow-Methods', 'OPTIONS')
[docs]class RequireJSON(object):
"""
Restrict only to JSON payloads.
"""
[docs] def process_request(self, req, resp):
"""
Do response processing
"""
if not req.client_accepts_json:
raise falcon.HTTPNotAcceptable(
'This API only supports responses encoded as JSON.',
href='http://docs.examples.com/api/json')
if req.method in ('POST', 'PUT'):
if req.content_length != 0 and \
'application/json' not in req.content_type and \
'text/plain' not in req.content_type:
raise falcon.HTTPUnsupportedMediaType(
'This API only supports requests encoded as JSON.',
href='http://docs.examples.com/api/json')
[docs]class JSONTranslator(object):
"""
Deserializes JSON payload into ``request.context['data']``
"""
[docs] def process_request(self, req, resp):
"""
Do response processing
"""
# req.stream corresponds to the WSGI wsgi.input environ variable,
# and allows you to read bytes from the request body.
#
# See also: PEP 3333
if req.content_length in (None, 0):
# Nothing to do
req.context['data'] = req.params.copy()
req.context['result'] = {}
return
else:
req.context['result'] = {}
body = req.stream.read()
if not body:
raise falcon.HTTPBadRequest('Empty request body',
'A valid JSON document is required.')
try:
json_data = body.decode('utf-8')
req.context['data'] = json.loads(json_data)
try:
log.info("REQUEST DATA: %s" % json_data)
except:
log.exception("ERR: REQUEST DATA CANT BE LOGGED ")
except (ValueError, UnicodeDecodeError):
raise falcon.HTTPError(falcon.HTTP_753,
'Malformed JSON',
'Could not decode the request body. The '
'JSON was incorrect or not encoded as '
'UTF-8.')
[docs] def process_response(self, req, resp, resource):
"""
Serializes ``req.context['result']`` to resp.body as JSON.
If :attr:`~zengine.settings.DEBUG` is True,
``sys._debug_db_queries`` (set by pyoko) added to response.
"""
if 'result' not in req.context:
return
req.context['result']['is_login'] = 'user_id' in req.env['session']
if settings.DEBUG:
req.context['result']['_debug_queries'] = sys._debug_db_queries
sys._debug_db_queries = []
if resp.body is None and req.context['result']:
resp.body = json.dumps(req.context['result'])
try:
log.debug("RESPONSE: %s" % resp.body)
except:
log.exception("ERR: RESPONSE CANT BE LOGGED ")