def date(dt):
    return dt.strftime("%a, %d %b %Y %H:%M:%S GMT") SERVER      = 'HTTPants (pants/%s)' % pants_version def abort(status=404, message=None, headers=None):
    """
    Raise a :class:`~pants.contrib.web.HTTPException` to display an error page.
    """
    raise HTTPException(status, message, headers) class Application(Module):
    """
    The Application class builds upon the :class:`Module` class and acts as a
    request handler for the :class:`~pants.http.server.HTTPServer`, with request
    routing, error handling, and a degree of convenience that makes sending
    output easier.

    Instances of Application are callable, and should be used as a HTTPServer's
    request handler.

    =========  ================================================================
    Argument   Description
    =========  ================================================================
    debug      *Optional.* If this is set to True, the automatically generated
               ``500 Internal Server Error`` pages will display additional
               debugging information.
    =========  ================================================================
    """
    current_app = None
    request = None

    def __init__(self, name=None, debug=False, fix_end_slash=False):
        super(Application, self).__init__(name)

        # Internal Stuff
        self._route_table = {}
        self._route_list = []
        self._name_table = {}

        # External Stuff
        self.json_encoder = JSONEncoder
        self.debug = debug
        self.fix_end_slash = fix_end_slash

    def run(self, address=None, ssl_options=None, engine=None):
        """
        This function exists for convenience, and when called creates a
        :class:`~pants.http.server.HTTPServer` instance with its request
        handler set to this application instance, calls
        :func:`~pants.http.server.HTTPServer.listen` on that HTTPServer, and
        finally, starts the Pants engine to process requests.

        ============  ============
        Argument      Description
        ============  ============
        address       *Optional.* The address to listen on. If this isn't specified, it will default to ``('', 80)``.
        ssl_options   *Optional.* A dictionary of SSL options for the server. See :meth:`pants.server.Server.startSSL` for more information.
        engine        *Optional.* The :class:`pants.engine.Engine` instance to use.
        ============  ============
        """
        if not engine:
            from pants.engine import Engine
            engine = Engine.instance()

        HTTPServer(self, ssl_options=ssl_options, engine=engine).listen(address)
        engine.start()

    ##### Error Handlers ######################################################

    def handle_404(self, request, err):
        if isinstance(err, HTTPException):
            return error(err.message, 404, request=request)
        return error(404, request=request)

    def handle_500(self, request, err):
        log.exception("Error handling HTTP request: %s %s" %
                      (request.method, request.url))
        if not self.debug:
            return error(500, request=request)

        # See if we can highlight the traceback.
        tb = getattr(request, '_tb', None) or traceback.format_exc()

        # Try to highlight the traceback.
        if hasattr(self, 'highlight_traceback'):
            try:
                tb = self.highlight_traceback(request, err, tb)
                if not u"<pre>" in tb:
                    tb = u"<pre>%s</pre>" % tb
            except Exception as err:
                log.exception("Error in highlight_traceback for %r." % self)
                tb = u"<pre>%s</pre>" % tb
        else:
            tb = u"<pre>%s</pre>" % tb

        response = u"\n".join([
            u"<h2>Traceback</h2>", tb,
            #u'<div id="console"><script type="text/javascript">%s</script></div>' % CONSOLE_JS,
            u"<h2>Route</h2>",
            u"<pre>route name   = %r" % getattr(request, "route_name", None),
            u"match groups = %r" % (request.match.groups() if request.match else None,),
            (u"match values = %r</pre>" % request._converted_match) if hasattr(request, '_converted_match') else u"</pre>",
            u"<h2>HTTP Request</h2>",
            request.__html__()
        ])

        return error(response, 500, request=request)

    ##### Routing Table Builder ###############################################

    def _recalculate_routes(self, processed=None, path=None, module=None,
                            nameprefix="", hooks=None):
        """
        This function does the heavy lifting of building the routing table, and
        it's called every time a route is updated. Fortunately, that generally
        only happens when the application is being created.
        """
        if path is None:
            # Initialize our storage variables.
            self._route_list = []
            self._route_table = {}
            self._name_table = {}

        # Get the unprocessed route table.
        routes = module._routes if module else self._routes
        modules = module._modules if module else self._modules
        mod_hooks = module.hooks if module else self.hooks

        # Update the name prefix.
        name = module.name if module else self.name
        if name:
            nameprefix = nameprefix + "." + name if nameprefix else name

        # Update the hooks system.
        if hooks:
            new_hooks = {}
            for k, v in hooks.iteritems():
                new_hooks[k] = v[:]
            hooks = new_hooks
        else:
            hooks = {}

        for k,v in mod_hooks.iteritems():
            if k in hooks:
                hooks[k].extend(v)
            else:
                hooks[k] = v[:]

        # Iterate through modules first, so our own rules are more important.
        for rule, mod in modules.iteritems():
            self._recalculate_routes(None, rule, mod, nameprefix, hooks)

        # Iterate through the unprocessed route table.
        for rule, table in routes.iteritems():
            # If path is set, and this isn't an absolute rule, merge the rule
            # with the path.
            if path and (rule[0] == "/" or not "/" in rule):
                if path[-1] == "/":
                    if rule[0] == "/":
                        rule = path + rule[1:]
                    else:
                        rule = path + rule
                else:
                    if rule[0] == "/":
                        rule = path + rule
                    else:
                        rule = path + "/" + rule

            # Parse the rule string.
            regex, converters, names, namegen, domain, rpath = \
                _rule_to_regex(rule)
            dkey, rkey = rule.split("/", 1)

            # Get the domain table.
            if not dkey in self._route_table:
                dt = self._route_table[dkey] = {}
                dl = dt[None] = [domain, dkey, []]
                self._route_list.append(dl)
            else:
                dt = self._route_table[dkey]
                dl = dt[None]

            # Determine if this is a new rule for the given domain.
            if not rkey in dt:
                rt = dt[rkey] = {}
                rl = rt[None] = [rpath, re.compile(regex), rkey, None, {},
                                 names, namegen, converters]
                dl[2].append(rl)
            else:
                rt = dt[rkey]
                rl = rt[None]

            # Get the method table
            method_table = rl[4]

            # Iterate through all the methods this rule provides.
            for method, (func, name, advanced, auto404, headers, content_type) \
                    in table.iteritems():
                method = method.upper()
                if method == 'GET' or rl[3] is None:
                    if nameprefix:
                        name = nameprefix + '.' + name
                    rl[3] = name
                if advanced:
                    for mthd in method_table:
                        if getattr(method_table[mthd], "wrapped_func", None) \
                                is func:
                            method_table[method] = method_table[mthd], \
                                                   headers, content_type
                            break
                    else:
                        method_table[method] = _get_runner(func, converters,
                                                            auto404), headers, \
                                                            content_type, hooks
                else:
                    method_table[method] = func, headers, content_type, hooks

            # Update the name table.
            self._name_table[rl[3]] = rl

        if path is None:
            # Sort everything.

            # Sort the domains first by the length of the domain key, in reverse
            # order; followed by the number of colons in the domain key, in
            # reverse order; and finally by the domain key alphabetically.
            self._route_list.sort(key=lambda x: (-len(x[1]), -(x[1].count(':')),
                                                 x[1]))

            # Sort the same way for each rule in each domain, but using the rule
            # key rather than the domain key.
            for domain, dkey, rl in self._route_list:
                rl.sort(key=lambda x: (-len(x[2]), -(x[2].count(':')), x[2]))

    ##### The Request Handler #################################################

    def __call__(self, request):
        """
        This function is called when a new request is received, and uses the
        method :meth:`Application.route_request` to select and execute the
        proper request handler, and then the method
        :meth:`Application.parse_output` to process the handler's output.
        """
        Application.current_app = self
        self.request = request

        try:
            request.auto_finish = True
            result = self.route_request(request)
            if request.auto_finish:
                self.parse_output(result)

        except Exception as err:
            # This should hopefully never happen, but it *could*.
            try:
                body, status, headers = self.handle_500(request, err)
            except Exception:
                # There's an error with our handle_500.
                log.exception("There was a problem handling a request, "
                              "and a problem running Application.handle_500 "
                              "for %r." % self)
                body, status, headers = error(500, request=request)

                # If an exception happens at *this* point, it's destined. Just
                # show the ugly page.

            if not 'Content-Length' in headers:
                headers['Content-Length'] = len(body)

            request.send_status(status)
            request.send_headers(headers)
            request.write(body)
            request.finish()

        finally:
            if hasattr(request, '_hooks'):
                hks = request._hooks.get('request_teardown')
                if hks:
                    for hf in hks:
                        try:
                            hf(request)
                        except Exception as err:
                            # Log the exception, but continue.
                            log.exception("There was a problem handling a "
                                          "request teardown hook for: %r",
                                            request)

            if hasattr(request, '_converted_match'):
                del request._converted_match

            Application.current_app = None
            self.request = None

    def route_request(self, request):
        """
        Determine which request handler to use for the given request, execute
        that handler, and return its output.
        """
        domain = request.hostname
        path = urllib.unquote_plus(request.path)
        matcher = domain + path
        method = request.method.upper()
        available_methods = set()

        request._rule_headers = None
        request._rule_content_type = None

        for dmn, dkey, rules in self._route_list:
            # Do basic domain matching.
            if ':' in dmn:
                if not request.host.lower().endswith(dmn):
                    continue
            elif not domain.endswith(dmn):
                continue

            # Iterate through the available rules, trying for a match.
            for rule, regex, rkey, name, method_table, names, namegen, \
                    converters in rules:
                if not path.startswith(rule):
                    continue
                match = regex.match(matcher)
                if match is None:
                    continue

                # We have a match. Check for a valid method.
                if not method in method_table:
                    available_methods.update(method_table.keys())
                    continue

                # It's a match. Run the method and return the result.
                request.route_name = name
                request.match = match

                try:
                    func, headers, content_type, hooks = method_table[method]
                    request._rule_headers = headers
                    request._rule_content_type = content_type
                    request._hooks = hooks

                    hks = hooks.get('request_started')
                    if hks:
                        for hf in hks:
                            hf(request)

                    output = func(request)

                    if request.auto_finish:
                        hks = hooks.get('request_finished')
                        if hks:
                            # Make sure the request_finished handler always gets
                            # an instance of Response. This way, it's always
                            # possible for it to be changed without taking
                            # return values.
                            if not isinstance(output, Response):
                                if isinstance(output, tuple):
                                    out = Response(*output)
                                else:
                                    out = Response(output)

                            for hf in hks:
                                hf(request, output)

                    return output

                except HTTPException as err:
                    request._rule_headers = None
                    request._rule_content_type = None

                    err_handler = getattr(self, "handle_%d" % err.status, None)
                    if err_handler:
                        return err_handler(request, err)
                    else:
                        return error(err.message, err.status, err.headers,
                                     request=request)
                except HTTPTransparentRedirect as err:
                    request._rule_headers = None
                    request._rule_content_type = None

                    request.url = err.url
                    request._parse_url()
                    return self.route_request(request)
                except Exception as err:
                    request._rule_headers = None
                    request._rule_content_type = None

                    return self.handle_500(request, err)

        if available_methods:
            if request.method == 'OPTIONS':
                return '', 200, {'Allow': ', '.join(available_methods)}
            else:
                return error(
                    "The method %s is not allowed for %r." % (method, path),
                    405, {'Allow': ', '.join(available_methods)}
                )

        elif self.fix_end_slash:
            # If there are no matching routes, and the path doesn't end with a
            # slash, try adding the slash.
            if not path[-1] == "/":
                path += "/"
                matcher += "/"
                for dmn, dkey, rules in self._route_list:
                    if ':' in dmn:
                        if not request.host.lower().endswith(dmn):
                            continue
                    elif not domain.endswith(dmn):
                        continue

                    for rule, regex, rkey, name, method_table, names, namegen, \
                            converters in rules:
                        if not path.startswith(rule):
                            continue
                        if regex.match(matcher):
                            if request.query:
                                return redirect("%s?%s" %
                                                (path, request.query))
                            else:
                                return redirect(path)

        return self.handle_404(request, None)

    def parse_output(self, result):
        """ Process the output of :meth:`Application.route_request`. """
        request = self.request

        if not request.auto_finish or request._finish is not None:
            return

        status = None

        if isinstance(result, Response):
            body = result.body
            status = result.status
            headers = result.headers

        elif isinstance(result, tuple):
            if len(result) == 3:
                body, status, headers = result
            else:
                body, status = result
                headers = HTTPHeaders()
                
        else:
            body = result
            headers = HTTPHeaders()

        # If we don't have a body, use a 204.
        if status is None:
            if body is None:
                status = 204
            else:
                status = 200

        # Use the rule headers stuff.
        if request._rule_headers:
            if isinstance(request._rule_headers, HTTPHeaders):
                rule_headers = request._rule_headers.copy()
            else:
                rule_headers = HTTPHeaders(request._rule_headers)

            if isinstance(headers, HTTPHeaders):
                rule_headers._data.update(headers._data)
            else:
                rule_headers.update(headers)

            headers = rule_headers

        # Determine if we're sending a body.
        send_body = request.method.upper() != 'HEAD' and status not in NO_BODY_CODES

        # Convert the body to something that we can send.
        if send_body:
            # Use the rule content-type.
            if request._rule_content_type and not 'Content-Type' in headers:
                headers['Content-Type'] = request._rule_content_type

            try:
                body = body.to_html()
                if not 'Content-Type' in headers:
                    headers['Content-Type'] = 'text/html'
            except AttributeError:
                pass

            # Set a Content-Type header if there isn't one already.
            if not 'Content-Type' in headers:
                if isinstance(body, basestring) and body[:5].lower() in \
                        ('<html', '<!doc'):
                    headers['Content-Type'] = 'text/html'
                elif isinstance(body, (tuple, list, dict)):
                    headers['Content-Type'] = 'application/json'
                else:
                    headers['Content-Type'] = 'text/plain'

            if isinstance(body, unicode):
                encoding = headers['Content-Type']
                if 'charset=' in encoding:
                    before, sep, enc = encoding.partition('charset=')
                else:
                    before = encoding
                    sep = '; charset='
                    enc = 'UTF-8'

                body = body.encode(enc)
                headers['Content-Type'] = before + sep + enc

            elif isinstance(body, (tuple, list, dict)):
                try:
                    body = json.dumps(body, cls=self.json_encoder)
                except Exception as err:
                    body, status, headers = self.handle_500(request, err)
                    body = body.encode('utf-8')
                    headers['Content-Type'] = 'text/html; charset=UTF-8'

            elif body is None:
                body = ''

            elif not isinstance(body, str):
                body = str(body)

            # More headers!
            if not 'Content-Length' in headers:
                headers['Content-Length'] = len(body)

        else:
            # We're not allowed to send the body, so strip out any headers about
            # the content of the body.
            if 'Content-Length' in headers:
                del headers['Content-Length']

            if 'Content-Type' in headers:
                del headers['Content-Type']

            if 'Transfer-Encoding' in headers:
                del headers['Transfer-Encoding']

        # Send the response.
        request.send_status(status)
        request.send_headers(headers)

        if send_body:
            request.write(body)

        request.finish() def redirect(url, status=302, request=None):
    """
    Construct a ``302 Found`` response to instruct the client's browser to
    redirect its request to a different URL. Other codes may be returned by
    specifying a status.

    =========  ========  ============
    Argument   Default   Description
    =========  ========  ============
    url                  The URL to redirect the client's browser to.
    status     ``302``   *Optional.* The status code to send with the response.
    =========  ========  ============
    """
    if isinstance(url, unicode):
        url = url.encode('utf-8')

    return error(
        'The document you have requested is located at <a href="%s">%s</a>.' % (
            url, url), status, {'Location': url}, request=request) DATE_FORMATS = (
    "%a, %d %b %Y %H:%M:%S %Z",
    "%A, %d-%b-%y %H:%M:%S %Z",
    "%a %b %d %H:%M:%S %Y",
    ) def decode(text):
    for enc in _encodings:
        try:
            return text.decode(enc)
        except UnicodeDecodeError:
            continue
    else:
        return text.decode('utf-8','ignore') DIRECTORY_ENTRY = resource_string("pants.web", "data/entry.html") DIRECTORY_PAGE = PAGE.safe_substitute(
                        title="Index of $path",
                        content=resource_string("pants.web",
                                                "data/directory.html"),
                        extra_css=DIRECTORY_CSS
                        ) class HTTPException(Exception):
    """
    Raising an instance of HTTPException will cause the Application to render
    an error page out to the client with the given
    `HTTP status code <http://en.wikipedia.org/wiki/List_of_HTTP_status_codes>`_,
    message, and any provided headers.

    This is, generally, preferable to allowing an exception of a different
    type to bubble up to the Application, which would result in a
    ``500 Internal Server Error`` page.

    The :func:`abort` helper function makes it easy to raise instances of
    this exception.

    =========  ============
    Argument   Description
    =========  ============
    status     *Optional.* The `HTTP status code <http://en.wikipedia.org/wiki/List_of_HTTP_status_codes>`_ to generate an error page for. If this isn't specified, a ``404 Not Found`` page will be generated.
    message    *Optional.* A text message to display on the error page.
    headers    *Optional.* A dict of extra HTTP headers to return with the rendered page.
    =========  ============
    """
    def __init__(self, status=404, message=None, headers=None):
        super(HTTPException, self).__init__(status, message, headers)

    def __str__(self):
        return "%d %s [message=%r, headers=%r]" % \
            (self.status, HTTP.get(self.status, ''), self.args[1], self.args[2])

    def __repr__(self):
        return "HTTPException(status=%r, message=%r, headers=%r)" % self.args

    @property
    def status(self):
        return self.args[0]

    @property
    def message(self):
        return self.args[1]

    @property
    def headers(self):
        return self.args[2]
