コード例 #1
0
    def validate(self):
        """Hook for late validation so we can use Response.
        """

        resource_fs_path = uri_to_fs(self.app.site_root,
                                     self.app.fs_root,
                                     self.app.uri_root,
                                     self.request.path,
                                     raw=True)

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

        if self.server.config.__:

            # Is the site's magic directory still on the filesystem?
            if self.server.config.__:
                if not os.path.isdir(self.server.config.__):
                    raise Response(
                        500,
                        "The site's magic directory has " + "disappeared.")

            # Protect the magic directory from direct access, but make sure we
            # can serve a default app.py from there.
            if resource_fs_path.startswith(self.server.config.__):
                app_py = os.path.join(self.server.config.__, 'app.py')
                if not resource_fs_path == app_py:
                    raise Response(404)
コード例 #2
0
ファイル: Request.py プロジェクト: chadwhitacre/public
    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:
            raise Response(
                400, "The Request-Line `%s' " % self.raw_line +
                "appears to be malformed because it has " +
                "neither two nor three tokens.")

        # 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:
            raise Response(
                400, "The HTTP-Version `%s' appears to " % version +
                "be malformed because it does not match the " +
                "pattern `^HTTP/\d+\.\d+$'.")

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

        self.method = method
        self.uri = uri
        self.path = uri['path']
コード例 #3
0
ファイル: Task.py プロジェクト: chadwhitacre/public
    def respond(self):
        """Execute one transaction.

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

        Application.respond is expected to raise a Response or other exception.

        """

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

        resource_fs_path = uri_to_fs(self.app.site_root,
                                     self.app.fs_root,
                                     self.app.uri_root,
                                     self.request.path,
                                     raw=True)

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

        if self.app.__:

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

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

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

        if not self.server.debug_mode:
            self.app.respond(self.request)
        else:
            try:
                self.app.respond(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!
        # ====================================

        raise Response(
            500, "%s.respond did not raise " % str(application) + "anything.")
コード例 #4
0
    def testBasic(self):
        request = self.make_request("/")

        expected = Response(200)
        expected.headers['Last-Modified'] = 'blah blah blah'
        expected.headers['Content-Type'] = 'text/html'
        try:
            self.app.respond(request)
        except Response, actual:
            pass
コード例 #5
0
    def testHeadersAreOptional(self):
        response = Response(200)
        response.headers = {}  # the default
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 200 OK', 'content-length: 0',
            'content-type: application/octet-stream', 'server: stub server', ''
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)
コード例 #6
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)
コード例 #7
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)
コード例 #8
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)
コード例 #9
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)
コード例 #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
コード例 #11
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)
コード例 #12
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)
コード例 #13
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)
コード例 #14
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)
コード例 #15
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)
コード例 #16
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)
コード例 #17
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)
コード例 #18
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)
コード例 #19
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)
コード例 #20
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)
コード例 #21
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)
コード例 #22
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
コード例 #23
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
コード例 #24
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
コード例 #25
0
 def respond(self, request):
     """Given a request, hand it off to a method handler.
     """
     try:
         if request.path != '/':
             response = Response(301)
             response.headers['location'] = '/'
             raise response
         handler = getattr(self, request.method, None)
         if handler:
             handler(request)
         else:
             raise Response(501) # Not Implemented
     finally:
         pass
コード例 #26
0
ファイル: Task.py プロジェクト: chadwhitacre/public
    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
コード例 #27
0
    def translate(self, path):
        """Given a URI path, return a corresponding filesystem path.

        The URI path is taken to be rooted in our application root. If it points
        to a directory, look for a default resource. If it points to a file,
        make sure the file exists.

        This method can raise the following Responses:

            301 Moved Permanently
            400 Bad Request
            403 Forbidden
            400 Not Found

        """

        full_path = os.path.join(self.config['root'], path.lstrip('/'))

        if os.path.isdir(full_path):

            if not path.endswith('/'):
                # redirect directory requests to trailing slash
                new_location = '%s/' % path
                response = Response(301)
                response.headers = {'Location': new_location}
                log(98, "Redirecting to trailing slash: %s" % path)
                raise response

            default = ''
            for name in self.config['defaults']:
                _path = os.path.join(full_path, name)
                if os.path.isfile(_path):
                    default = _path
                    break
            full_path = default

            if not default:
                log(94, "No default resource in %s" % path)
                raise Response(403)

        else:
            if not os.path.exists(full_path):
                log(94, "Not Found: %s" % path)
                raise Response(404)

        return full_path
コード例 #28
0
ファイル: Doc.py プロジェクト: chadwhitacre/public
    def respond(self, request):

        # Preen the request.
        # ==================

        if request.method != 'GET':
            raise Response(501)
        if self.uri_root != '/' and request.path.startswith(self.uri_root):
            request.fullpath = request.path
            request.path = request.path[len(self.uri_root):]
        if request.path == '':
            raise Response(302, '', {'Location': self.uri_root + '/'})

        if request.path == '/':
            self.serve_index(request)
        else:
            self.serve_doc(request)
コード例 #29
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
コード例 #30
0
    def testStatusLineGetsWritten(self):
        response = Response(505)
        self.task.deliver(response)

        expected = [
            'HTTP/1.0 505 HTTP Version not supported', 'content-length: 23',
            'content-type: text/plain', 'server: stub server', '',
            'Cannot fulfill request.'
        ]
        actual = self.task.channel.getvalue().splitlines()
        self.assertEqual(expected, actual)