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