0001import re
0002import urllib
0003import traceback
0004from Cookie import SimpleCookie
0005from paste.util import classinit
0006from paste.util import classinstance
0007from paste import httpexceptions
0008from paste import wsgilib
0009import cgifields
0010import event
0011import cookiewriter
0012
0013__all__ = ['Servlet']
0014
0015class ForwardRequest(Exception):
0016    def __init__(self, app):
0017        self.application = app
0018
0019class Servlet(object):
0020
0021    encoding = 'utf8'
0022    app_name = 'app'
0023    listeners = []
0024    _title = None
0025    _html_title = None
0026
0027    __metaclass__ = classinit.ClassInitMeta
0028
0029    def __classinit__(cls, new_attrs):
0030        if not new_attrs.has_key('listeners'):
0031            cls.listeners = cls.listeners[:]
0032        for attr, value in new_attrs.items():
0033            cls.add_attr(attr, value, set=False)
0034        classinit.build_properties(cls, new_attrs)
0035
0036    @classmethod
0037    def add_attr(cls, attr, value, set=True):
0038        if set:
0039            setattr(cls, attr, value)
0040        if hasattr(value, '__addtoclass__'):
0041            value.__addtoclass__(attr, cls)
0042
0043    def __call__(self, environ, start_response):
0044        try:
0045            status, headers, app_iter = self._process(
0046                environ, start_response)
0047        except ForwardRequest, e:
0048            return e.application(environ, start_response)
0049        else:
0050            start_response(status, headers)
0051            return app_iter
0052
0053    def _process(self, environ, start_response):
0054        value = event.raise_event('call', self, environ, start_response)
0055        if value is not event.Continue:
0056            status, headers, app_iter = value
0057            return status, headers, app_iter
0058        self.environ = environ
0059        self.config = self.environ.get('paste.config', {})
0060        if self.config.get('app_name'):
0061            self.app_name = self.config['app_name']
0062        self._cached_output = []
0063        self.headers_out = {
0064            'content-type': 'text/html; charset=%s' % self.encoding}
0065        self.status = '200 OK'
0066        self._cookies_out = {}
0067        self.request_method = environ['REQUEST_METHOD'].upper()
0068        self.app_url = self.environ.get('%s.base_url' % self.app_name, '')
0069        self.app_static_url = self.config.get(
0070            'static_url', self.app_url + '/static')
0071        self.path_info = self.environ.get('PATH_INFO', '')
0072        if self.path_info:
0073            self.path_parts = filter(None, self.path_info[1:].split('/'))
0074        else:
0075            # Note that you have to look at self.path_info to
0076            # distinguish between '' and '/'
0077            self.path_parts = []
0078        self.fields = cgifields.Fields(wsgilib.parse_formvars(environ))
0079        self.cookies = {}
0080        if 'HTTP_COOKIE' in environ:
0081            cookies = SimpleCookie()
0082            try:
0083                cookies.load(environ['HTTP_COOKIE'])
0084            except:
0085                traceback.print_exc(file=self._environ['wsgi.errors'])
0086            for key in cookies.keys():
0087                self.cookies[key] = cookies[key].value
0088        self.run()
0089        headers = []
0090        for name, value in self.headers_out.items():
0091            if isinstance(value, list):
0092                for v in value:
0093                    headers.append((name, v))
0094            else:
0095                headers.append((name, value))
0096        for cookie in self._cookies_out.values():
0097            headers.append(('Set-Cookie', cookie.header()))
0098        return self.status, headers, self._cached_output
0099
0100    @event.wrap_func
0101    def run(self):
0102        __traceback_hide__ = 'before_and_this'
0103        try:
0104            event.wrap_func(self.awake.im_func)(self)
0105            event.wrap_func(self.respond.im_func)(self)
0106        finally:
0107            event.wrap_func(self.sleep.im_func)(self)
0108
0109    def awake(self, call_setup=True):
0110        if call_setup:
0111            self.setup()
0112
0113    def setup(self):
0114        pass
0115
0116    def respond(self):
0117        pass
0118
0119    def sleep(self, call_teardown=True):
0120        if call_teardown:
0121            self.teardown()
0122
0123    def teardown(self):
0124        pass
0125
0126    ############################################################
0127    ## Request
0128    ############################################################
0129
0130    def session__get(self):
0131        if 'paste.session.factory' in self.environ:
0132            sess = self.environ['paste.session.factory']()
0133        elif 'paste.flup_session_service' in self.environ:
0134            sess = self.environ['paste.flup_session_service'].session
0135        self.__dict__['session'] = sess
0136        return sess
0137
0138    ############################################################
0139    ## Response
0140    ############################################################
0141
0142    def title__get(self):
0143        return self._title or self.__class__.__name__
0144
0145    def title__set(self, value):
0146        self._title = value
0147
0148    def html_title__get(self):
0149        return self._html_title or self.title
0150
0151    def html_title__set(self, value):
0152        self._html_title = value
0153
0154    def set_cookie(self, cookie_name, value, path='/',
0155                   expires='ONCLOSE', secure=False):
0156        c = cookiewriter.Cookie(cookie_name, value, path=path,
0157                                expires=expires, secure=secure)
0158        self._cookies_out[cookie_name] = c
0159
0160    def set_header(self, header_name, header_value):
0161        header_name = header_name.lower()
0162        if header_name == 'status':
0163            self.status = header_value
0164            return
0165        self.headers_out[header_name] = header_value
0166
0167    def add_header(self, header_name, header_value):
0168        header_name = header_name.lower()
0169        if header_name == 'status':
0170            self.status = header_value
0171            return
0172        if self.headers_out.has_key(header_name):
0173            if not isinstance(self.headers_out[header_name], list):
0174                self.headers_out[header_name] = [self.headers_out[header_name],
0175                                             header_value]
0176            else:
0177                self.headers_out[header_name].append(header_value)
0178        else:
0179            self.headers_out[header_name] = header_value
0180
0181    def write(self, *obj):
0182        for v in obj:
0183            if v is None:
0184                continue
0185            elif isinstance(v, str):
0186                self._cached_output.append(v)
0187            elif isinstance(v, unicode):
0188                self._cached_output.append(
0189                    v.encode(self.encoding))
0190            else:
0191                self._cached_output.append(
0192                    unicode(v).encode(self.encoding))
0193
0194    def redirect(self, url, **query_vars):
0195        url = str(url)
0196        if not url.startswith('/') and not abs_regex.search(url):
0197            url = self.app_url + '/' + url
0198        if 'status' in query_vars:
0199            status = query_vars.pop('status')
0200            if isinstance(status, (str, unicode)):
0201                status = int(status.split()[0])
0202        else:
0203            status = 303
0204        if query_vars:
0205            if '?' in url:
0206                url += '&'
0207            else:
0208                url += '?'
0209            url += urllib.urlencode(query_vars)
0210        raise httpexceptions.get_exception(status)(
0211            "This resource has been redirected",
0212            headers=[('Location', url)])
0213
0214    def forward_to_wsgiapp(self, app):
0215        """
0216        Forwards the request to the given WSGI application
0217        """
0218        raise ForwardRequest(app)
0219
0220    @classinstance.classinstancemethod
0221    def wsgi_application(self, cls, environ, application):
0222        if self is not None:
0223            return self(environ, application)
0224        else:
0225            return cls()(environ, application)
0226
0227abs_regex = re.compile(r'^[a-zA-Z]+:')