[Zope3-checkins] SVN: Zope3/trunk/ Implemented HTTP 405 Method Not Allowed handling in HTTPPublication.

Albertas Agejevas alga at pov.lt
Mon Apr 18 14:36:19 EDT 2005


Log message for revision 30025:
  Implemented HTTP 405 Method Not Allowed handling in HTTPPublication.
  
  This does not work for DELETE and PUT methods, which have views registered for
  "*" and try to adapt context to IWriteFile.
  

Changed:
  U   Zope3/trunk/doc/CHANGES.txt
  U   Zope3/trunk/src/zope/app/http/exception/configure.zcml
  A   Zope3/trunk/src/zope/app/http/exception/methodnotallowed.py
  A   Zope3/trunk/src/zope/app/http/exception/tests/test_methodnotallowed.py
  U   Zope3/trunk/src/zope/app/publication/ftests.py
  U   Zope3/trunk/src/zope/app/publication/http.py
  A   Zope3/trunk/src/zope/app/publication/methodnotallowed.txt

-=-
Modified: Zope3/trunk/doc/CHANGES.txt
===================================================================
--- Zope3/trunk/doc/CHANGES.txt	2005-04-18 13:48:48 UTC (rev 30024)
+++ Zope3/trunk/doc/CHANGES.txt	2005-04-18 18:36:18 UTC (rev 30025)
@@ -10,6 +10,10 @@
 
     New features
 
+      - Implemented proper HTTP 405 Method Not Allowed handling in the
+        HTTP publication if a requested method does not have a corresponding
+        view.
+
       - Implemented a generic user preferences system.
 
         * User preferences are combined in groups that are described by

Modified: Zope3/trunk/src/zope/app/http/exception/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/http/exception/configure.zcml	2005-04-18 13:48:48 UTC (rev 30024)
+++ Zope3/trunk/src/zope/app/http/exception/configure.zcml	2005-04-18 18:36:18 UTC (rev 30025)
@@ -28,4 +28,18 @@
     name="index.html"
     />
 
+<view
+    for="zope.app.publication.http.IMethodNotAllowed"
+    factory="zope.app.http.exception.methodnotallowed.MethodNotAllowedView"
+    name="index.html"
+    type="zope.publisher.interfaces.http.IHTTPRequest"
+    permission="zope.Public"
+    />
+
+<defaultView
+    for="zope.app.publication.http.IMethodNotAllowed"
+    type="zope.publisher.interfaces.http.IHTTPRequest"
+    name="index.html"
+    />
+
 </configure>

Added: Zope3/trunk/src/zope/app/http/exception/methodnotallowed.py
===================================================================
--- Zope3/trunk/src/zope/app/http/exception/methodnotallowed.py	2005-04-18 13:48:48 UTC (rev 30024)
+++ Zope3/trunk/src/zope/app/http/exception/methodnotallowed.py	2005-04-18 18:36:18 UTC (rev 30025)
@@ -0,0 +1,40 @@
+##############################################################################
+# Copyright (c) 2003 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.
+##############################################################################
+"""HTTP 405: Method Not Allowed view
+
+$Id$
+"""
+from zope.interface import Interface
+from zope.app import zapi
+from zope.app.publication.http import IMethodNotAllowed
+
+
+class MethodNotAllowedView(object):
+    """A view for MethodNotAllowed that renders a HTTP 405 response."""
+
+    __used_for__ = IMethodNotAllowed
+
+    def __init__(self, error, request):
+        self.error = error
+        self.request = request
+        self.allow = [
+            name for name, adapter
+            in zapi.getAdapters((error.object, error.request), Interface)
+            if hasattr(adapter, name)]
+        self.allow.sort()
+
+    def __call__(self):
+        self.request.response.setHeader('Allow', ', '.join(self.allow))
+        self.request.response.setStatus(405)
+        return 'Method Not Allowed'
+
+


Property changes on: Zope3/trunk/src/zope/app/http/exception/methodnotallowed.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/app/http/exception/tests/test_methodnotallowed.py
===================================================================
--- Zope3/trunk/src/zope/app/http/exception/tests/test_methodnotallowed.py	2005-04-18 13:48:48 UTC (rev 30024)
+++ Zope3/trunk/src/zope/app/http/exception/tests/test_methodnotallowed.py	2005-04-18 18:36:18 UTC (rev 30025)
@@ -0,0 +1,86 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+"""Tests for HTTP error views
+
+$Id$
+"""
+from unittest import TestCase, TestSuite, main, makeSuite
+from StringIO import StringIO
+
+from zope.interface import Interface, implements
+from zope.publisher.http import HTTPRequest
+from zope.publisher.interfaces.http import IHTTPRequest
+
+from zope.app.testing import ztapi
+from zope.app.testing.placelesssetup import PlacelessSetup
+
+
+class I(Interface):
+    pass
+
+
+class C(object):
+    implements(I)
+
+
+class GetView(object):
+    def __init__(self, context, request):
+        pass
+    def GET(self):
+        pass
+
+
+class DeleteView(object):
+    def __init__(self, context, request):
+        pass
+    def DELETE(self):
+        pass
+
+
+class TestMethodNotAllowedView(PlacelessSetup, TestCase):
+
+    def setUp(self):
+        from zope.publisher.interfaces.http import IHTTPRequest
+        PlacelessSetup.setUp(self)
+        ztapi.provideView(I, IHTTPRequest, Interface, 'GET', GetView)
+        ztapi.provideView(I, IHTTPRequest, Interface, 'DELETE', DeleteView)
+        ztapi.provideView(I, IHTTPRequest, Interface, 'irrelevant', GetView)
+        ztapi.provideView(I, IHTTPRequest, Interface, 'also_irr.', DeleteView)
+
+    def test(self):
+        from zope.app.publication.http import MethodNotAllowed
+        from zope.app.http.exception.methodnotallowed \
+             import MethodNotAllowedView
+        from zope.publisher.http import HTTPRequest
+
+        context = C()
+        request = HTTPRequest(StringIO('PUT /bla/bla HTTP/1.1\n\n'),
+                              StringIO(), {})
+        error = MethodNotAllowed(context, request)
+        view = MethodNotAllowedView(error, request)
+
+        result = view()
+
+        self.assertEqual(request.response.getStatus(), 405)
+        self.assertEqual(request.response.getHeader('Allow'), 'DELETE, GET')
+        self.assertEqual(result, 'Method Not Allowed')
+
+
+def test_suite():
+    return TestSuite((
+        makeSuite(TestMethodNotAllowedView),
+        ))
+
+if __name__=='__main__':
+    main(defaultTest='test_suite')


