Exemple #1
0
 def wsgi(self, environ, start_response):
     """ The bottle WSGI-interface. """
     try:
         out = self._cast(self._handle(environ))
         # rfc2616 section 4.3
         if http_wsgi.response._status_code in (
                 100, 101, 204, 304) or environ['REQUEST_METHOD'] == 'HEAD':
             if hasattr(out, 'close'):
                 out.close()
             out = []
         start_response(http_wsgi.response._status_line,
                        http_wsgi.response.headerlist)
         return out
     except (KeyboardInterrupt, SystemExit, MemoryError):
         raise
     except Exception:
         if not self.catchall: raise
         err = '<h1>Critical error while processing request: %s</h1>' \
               % http_wsgi.html_escape(environ.get('PATH_INFO', '/'))
         if settings.DEBUG:
             err += '<h2>Error:</h2>\n<pre>\n%s\n</pre>\n' \
                    '<h2>Traceback:</h2>\n<pre>\n%s\n</pre>\n' \
                    % (http_wsgi.html_escape(repr(settings._e())), http_wsgi.html_escape(settings.format_exc()))
         environ['wsgi.errors'].write(err)
         headers = [('Content-Type', 'text/html; charset=UTF-8')]
         start_response('500 INTERNAL SERVER ERROR', headers,
                        sys.exc_info())
         return [settings.tob(err)]
Exemple #2
0
 def build(self, _name, *anons, **query):
     ''' Build an URL by filling the wildcards in a rule. '''
     builder = self.builder.get(_name)
     if not builder:
         raise RouteBuildError("No route with that name.", _name)
     try:
         for i, value in enumerate(anons):
             query['anon%d' % i] = value
         url = ''.join([f(query.pop(n)) if n else f for (n, f) in builder])
         return url if not query else url + '?' + settings.urlencode(query)
     except KeyError:
         raise RouteBuildError('Missing URL argument: %r' %
                               settings._e().args[0])
Exemple #3
0
    def _handle(self, environ):
        path = environ['bottle.raw_path'] = environ['PATH_INFO']
        if settings.py3k:
            try:
                environ['PATH_INFO'] = path.encode('latin1').decode('utf8')
            except UnicodeError:
                return http_wsgi.HTTPError(
                    400, 'Invalid path string. Expected UTF-8')

        try:
            environ['bottle.app'] = self
            http_wsgi.request.bind(environ)
            http_wsgi.response.bind()
            try:
                self.trigger_hook('before_request')
                route, args = self.router.match(environ)
                environ['route.handle'] = route
                environ['bottle.route'] = route
                environ['route.url_args'] = args
                return route.call(**args)
            finally:
                self.trigger_hook('after_request')

        except http_wsgi.HTTPResponse:
            return settings._e()
        except routings.RouteReset:
            route.reset()
            return self._handle(environ)
        except (KeyboardInterrupt, SystemExit, MemoryError):
            raise
        except Exception:
            if not self.catchall: raise
            stacktrace = settings.format_exc()
            environ['wsgi.errors'].write(stacktrace)
            return http_wsgi.HTTPError(500, "Internal Server Error",
                                       settings._e(), stacktrace)
Exemple #4
0
        def wrapper(*a, **ka):
            try:
                rv = callback(*a, **ka)
            except http_wsgi.HTTPError:
                rv = settings._e()

            if isinstance(rv, dict):
                #Attempt to serialize, raises exception on failure
                json_response = dumps(rv)
                #Set content type only if serialization succesful
                http_wsgi.response.content_type = 'application/json'
                return json_response
            elif isinstance(rv, http_wsgi.HTTPResponse) and isinstance(
                    rv.body, dict):
                rv.body = dumps(rv.body)
                rv.content_type = 'application/json'
            return rv
