extended HTTP method support in 2.3
Andy Altepeter <[email protected]> Wed, 27 Apr 2011 11:09:42 -0500
| Newsgroups | gmane.comp.web.zope.silva.devel |
|---|---|
| Message-ID | <[email protected]> |
--=-8sBdSCT8jd2Si+FBXeB8
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: 7bit
Greetings Devs,
Our current production environment is Silva 2.1. We have a lot built on
top of Silva. We found in the zope error logs that for our various
browser views attached to Silva Objects (e.g. those accessed via
@@somename.html), we saw a number of errors for PROPFIND and OPTIONS
requests. It turns out that Five views did not handle these HTTP
methods. (they didn't handle HEAD, either).
Infrae added a hack to support HEAD in Products.SilvaLayout (using
MyRolesProxy). MyRolesProxy essentially tells the object the view is on
to handle the requested http method.We've employed the same approach for
PROPFIND and OPTIONS.
Silva 2.3 is a huge restructing. In particular, silva.core.views and
silva.core.layout were added. silva.core.views.views.SilvaPage DOES
support the HEAD method.
However, it does not support OPTIONS and PROPFIND. Not only are they
not supported, Silva does not fail gracefully. (see attached telnet
session for doing an OPTIONS on /silva/index.html).
I'd like to see support for OPTIONS and PROPFIND (at least), or some
means of proxying all http methods not handled by the SilvaPage to the
context itself. Silva isn't in control of what clients request, and
_should_ fail gracefully if it refuses to handle standard http methods
on SilvaPages.
On a related note, there seems to be some issue with the OPTIONS request
to a Silva object. I think this is in infrae.wsgi. Here's a telnet
session:
----------------------------------
> telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
OPTIONS /silva/ HTTP/1.1
HOST: localhost
HTTP/1.0 204 No Content
Server: PasteWSGIServer/0.5 Python/2.6.5
Date: Wed, 27 Apr 2011 15:58:29 GMT
Date: Wed, 27 Apr 2011 15:58:29 GMT
Connection: close
Dav: 1,2
Content-Length: 0
Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE, PROPFIND,
PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
<infrae.wsgi.response.WSGIResponse object at 0xc8f39ec>Connection closed
by foreign host.
--------------------------
In the body of the response, what's with the __repr__ of the
WSGIResponse?
In writing a unittest to cover various http methods, I came across the
following error.
Here's the test:
------------------------
class PortoHTTPMethodTestCase(TestCase):
layer = FunctionalLayer
def setUp(self):
self.layer.grok('silva.core.layout.tests.grok.porto_fixtures')
self.root = self.layer.get_application()
smb = self.root.service_metadata.getMetadata(self.root)
smb.setValues('silva-layout', {'skin': "New theme"})
def test_OPTIONS(self):
browser = self.layer.get_browser()
browser.options.handle_errors = False
status = browser.open('/root/', method='OPTIONS')
self.assertEquals(status, 200)
-------------------------
The error is:
rror in test test_OPTIONS
(silva.core.layout.tests.test_porto_http.PortoHTTPMethodTestCase)
Traceback (most recent call last):
File "/usr/lib/python2.6/unittest.py", line 279, in run
testMethod()
File
"/home/aaltepet/silva/local-Silva-2.3/src/silva.core.layout/src/silva/core/layout/tests/test_porto_http.py", line 27, in test_OPTIONS
status = browser.open('/root/', method='OPTIONS')
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.testbrowser-1.1-py2.6.egg/infrae/testbrowser/browser.py", line 205, in open
self._query_application(url, method, query, data, data_type)
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.testbrowser-1.1-py2.6.egg/infrae/testbrowser/browser.py", line 144, in _query_application
self.__server(method, uri, headers.items(), data, data_type))
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.testbrowser-1.1-py2.6.egg/infrae/testbrowser/wsgi.py", line 88, in __call__
response()
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.testbrowser-1.1-py2.6.egg/infrae/testbrowser/wsgi.py", line 32, in __call__
self.output.write(item)
TypeError: write() argument 1 must be string or read-only character
buffer, not WSGIResponse
I'm not sure what's going on here.
Interestingly, a similar test (in the same class) for PROPFIND succeeds
when checking the SilvaObject, but fails when checking the SilvaPage:
def test_PROPFIND(self):
browser = self.layer.get_browser()
browser.options.handle_errors = False
#baseline for SilvaObject, expect auth requested response
status = browser.open('/root/', method='PROPFIND')
self.assertEquals(status, 401)
#what should the SilvaPage do? proxy to /root/?
status = browser.open('/root/awesome.html', method='PROPFIND')
self.assertEquals(status, 401)
The error for testing /root/awesome.html is this:
Error in test test_PROPFIND
(silva.core.layout.tests.test_porto_http.PortoHTTPMethodTestCase)
Traceback (most recent call last):
File "/usr/lib/python2.6/unittest.py", line 279, in run
testMethod()
File
"/home/aaltepet/silva/local-Silva-2.3/src/silva.core.layout/src/silva/core/layout/tests/test_porto_http.py", line 42, in test_PROPFIND
status = browser.open('/root/awesome.html', method='PROPFIND')
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.testbrowser-1.1-py2.6.egg/infrae/testbrowser/browser.py", line 205, in open
self._query_application(url, method, query, data, data_type)
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.testbrowser-1.1-py2.6.egg/infrae/testbrowser/browser.py", line 144, in _query_application
self.__server(method, uri, headers.items(), data, data_type))
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.testbrowser-1.1-py2.6.egg/infrae/testbrowser/wsgi.py", line 88, in __call__
response()
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.testbrowser-1.1-py2.6.egg/infrae/testbrowser/wsgi.py", line 31, in __call__
for item in result:
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.wsgi-1.2-py2.6.egg/infrae/wsgi/testing.py", line 101, in next
self.__result = self.app(self.environ, self.start_response)
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.wsgi-1.2-py2.6.egg/infrae/wsgi/publisher.py", line 377, in __call__
return publication()
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.wsgi-1.2-py2.6.egg/infrae/wsgi/publisher.py", line 315, in __call__
data = self.publish_and_retry()
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.wsgi-1.2-py2.6.egg/infrae/wsgi/publisher.py", line 286, in publish_and_retry
data = self.publish()
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.wsgi-1.2-py2.6.egg/infrae/wsgi/publisher.py", line 233, in publish
path, validated_hook=Zope2.zpublisher_validated_hook)
File
"/home/aaltepet/silva/local-Silva-2.3/eggs/Zope2-2.12.15-py2.6-linux-i686.egg/ZPublisher/BaseRequest.py", line 539, in traverse
parents[1].aq_base.has_key(entry_name)):
AttributeError: has_key
Do you dev's have any idea what's going on here?
peace,
Andy
--=-8sBdSCT8jd2Si+FBXeB8
Content-Disposition: attachment; filename="options.txt"
Content-Type: text/plain; name="options.txt"; charset="UTF-8"
Content-Transfer-Encoding: 7bit
> telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
OPTIONS /silva/index.html HTTP/1.1
HOST: localhost
HTTP/1.0 500 Internal Server Error
Server: PasteWSGIServer/0.5 Python/2.6.5
Date: Wed, 27 Apr 2011 16:08:16 GMT
content-type: text/html
X-Debug-URL: http://localhost/_debug/view/1303920462
Connection: close
<html>
<head>
<title>Server Error</title>
<style type="text/css">
body {
font-family: Helvetica, sans-serif;
}
table {
width: 100%;
}
tr.header {
background-color: #006;
color: #fff;
}
tr.even {
background-color: #ddd;
}
table.variables td {
vertical-align: top;
overflow: auto;
}
a.button {
background-color: #ccc;
border: 2px outset #aaa;
color: #000;
text-decoration: none;
}
a.button:hover {
background-color: #ddd;
}
code.source {
color: #006;
}
a.switch_source {
color: #090;
text-decoration: none;
}
a.switch_source:hover {
background-color: #ddd;
}
.source-highlight {
background-color: #ff9;
}
</style>
<script type="text/javascript">
function hide_display(id) {
var el = document.getElementById(id);
if (el.className == "hidden-data") {
el.className = "";
return true;
} else {
el.className = "hidden-data";
return false;
}
}
document.write('<style type="text/css">\n');
document.write('.hidden-data {display: none}\n');
document.write('</style>\n');
function show_button(toggle_id, name) {
document.write('<a href="#' + toggle_id
+ '" onclick="javascript:hide_display(\'' + toggle_id
+ '\')" class="button">' + name + '</a><br>');
}
function switch_source(el, hide_type) {
while (el) {
if (el.getAttribute &&
el.getAttribute('source-type') == hide_type) {
break;
}
el = el.parentNode;
}
if (! el) {
return false;
}
el.style.display = 'none';
if (hide_type == 'long') {
while (el) {
if (el.getAttribute &&
el.getAttribute('source-type') == 'short') {
break;
}
el = el.nextSibling;
}
} else {
while (el) {
if (el.getAttribute &&
el.getAttribute('source-type') == 'long') {
break;
}
el = el.previousSibling;
}
}
if (el) {
el.style.display = '';
}
return false;
}
</script><script type="text/javascript" src="http://localhost/_debug/media/MochiKit.packed.js"></script>
<script type="text/javascript" src="http://localhost/_debug/media/debug.js"></script>
<script type="text/javascript">
debug_base = 'http://localhost/_debug';
debug_count = 1303920462;
</script>
</head>
<body>
<div id="error-area" style="display: none; background-color: #600; color: #fff; border: 2px solid black">
<div id="error-container"></div>
<button onclick="return clearError()">clear this</button>
</div>
URL: <a href="http://localhost/silva/index.html">http://localhost/silva/index.html</a><br>
Module <span class="module" title="/home/aaltepet/silva/local-Silva-2.3/eggs/Paste-1.7.5.1-py2.6.egg/paste/evalexception/middleware.py">paste.evalexception.middleware</span>:<b>306</b> in <code>respond</code> <a href="#" class="switch_source" tbid="192774348" onClick="return showFrame(this)"> <img src="http://localhost/_debug/media/plus.jpg" border=0 width=9 height=9> </a><br>
<code style="display: none" class="source" source-type="long"><a class="switch_source" onclick="return switch_source(this, 'long')" href="#"><< </a> <font color="#0000AF"><b>try</b></font><b>:</b><br>
<font color="#333333">__traceback_supplement__</font> <b>=</b> <font color="#333333">errormiddleware</font><b>.</b><font color="#333333">Supplement</font><b>,</b> <font color="#333333">self</font><b>,</b> <font color="#333333">environ</font><br>
<span class="source-highlight"> <font color="#333333">app_iter</font> <b>=</b> <font color="#333333">self</font><b>.</b><font color="#333333">application</font><b>(</b><font color="#333333">environ</font><b>,</b> <font color="#333333">detect_start_response</font><b>)</b></span><br>
<font color="#0000AF"><b>try</b></font><b>:</b><br>
<font color="#333333">return_iter</font> <b>=</b> <font color="#333333">list</font><b>(</b><font color="#333333">app_iter</font><b>)</b></code><code class="source" source-type="short"><a onclick="return switch_source(this, 'short')" class="switch_source" href="#">>> </a><font color="#333333">app_iter</font> <b>=</b> <font color="#333333">self</font><b>.</b><font color="#333333">application</font><b>(</b><font color="#333333">environ</font><b>,</b> <font color="#333333">detect_start_response</font><b>)</b></code><br>
Module <span class="module" title="/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.wsgi-1.2-py2.6.egg/infrae/wsgi/publisher.py">infrae.wsgi.publisher</span>:<b>377</b> in <code>__call__</code> <a href="#" class="switch_source" tbid="192873572" onClick="return showFrame(this)"> <img src="http://localhost/_debug/media/plus.jpg" border=0 width=9 height=9> </a><br>
<code style="display: none" class="source" source-type="long"><a class="switch_source" onclick="return switch_source(this, 'long')" href="#"><< </a> publication = WSGIPublication(self, request, response)<br>
<br>
<span class="source-highlight"> return publication()</span><br>
except AbortPublication, error:<br>
if not error.response_started:</code><code class="source" source-type="short"><a onclick="return switch_source(this, 'short')" class="switch_source" href="#">>> </a><font color="#0000AF"><b>return</b></font> <font color="#333333">publication</font><b>(</b><b>)</b></code><br>
Module <span class="module" title="/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.wsgi-1.2-py2.6.egg/infrae/wsgi/publisher.py">infrae.wsgi.publisher</span>:<b>315</b> in <code>__call__</code> <a href="#" class="switch_source" tbid="195577852" onClick="return showFrame(this)"> <img src="http://localhost/_debug/media/plus.jpg" border=0 width=9 height=9> </a><br>
<code style="display: none" class="source" source-type="long"><a class="switch_source" onclick="return switch_source(this, 'long')" href="#"><< </a> """<br>
try:<br>
<span class="source-highlight"> data = self.publish_and_retry()</span><br>
self.response.startWSGIResponse()<br>
return WSGIResult(self.request, self, data)</code><code class="source" source-type="short"><a onclick="return switch_source(this, 'short')" class="switch_source" href="#">>> </a><font color="#333333">data</font> <b>=</b> <font color="#333333">self</font><b>.</b><font color="#333333">publish_and_retry</font><b>(</b><b>)</b></code><br>
Module <span class="module" title="/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.wsgi-1.2-py2.6.egg/infrae/wsgi/publisher.py">infrae.wsgi.publisher</span>:<b>286</b> in <code>publish_and_retry</code> <a href="#" class="switch_source" tbid="195857380" onClick="return showFrame(this)"> <img src="http://localhost/_debug/media/plus.jpg" border=0 width=9 height=9> </a><br>
<code style="display: none" class="source" source-type="long"><a class="switch_source" onclick="return switch_source(this, 'long')" href="#"><< </a> """<br>
try:<br>
<span class="source-highlight"> data = self.publish()</span><br>
except (ConflictError, Retry) as error:<br>
self.abort()</code><code class="source" source-type="short"><a onclick="return switch_source(this, 'short')" class="switch_source" href="#">>> </a><font color="#333333">data</font> <b>=</b> <font color="#333333">self</font><b>.</b><font color="#333333">publish</font><b>(</b><b>)</b></code><br>
Module <span class="module" title="/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.wsgi-1.2-py2.6.egg/infrae/wsgi/publisher.py">infrae.wsgi.publisher</span>:<b>233</b> in <code>publish</code> <a href="#" class="switch_source" tbid="196466604" onClick="return showFrame(this)"> <img src="http://localhost/_debug/media/plus.jpg" border=0 width=9 height=9> </a><br>
<code style="display: none" class="source" source-type="long"><a class="switch_source" onclick="return switch_source(this, 'long')" href="#"><< </a> <font color="#007F00"># Get object to publish/render</font><br>
<font color="#333333">published_content</font> <b>=</b> <font color="#333333">self</font><b>.</b><font color="#333333">request</font><b>.</b><font color="#333333">traverse</font><b>(</b><br>
<span class="source-highlight"> <font color="#333333">path</font><b>,</b> <font color="#333333">validated_hook</font><b>=</b><font color="#333333">Zope2</font><b>.</b><font color="#333333">zpublisher_validated_hook</font><b>)</b></span><br>
<font color="#333333">__traceback_supplement__</font> <b>=</b> <b>(</b><font color="#333333">ErrorSupplement</font><b>,</b> <font color="#333333">published_content</font><b>)</b></code><code class="source" source-type="short"><a onclick="return switch_source(this, 'short')" class="switch_source" href="#">>> </a><font color="#333333">path</font><b>,</b> <font color="#333333">validated_hook</font><b>=</b><font color="#333333">Zope2</font><b>.</b><font color="#333333">zpublisher_validated_hook</font><b>)</b></code><br>
Module <span class="module" title="/home/aaltepet/silva/local-Silva-2.3/eggs/Zope2-2.12.15-py2.6-linux-i686.egg/ZPublisher/BaseRequest.py">ZPublisher.BaseRequest</span>:<b>539</b> in <code>traverse</code> <a href="#" class="switch_source" tbid="196466644" onClick="return showFrame(this)"> <img src="http://localhost/_debug/media/plus.jpg" border=0 width=9 height=9> </a><br>
<code style="display: none" class="source" source-type="long"><a class="switch_source" onclick="return switch_source(this, 'long')" href="#"><< </a> <font color="#0000AF"><b>not</b></font> <font color="#333333">hasattr</font><b>(</b><font color="#333333">parents</font><b>[</b><font color="#DD2200"><b>1</b></font><b>]</b><b>,</b><font color="#600080">'__bobo_traverse__'</font><b>)</b><b>)</b><b>:</b><br>
<font color="#0000AF"><b>if</b></font> <font color="#0000AF"><b>not</b></font> <b>(</b><font color="#333333">hasattr</font><b>(</b><font color="#333333">parents</font><b>[</b><font color="#DD2200"><b>1</b></font><b>]</b><b>.</b><font color="#333333">aq_base</font><b>,</b> <font color="#333333">entry_name</font><b>)</b> <font color="#0000AF"><b>or</b></font><br>
<span class="source-highlight"> <font color="#333333">parents</font><b>[</b><font color="#DD2200"><b>1</b></font><b>]</b><b>.</b><font color="#333333">aq_base</font><b>.</b><font color="#333333">has_key</font><b>(</b><font color="#333333">entry_name</font><b>)</b><b>)</b><b>:</b></span><br>
<font color="#0000AF"><b>raise</b></font> <font color="#333333">AttributeError</font><b>,</b> <font color="#333333">entry_name</font></code><code class="source" source-type="short"><a onclick="return switch_source(this, 'short')" class="switch_source" href="#">>> </a><font color="#333333">parents</font><b>[</b><font color="#DD2200"><b>1</b></font><b>]</b><b>.</b><font color="#333333">aq_base</font><b>.</b><font color="#333333">has_key</font><b>(</b><font color="#333333">entry_name</font><b>)</b><b>)</b><b>:</b></code><br>
<b>AttributeError: has_key</b><br>
<script type="text/javascript">
show_button('extra_data', 'extra data');
</script>
<div id="extra_data" class="hidden-data">
<br>
<table class="variables">
<tr class="header"><th colspan="2">CGI Variables</th></tr>
<tr class="odd"><td>CONTENT_LENGTH</td>
<td><tt>'0'</tt></td></tr>
<tr class="even"><td>HTTP_HOST</td>
<td><tt>'localhost'</tt></td></tr>
<tr class="odd"><td>PATH_INFO</td>
<td><tt>'/silva/index.html'</tt></td></tr>
<tr class="even"><td>REMOTE_ADDR</td>
<td><tt>'127.0.0.1'</tt></td></tr>
<tr class="odd"><td>REQUEST_METHOD</td>
<td><tt>'OPTIONS'</tt></td></tr>
<tr class="even"><td>SERVER_NAME</td>
<td><tt>'0.0.0.0'</tt></td></tr>
<tr class="odd"><td>SERVER_PORT</td>
<td><tt>'8080'</tt></td></tr>
<tr class="even"><td>SERVER_PROTOCOL</td>
<td><tt>'HTTP/1.1'</tt></td></tr>
</table><br>
<table class="variables">
<tr class="header"><th colspan="2">WSGI Variables</th></tr>
<tr class="odd"><td>application</td>
<td><tt><infrae.wsgi.publisher.WSGIApplication object at 0xbe034ec></tt></td></tr>
<tr class="even"><td>paste.evalexception</td>
<td><tt><z3c.evalexception.ZopeEvalException object at 0x85fec0c></tt></td></tr>
<tr class="odd"><td>paste.evalexception.debug_count</td>
<td><tt>1303920462</tt></td></tr>
<tr class="even"><td>paste.httpserver.thread_pool</td>
<td><tt><paste.httpserver.ThreadPool object at 0xbe06a6c></tt></td></tr>
<tr class="odd"><td>paste.throw_errors</td>
<td><tt>True</tt></td></tr>
<tr class="even"><td>wsgi process</td>
<td><tt>'Multithreaded'</tt></td></tr>
<tr class="odd"><td>wsgi.handleErrors</td>
<td><tt>False</tt></td></tr>
</table><br>
</div>
<br>
<script type="text/javascript">
show_button('text_version', 'text version')
</script>
<div id="text_version" class="hidden-data">
<textarea style="width: 100%" rows=10 cols=60>URL: http://localhost/silva/index.html
File '/home/aaltepet/silva/local-Silva-2.3/eggs/Paste-1.7.5.1-py2.6.egg/paste/evalexception/middleware.py', line 306 in respond
app_iter = self.application(environ, detect_start_response)
File '/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.wsgi-1.2-py2.6.egg/infrae/wsgi/publisher.py', line 377 in __call__
return publication()
File '/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.wsgi-1.2-py2.6.egg/infrae/wsgi/publisher.py', line 315 in __call__
data = self.publish_and_retry()
File '/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.wsgi-1.2-py2.6.egg/infrae/wsgi/publisher.py', line 286 in publish_and_retry
data = self.publish()
File '/home/aaltepet/silva/local-Silva-2.3/eggs/infrae.wsgi-1.2-py2.6.egg/infrae/wsgi/publisher.py', line 233 in publish
path, validated_hook=Zope2.zpublisher_validated_hook)
File '/home/aaltepet/silva/local-Silva-2.3/eggs/Zope2-2.12.15-py2.6-linux-i686.egg/ZPublisher/BaseRequest.py', line 539 in traverse
parents[1].aq_base.has_key(entry_name)):
AttributeError: has_key
CGI Variables
-------------
CONTENT_LENGTH: '0'
HTTP_HOST: 'localhost'
PATH_INFO: '/silva/index.html'
REMOTE_ADDR: '127.0.0.1'
REQUEST_METHOD: 'OPTIONS'
SERVER_NAME: '0.0.0.0'
SERVER_PORT: '8080'
SERVER_PROTOCOL: 'HTTP/1.1'
WSGI Variables
--------------
application: <infrae.wsgi.publisher.WSGIApplication object at 0xbe034ec>
paste.evalexception: <z3c.evalexception.ZopeEvalException object at 0x85fec0c>
paste.evalexception.debug_count: 1303920462
paste.httpserver.thread_pool: <paste.httpserver.ThreadPool object at 0xbe06a6c>
paste.throw_errors: True
wsgi process: 'Multithreaded'
wsgi.handleErrors: False</textarea>
</div>
</body>
</html>
Connection closed by foreign host.
--=-8sBdSCT8jd2Si+FBXeB8
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
_______________________________________________
silva-dev mailing list
silva-dev-IAPFreCvJWM6s/[email protected]
https://lists.infrae.com/mailman/listinfo/silva-dev
--=-8sBdSCT8jd2Si+FBXeB8--