Property changes on: Zope3/trunk/src/zope/app/http/exception/tests/test_methodnotallowed.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: Zope3/trunk/src/zope/app/publication/ftests.py
===================================================================
--- Zope3/trunk/src/zope/app/publication/ftests.py	2005-04-18 13:48:48 UTC (rev 30024)
+++ Zope3/trunk/src/zope/app/publication/ftests.py	2005-04-18 18:36:18 UTC (rev 30025)
@@ -21,6 +21,7 @@
 def test_suite():
     return unittest.TestSuite((
         functional.FunctionalDocFileSuite('notfound.txt'),
+        functional.FunctionalDocFileSuite('methodnotallowed.txt'),
         ))
 
 if __name__ == '__main__':

Modified: Zope3/trunk/src/zope/app/publication/http.py
===================================================================
--- Zope3/trunk/src/zope/app/publication/http.py	2005-04-18 13:48:48 UTC (rev 30024)
+++ Zope3/trunk/src/zope/app/publication/http.py	2005-04-18 18:36:18 UTC (rev 30025)
@@ -19,9 +19,33 @@
 from zope.publisher.publish import mapply
 
 from zope.app import zapi
+from zope.interface import Interface, implements, Attribute
+from zope.interface.common.interfaces import IException
 from zope.app.http.interfaces import IHTTPException
 from zope.app.publication.zopepublication import ZopePublication
 
+
+class IMethodNotAllowed(IException):
+    """An exception that signals the 405 Method Not Allowed HTTP error"""
+
+    object = Attribute("""The object on which the error occured""")
+
+    request = Attribute("""The request in which the error occured""")
+
+
+class MethodNotAllowed(Exception):
+    """An exception that signals the 405 Method Not Allowed HTTP error"""
+
+    implements(IMethodNotAllowed)
+
+    def __init__(self, object, request):
+        self.object = object
+        self.request = request
+
+    def __str__(self):
+        return "%r, %r" % (object, request)
+
+
 class BaseHTTPPublication(ZopePublication):
     """Base for HTTP-based protocol publications"""
 
@@ -32,12 +56,16 @@
         txn.setExtendedInfo('request_info', request_info)
         return txn
 
+
 class HTTPPublication(BaseHTTPPublication):
     """HTTP-specific publication"""
 
     def callObject(self, request, ob):
         # Exception handling, dont try to call request.method
+        orig = ob
         if not IHTTPException.providedBy(ob):
-            ob = zapi.getMultiAdapter((ob, request), name=request.method)
-            ob = getattr(ob, request.method)
+            ob = zapi.queryMultiAdapter((ob, request), name=request.method)
+            ob = getattr(ob, request.method, None)
+            if ob is None:
+                raise MethodNotAllowed(orig, request)
         return mapply(ob, request.getPositionalArguments(), request)

Added: Zope3/trunk/src/zope/app/publication/methodnotallowed.txt
===================================================================
--- Zope3/trunk/src/zope/app/publication/methodnotallowed.txt	2005-04-18 13:48:48 UTC (rev 30024)
+++ Zope3/trunk/src/zope/app/publication/methodnotallowed.txt	2005-04-18 18:36:18 UTC (rev 30025)
@@ -0,0 +1,31 @@
+Method Not Allowed errors
+=========================
+
+If we get a request with a method that does not have a corresponding
+view,  HTTP 405 Method Not Allowed response is returned:
+
+  >>> print http(r"""
+  ... FROG / HTTP/1.1
+  ... """)
+  HTTP/1.1 405 Method Not Allowed
+  Allow: DELETE, MKCOL, OPTIONS, PROPFIND, PROPPATCH, PUT
+  Content-Length: 18
+  <BLANKLINE>
+  Method Not Allowed
+
+The requests below should return 405, but instead crash with a TypeError,
+when the view tries to adapt context to IWriteFile.
+
+#   >>> print http(r"""
+#   ... DELETE / HTTP/1.1
+#   ... Authorization: Basic mgr:mgrpw
+#   ... """, handle_errors=False)
+#   HTTP/1.1 405 Method Not Allowed
+#   ...
+#
+#   >>> print http(r"""
+#   ... PUT / HTTP/1.1
+#   ... Authorization: Basic mgr:mgrpw
+#   ... """, handle_errors=False)
+#   HTTP/1.1 405 Method Not Allowed
+#   ...


Property changes on: Zope3/trunk/src/zope/app/publication/methodnotallowed.txt
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native



More information about the Zope3-Checkins mailing list