Exemple #5
0
    def add(self, rule, method, target, name=None):
        ''' Add a new rule or replace the target for an existing rule. '''
        anons = 0  # Number of anonymous wildcards found
        keys = []  # Names of keys
        pattern = ''  # Regular expression pattern with named groups
        filters = []  # Lists of wildcard input filters
        builder = []  # Data structure for the URL builder
        is_static = True

        for key, mode, conf in self._itertokens(rule):
            if mode:
                is_static = False
                if mode == 'default':
                    mode = self.default_filter
                mask, in_filter, out_filter = self.filters[mode](conf)
                if not key:
                    pattern += '(?:%s)' % mask
                    key = 'anon%d' % anons
                    anons += 1
                else:
                    pattern += '(?P<%s>%s)' % (key, mask)
                    keys.append(key)
                if in_filter:
                    filters.append((key, in_filter))
                builder.append((key, out_filter or str))
            elif key:
                pattern += settings.re.escape(key)
                builder.append((None, key))

        self.builder[rule] = builder
        if name:
            self.builder[name] = builder

        if is_static and not self.strict_order:
            self.static.setdefault(method, {})
            self.static[method][self.build(rule)] = (target, None)
            return

        try:
            re_pattern = settings.re.compile('^(%s)$' % pattern)
            re_match = re_pattern.match
        except settings.re.error:
            raise RouteSyntaxError("Could not add Route: %s (%s)" %
                                   (rule, settings._e()))

        if filters:

            def getargs(path):
                url_args = re_match(path).groupdict()
                for name, wildcard_filter in filters:
                    try:
                        url_args[name] = wildcard_filter(url_args[name])
                    except ValueError:
                        raise http_wsgi.HTTPError(400,
                                                  'Path has wrong format.')
                return url_args
        elif re_pattern.groupindex:

            def getargs(path):
                return re_match(path).groupdict()
        else:
            getargs = None

        flatpat = _re_flatten(pattern)
        whole_rule = (rule, flatpat, target, getargs)

        if (flatpat, method) in self._groups:
            if settings.DEBUG:
                msg = 'Route <%s %s> overwrites a previously defined route'
                settings.warnings.warn(msg % (method, rule), RuntimeWarning)
            self.dyna_routes[method][self._groups[flatpat,
                                                  method]] = whole_rule
        else:
            self.dyna_routes.setdefault(method, []).append(whole_rule)
            self._groups[flatpat, method] = len(self.dyna_routes[method]) - 1

        self._compile(method)
Exemple #6
0
    def _cast(self, out, peek=None):
        """ Try to convert the parameter into something WSGI compatible and set
        correct HTTP headers when possible.
        Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like,
        iterable of strings and iterable of unicodes
        """

        # Empty output is done here
        if not out:
            if 'Content-Length' not in http_wsgi.response:
                http_wsgi.response['Content-Length'] = 0
            return []
        # Join lists of byte or unicode strings. Mixed lists are NOT supported
        if isinstance(out,
                      (tuple, list)) and isinstance(out[0],
                                                    (bytes, settings.unicode)):
            out = out[0][0:0].join(out)  # b'abc'[0:0] -> b''
        # Encode unicode strings
        if isinstance(out, settings.unicode):
            out = out.encode(http_wsgi.response.charset)
        # Byte Strings are just returned
        if isinstance(out, bytes):
            if 'Content-Length' not in http_wsgi.response:
                http_wsgi.response['Content-Length'] = len(out)
            return [out]
        # HTTPError or HTTPException (recursive, because they may wrap anything)
        # TODO: Handle these explicitly in handle() or make them iterable.
        if isinstance(out, http_wsgi.HTTPError):
            out.apply(http_wsgi.response)
            out = self.error_handler.get(out.status_code,
                                         self.default_error_handler)(out)
            return self._cast(out)
        if isinstance(out, http_wsgi.HTTPResponse):
            out.apply(http_wsgi.response)
            return self._cast(out.body)

        # File-like objects.
        if hasattr(out, 'read'):
            if 'wsgi.file_wrapper' in http_wsgi.request.environ:
                return http_wsgi.request.environ['wsgi.file_wrapper'](out)
            elif hasattr(out, 'close') or not hasattr(out, '__iter__'):
                return commons.WSGIFileWrapper(out)

        # Handle Iterables. We peek into them to detect their inner type.
        try:
            iout = iter(out)
            first = next(iout)
            while not first:
                first = next(iout)
        except StopIteration:
            return self._cast('')
        except http_wsgi.HTTPResponse:
            first = settings._e()
        except (KeyboardInterrupt, SystemExit, MemoryError):
            raise
        except Exception:
            if not self.catchall: raise
            first = http_wsgi.HTTPError(500, 'Unhandled exception',
                                        settings._e(), settings.format_exc())

        # These are the inner types allowed in iterator or generator objects.
        if isinstance(first, http_wsgi.HTTPResponse):
            return self._cast(first)
        elif isinstance(first, bytes):
            new_iter = settings.itertools.chain([first], iout)
        elif isinstance(first, settings.unicode):
            encoder = lambda x: x.encode(http_wsgi.response.charset)
            new_iter = settings.imap(encoder,
                                     settings.itertools.chain([first], iout))
        else:
            msg = 'Unsupported response type: %s' % type(first)
            return self._cast(http_wsgi.HTTPError(500, msg))
        if hasattr(out, 'close'):
            new_iter = commons._closeiter(new_iter, out.close)
        return new_iter