Example #1
0
    def process(self):
        """Execute one transaction.

        The call to Transaction.process is complicated by the fact that in
        development mode we want to drop into the post-mortem debugger when
        there is an exception (other than Response, of course).

        Transaction.process is expected to raise a Response or other exception.

        """

        config = TransactionConfig(self.app, self.server_config)

        # Do some late validation so we can use Response.
        # ===============================================

        resource_fs_path = uri_to_fs(config, self.request.path, raw=True)

        # Is the app still on the filesystem?
        if not os.path.isdir(config.app_fs_root):
            raise Response(404)

        if config.__:

            # Is the app's magic directory still on the filesystem?
            if not os.path.isdir(config.__):
                response = Response(500)
                response.body = ("The application's magic directory has " +
                                 "disappeared.")
                raise response

            # Protect the magic directory from direct access.
            if resource_fs_path.startswith(config.__):
                raise Response(404)

        # Get out of the way.
        # ===================

        transaction = self.app.module.Transaction(config)
        if not self.dev_mode:
            transaction.process(self.request)
        else:
            try:
                transaction.process(self.request)
            except Response:
                raise
            except:
                log(90, traceback.format_exc())
                pdb.post_mortem(sys.exc_info()[2])
                raise

        # You know something? No soup for you!
        # ====================================

        response = Response(500)
        response.body = "%s.process did not raise anything." % str(transaction)
        raise response
Example #2
0
    def serve_static(self, fs_path, ims):
        """Given a filesystem path to a static resource, serve it.

        This is factored out for easier reuse.

        """

        # Get basic info from the filesystem and start building a response.
        # =================================================================

        mtime = os.stat(fs_path)[stat.ST_MTIME]
        content_type = mimetypes.guess_type(fs_path)[0] or 'text/plain'
        response = Response(200)

        # Support 304s, but only in deployment mode.
        # ==========================================

        if self.deploy_mode:
            if ims:
                mod_since = rfc822.parsedate(ims)
                last_modified = time.gmtime(mtime)
                if last_modified[:6] <= mod_since[:6]:
                    response.code = 304

        # Finish building the response and raise it.
        # ========================================

        response.headers['Last-Modified'] = rfc822.formatdate(mtime)
        response.headers['Content-Type'] = content_type
        if response.code != 304:
            response.body = file(fs_path, 'rb').read()
        raise response