import mimetypes
import os
import re
import sys
import time
import urllib
        import win32api, win32con
    import optparse
from datetime import datetime, timedelta
from pants.http.utils import date, SERVER
from pants.web.application import abort, Application, redirect
from pants.web.utils import DATE_FORMATS, decode, DIRECTORY_ENTRY, \
    DIRECTORY_PAGE, HTTPException
            path = decode(path)

        self.path = os.path.normpath(os.path.realpath(path))
        self.defaults = defaults

        # Build the blacklist.
        self.blacklist = []
        for bl in blacklist:
            if isinstance(bl, str):
                bl = re.compile(bl)
            self.blacklist.append(bl)

    def attach(self, app, path='/static/'):
        """
        Attach this FileServer to an :class:`~pants.web.application.Application`,
        bypassing the usual route decorator to ensure the rule is configured as
        FileServer expects.

        =========  ===============  ============
        Argument   Default          Description
        =========  ===============  ============
        app                         The :class:`~pants.contrib.web.Application` instance to attach to.
        rule       ``'/static/'``   *Optional.* The path to serve requests from.
        =========  ===============  ============
        """
        if not path.endswith("/"):
            path += '/'
        app.basic_route(path + '<regex("(.*)"):path>', func=self)

    def check_blacklist(self, path):
        """
        Check the given path to make sure it isn't blacklisted. If it is
        blacklisted, then raise an :class:`~pants.contrib.web.HTTPException`
        via :func:`~pants.contrib.web.abort`.

        =========  ============
        Argument   Description
        =========  ============
        path       The path to check against the blacklist.
        =========  ============
        """
        for bl in self.blacklist:
            if isinstance(bl, unicode):
                if bl in path:
                    abort(403)
            elif bl.match(path):
                abort(403)

    def __call__(self, request):
        """
        Serve a request.
        """

        try:
            path = request.match.groups()[-1]
            if path is None:
                path = urllib.unquote_plus(request.path)
        except (AttributeError, IndexError):
            path = urllib.unquote_plus(request.path)

        # Convert the path to unicode.
        path = decode(path)

        # Strip off a starting quote.
        if path.startswith('/') or path.startswith('\\'):
            path = path[1:]

        # Normalize the path.
        full_path = os.path.normpath(os.path.join(self.path, path))

        # Validate the request.
        if not full_path.startswith(self.path):
            abort(403)
        elif not os.path.exists(full_path):
            abort()
        elif not os.access(full_path, os.R_OK):
            abort(403)

        # Is this a directory?
        if os.path.isdir(full_path):
            # Check defaults.
            for f in self.defaults:
                full = os.path.join(full_path, f)
                if os.path.exists(full):
                    request.path = urllib.quote(full.encode('utf8'))
                    if hasattr(request, 'match'):
                        del request.match
                    return self.__call__(request)

            # Guess not. List it.
            if hasattr(request, 'match'):
                return self.list_directory(request, path)
            else:
                body, status, headers = self.list_directory(request, path)
                if isinstance(body, unicode):
                    body = body.encode('utf-8')
                headers['Content-Length'] = len(body)
                request.send_status(status)
                request.send_headers(headers)
                request.send(body)
                request.finish()
                return

        # Blacklist Checking.
        self.check_blacklist(full_path)

        # Let's send the file.
        request.auto_finish = False
        request.send_file(full_path)


    def list_directory(self, request, path):
        """
        Generate a directory listing and return it.
        """

        # Normalize the path.
        full_path = os.path.normpath(os.path.join(self.path, path))

        # Get the URL, which is just request.path decoded.

        url = decode(urllib.unquote(request.path))