1# Copyright 2011 OpenStack Foundation.
2# All Rights Reserved.
3#
4#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5#    not use this file except in compliance with the License. You may obtain
6#    a copy of the License at
7#
8#         http://www.apache.org/licenses/LICENSE-2.0
9#
10#    Unless required by applicable law or agreed to in writing, software
11#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13#    License for the specific language governing permissions and limitations
14#    under the License.
15
16"""Base class(es) for WSGI Middleware."""
17
18from inspect import getfullargspec
19from oslo_config import cfg
20import webob.dec
21import webob.request
22import webob.response
23
24
25class NoContentTypeResponse(webob.response.Response):
26
27    default_content_type = None  # prevents webob assigning content type
28
29
30class NoContentTypeRequest(webob.request.Request):
31
32    ResponseClass = NoContentTypeResponse
33
34
35class ConfigurableMiddleware(object):
36    """Base WSGI middleware wrapper.
37
38    These classes require an application to be initialized that will be called
39    next.  By default the middleware will simply call its wrapped app, or you
40    can override __call__ to customize its behavior.
41    """
42
43    @classmethod
44    def factory(cls, global_conf, **local_conf):
45        """Factory method for paste.deploy.
46
47        :param global_conf: dict of options for all middlewares
48                            (usually the [DEFAULT] section of the paste deploy
49                            configuration file)
50        :param local_conf: options dedicated to this middleware
51                           (usually the option defined in the middleware
52                           section of the paste deploy configuration file)
53        """
54        conf = global_conf.copy() if global_conf else {}
55        conf.update(local_conf)
56
57        def middleware_filter(app):
58            return cls(app, conf)
59
60        return middleware_filter
61
62    def __init__(self, application, conf=None):
63        """Base middleware constructor
64
65        :param  conf: a dict of options or a cfg.ConfigOpts object
66        """
67        self.application = application
68
69        # NOTE(sileht): If the configuration come from oslo.config
70        # just use it.
71        if isinstance(conf, cfg.ConfigOpts):
72            self.conf = {}
73            self.oslo_conf = conf
74        else:
75            self.conf = conf or {}
76            if "oslo_config_project" in self.conf:
77                if 'oslo_config_file' in self.conf:
78                    default_config_files = [self.conf['oslo_config_file']]
79                else:
80                    default_config_files = None
81
82                if 'oslo_config_program' in self.conf:
83                    program = self.conf['oslo_config_program']
84                else:
85                    program = None
86
87                self.oslo_conf = cfg.ConfigOpts()
88                self.oslo_conf([],
89                               project=self.conf['oslo_config_project'],
90                               prog=program,
91                               default_config_files=default_config_files,
92                               validate_default_values=True)
93
94            else:
95                # Fallback to global object
96                self.oslo_conf = cfg.CONF
97
98    def _conf_get(self, key, group="oslo_middleware"):
99        if key in self.conf:
100            # Validate value type
101            self.oslo_conf.set_override(key, self.conf[key], group=group)
102        return getattr(getattr(self.oslo_conf, group), key)
103
104    @staticmethod
105    def process_request(req):
106        """Called on each request.
107
108        If this returns None, the next application down the stack will be
109        executed. If it returns a response then that response will be returned
110        and execution will stop here.
111        """
112        return None
113
114    @staticmethod
115    def process_response(response, request=None):
116        """Do whatever you'd like to the response."""
117        return response
118
119    @webob.dec.wsgify(RequestClass=NoContentTypeRequest)
120    def __call__(self, req):
121        response = self.process_request(req)
122        if response:
123            return response
124        response = req.get_response(self.application)
125
126        args = getfullargspec(self.process_response)[0]
127        if 'request' in args:
128            return self.process_response(response, request=req)
129        return self.process_response(response)
130
131
132class Middleware(ConfigurableMiddleware):
133    """Legacy base WSGI middleware wrapper.
134
135    Legacy interface that doesn't pass configuration options
136    to the middleware when it's loaded via paste.deploy.
137    """
138
139    @classmethod
140    def factory(cls, global_conf, **local_conf):
141        """Factory method for paste.deploy."""
142        return cls
143