Example #3
0
    def testBodyNotWrittenFor304(self):
        response = Response(304)
        response.body = "Greetings, program!"
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 304 Not modified', 'content-length: 19',
            'content-type: text/plain', 'server: stub server', ''
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #4
0
    def testButOnlyForNonStrings(self):
        response = Response(537)
        response.body = 'foo'
        self.task.server.deploy_mode = False
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 537 HTTPY App Dev', 'content-length: 3',
            'content-type: text/plain', 'server: stub server', '', 'foo'
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #5
0
    def testJustLikeFor200(self):
        response = Response(200)
        response.headers['content-type'] = 'text/plain'
        response.body = ''
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 200 OK', 'content-length: 0', 'content-type: text/plain',
            'server: stub server', ''
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #6
0
    def testOtherwiseBodyIsWritten(self):
        response = Response(200)
        response.body = "Greetings, program!"
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 200 OK', 'content-length: 19',
            'content-type: application/octet-stream', 'server: stub server',
            '', 'Greetings, program!'
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #7
0
    def testButReasonMessageCanBeOverriden(self):
        response = Response(505)
        response.body = "Just leave me alone, ok!"
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 505 HTTP Version not supported', 'content-length: 24',
            'content-type: text/plain', 'server: stub server', '',
            'Just leave me alone, ok!'
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #8
0
    def testContentTypeDefaultsToApplicationOctetStream(self):
        response = Response(200)
        response.body = "Greetings, program!"
        self.task.respond(response)

        expected = [
            'HTTP/1.0 200 OK', 'content-length: 19',
            'content-type: application/octet-stream', 'server: stub server',
            '', 'Greetings, program!'
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #9
0
    def testIncludingNoneForExample(self):
        response = Response(537)
        response.body = None
        self.task.server.deploy_mode = False
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 537 HTTPY App Dev', 'content-length: 4',
            'content-type: text/plain', 'server: stub server', '', 'None'
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #10
0
    def testDevelopment_ModifiedSinceIsFalse(self):
        headers = {'If-Modified-Since': 'Fri, 31 Dec 9999 23:59:59 GMT'}
        request = self.make_request("/foo.html", headers)

        expected = Response(200)
        expected.headers['Last-Modified'] = 'blah blah blah'
        expected.headers['Content-Type'] = 'text/html'
        expected.body = 'Greetings, program!'
        try:
            self.txn.process(request)
        except Response, actual:
            pass
Example #11
0
    def testAndEmptyValuesArePreserved(self):
        response = Response(537)
        response.body = {}
        self.task.server.deploy_mode = False
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 537 HTTPY App Dev', 'content-length: 2',
            'content-type: text/plain', 'server: stub server', '', '{}'
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #12
0
    def testBasic(self):
        response = Response(200)
        response.headers['content-type'] = 'text/plain'
        response.body = "Greetings, program!"
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 200 OK', 'content-length: 19',
            'content-type: text/plain', 'server: stub server', '',
            'Greetings, program!'
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #13
0
    def testButYouCantOverrideContentLength(self):
        response = Response(200)
        response.headers['content-length'] = 50000
        response.body = "Greetings, program!"
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 200 OK', 'content-length: 19',
            'content-type: application/octet-stream', 'server: stub server',
            '', 'Greetings, program!'
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #14
0
    def testExceptForNonSuccessfulRequests_InWhichCaseItIsTextPlain(self):
        response = Response(301)
        response.headers['location'] = 'http://www.google.com/'
        response.body = "Greetings, program!"
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 301 Moved Permanently', 'content-length: 19',
            'content-type: text/plain', 'location: http://www.google.com/',
            'server: stub server', '', 'Greetings, program!'
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #15
0
    def testYouCanOverrideServerAndContentType(self):
        response = Response(200)
        response.headers['server'] = 'MY SERVER! MINE!'
        response.headers['content-type'] = 'cheese/yummy'
        response.body = "Greetings, program!"
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 200 OK', 'content-length: 19',
            'content-type: cheese/yummy', 'server: MY SERVER! MINE!', '',
            'Greetings, program!'
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #16
0
    def testYouCanAddArbitraryHeaders_TheyWillBeLowerCasedButWillMakeIt(self):
        response = Response(200)
        response.headers['cheese'] = 'yummy'
        response.headers['FOO'] = 'Bar'
        response.body = "Greetings, program!"
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 200 OK', 'cheese: yummy', 'content-length: 19',
            'foo: Bar', 'content-type: application/octet-stream',
            'server: stub server', '', 'Greetings, program!'
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #17
0
    def testBodyIsFormattedFor537(self):
        response = Response(537)
        response.body = ('#' * 20, ['#' * 70])
        self.task.server.deploy_mode = False
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 537 HTTPY App Dev', 'content-length: 101',
            'content-type: text/plain', 'server: stub server', '',
            u"('####################',",
            u" ['######################################################################'])"
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #18
0
    def testBodyNotWrittenForHEADRequest(self):
        self.task.request = ZopeRequest()
        self.task.request.received("HEAD / HTTP/1.1\r\n\r\n")

        response = Response(200)
        response.body = "Greetings, program!"
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 200 OK', 'content-length: 19',
            'content-type: application/octet-stream', 'server: stub server', ''
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
Example #19
0
    def testDeployment_ModifiedSinceIsFalse(self):
        self.config.mode = 'deployment'
        self.txn = DefaultApp.Transaction(self.config)

        headers = {'If-Modified-Since': 'Fri, 31 Dec 9999 23:59:59 GMT'}
        request = self.make_request("/foo.html", headers)

        expected = Response(304)
        expected.headers['Last-Modified'] = 'blah blah blah'
        expected.headers['Content-Type'] = 'text/html'
        expected.body = ''  # no body for 304
        try:
            self.txn.process(request)
        except Response, actual:
            pass
Example #20
0
    def testDeployment_ModifiedSinceIsTrue(self):
        self.config.mode = 'deployment'
        self.txn = DefaultApp.Transaction(self.config)

        headers = {'If-Modified-Since': 'Fri, 01 Jan 1970 00:00:00 GMT'}
        request = self.make_request("/foo.html", headers)

        expected = Response(200)
        expected.headers['Last-Modified'] = 'blah blah blah'
        expected.headers['Content-Type'] = 'text/html'
        expected.body = 'Greetings, program!'
        try:
            self.txn.process(request)
        except Response, actual:
            pass
Example #21
0
    def testDevelopment_ModifiedSinceIsFalse(self):
        os.environ['HTTPY_MODE'] = 'development'
        self.app = DefaultApp.Application()

        headers = {'If-Modified-Since': 'Fri, 31 Dec 9999 23:59:59 GMT'}
        request = self.make_request("/foo.html", headers)

        expected = Response(200)
        expected.headers['Last-Modified'] = 'blah blah blah'
        expected.headers['Content-Type'] = 'text/html'
        expected.body = 'Greetings, program!'
        try:
            self.app.respond(request)
        except Response, actual:
            pass
Example #22
0
    def respond(self, request):

        if request.method != 'POST':
            raise Response(501)
        else:
            response = Response(200)
            try:
                response.body = self.serve_xmlrpc(request)
            except:
                # Bug in the module.
                logger.error("Error serving request.")
                logger.debug(traceback.format_exc())
                raise Response(500)
            else:
                # Valid XMLRPC response.
                response.headers = {'Content-Type': 'text/xml'}
                raise response
Example #23
0
    def process(self, request):
        """Given an httpy.Request, raise an httpy.Response.
        """

        path = self.translate(request.path)

        mtime = os.stat(path)[stat.ST_MTIME]
        content_length = os.stat(path)[stat.ST_SIZE]

        if self.config['mode'] == 'deployment':

            ims = self.request.message.get('If-Modified-Since')

            length_match = True
            if ims:
                length = ims.group(4)
                if length:
                    try:
                        length = int(length)
                        if length != content_length:
                            length_match = False
                    except:
                        pass

            ims_date = False
            if ims:
                ims_date = http_date.parse_http_date(ims.group(1))

            if length_match and ims_date:
                if mtime <= ims_date:
                    raise NotModified

        # Build a response and raise.
        # ===========================

        content_type = mimetypes.guess_type(path)[0] or 'text/plain'
        log(88, "content type for %s: %s" % (path, content_type))

        response = Response(200)
        #response.headers['Last-Modified'] = mtime
        response.headers['content-type'] = content_type
        response.body = open(path, 'rb').read()
        raise response
Example #24
0
    def serve_index(self, request):
        """Serve a top-level package/module index.
        """

        data = {}

        data['title'] = "doc537 &mdash; a Python documentation server"
        data['crumbs'] = data['title']
        data['nav'] = ''
        data['callable'] = "<h1>index</h1>"
        data['content'] = self.getindex()

        data['version'] = __version__
        data['date'] = time.strftime('%B %d, %Y')
        data['base'] = self.uri_root

        response = Response(200)
        response.headers = {'Content-Type': 'text/html'}
        response.body = TEMPLATE % data
        raise response
Example #25
0
    def GET(self, request):

        # Parse the querystring.
        # ======================
        # We only want the first value for each argument.

        query = {}
        for k, v in parse_qs(request.uri['query'], True).items():
            query[k] = v[0]


        # Implement GET with no query.
        # ============================
        # "The simplest form of a query is a plain HTTP GET.  It returns all
        # the RDF statements in the model at that URL."
        # -- http://w3.org/Submission/2003/SUBM-rdf-netapi-20031002/#http-query

        if query == {}:
            response = Response(200)
            response.headers['Content-Type'] = self.content_type
            response.body = self.store.serialize()
            raise response


        # Implement GET with a query.
        # ===========================
        # Query implementations should take the query less lang as keyword
        # arguments.

        else:
            if 'lang' not in query:
                raise Response(400, "You must specify a query language.")

            lang = urllib.unquote(query['lang'])
            del query['lang']
            query_processor = self.get_query_processor(lang)
            if query_processor:
                query_processor(**query)
            else:
                raise Response(400, "Sorry, we don't support the " +
                                    "`%s' query language." % lang)
Example #26
0
    def TriplePattern(self, subject='', predicate='', object='', literal=''):
        """Given a triple pattern, return an RDF/XML graph.
        """

        # Validate and parse incoming data.
        # =================================

        if object and literal:
            raise Response(400, "Only one of parameters 'object' or " +
                                "'literal' may be used in a single request.")

        def _parse(a):
            if a in ('', '*'):  # wildcard
                return None
            else:               # URI
                return rdflib.URIRef(urllib.unquote(a))

        s = _parse(subject)
        p = _parse(predicate)
        if literal:
            o = rdflib.Literal(literal)
        else:
            o = _parse(object)


        # Build outgoing graph and send it along.
        # =======================================

        out = rdflib.Graph("Memory")
        for statement in self.store.triples((s,p,o)):
            out.add(statement)

        response = Response(200)
        response.headers['Content-Type'] = self.content_type
        response.body = out.serialize()
        raise response
Example #27
0
 def testBut537OnlyAvailableInDevMode(self):
     response = Response(537)
     response.body = ('#' * 20, ['#' * 70])
     self.task.dev_mode = False
     self.assertRaises(Exception, self.task.respond, response)
Example #28
0
    def parse_line(self):
        """Parse and validate the Request-Line.
        """

        # Tokenize the first line as a Request-Line.
        # ==========================================

        tokens = self.raw_line.strip().split()
        if len(tokens) == 3:
            method, uri, version = tokens
        elif len(tokens) == 2:
            method, uri = tokens
            version = 'HTTP/0.9'
        else:
            response = Response(400)
            response.body = (
                "The Request-Line `%s' appears to be " % self.raw_line +
                "malformed because it has neither two nor " + "three tokens.")
            raise response

        # Validate the method.
        # ====================

        if method not in ('GET', 'HEAD', 'POST'):
            response = Response(501)
            response.body = ("This server does not implement the " +
                             "'%s' method." % method)
            raise response

        # Validate the URI.
        # =================
        # We build a mapping using the urlparse naming convention for the keys.
        # Then we do a little validation and cleanup.

        uri = urlparse.urlparse(uri)
        keys = ('scheme', 'netloc', 'path', 'parameters', 'query', 'fragment')
        _uri = {}
        for i in range(len(uri)):
            k = keys[i]
            v = uri[i]
            _uri[k] = v
        uri = _uri

        if not uri['path']:
            # this catches, e.g., '//foo'
            raise Response(400)
        if '%' in uri['path']:
            uri['path'] = urllib.unquote(uri['path'])

        # Validate the version.
        # =====================
        # Consider raising Response(505) here ... for 0.9? 2.0?

        m = HTTP_VERSION.match(version)
        if not m:
            response = Response(400)
            response.body = ("The HTTP-Version `%s' appears to " % version +
                             "be malformed because it does not match the " +
                             "pattern `^HTTP/\d+\.\d+$'.")
            raise response

        # Save a few absolutely basic things for application use.
        # =======================================================

        self.method = method
        self.uri = uri
        self.path = uri['path']
Example #29
0
 def testAnd537NotAvailableInDeploymentMode(self):
     response = Response(537)
     response.body = ('#' * 20, ['#' * 70])
     self.task.server.deploy_mode = True
     self.assertRaises(StandardError, self.task.respond, response)
Example #30
0
    def set_line(self, stream):
        """Given an HTTP stream, parse and store the Request-Line.

        The HTTP stream must be positioned at the start of a request. The
        Request-Line is resolved into method, uri, and version, which are
        validated. Some helpful values are derived from these tokens, and all
        this information is stored on self.

        """

        # Read and tokenize the Request-Line.
        # ===================================

        raw_line = stream.readline()
        while raw_line == '\r\n':
            # Skip initial blank lines to account for IE's buggy POST
            # implementation.
            raw_line = stream.readline()
        line = raw_line.rstrip('/n').rstrip('/r')
        tokens = line.split()
        if len(tokens) == 3:
            method, uri, version_string = tokens
        elif len(tokens) == 2:
            method, uri = tokens
            version_string = 'HTTP/0.9'
        else:
            response = Response(400)
            response.body = ("The Request-Line `%s' appears to be " % line +
                             "malformed because it has neither two nor " +
                             "three tokens.")
            raise response

        # Validate the method.
        # ====================

        if method not in ('GET', 'HEAD', 'POST'):
            response = Response(501)
            response.body = ("This server does not implement the " +
                             "`%s' method." % method)
            raise response

        # Parse the URI into a dictionary.
        # ================================
        # We use the urlparse naming convention for uri's keys.

        uri = urlparse.urlsplit(uri)
        keys = ('scheme', 'netloc', 'path', 'query', 'fragment')
        _uri = {}
        for i in range(len(uri)):
            k = keys[i]
            v = uri[i]
            _uri[k] = v
        uri = _uri

        # Validate and possibly unencode the path.
        # ========================================

        if not uri['path']:
            # this catches, e.g., '//foo'
            raise Response(400)
        if '%' in uri['path']:
            uri['path'] = urllib.unquote(uri['path'])

        # Validate the version.
        # =====================
        # Consider raising an HTTPVersionNotSupported here ... for 0.9? 2.0?

        m = HTTP_VERSION.match(version_string)
        if not m:
            response = Response(400)
            response.body = ("The HTTP-Version `%s' appears to " % version +
                             "be malformed because it does not match the " +
                             "pattern `^HTTP/\d+\.\d+$'.")
            raise response

        version = tuple([int(i) for i in m.groups()])

        # Store the most important info on self.
        # ======================================

        # raw line
        self.raw.append(raw_line)
        self.raw_line = line

        # tokens
        self.method = method
        self.uri = uri
        self.version_string = version_string

        # derivatives
        self.path = uri['path']
        self.querystring = uri['query']
        self.version = version