Index: src/zope/app/http/tests/test_options.py =================================================================== --- src/zope/app/http/tests/test_options.py (revision 0) +++ src/zope/app/http/tests/test_options.py (revision 0) @@ -0,0 +1,66 @@ +############################################################################## +# +# Copyright (c) 2007 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Test HTTP OPTIONS verb + +$Id$ +""" +from unittest import TestCase, TestSuite, makeSuite + +from zope.publisher.browser import TestRequest + +import zope.app.http.put +import zope.app.http.options +from zope.app.testing.placelesssetup import PlacelessSetup + + +class TestNullOPTIONS(PlacelessSetup, TestCase): + + def test(self): + request = TestRequest() + null = zope.app.http.put.NullResource(object(), 'spam') + options = zope.app.http.options.NullOPTIONS(null, request) + self.assertEqual(options.OPTIONS(), '') + + # Check HTTP Response + self.assertEqual(request.response.getStatus(), 200) + self.assertEqual( + request.response.getHeader("DAV", literal=True), "1,2") + self.assertEqual( + request.response.getHeader("MS-Author-Via", literal=True), "DAV") + self.assertEqual( + request.response.getHeader("Allow"), "") + + +class TestOPTIONS(PlacelessSetup, TestCase): + + def test(self): + request = TestRequest() + options = zope.app.http.options.OPTIONS(object(), request) + self.assertEqual(options.OPTIONS(), '') + + # Check HTTP Response + self.assertEqual(request.response.getStatus(), 200) + self.assertEqual( + request.response.getHeader("DAV", literal=True), "1,2") + self.assertEqual( + request.response.getHeader("MS-Author-Via", literal=True), "DAV") + self.assertEqual( + request.response.getHeader("Allow"), "GET, HEAD, POST") + + +def test_suite(): + return TestSuite(( + makeSuite(TestNullOPTIONS), + makeSuite(TestOPTIONS), + )) Property changes on: src/zope/app/http/tests/test_options.py ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Index: src/zope/app/http/tests/test_functional_options.py =================================================================== --- src/zope/app/http/tests/test_functional_options.py (revision 0) +++ src/zope/app/http/tests/test_functional_options.py (revision 0) @@ -0,0 +1,57 @@ +############################################################################## +# +# Copyright (c) 2007 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Test HTTP OPTIONS verb + +$Id$ +""" + +from unittest import TestSuite, makeSuite + +from zope.app.testing.functional import FunctionalTestCase, HTTPCaller +from zope.app.http.testing import AppHttpLayer + +class TestOPTIONS(FunctionalTestCase): + + def test_options(self): + # First test an existing resource + response = HTTPCaller()(r"""OPTIONS / HTTP/1.1 +Authorization: Basic bWdyOm1ncnB3""") + self.assertEquals(response._response.getStatus(), 200) + self.assertEquals( + response._response.getHeader("DAV", literal=True), "1,2") + self.assertEquals( + response._response.getHeader("MS-Author-Via", literal=True), "DAV") + self.assertEquals( + response._response.getHeader("Allow"), + "GET, HEAD, POST, PUT, DELETE, OPTIONS") + self.assertEquals(response.getBody(), "") + + # Then a NullResource + response = HTTPCaller()(r"""OPTIONS /foo HTTP/1.1 +Authorization: Basic bWdyOm1ncnB3""") + self.assertEquals(response._response.getStatus(), 200) + self.assertEquals( + response._response.getHeader("DAV", literal=True), "1,2") + self.assertEquals( + response._response.getHeader("MS-Author-Via", literal=True), "DAV") + self.assertEquals( + response._response.getHeader("Allow"), "PUT, OPTIONS") + self.assertEquals(response.getBody(), "") + + +def test_suite(): + TestOPTIONS.layer = AppHttpLayer + return TestSuite(( + makeSuite(TestOPTIONS), + )) Property changes on: src/zope/app/http/tests/test_functional_options.py ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Index: src/zope/app/http/tests/test_traversers.py =================================================================== --- src/zope/app/http/tests/test_traversers.py (revision 81891) +++ src/zope/app/http/tests/test_traversers.py (working copy) @@ -52,9 +52,8 @@ traverser = self.Traverser(container, request) self.assertRaises(NotFound, traverser.publishTraverse, request, 'bar') - - def testNull(self): + def testNullPUT(self): container = self.Container({'foo': 42}) request = TestRequest() request.method = 'PUT' @@ -63,8 +62,38 @@ self.assertEqual(null.__class__, NullResource) self.assertEqual(null.container, container) self.assertEqual(null.name, 'bar') - + def testNullMKCOL(self): + container = self.Container({'foo': 42}) + request = TestRequest() + request.method = 'MKCOL' + traverser = self.Traverser(container, request) + null = traverser.publishTraverse(request, 'bar') + self.assertEqual(null.__class__, NullResource) + self.assertEqual(null.container, container) + self.assertEqual(null.name, 'bar') + + def testNullOPTIONS(self): + container = self.Container({'foo': 42}) + request = TestRequest() + request.method = 'OPTIONS' + traverser = self.Traverser(container, request) + null = traverser.publishTraverse(request, 'bar') + self.assertEqual(null.__class__, NullResource) + self.assertEqual(null.container, container) + self.assertEqual(null.name, 'bar') + + def testNullLOCK(self): + container = self.Container({'foo': 42}) + request = TestRequest() + request.method = 'LOCK' + traverser = self.Traverser(container, request) + null = traverser.publishTraverse(request, 'bar') + self.assertEqual(null.__class__, NullResource) + self.assertEqual(null.container, container) + self.assertEqual(null.name, 'bar') + + class TestItem(TestContainer): Container = Items Index: src/zope/app/http/options.py =================================================================== --- src/zope/app/http/options.py (revision 81891) +++ src/zope/app/http/options.py (working copy) @@ -20,6 +20,8 @@ 'COPY', 'MOVE', 'LOCK', 'UNLOCK', 'TRACE'] # 'GET', 'HEAD', 'POST' are always available. See OPTIONS() method. +_null_methods = ['PUT', 'MKCOL', 'OPTIONS', 'LOCK'] + from zope.app import zapi class OPTIONS(object): @@ -30,7 +32,7 @@ self.context = context self.request = request - def OPTIONS(self): + def Allow(self): allowed = ['GET', 'HEAD', 'POST'] # TODO: This could be cleaned up by providing special target # interfaces for HTTP methods. This way we can even list verbs that @@ -39,8 +41,10 @@ view = zapi.queryMultiAdapter((self.context, self.request), name=m) if view is not None: allowed.append(m) + return allowed - self.request.response.setHeader('Allow', ', '.join(allowed)) + def OPTIONS(self): + self.request.response.setHeader('Allow', ', '.join(self.Allow())) # TODO: Most of the time, this is a lie. We not fully support # DAV 2 on all objects, so probably an interface check is needed. self.request.response.setHeader('DAV', '1,2', literal=True) @@ -49,3 +53,18 @@ self.request.response.setStatus(200) return '' + +class NullOPTIONS(OPTIONS): + """`OPTIONS` handler for NullResource objects + """ + + def Allow(self): + allowed = [] + # TODO: This could be cleaned up by providing special target + # interfaces for HTTP methods. This way we can even list verbs that + # are not in the lists above. + for m in _null_methods: + view = zapi.queryMultiAdapter((self.context, self.request), name=m) + if view is not None: + allowed.append(m) + return allowed Index: src/zope/app/http/configure.zcml =================================================================== --- src/zope/app/http/configure.zcml (revision 81891) +++ src/zope/app/http/configure.zcml (working copy) @@ -50,6 +50,15 @@ /> + +