<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[14120] CalendarServer/trunk/txweb2</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/14120">14120</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2014-10-29 09:28:01 -0700 (Wed, 29 Oct 2014)</dd>
</dl>
<h3>Log Message</h3>
<pre>Whitespace.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServertrunktxweb2authdigestpy">CalendarServer/trunk/txweb2/auth/digest.py</a></li>
<li><a href="#CalendarServertrunktxweb2authinterfacespy">CalendarServer/trunk/txweb2/auth/interfaces.py</a></li>
<li><a href="#CalendarServertrunktxweb2authwrapperpy">CalendarServer/trunk/txweb2/auth/wrapper.py</a></li>
<li><a href="#CalendarServertrunktxweb2channelhttppy">CalendarServer/trunk/txweb2/channel/http.py</a></li>
<li><a href="#CalendarServertrunktxweb2clienthttppy">CalendarServer/trunk/txweb2/client/http.py</a></li>
<li><a href="#CalendarServertrunktxweb2clientinterfacespy">CalendarServer/trunk/txweb2/client/interfaces.py</a></li>
<li><a href="#CalendarServertrunktxweb2davauthpy">CalendarServer/trunk/txweb2/dav/auth.py</a></li>
<li><a href="#CalendarServertrunktxweb2davfileoppy">CalendarServer/trunk/txweb2/dav/fileop.py</a></li>
<li><a href="#CalendarServertrunktxweb2davhttppy">CalendarServer/trunk/txweb2/dav/http.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethod__init__py">CalendarServer/trunk/txweb2/dav/method/__init__.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethodaclpy">CalendarServer/trunk/txweb2/dav/method/acl.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethodcopymovepy">CalendarServer/trunk/txweb2/dav/method/copymove.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethoddeletepy">CalendarServer/trunk/txweb2/dav/method/delete.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethoddelete_commonpy">CalendarServer/trunk/txweb2/dav/method/delete_common.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethodgetpy">CalendarServer/trunk/txweb2/dav/method/get.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethodlockpy">CalendarServer/trunk/txweb2/dav/method/lock.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethodmkcolpy">CalendarServer/trunk/txweb2/dav/method/mkcol.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethodprop_commonpy">CalendarServer/trunk/txweb2/dav/method/prop_common.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethodputpy">CalendarServer/trunk/txweb2/dav/method/put.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethodput_commonpy">CalendarServer/trunk/txweb2/dav/method/put_common.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethodreportpy">CalendarServer/trunk/txweb2/dav/method/report.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethodreport_acl_principal_prop_setpy">CalendarServer/trunk/txweb2/dav/method/report_acl_principal_prop_set.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethodreport_principal_matchpy">CalendarServer/trunk/txweb2/dav/method/report_principal_match.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethodreport_principal_property_searchpy">CalendarServer/trunk/txweb2/dav/method/report_principal_property_search.py</a></li>
<li><a href="#CalendarServertrunktxweb2davmethodreport_principal_search_property_setpy">CalendarServer/trunk/txweb2/dav/method/report_principal_search_property_set.py</a></li>
<li><a href="#CalendarServertrunktxweb2davnonepropspy">CalendarServer/trunk/txweb2/dav/noneprops.py</a></li>
<li><a href="#CalendarServertrunktxweb2davresourcepy">CalendarServer/trunk/txweb2/dav/resource.py</a></li>
<li><a href="#CalendarServertrunktxweb2davstaticpy">CalendarServer/trunk/txweb2/dav/static.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtest__init__py">CalendarServer/trunk/txweb2/dav/test/__init__.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_aclpy">CalendarServer/trunk/txweb2/dav/test/test_acl.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_authpy">CalendarServer/trunk/txweb2/dav/test/test_auth.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_copypy">CalendarServer/trunk/txweb2/dav/test/test_copy.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_deletepy">CalendarServer/trunk/txweb2/dav/test/test_delete.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_httppy">CalendarServer/trunk/txweb2/dav/test/test_http.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_lockpy">CalendarServer/trunk/txweb2/dav/test/test_lock.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_mkcolpy">CalendarServer/trunk/txweb2/dav/test/test_mkcol.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_movepy">CalendarServer/trunk/txweb2/dav/test/test_move.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_optionspy">CalendarServer/trunk/txweb2/dav/test/test_options.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_proppy">CalendarServer/trunk/txweb2/dav/test/test_prop.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_putpy">CalendarServer/trunk/txweb2/dav/test/test_put.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_quotapy">CalendarServer/trunk/txweb2/dav/test/test_quota.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_reportpy">CalendarServer/trunk/txweb2/dav/test/test_report.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_report_expandpy">CalendarServer/trunk/txweb2/dav/test/test_report_expand.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_resourcepy">CalendarServer/trunk/txweb2/dav/test/test_resource.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_staticpy">CalendarServer/trunk/txweb2/dav/test/test_static.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttest_xattrpropspy">CalendarServer/trunk/txweb2/dav/test/test_xattrprops.py</a></li>
<li><a href="#CalendarServertrunktxweb2davtesttworequest_clientpy">CalendarServer/trunk/txweb2/dav/test/tworequest_client.py</a></li>
<li><a href="#CalendarServertrunktxweb2davxattrpropspy">CalendarServer/trunk/txweb2/dav/xattrprops.py</a></li>
<li><a href="#CalendarServertrunktxweb2errorpy">CalendarServer/trunk/txweb2/error.py</a></li>
<li><a href="#CalendarServertrunktxweb2fileuploadpy">CalendarServer/trunk/txweb2/fileupload.py</a></li>
<li><a href="#CalendarServertrunktxweb2filtergzippy">CalendarServer/trunk/txweb2/filter/gzip.py</a></li>
<li><a href="#CalendarServertrunktxweb2filterlocationpy">CalendarServer/trunk/txweb2/filter/location.py</a></li>
<li><a href="#CalendarServertrunktxweb2filterrangepy">CalendarServer/trunk/txweb2/filter/range.py</a></li>
<li><a href="#CalendarServertrunktxweb2httppy">CalendarServer/trunk/txweb2/http.py</a></li>
<li><a href="#CalendarServertrunktxweb2http_headerspy">CalendarServer/trunk/txweb2/http_headers.py</a></li>
<li><a href="#CalendarServertrunktxweb2iwebpy">CalendarServer/trunk/txweb2/iweb.py</a></li>
<li><a href="#CalendarServertrunktxweb2logpy">CalendarServer/trunk/txweb2/log.py</a></li>
<li><a href="#CalendarServertrunktxweb2resourcepy">CalendarServer/trunk/txweb2/resource.py</a></li>
<li><a href="#CalendarServertrunktxweb2responsecodepy">CalendarServer/trunk/txweb2/responsecode.py</a></li>
<li><a href="#CalendarServertrunktxweb2serverpy">CalendarServer/trunk/txweb2/server.py</a></li>
<li><a href="#CalendarServertrunktxweb2staticpy">CalendarServer/trunk/txweb2/static.py</a></li>
<li><a href="#CalendarServertrunktxweb2streampy">CalendarServer/trunk/txweb2/stream.py</a></li>
<li><a href="#CalendarServertrunktxweb2test__init__py">CalendarServer/trunk/txweb2/test/__init__.py</a></li>
<li><a href="#CalendarServertrunktxweb2testsimple_clientpy">CalendarServer/trunk/txweb2/test/simple_client.py</a></li>
<li><a href="#CalendarServertrunktxweb2testtest_clientpy">CalendarServer/trunk/txweb2/test/test_client.py</a></li>
<li><a href="#CalendarServertrunktxweb2testtest_fileuploadpy">CalendarServer/trunk/txweb2/test/test_fileupload.py</a></li>
<li><a href="#CalendarServertrunktxweb2testtest_httppy">CalendarServer/trunk/txweb2/test/test_http.py</a></li>
<li><a href="#CalendarServertrunktxweb2testtest_http_headerspy">CalendarServer/trunk/txweb2/test/test_http_headers.py</a></li>
<li><a href="#CalendarServertrunktxweb2testtest_httpauthpy">CalendarServer/trunk/txweb2/test/test_httpauth.py</a></li>
<li><a href="#CalendarServertrunktxweb2testtest_logpy">CalendarServer/trunk/txweb2/test/test_log.py</a></li>
<li><a href="#CalendarServertrunktxweb2testtest_resourcepy">CalendarServer/trunk/txweb2/test/test_resource.py</a></li>
<li><a href="#CalendarServertrunktxweb2testtest_serverpy">CalendarServer/trunk/txweb2/test/test_server.py</a></li>
<li><a href="#CalendarServertrunktxweb2testtest_staticpy">CalendarServer/trunk/txweb2/test/test_static.py</a></li>
<li><a href="#CalendarServertrunktxweb2testtest_streampy">CalendarServer/trunk/txweb2/test/test_stream.py</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServertrunktxweb2authdigestpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/auth/digest.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/auth/digest.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/auth/digest.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -81,6 +81,8 @@
</span><span class="cx"> return _origCalcHA1(pszAlg, pszUserName, pszRealm, pszPassword, pszNonce,
</span><span class="cx"> pszCNonce, preHA1)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> # DigestCalcResponse
</span><span class="cx"> def calcResponse(
</span><span class="cx"> HA1,
</span></span></pre></div>
<a id="CalendarServertrunktxweb2authinterfacespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/auth/interfaces.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/auth/interfaces.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/auth/interfaces.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -60,6 +60,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class IAuthenticatedRequest(Interface):
</span><span class="cx"> """
</span><span class="cx"> A request that has been authenticated with the use of Cred,
</span><span class="lines">@@ -73,6 +74,7 @@
</span><span class="cx"> "the application's realm")
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class IHTTPUser(Interface):
</span><span class="cx"> """
</span><span class="cx"> A generic interface that can implemented by an avatar to provide
</span></span></pre></div>
<a id="CalendarServertrunktxweb2authwrapperpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/auth/wrapper.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/auth/wrapper.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/auth/wrapper.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -45,6 +45,7 @@
</span><span class="cx"> responsecode.UNAUTHORIZED,
</span><span class="cx"> "You are not authorized to access this resource.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _generateHeaders(self, factories, remoteAddr=None):
</span><span class="cx"> """
</span><span class="cx"> Set up the response's headers.
</span><span class="lines">@@ -76,7 +77,7 @@
</span><span class="cx"> """
</span><span class="cx"> response = UnauthorizedResponse()
</span><span class="cx"> d = response._generateHeaders(factories, remoteAddr)
</span><del>- d.addCallback(lambda _:response)
</del><ins>+ d.addCallback(lambda _: response)
</ins><span class="cx"> return d
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -115,6 +116,7 @@
</span><span class="cx"> self.portal = portal
</span><span class="cx"> self.interfaces = interfaces
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _loginSucceeded(self, avatar, request):
</span><span class="cx"> """
</span><span class="cx"> Callback for successful login.
</span><span class="lines">@@ -239,6 +241,7 @@
</span><span class="cx"> """
</span><span class="cx"> return self.authenticate(request), seg
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def renderHTTP(self, request):
</span><span class="cx"> """
</span><span class="cx"> Authenticate the request then return the result of calling renderHTTP
</span></span></pre></div>
<a id="CalendarServertrunktxweb2channelhttppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/channel/http.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/channel/http.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/channel/http.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -53,6 +53,7 @@
</span><span class="cx"> self.retryAfter = retryAfter
</span><span class="cx"> self.outstandingRequests = outstandingRequests
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def connectionMade(self):
</span><span class="cx"> log.info(overloaded=self)
</span><span class="cx">
</span><span class="lines">@@ -76,6 +77,7 @@
</span><span class="cx"> self.transport.loseConnection()
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class SSLRedirectRequest(Request):
</span><span class="cx"> """
</span><span class="cx"> An L{SSLRedirectRequest} prevents processing if the request is over plain
</span><span class="lines">@@ -103,7 +105,7 @@
</span><span class="cx">
</span><span class="cx"> # >%
</span><span class="cx">
</span><del>-PERSIST_NO_PIPELINE, PERSIST_PIPELINE = (1,2)
</del><ins>+PERSIST_NO_PIPELINE, PERSIST_PIPELINE = (1, 2)
</ins><span class="cx">
</span><span class="cx"> _cachedHostNames = {}
</span><span class="cx"> def _cachedGetHostByAddr(hostaddr):
</span><span class="lines">@@ -113,9 +115,11 @@
</span><span class="cx"> hostname = socket.gethostbyaddr(hostaddr)[0]
</span><span class="cx"> except socket.herror:
</span><span class="cx"> hostname = hostaddr
</span><del>- _cachedHostNames[hostaddr]=hostname
</del><ins>+ _cachedHostNames[hostaddr] = hostname
</ins><span class="cx"> return hostname
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class StringTransport(object):
</span><span class="cx"> """
</span><span class="cx"> I am a StringIO wrapper that conforms for the transport API. I support
</span><span class="lines">@@ -123,15 +127,22 @@
</span><span class="cx"> """
</span><span class="cx"> def __init__(self):
</span><span class="cx"> self.s = StringIO()
</span><ins>+
+
</ins><span class="cx"> def writeSequence(self, seq):
</span><span class="cx"> self.s.write(''.join(seq))
</span><ins>+
+
</ins><span class="cx"> def __getattr__(self, attr):
</span><span class="cx"> return getattr(self.__dict__['s'], attr)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class AbortedException(Exception):
</span><span class="cx"> pass
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class HTTPParser(object):
</span><span class="cx"> """This class handles the parsing side of HTTP processing. With a suitable
</span><span class="cx"> subclass, it can parse either the client side or the server side of the
</span><span class="lines">@@ -179,6 +190,7 @@
</span><span class="cx"> self.inHeaders = http_headers.Headers()
</span><span class="cx"> self.channel = channel
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def lineReceived(self, line):
</span><span class="cx"> if self.chunkedIn:
</span><span class="cx"> # Parsing a chunked input
</span><span class="lines">@@ -246,6 +258,7 @@
</span><span class="cx"> self.headerReceived(self.partialHeader)
</span><span class="cx"> self.partialHeader = line
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def rawDataReceived(self, data):
</span><span class="cx"> """Handle incoming content."""
</span><span class="cx"> datalen = len(data)
</span><span class="lines">@@ -294,6 +307,7 @@
</span><span class="cx"> self.setConnectionParams(connHeaders)
</span><span class="cx"> self.connHeaders = connHeaders
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def allContentReceived(self):
</span><span class="cx"> self.finishedReading = True
</span><span class="cx"> self.channel.requestReadFinished(self)
</span><span class="lines">@@ -342,7 +356,7 @@
</span><span class="cx"> connHeaders = http_headers.Headers()
</span><span class="cx">
</span><span class="cx"> move('connection')
</span><del>- if self.version < (1,1):
</del><ins>+ if self.version < (1, 1):
</ins><span class="cx"> # Remove all headers mentioned in Connection, because a HTTP 1.0
</span><span class="cx"> # proxy might have erroneously forwarded it from a 1.1 client.
</span><span class="cx"> for name in connHeaders.getHeader('connection', ()):
</span><span class="lines">@@ -366,9 +380,10 @@
</span><span class="cx">
</span><span class="cx"> return connHeaders
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def setConnectionParams(self, connHeaders):
</span><span class="cx"> # Figure out persistent connection stuff
</span><del>- if self.version >= (1,1):
</del><ins>+ if self.version >= (1, 1):
</ins><span class="cx"> if 'close' in connHeaders.getHeader('connection', ()):
</span><span class="cx"> readPersistent = False
</span><span class="cx"> else:
</span><span class="lines">@@ -424,6 +439,7 @@
</span><span class="cx"> # Set the calculated persistence
</span><span class="cx"> self.channel.setReadPersistent(readPersistent)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def abortParse(self):
</span><span class="cx"> # If we're erroring out while still reading the request
</span><span class="cx"> if not self.finishedReading:
</span><span class="lines">@@ -431,19 +447,24 @@
</span><span class="cx"> self.channel.setReadPersistent(False)
</span><span class="cx"> self.channel.requestReadFinished(self)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> # producer interface
</span><span class="cx"> def pauseProducing(self):
</span><span class="cx"> if not self.finishedReading:
</span><span class="cx"> self.channel.pauseProducing()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def resumeProducing(self):
</span><span class="cx"> if not self.finishedReading:
</span><span class="cx"> self.channel.resumeProducing()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def stopProducing(self):
</span><span class="cx"> if not self.finishedReading:
</span><span class="cx"> self.channel.stopProducing()
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class HTTPChannelRequest(HTTPParser):
</span><span class="cx"> """This class handles the state and parsing for one HTTP request.
</span><span class="cx"> It is responsible for all the low-level connection oriented behavior.
</span><span class="lines">@@ -458,7 +479,7 @@
</span><span class="cx">
</span><span class="cx"> def __init__(self, channel, queued=0):
</span><span class="cx"> HTTPParser.__init__(self, channel)
</span><del>- self.queued=queued
</del><ins>+ self.queued = queued
</ins><span class="cx">
</span><span class="cx"> # Buffer writes to a string until we're first in line
</span><span class="cx"> # to write a response
</span><span class="lines">@@ -468,7 +489,7 @@
</span><span class="cx"> self.transport = self.channel.transport
</span><span class="cx">
</span><span class="cx"> # set the version to a fallback for error generation
</span><del>- self.version = (1,0)
</del><ins>+ self.version = (1, 0)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def gotInitialLine(self, initialLine):
</span><span class="lines">@@ -501,36 +522,44 @@
</span><span class="cx"> # simulate end of headers, as HTTP 0 doesn't have headers.
</span><span class="cx"> self.lineReceived('')
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def lineLengthExceeded(self, line, wasFirst=False):
</span><span class="cx"> code = wasFirst and responsecode.REQUEST_URI_TOO_LONG or responsecode.BAD_REQUEST
</span><span class="cx"> self._abortWithError(code, 'Header line too long.')
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def createRequest(self):
</span><span class="cx"> self.request = self.channel.requestFactory(self, self.command, self.path, self.version, self.length, self.inHeaders)
</span><span class="cx"> del self.inHeaders
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def processRequest(self):
</span><span class="cx"> self.request.process()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def handleContentChunk(self, data):
</span><span class="cx"> self.request.handleContentChunk(data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def handleContentComplete(self):
</span><span class="cx"> self.request.handleContentComplete()
</span><span class="cx">
</span><del>-############## HTTPChannelRequest *RESPONSE* methods #############
</del><ins>+ # HTTPChannelRequest *RESPONSE* methods #
</ins><span class="cx"> producer = None
</span><span class="cx"> chunkedOut = False
</span><span class="cx"> finished = False
</span><span class="cx">
</span><del>- ##### Request Callbacks #####
</del><ins>+
+ # Request Callbacks #
</ins><span class="cx"> def writeIntermediateResponse(self, code, headers=None):
</span><del>- if self.version >= (1,1):
</del><ins>+ if self.version >= (1, 1):
</ins><span class="cx"> self._writeHeaders(code, headers, False)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def writeHeaders(self, code, headers):
</span><span class="cx"> self._writeHeaders(code, headers, True)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _writeHeaders(self, code, headers, addConnectionHeaders):
</span><span class="cx"> # HTTP 0.9 doesn't have headers.
</span><span class="cx"> if self.version[0] == 0:
</span><span class="lines">@@ -549,9 +578,11 @@
</span><span class="cx"> if addConnectionHeaders:
</span><span class="cx"> # if we don't have a content length, we send data in
</span><span class="cx"> # chunked mode, so that we can support persistent connections.
</span><del>- if (headers.getHeader('content-length') is None and
- self.command != "HEAD" and code not in http.NO_BODY_CODES):
- if self.version >= (1,1):
</del><ins>+ if (
+ headers.getHeader('content-length') is None and
+ self.command != "HEAD" and code not in http.NO_BODY_CODES
+ ):
+ if self.version >= (1, 1):
</ins><span class="cx"> l.append("%s: %s\r\n" % ('Transfer-Encoding', 'chunked'))
</span><span class="cx"> self.chunkedOut = True
</span><span class="cx"> else:
</span><span class="lines">@@ -560,7 +591,7 @@
</span><span class="cx">
</span><span class="cx"> if self.channel.isLastRequest(self):
</span><span class="cx"> l.append("%s: %s\r\n" % ('Connection', 'close'))
</span><del>- elif self.version < (1,1):
</del><ins>+ elif self.version < (1, 1):
</ins><span class="cx"> l.append("%s: %s\r\n" % ('Connection', 'Keep-Alive'))
</span><span class="cx">
</span><span class="cx"> l.append("\r\n")
</span><span class="lines">@@ -575,6 +606,7 @@
</span><span class="cx"> else:
</span><span class="cx"> self.transport.write(data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def finish(self):
</span><span class="cx"> """We are finished writing data."""
</span><span class="cx"> if self.finished:
</span><span class="lines">@@ -612,18 +644,21 @@
</span><span class="cx"> else:
</span><span class="cx"> self._cleanup()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getHostInfo(self):
</span><span class="cx"> return self.channel._host, self.channel._secure
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getRemoteHost(self):
</span><span class="cx"> return self.channel.transport.getPeer()
</span><span class="cx">
</span><del>- ##### End Request Callbacks #####
</del><ins>+ # End Request Callbacks #
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def _abortWithError(self, errorcode, text=''):
</span><span class="cx"> """Handle low level protocol errors."""
</span><span class="cx"> headers = http_headers.Headers()
</span><del>- headers.setHeader('content-length', len(text)+1)
</del><ins>+ headers.setHeader('content-length', len(text) + 1)
</ins><span class="cx">
</span><span class="cx"> self.abortConnection(closeWrite=False)
</span><span class="cx"> self.writeHeaders(errorcode, headers)
</span><span class="lines">@@ -633,6 +668,7 @@
</span><span class="cx"> log.warn("Aborted request (%d) %s" % (errorcode, text))
</span><span class="cx"> raise AbortedException
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _cleanup(self):
</span><span class="cx"> """Called when have finished responding and are no longer queued."""
</span><span class="cx"> if self.producer:
</span><span class="lines">@@ -641,6 +677,7 @@
</span><span class="cx"> self.channel.requestWriteFinished(self)
</span><span class="cx"> del self.transport
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> # methods for channel - end users should not use these
</span><span class="cx">
</span><span class="cx"> def noLongerQueued(self):
</span><span class="lines">@@ -685,12 +722,14 @@
</span><span class="cx"> else:
</span><span class="cx"> self.transport.registerProducer(producer, streaming)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def unregisterProducer(self):
</span><span class="cx"> """Unregister the producer."""
</span><span class="cx"> if not self.queued:
</span><span class="cx"> self.transport.unregisterProducer()
</span><span class="cx"> self.producer = None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def connectionLost(self, reason):
</span><span class="cx"> """connection was lost"""
</span><span class="cx"> if self.queued and self.producer:
</span><span class="lines">@@ -699,6 +738,8 @@
</span><span class="cx"> if self.request:
</span><span class="cx"> self.request.connectionLost(reason)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class HTTPChannel(basic.LineReceiver, policies.TimeoutMixin, object):
</span><span class="cx"> """A receiver for HTTP requests. Handles splitting up the connection
</span><span class="cx"> for the multiple HTTPChannelRequests that may be in progress on this
</span><span class="lines">@@ -717,7 +758,7 @@
</span><span class="cx">
</span><span class="cx"> implements(interfaces.IHalfCloseableProtocol)
</span><span class="cx">
</span><del>- ## Configuration parameters. Set in instances or subclasses.
</del><ins>+ # Configuration parameters. Set in instances or subclasses.
</ins><span class="cx">
</span><span class="cx"> # How many simultaneous requests to handle.
</span><span class="cx"> maxPipeline = 4
</span><span class="lines">@@ -754,10 +795,12 @@
</span><span class="cx"> def _callLater(self, secs, fun):
</span><span class="cx"> reactor.callLater(secs, fun)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __init__(self):
</span><span class="cx"> # the request queue
</span><span class="cx"> self.requests = []
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def connectionMade(self):
</span><span class="cx"> self._secure = interfaces.ISSLTransport(self.transport, None) is not None
</span><span class="cx"> address = self.transport.getHost()
</span><span class="lines">@@ -765,6 +808,7 @@
</span><span class="cx"> self.setTimeout(self.inputTimeOut)
</span><span class="cx"> self.factory.addConnectedChannel(self)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def lineReceived(self, line):
</span><span class="cx"> if self._first_line:
</span><span class="cx"> self.setTimeout(self.inputTimeOut)
</span><span class="lines">@@ -798,6 +842,7 @@
</span><span class="cx"> except AbortedException:
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def lineLengthExceeded(self, line):
</span><span class="cx"> if self._first_line:
</span><span class="cx"> # Fabricate a request object to respond to the line length violation.
</span><span class="lines">@@ -810,6 +855,7 @@
</span><span class="cx"> except AbortedException:
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def rawDataReceived(self, data):
</span><span class="cx"> self.setTimeout(self.inputTimeOut)
</span><span class="cx"> try:
</span><span class="lines">@@ -817,6 +863,7 @@
</span><span class="cx"> except AbortedException:
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def requestReadFinished(self, request):
</span><span class="cx"> if(self.readPersistent is PERSIST_NO_PIPELINE or
</span><span class="cx"> len(self.requests) >= self.maxPipeline):
</span><span class="lines">@@ -832,6 +879,7 @@
</span><span class="cx"> if len(self.requests) > 0:
</span><span class="cx"> self.setTimeout(self.idleTimeOut)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _startNextRequest(self):
</span><span class="cx"> # notify next request, if present, it can start writing
</span><span class="cx"> del self.requests[0]
</span><span class="lines">@@ -854,11 +902,13 @@
</span><span class="cx"> self.setTimeout(self.betweenRequestsTimeOut)
</span><span class="cx"> self.resumeProducing()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def setReadPersistent(self, persistent):
</span><span class="cx"> if self.readPersistent:
</span><span class="cx"> # only allow it to be set if it's not currently False
</span><span class="cx"> self.readPersistent = persistent
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def dropQueuedRequests(self):
</span><span class="cx"> """Called when a response is written that forces a connection close."""
</span><span class="cx"> self.readPersistent = False
</span><span class="lines">@@ -867,13 +917,16 @@
</span><span class="cx"> request.connectionLost(None)
</span><span class="cx"> del self.requests[1:]
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def isLastRequest(self, request):
</span><span class="cx"> # Is this channel handling the last possible request
</span><span class="cx"> return not self.readPersistent and self.requests[-1] == request
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def requestWriteFinished(self, request):
</span><span class="cx"> """Called by first request in queue when it is done."""
</span><del>- if request != self.requests[0]: raise TypeError
</del><ins>+ if request != self.requests[0]:
+ raise TypeError
</ins><span class="cx">
</span><span class="cx"> # Don't del because we haven't finished cleanup, so,
</span><span class="cx"> # don't want queue len to be 0 yet.
</span><span class="lines">@@ -888,20 +941,23 @@
</span><span class="cx"> # Set an abort timer in case an orderly close hangs
</span><span class="cx"> self.setTimeout(None)
</span><span class="cx"> self._abortTimer = reactor.callLater(self.closeTimeOut, self._abortTimeout)
</span><del>- #reactor.callLater(0.1, self.transport.loseConnection)
</del><ins>+ # eactor.callLater(0.1, self.transport.loseConnection)
</ins><span class="cx"> self.transport.loseConnection()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def timeoutConnection(self):
</span><del>- #log.info("Timing out client: %s" % str(self.transport.getPeer()))
</del><ins>+ # log.info("Timing out client: %s" % str(self.transport.getPeer()))
</ins><span class="cx"> # Set an abort timer in case an orderly close hangs
</span><span class="cx"> self._abortTimer = reactor.callLater(self.closeTimeOut, self._abortTimeout)
</span><span class="cx"> policies.TimeoutMixin.timeoutConnection(self)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _abortTimeout(self):
</span><span class="cx"> log.error("Connection aborted - took too long to close: {c}", c=str(self.transport.getPeer()))
</span><span class="cx"> self._abortTimer = None
</span><span class="cx"> self.transport.abortConnection()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def readConnectionLost(self):
</span><span class="cx"> """Read connection lost"""
</span><span class="cx"> # If in the lingering-close state, lose the socket.
</span><span class="lines">@@ -923,6 +979,7 @@
</span><span class="cx"> if self.chanRequest:
</span><span class="cx"> self.transport.loseConnection()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def connectionLost(self, reason):
</span><span class="cx"> self.factory.removeConnectedChannel(self)
</span><span class="cx">
</span><span class="lines">@@ -935,6 +992,8 @@
</span><span class="cx"> if request is not None:
</span><span class="cx"> request.connectionLost(reason)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class OverloadedServerProtocol(protocol.Protocol):
</span><span class="cx"> def connectionMade(self):
</span><span class="cx"> self.transport.write("HTTP/1.0 503 Service Unavailable\r\n"
</span><span class="lines">@@ -980,7 +1039,7 @@
</span><span class="cx">
</span><span class="cx"> p = protocol.ServerFactory.buildProtocol(self, addr)
</span><span class="cx">
</span><del>- for arg,value in self.protocolArgs.iteritems():
</del><ins>+ for arg, value in self.protocolArgs.iteritems():
</ins><span class="cx"> setattr(p, arg, value)
</span><span class="cx"> return p
</span><span class="cx">
</span><span class="lines">@@ -1007,6 +1066,7 @@
</span><span class="cx"> if self.outstandingRequests == 0:
</span><span class="cx"> self.allConnectionsClosedDeferred.callback(None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @property
</span><span class="cx"> def outstandingRequests(self):
</span><span class="cx"> return len(self.connectedChannels)
</span><span class="lines">@@ -1033,9 +1093,10 @@
</span><span class="cx"> self.vary = vary
</span><span class="cx"> HTTPFactory.__init__(self, requestFactory, maxRequests, **kwargs)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def buildProtocol(self, addr):
</span><span class="cx"> if self.vary:
</span><del>- retryAfter = randint(int(self.retryAfter * 1/2), int(self.retryAfter * 3/2))
</del><ins>+ retryAfter = randint(int(self.retryAfter * 1 / 2), int(self.retryAfter * 3 / 2))
</ins><span class="cx"> else:
</span><span class="cx"> retryAfter = self.retryAfter
</span><span class="cx">
</span><span class="lines">@@ -1044,11 +1105,13 @@
</span><span class="cx">
</span><span class="cx"> p = protocol.ServerFactory.buildProtocol(self, addr)
</span><span class="cx">
</span><del>- for arg,value in self.protocolArgs.iteritems():
</del><ins>+ for arg, value in self.protocolArgs.iteritems():
</ins><span class="cx"> setattr(p, arg, value)
</span><span class="cx">
</span><span class="cx"> return p
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class HTTPLoggingChannelRequest(HTTPChannelRequest):
</span><span class="cx">
</span><span class="cx"> class TransportLoggingWrapper(object):
</span><span class="lines">@@ -1071,11 +1134,13 @@
</span><span class="cx"> def __getattr__(self, attr):
</span><span class="cx"> return getattr(self.__dict__['transport'], attr)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class LogData(object):
</span><span class="cx"> def __init__(self):
</span><span class="cx"> self.request = []
</span><span class="cx"> self.response = []
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __init__(self, channel, queued=0):
</span><span class="cx"> super(HTTPLoggingChannelRequest, self).__init__(channel, queued)
</span><span class="cx">
</span><span class="lines">@@ -1085,6 +1150,7 @@
</span><span class="cx"> else:
</span><span class="cx"> self.logData = None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def gotInitialLine(self, initialLine):
</span><span class="cx"> if self.logData is not None:
</span><span class="cx"> self.startTime = time.time()
</span><span class="lines">@@ -1092,6 +1158,7 @@
</span><span class="cx"> self.logData.request.append("%s\r\n" % (initialLine,))
</span><span class="cx"> super(HTTPLoggingChannelRequest, self).gotInitialLine(initialLine)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def lineReceived(self, line):
</span><span class="cx">
</span><span class="cx"> if self.logData is not None:
</span><span class="lines">@@ -1104,12 +1171,14 @@
</span><span class="cx"> self.logData.request.append("%s\r\n" % (loggedLine,))
</span><span class="cx"> super(HTTPLoggingChannelRequest, self).lineReceived(line)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def handleContentChunk(self, data):
</span><span class="cx">
</span><span class="cx"> if self.logData is not None:
</span><span class="cx"> self.logData.request.append(data)
</span><span class="cx"> super(HTTPLoggingChannelRequest, self).handleContentChunk(data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def handleContentComplete(self):
</span><span class="cx">
</span><span class="cx"> if self.logData is not None:
</span><span class="lines">@@ -1117,12 +1186,14 @@
</span><span class="cx"> self.logData.request.append("\r\n\r\n>>>> Request complete at: %.3f (elapsed: %.1f ms)" % (doneTime, 1000 * (doneTime - self.startTime),))
</span><span class="cx"> super(HTTPLoggingChannelRequest, self).handleContentComplete()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def writeHeaders(self, code, headers):
</span><span class="cx"> if self.logData is not None:
</span><span class="cx"> doneTime = time.time()
</span><span class="cx"> self.logData.response.append("\r\n\r\n<<<< Response sending at: %.3f (elapsed: %.1f ms)\r\n\r\n" % (doneTime, 1000 * (doneTime - self.startTime),))
</span><span class="cx"> super(HTTPLoggingChannelRequest, self).writeHeaders(code, headers)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def finish(self):
</span><span class="cx">
</span><span class="cx"> super(HTTPLoggingChannelRequest, self).finish()
</span><span class="lines">@@ -1144,11 +1215,11 @@
</span><span class="cx"> L{LimitingHTTPFactory} will limit. This must be set externally.
</span><span class="cx"> """
</span><span class="cx">
</span><del>- def __init__(self, requestFactory, maxRequests=600, maxAccepts=100,
- **kwargs):
</del><ins>+ def __init__(self, requestFactory, maxRequests=600, maxAccepts=100, **kwargs):
</ins><span class="cx"> HTTPFactory.__init__(self, requestFactory, maxRequests, **kwargs)
</span><span class="cx"> self.maxAccepts = maxAccepts
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def buildProtocol(self, addr):
</span><span class="cx"> """
</span><span class="cx"> Override L{HTTPFactory.buildProtocol} in order to avoid ever returning
</span><span class="lines">@@ -1159,6 +1230,7 @@
</span><span class="cx"> setattr(p, arg, value)
</span><span class="cx"> return p
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def addConnectedChannel(self, channel):
</span><span class="cx"> """
</span><span class="cx"> Override L{HTTPFactory.addConnectedChannel} to pause listening on the
</span></span></pre></div>
<a id="CalendarServertrunktxweb2clienthttppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/client/http.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/client/http.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/client/http.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -104,6 +104,7 @@
</span><span class="cx"> self.transport = self.channel.transport
</span><span class="cx"> self.responseDefer = Deferred()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def submit(self):
</span><span class="cx"> l = []
</span><span class="cx"> request = self.request
</span><span class="lines">@@ -138,15 +139,18 @@
</span><span class="cx"> d = StreamProducer(request.stream).beginProducing(self)
</span><span class="cx"> d.addCallback(self._finish).addErrback(self._error)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def registerProducer(self, producer, streaming):
</span><span class="cx"> """
</span><span class="cx"> Register a producer.
</span><span class="cx"> """
</span><span class="cx"> self.transport.registerProducer(producer, streaming)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def unregisterProducer(self):
</span><span class="cx"> self.transport.unregisterProducer()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def write(self, data):
</span><span class="cx"> if not data:
</span><span class="cx"> return
</span><span class="lines">@@ -155,6 +159,7 @@
</span><span class="cx"> else:
</span><span class="cx"> self.transport.write(data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _finish(self, x):
</span><span class="cx"> """
</span><span class="cx"> We are finished writing data.
</span><span class="lines">@@ -167,6 +172,7 @@
</span><span class="cx"> self.channel.requestWriteFinished(self)
</span><span class="cx"> del self.transport
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _error(self, err):
</span><span class="cx"> """
</span><span class="cx"> Abort parsing, and depending of the status of the request, either fire
</span><span class="lines">@@ -179,15 +185,18 @@
</span><span class="cx"> else:
</span><span class="cx"> self.responseDefer.errback(err)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _abortWithError(self, errcode, text):
</span><span class="cx"> """
</span><span class="cx"> Abort parsing by forwarding a C{ProtocolError} to C{_error}.
</span><span class="cx"> """
</span><span class="cx"> self._error(ProtocolError(text))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def connectionLost(self, reason):
</span><span class="cx"> self._error(reason)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def gotInitialLine(self, initialLine):
</span><span class="cx"> parts = initialLine.split(' ', 2)
</span><span class="cx">
</span><span class="lines">@@ -197,7 +206,7 @@
</span><span class="cx"> "Bad response line: %s" % (initialLine,))
</span><span class="cx"> return
</span><span class="cx">
</span><del>- strversion, self.code, message = parts
</del><ins>+ strversion, self.code, _ignore_message = parts
</ins><span class="cx">
</span><span class="cx"> try:
</span><span class="cx"> protovers = parseVersion(strversion)
</span><span class="lines">@@ -216,7 +225,8 @@
</span><span class="cx"> 'Only HTTP 1.x is supported.')
</span><span class="cx"> return
</span><span class="cx">
</span><del>- ## FIXME: Actually creates Response, function is badly named!
</del><ins>+
+ # FIXME: Actually creates Response, function is badly named!
</ins><span class="cx"> def createRequest(self):
</span><span class="cx"> self.stream = ProducerStream(self.length)
</span><span class="cx"> self.response = Response(self.code, self.inHeaders, self.stream)
</span><span class="lines">@@ -224,13 +234,16 @@
</span><span class="cx">
</span><span class="cx"> del self.inHeaders
</span><span class="cx">
</span><del>- ## FIXME: Actually processes Response, function is badly named!
</del><ins>+
+ # FIXME: Actually processes Response, function is badly named!
</ins><span class="cx"> def processRequest(self):
</span><span class="cx"> self.responseDefer.callback(self.response)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def handleContentChunk(self, data):
</span><span class="cx"> self.stream.write(data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def handleContentComplete(self):
</span><span class="cx"> self.stream.finish()
</span><span class="cx">
</span><span class="lines">@@ -247,12 +260,15 @@
</span><span class="cx"> def clientBusy(self, proto):
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def clientIdle(self, proto):
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def clientPipelining(self, proto):
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def clientGone(self, proto):
</span><span class="cx"> pass
</span><span class="cx">
</span><span class="lines">@@ -285,6 +301,7 @@
</span><span class="cx"> manager = EmptyHTTPClientManager()
</span><span class="cx"> self.manager = manager
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def lineReceived(self, line):
</span><span class="cx"> if not self.inRequests:
</span><span class="cx"> # server sending random unrequested data.
</span><span class="lines">@@ -301,6 +318,7 @@
</span><span class="cx"> else:
</span><span class="cx"> self.inRequests[0].lineReceived(line)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def rawDataReceived(self, data):
</span><span class="cx"> if not self.inRequests:
</span><span class="cx"> # Server sending random unrequested data.
</span><span class="lines">@@ -313,6 +331,7 @@
</span><span class="cx">
</span><span class="cx"> self.inRequests[0].rawDataReceived(data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def submitRequest(self, request, closeAfter=True):
</span><span class="cx"> """
</span><span class="cx"> @param request: The request to send to a remote server.
</span><span class="lines">@@ -337,13 +356,13 @@
</span><span class="cx"> if closeAfter:
</span><span class="cx"> self.readPersistent = False
</span><span class="cx">
</span><del>- self.outRequest = chanRequest = HTTPClientChannelRequest(self,
- request, closeAfter)
</del><ins>+ self.outRequest = chanRequest = HTTPClientChannelRequest(self, request, closeAfter)
</ins><span class="cx"> self.inRequests.append(chanRequest)
</span><span class="cx">
</span><span class="cx"> chanRequest.submit()
</span><span class="cx"> return chanRequest.responseDefer
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def requestWriteFinished(self, request):
</span><span class="cx"> assert request is self.outRequest
</span><span class="cx">
</span><span class="lines">@@ -353,6 +372,7 @@
</span><span class="cx"> if self.readPersistent is PERSIST_PIPELINE:
</span><span class="cx"> self.manager.clientPipelining(self)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def requestReadFinished(self, request):
</span><span class="cx"> assert self.inRequests[0] is request
</span><span class="cx">
</span><span class="lines">@@ -366,6 +386,7 @@
</span><span class="cx"> else:
</span><span class="cx"> self.transport.loseConnection()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def setReadPersistent(self, persist):
</span><span class="cx"> self.readPersistent = persist
</span><span class="cx"> if not persist:
</span><span class="lines">@@ -374,6 +395,7 @@
</span><span class="cx"> request.connectionLost(None)
</span><span class="cx"> del self.inRequests[1:]
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def connectionLost(self, reason):
</span><span class="cx"> self.readPersistent = False
</span><span class="cx"> self.setTimeout(None)
</span><span class="lines">@@ -382,4 +404,3 @@
</span><span class="cx"> for request in self.inRequests:
</span><span class="cx"> if request is not None:
</span><span class="cx"> request.connectionLost(reason)
</span><del>-
</del></span></pre></div>
<a id="CalendarServertrunktxweb2clientinterfacespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/client/interfaces.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/client/interfaces.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/client/interfaces.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -26,7 +26,7 @@
</span><span class="cx"> from zope.interface import Interface
</span><span class="cx">
</span><span class="cx"> class IHTTPClientManager(Interface):
</span><del>- """I coordinate between multiple L{HTTPClientProtocol} objects connected to a
</del><ins>+ """I coordinate between multiple L{HTTPClientProtocol} objects connected to a
</ins><span class="cx"> single server to facilite request queuing and pipelining.
</span><span class="cx"> """
</span><span class="cx">
</span><span class="lines">@@ -35,27 +35,30 @@
</span><span class="cx"> requests.
</span><span class="cx">
</span><span class="cx"> @param proto: The L{HTTPClientProtocol} that is changing state.
</span><del>- @type proto: L{HTTPClientProtocol}
</del><ins>+ @type proto: L{HTTPClientProtocol}
</ins><span class="cx"> """
</span><span class="cx"> pass
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def clientIdle(proto):
</span><span class="cx"> """Called when an L{HTTPClientProtocol} is able to accept more requests.
</span><del>-
</del><ins>+
</ins><span class="cx"> @param proto: The L{HTTPClientProtocol} that is changing state.
</span><span class="cx"> @type proto: L{HTTPClientProtocol}
</span><span class="cx"> """
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def clientPipelining(proto):
</span><span class="cx"> """Called when the L{HTTPClientProtocol} determines that it is able to
</span><span class="cx"> support request pipelining.
</span><del>-
</del><ins>+
</ins><span class="cx"> @param proto: The L{HTTPClientProtocol} that is changing state.
</span><span class="cx"> @type proto: L{HTTPClientProtocol}
</span><span class="cx"> """
</span><span class="cx"> pass
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def clientGone(proto):
</span><span class="cx"> """Called when the L{HTTPClientProtocol} disconnects from the server.
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davauthpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/auth.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/auth.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/auth.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -37,9 +37,11 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> class AuthenticationWrapper(WrapperResource):
</span><del>- def __init__(self, resource, portal,
</del><ins>+ def __init__(
+ self, resource, portal,
</ins><span class="cx"> wireEncryptedCredentialFactories, wireUnencryptedCredentialFactories,
</span><del>- loginInterfaces):
</del><ins>+ loginInterfaces
+ ):
</ins><span class="cx"> """
</span><span class="cx"> Wrap the given resource and use the parameters to set up the request
</span><span class="cx"> to allow anyone to challenge and handle authentication.
</span><span class="lines">@@ -58,10 +60,14 @@
</span><span class="cx"> super(AuthenticationWrapper, self).__init__(resource)
</span><span class="cx">
</span><span class="cx"> self.portal = portal
</span><del>- self.wireEncryptedCredentialFactories = dict([(factory.scheme, factory)
- for factory in wireEncryptedCredentialFactories])
- self.wireUnencryptedCredentialFactories = dict([(factory.scheme, factory)
- for factory in wireUnencryptedCredentialFactories])
</del><ins>+ self.wireEncryptedCredentialFactories = dict([
+ (factory.scheme, factory)
+ for factory in wireEncryptedCredentialFactories
+ ])
+ self.wireUnencryptedCredentialFactories = dict([
+ (factory.scheme, factory)
+ for factory in wireUnencryptedCredentialFactories
+ ])
</ins><span class="cx"> self.loginInterfaces = loginInterfaces
</span><span class="cx">
</span><span class="cx"> # FIXME: some unit tests access self.credentialFactories, so assigning here
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davfileoppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/fileop.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/fileop.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/fileop.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -170,6 +170,8 @@
</span><span class="cx">
</span><span class="cx"> return succeed(response)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def copy(source_filepath, destination_filepath, destination_uri, depth):
</span><span class="cx"> """
</span><span class="cx"> Perform a X{COPY} from the given source and destination filepaths.
</span><span class="lines">@@ -203,7 +205,7 @@
</span><span class="cx"> Failure(),
</span><span class="cx"> "opening file for reading: %s" % (source_filepath.path,)
</span><span class="cx"> ))
</span><del>-
</del><ins>+
</ins><span class="cx"> source_stream = FileStream(source_file)
</span><span class="cx"> response = waitForDeferred(put(source_stream, destination_filepath, destination_uri))
</span><span class="cx"> yield response
</span><span class="lines">@@ -257,7 +259,7 @@
</span><span class="cx"> "creating directory %s" % (destination_basename,)
</span><span class="cx"> ))
</span><span class="cx">
</span><del>- if depth == "0":
</del><ins>+ if depth == "0":
</ins><span class="cx"> yield success_code
</span><span class="cx"> return
</span><span class="cx"> else:
</span><span class="lines">@@ -279,7 +281,7 @@
</span><span class="cx"> def paths(basepath, subpath):
</span><span class="cx"> source_path = os.path.join(basepath, subpath)
</span><span class="cx"> assert source_path.startswith(source_basename)
</span><del>- destination_path = os.path.join(destination_basename, source_path[source_basename_len+1:])
</del><ins>+ destination_path = os.path.join(destination_basename, source_path[source_basename_len + 1:])
</ins><span class="cx"> return source_path, destination_path
</span><span class="cx">
</span><span class="cx"> for dir, subdirs, files in os.walk(source_filepath.path, topdown=True):
</span><span class="lines">@@ -493,6 +495,8 @@
</span><span class="cx">
</span><span class="cx"> return succeed(responsecode.CREATED)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def rmdir(dirname):
</span><span class="cx"> """
</span><span class="cx"> Removes the directory with the given name, as well as its contents.
</span><span class="lines">@@ -510,6 +514,8 @@
</span><span class="cx">
</span><span class="cx"> os.rmdir(dirname)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def checkResponse(response, method, *codes):
</span><del>- assert response in codes, \
</del><ins>+ assert response in codes, \
</ins><span class="cx"> "%s() returned %r, but should have returned one of %r instead" % (method, response, codes)
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davhttppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/http.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/http.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/http.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -162,12 +162,13 @@
</span><span class="cx"> @param success_response: the response to return in lieu of a
</span><span class="cx"> L{MultiStatusResponse} if no responses are added to this queue.
</span><span class="cx"> """
</span><del>- self.responses = []
- self.path_basename = path_basename
</del><ins>+ self.responses = []
+ self.path_basename = path_basename
</ins><span class="cx"> self.path_basename_len = len(path_basename)
</span><del>- self.method = method
- self.success_response = success_response
</del><ins>+ self.method = method
+ self.success_response = success_response
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def add(self, path, what):
</span><span class="cx"> """
</span><span class="cx"> Add a response.
</span><span class="lines">@@ -178,12 +179,12 @@
</span><span class="cx"> assert path.startswith(self.path_basename), "%s does not start with %s" % (path, self.path_basename)
</span><span class="cx">
</span><span class="cx"> if type(what) is int:
</span><del>- code = what
- error = None
</del><ins>+ code = what
+ error = None
</ins><span class="cx"> message = responsecode.RESPONSES[code]
</span><span class="cx"> elif isinstance(what, Failure):
</span><del>- code = statusForFailure(what)
- error = errorForFailure(what)
</del><ins>+ code = statusForFailure(what)
+ error = errorForFailure(what)
</ins><span class="cx"> message = messageForFailure(what)
</span><span class="cx"> else:
</span><span class="cx"> raise AssertionError("Unknown data type: %r" % (what,))
</span><span class="lines">@@ -205,6 +206,7 @@
</span><span class="cx"> children.append(element.ResponseDescription(message))
</span><span class="cx"> self.responses.append(element.StatusResponse(*children))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def response(self):
</span><span class="cx"> """
</span><span class="cx"> Generate a L{MultiStatusResponse} with the responses contained in the
</span><span class="lines">@@ -231,11 +233,12 @@
</span><span class="cx"> @param success_response: the status to return if no
</span><span class="cx"> L{PropertyStatus} are added to this queue.
</span><span class="cx"> """
</span><del>- self.method = method
- self.uri = uri
- self.propstats = []
- self.success_response = success_response
</del><ins>+ self.method = method
+ self.uri = uri
+ self.propstats = []
+ self.success_response = success_response
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def add(self, what, property):
</span><span class="cx"> """
</span><span class="cx"> Add a response.
</span><span class="lines">@@ -243,12 +246,12 @@
</span><span class="cx"> @param property: the property whose status is being reported.
</span><span class="cx"> """
</span><span class="cx"> if type(what) is int:
</span><del>- code = what
- error = None
</del><ins>+ code = what
+ error = None
</ins><span class="cx"> message = responsecode.RESPONSES[code]
</span><span class="cx"> elif isinstance(what, Failure):
</span><del>- code = statusForFailure(what)
- error = errorForFailure(what)
</del><ins>+ code = statusForFailure(what)
+ error = errorForFailure(what)
</ins><span class="cx"> message = messageForFailure(what)
</span><span class="cx"> else:
</span><span class="cx"> raise AssertionError("Unknown data type: %r" % (what,))
</span><span class="lines">@@ -274,6 +277,7 @@
</span><span class="cx"> children.append(element.ResponseDescription(message))
</span><span class="cx"> self.propstats.append(element.PropertyStatus(*children))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def error(self):
</span><span class="cx"> """
</span><span class="cx"> Convert any 2xx codes in the propstat responses to 424 Failed
</span><span class="lines">@@ -294,6 +298,7 @@
</span><span class="cx"> newchildren.append(child)
</span><span class="cx"> self.propstats[index] = element.PropertyStatus(*newchildren)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def response(self):
</span><span class="cx"> """
</span><span class="cx"> Generate a response from the responses contained in the queue or, if
</span><span class="lines">@@ -313,6 +318,7 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> ##
</span><span class="cx"> # Exceptions and response codes
</span><span class="cx"> ##
</span><span class="lines">@@ -355,6 +361,7 @@
</span><span class="cx"> failure.raiseException()
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def errorForFailure(failure):
</span><span class="cx"> if failure.check(HTTPError) and isinstance(failure.value.response, ErrorResponse):
</span><span class="cx"> return element.Error(failure.value.response.error)
</span><span class="lines">@@ -362,6 +369,7 @@
</span><span class="cx"> return None
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def messageForFailure(failure):
</span><span class="cx"> if failure.check(HTTPError):
</span><span class="cx"> if isinstance(failure.value.response, ErrorResponse):
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethod__init__py"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/__init__.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/__init__.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/__init__.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethodaclpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/acl.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/acl.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/acl.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethodcopymovepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/copymove.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/copymove.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/copymove.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -84,7 +84,7 @@
</span><span class="cx"> # May need to add a location header
</span><span class="cx"> addLocation(request, destination_uri)
</span><span class="cx">
</span><del>- #x = waitForDeferred(copy(self.fp, destination.fp, destination_uri, depth))
</del><ins>+ # x = waitForDeferred(copy(self.fp, destination.fp, destination_uri, depth))
</ins><span class="cx"> x = waitForDeferred(put_common.storeResource(request,
</span><span class="cx"> source=self,
</span><span class="cx"> source_uri=request.uri,
</span><span class="lines">@@ -214,6 +214,8 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def _prepareForCopy(destination, destination_uri, request, depth):
</span><span class="cx"> #
</span><span class="cx"> # Destination must be a DAV resource
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethoddeletepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/delete.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/delete.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/delete.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethoddelete_commonpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/delete_common.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/delete_common.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/delete_common.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -66,7 +66,7 @@
</span><span class="cx"> d = waitForDeferred(resource.quotaSizeAdjust(request, -old_size))
</span><span class="cx"> yield d
</span><span class="cx"> d.getResult()
</span><del>-
</del><ins>+
</ins><span class="cx"> yield result
</span><span class="cx">
</span><span class="cx"> deleteResource = deferredGenerator(deleteResource)
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethodgetpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/get.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/get.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/get.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -39,16 +39,22 @@
</span><span class="cx"> d.addCallback(lambda _: super(txweb2.dav.resource.DAVResource, self).http_OPTIONS(request))
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def http_HEAD(self, request):
</span><span class="cx"> d = authorize(self, request)
</span><span class="cx"> d.addCallback(lambda _: super(txweb2.dav.resource.DAVResource, self).http_HEAD(request))
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def http_GET(self, request):
</span><span class="cx"> d = authorize(self, request)
</span><span class="cx"> d.addCallback(lambda _: super(txweb2.dav.resource.DAVResource, self).http_GET(request))
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def authorize(self, request):
</span><span class="cx"> if self.exists():
</span><span class="cx"> d = self.authorize(request, (davxml.Read(),))
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethodlockpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/lock.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/lock.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/lock.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -37,6 +37,8 @@
</span><span class="cx"> """
</span><span class="cx"> return responsecode.NOT_IMPLEMENTED
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def http_UNLOCK(self, request):
</span><span class="cx"> """
</span><span class="cx"> Respond to a UNLOCK request. (RFC 2518, section 8.11)
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethodmkcolpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/mkcol.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/mkcol.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/mkcol.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethodprop_commonpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/prop_common.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/prop_common.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/prop_common.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -59,9 +59,11 @@
</span><span class="cx"> @param resource: the L{DAVFile} for the targetted resource.
</span><span class="cx"> @return: a map of OK and NOT FOUND property values.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> return _namedPropertiesForResource(request, prop.children, resource)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def _namedPropertiesForResource(request, props, resource):
</span><span class="cx"> """
</span><span class="cx"> Return the specified properties on the specified resource.
</span><span class="lines">@@ -74,13 +76,13 @@
</span><span class="cx"> responsecode.OK : [],
</span><span class="cx"> responsecode.NOT_FOUND : [],
</span><span class="cx"> }
</span><del>-
</del><ins>+
</ins><span class="cx"> for property in props:
</span><span class="cx"> if isinstance(property, element.WebDAVElement):
</span><span class="cx"> qname = property.qname()
</span><span class="cx"> else:
</span><span class="cx"> qname = property
</span><del>-
</del><ins>+
</ins><span class="cx"> props = waitForDeferred(resource.listProperties(request))
</span><span class="cx"> yield props
</span><span class="cx"> props = props.getResult()
</span><span class="lines">@@ -96,11 +98,12 @@
</span><span class="cx"> if status != responsecode.NOT_FOUND:
</span><span class="cx"> log.error("Error reading property %r for resource %s: %s" %
</span><span class="cx"> (qname, request.uri, f.value))
</span><del>- if status not in properties_by_status: properties_by_status[status] = []
</del><ins>+ if status not in properties_by_status:
+ properties_by_status[status] = []
</ins><span class="cx"> properties_by_status[status].append(propertyName(qname))
</span><span class="cx"> else:
</span><span class="cx"> properties_by_status[responsecode.NOT_FOUND].append(propertyName(qname))
</span><del>-
</del><ins>+
</ins><span class="cx"> yield properties_by_status
</span><span class="cx">
</span><span class="cx"> _namedPropertiesForResource = deferredGenerator(_namedPropertiesForResource)
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethodputpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/put.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/put.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/put.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -69,15 +69,15 @@
</span><span class="cx"> # Implemented error if we get a Content-* header which we don't
</span><span class="cx"> # recognize and handle properly.
</span><span class="cx"> #
</span><del>- for header, value in request.headers.getAllRawHeaders():
</del><ins>+ for header, _ignore_value in request.headers.getAllRawHeaders():
</ins><span class="cx"> if header.startswith("Content-") and header not in (
</span><del>- #"Content-Base", # Doesn't make sense in PUT?
- #"Content-Encoding", # Requires that we decode it?
</del><ins>+ # "Content-Base", # Doesn't make sense in PUT?
+ # "Content-Encoding", # Requires that we decode it?
</ins><span class="cx"> "Content-Language",
</span><span class="cx"> "Content-Length",
</span><del>- #"Content-Location", # Doesn't make sense in PUT?
</del><ins>+ # "Content-Location", # Doesn't make sense in PUT?
</ins><span class="cx"> "Content-MD5",
</span><del>- #"Content-Range", # FIXME: Need to implement this
</del><ins>+ # "Content-Range", # FIXME: Need to implement this
</ins><span class="cx"> "Content-Type",
</span><span class="cx"> ):
</span><span class="cx"> log.error("Client sent unrecognized content header in PUT request: %s"
</span><span class="lines">@@ -100,5 +100,5 @@
</span><span class="cx"> # to return a MULTI_STATUS response, which is WebDAV-specific (and PUT is
</span><span class="cx"> # not).
</span><span class="cx"> #
</span><del>- #return put(request.stream, self.fp)
</del><ins>+ # return put(request.stream, self.fp)
</ins><span class="cx"> return put_common.storeResource(request, destination=self, destination_uri=request.uri)
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethodput_commonpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/put_common.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/put_common.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/put_common.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -55,7 +55,7 @@
</span><span class="cx"> ):
</span><span class="cx"> """
</span><span class="cx"> Function that does common PUT/COPY/MOVE behaviour.
</span><del>-
</del><ins>+
</ins><span class="cx"> @param request: the L{txweb2.server.Request} for the current HTTP request.
</span><span class="cx"> @param source: the L{DAVFile} for the source resource to copy from, or None if source data
</span><span class="cx"> is to be read from the request.
</span><span class="lines">@@ -67,7 +67,7 @@
</span><span class="cx"> @param depth: a C{str} containing the COPY/MOVE Depth header value.
</span><span class="cx"> @return: status response.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> try:
</span><span class="cx"> assert request is not None and destination is not None and destination_uri is not None
</span><span class="cx"> assert (source is None) or (source is not None and source_uri is not None)
</span><span class="lines">@@ -84,20 +84,22 @@
</span><span class="cx"> log.error("depth=%s\n" % (depth,))
</span><span class="cx"> raise
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class RollbackState(object):
</span><span class="cx"> """
</span><span class="cx"> This class encapsulates the state needed to rollback the entire PUT/COPY/MOVE
</span><span class="cx"> transaction, leaving the server state the same as it was before the request was
</span><span class="cx"> processed. The DoRollback method will actually execute the rollback operations.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def __init__(self):
</span><span class="cx"> self.active = True
</span><span class="cx"> self.source_copy = None
</span><span class="cx"> self.destination_copy = None
</span><span class="cx"> self.destination_created = False
</span><span class="cx"> self.source_deleted = False
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def Rollback(self):
</span><span class="cx"> """
</span><span class="cx"> Rollback the server state. Do not allow this to raise another exception. If
</span><span class="lines">@@ -142,7 +144,7 @@
</span><span class="cx"> self.destination_copy = None
</span><span class="cx"> self.destination_created = False
</span><span class="cx"> self.source_deleted = False
</span><del>-
</del><ins>+
</ins><span class="cx"> rollback = RollbackState()
</span><span class="cx">
</span><span class="cx"> try:
</span><span class="lines">@@ -164,7 +166,7 @@
</span><span class="cx"> old_dest_size = old_dest_size.getResult()
</span><span class="cx"> else:
</span><span class="cx"> old_dest_size = 0
</span><del>-
</del><ins>+
</ins><span class="cx"> if source is not None:
</span><span class="cx"> sourcequota = waitForDeferred(source.quota(request))
</span><span class="cx"> yield sourcequota
</span><span class="lines">@@ -193,7 +195,7 @@
</span><span class="cx"> rollback.source_copy = FilePath(source.fp.path)
</span><span class="cx"> rollback.source_copy.path += ".rollback"
</span><span class="cx"> source.fp.copyTo(rollback.source_copy)
</span><del>-
</del><ins>+
</ins><span class="cx"> """
</span><span class="cx"> Handle actual store operations here.
</span><span class="cx"> """
</span><span class="lines">@@ -231,7 +233,7 @@
</span><span class="cx"> destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(content_type)))
</span><span class="cx">
</span><span class="cx"> response = IResponse(response)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Do quota check on destination
</span><span class="cx"> if destquota is not None:
</span><span class="cx"> # Get size of new/old resources
</span><span class="lines">@@ -265,7 +267,7 @@
</span><span class="cx">
</span><span class="cx"> yield response
</span><span class="cx"> return
</span><del>-
</del><ins>+
</ins><span class="cx"> except:
</span><span class="cx"> # Roll back changes to original server state. Note this may do nothing
</span><span class="cx"> # if the rollback has already ocurred or changes already committed.
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethodreportpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/report.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/report.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/report.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -52,15 +52,18 @@
</span><span class="cx"> max_number_of_matches = 500
</span><span class="cx">
</span><span class="cx"> class NumberOfMatchesWithinLimits(Exception):
</span><del>-
</del><ins>+
</ins><span class="cx"> def __init__(self, limit):
</span><del>-
</del><ins>+
</ins><span class="cx"> super(NumberOfMatchesWithinLimits, self).__init__()
</span><span class="cx"> self.limit = limit
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def maxLimit(self):
</span><span class="cx"> return self.limit
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def http_REPORT(self, request):
</span><span class="cx"> """
</span><span class="cx"> Respond to a REPORT request. (RFC 3253, section 3.6)
</span><span class="lines">@@ -124,7 +127,7 @@
</span><span class="cx">
</span><span class="cx"> try:
</span><span class="cx"> method = getattr(self, method_name)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Also double-check via supported-reports property
</span><span class="cx"> reports = self.supportedReports()
</span><span class="cx"> test = lookupElement((namespace, name))
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethodreport_acl_principal_prop_setpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/report_acl_principal_prop_set.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/report_acl_principal_prop_set.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/report_acl_principal_prop_set.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -58,7 +58,7 @@
</span><span class="cx"> if depth != "0":
</span><span class="cx"> log.error("Error in prinicpal-prop-set REPORT, Depth set to %s" % (depth,))
</span><span class="cx"> raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth,)))
</span><del>-
</del><ins>+
</ins><span class="cx"> #
</span><span class="cx"> # Check authentication and access controls
</span><span class="cx"> #
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethodreport_principal_matchpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/report_principal_match.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/report_principal_match.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/report_principal_match.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -60,7 +60,7 @@
</span><span class="cx"> if depth != "0":
</span><span class="cx"> log.error("Non-zero depth is not allowed: %s" % (depth,))
</span><span class="cx"> raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth,)))
</span><del>-
</del><ins>+
</ins><span class="cx"> # Get a single DAV:prop element from the REPORT request body
</span><span class="cx"> propertiesForResource = None
</span><span class="cx"> propElement = None
</span><span class="lines">@@ -98,19 +98,19 @@
</span><span class="cx"> if lookForPrincipals:
</span><span class="cx">
</span><span class="cx"> # Find the set of principals that represent "self".
</span><del>-
</del><ins>+
</ins><span class="cx"> # First add "self"
</span><span class="cx"> principal = waitForDeferred(request.locateResource(str(myPrincipalURL)))
</span><span class="cx"> yield principal
</span><span class="cx"> principal = principal.getResult()
</span><del>- selfItems = [principal,]
-
</del><ins>+ selfItems = [principal, ]
+
</ins><span class="cx"> # Get group memberships for "self" and add each of those
</span><span class="cx"> d = waitForDeferred(principal.groupMemberships())
</span><span class="cx"> yield d
</span><span class="cx"> memberships = d.getResult()
</span><span class="cx"> selfItems.extend(memberships)
</span><del>-
</del><ins>+
</ins><span class="cx"> # Now add each principal found to the response provided the principal resource is a child of
</span><span class="cx"> # the current resource.
</span><span class="cx"> for principal in selfItems:
</span><span class="lines">@@ -118,7 +118,7 @@
</span><span class="cx"> # FIXME: making the assumption that the principalURL() is the URL of the resource we found
</span><span class="cx"> principal_uris = [principal.principalURL()]
</span><span class="cx"> principal_uris.extend(principal.alternateURIs())
</span><del>-
</del><ins>+
</ins><span class="cx"> # Compare each one to the request URI and return at most one that matches
</span><span class="cx"> for uri in principal_uris:
</span><span class="cx"> if uri.startswith(request.uri):
</span><span class="lines">@@ -126,7 +126,7 @@
</span><span class="cx"> matchcount += 1
</span><span class="cx"> if matchcount > max_number_of_matches:
</span><span class="cx"> raise NumberOfMatchesWithinLimits(max_number_of_matches)
</span><del>-
</del><ins>+
</ins><span class="cx"> d = waitForDeferred(prop_common.responseForHref(
</span><span class="cx"> request,
</span><span class="cx"> responses,
</span><span class="lines">@@ -144,9 +144,9 @@
</span><span class="cx"> filteredaces = waitForDeferred(self.inheritedACEsforChildren(request))
</span><span class="cx"> yield filteredaces
</span><span class="cx"> filteredaces = filteredaces.getResult()
</span><del>-
</del><ins>+
</ins><span class="cx"> children = []
</span><del>- d = waitForDeferred(self.findChildren("infinity", request, lambda x, y: children.append((x,y)),
</del><ins>+ d = waitForDeferred(self.findChildren("infinity", request, lambda x, y: children.append((x, y)),
</ins><span class="cx"> privileges=(element.Read(),), inherited_aces=filteredaces))
</span><span class="cx"> yield d
</span><span class="cx"> d.getResult()
</span><span class="lines">@@ -157,7 +157,8 @@
</span><span class="cx"> prop = waitForDeferred(child.readProperty(principalPropElement.qname(), request))
</span><span class="cx"> yield prop
</span><span class="cx"> prop = prop.getResult()
</span><del>- if prop: prop.removeWhitespaceNodes()
</del><ins>+ if prop:
+ prop.removeWhitespaceNodes()
</ins><span class="cx">
</span><span class="cx"> if prop and len(prop.children) == 1 and isinstance(prop.children[0], element.HRef):
</span><span class="cx"> # Find principal associated with this property and test it
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethodreport_principal_property_searchpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/report_principal_property_search.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/report_principal_property_search.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/report_principal_property_search.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -61,7 +61,7 @@
</span><span class="cx"> if depth != "0":
</span><span class="cx"> log.error("Error in prinicpal-property-search REPORT, Depth set to %s" % (depth,))
</span><span class="cx"> raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth,)))
</span><del>-
</del><ins>+
</ins><span class="cx"> # Get a single DAV:prop element from the REPORT request body
</span><span class="cx"> propertiesForResource = None
</span><span class="cx"> propElement = None
</span><span class="lines">@@ -78,7 +78,8 @@
</span><span class="cx"> props.removeWhitespaceNodes()
</span><span class="cx"> match = child.childOfType(element.Match)
</span><span class="cx"> propertySearches.append((props.children, str(match).lower()))
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def nodeMatch(node, match):
</span><span class="cx"> """
</span><span class="cx"> See if the content of the supplied node matches the supplied text.
</span><span class="lines">@@ -97,7 +98,8 @@
</span><span class="cx"> return nodeMatch(child, match)
</span><span class="cx"> else:
</span><span class="cx"> return False
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def propertySearch(resource, request):
</span><span class="cx"> """
</span><span class="cx"> Test the resource to see if it contains properties matching the
</span><span class="lines">@@ -120,7 +122,7 @@
</span><span class="cx"> # No property => no match
</span><span class="cx"> yield False
</span><span class="cx"> return
</span><del>-
</del><ins>+
</ins><span class="cx"> yield True
</span><span class="cx">
</span><span class="cx"> propertySearch = deferredGenerator(propertySearch)
</span><span class="lines">@@ -143,7 +145,7 @@
</span><span class="cx"> resources.append((self, request.uri))
</span><span class="cx">
</span><span class="cx"> # Loop over all collections and principal resources within
</span><del>- for resource, ruri in resources:
</del><ins>+ for resource, _ignore_ruri in resources:
</ins><span class="cx">
</span><span class="cx"> # Do some optimisation of access control calculation by determining any inherited ACLs outside of
</span><span class="cx"> # the child resource loop and supply those to the checkPrivileges on each child.
</span><span class="lines">@@ -152,7 +154,7 @@
</span><span class="cx"> filteredaces = filteredaces.getResult()
</span><span class="cx">
</span><span class="cx"> children = []
</span><del>- d = waitForDeferred(resource.findChildren("infinity", request, lambda x, y: children.append((x,y)),
</del><ins>+ d = waitForDeferred(resource.findChildren("infinity", request, lambda x, y: children.append((x, y)),
</ins><span class="cx"> privileges=(element.Read(),), inherited_aces=filteredaces))
</span><span class="cx"> yield d
</span><span class="cx"> d.getResult()
</span><span class="lines">@@ -167,7 +169,7 @@
</span><span class="cx"> matchcount += 1
</span><span class="cx"> if matchcount > max_number_of_matches:
</span><span class="cx"> raise NumberOfMatchesWithinLimits(max_number_of_matches)
</span><del>-
</del><ins>+
</ins><span class="cx"> d = waitForDeferred(prop_common.responseForHref(
</span><span class="cx"> request,
</span><span class="cx"> responses,
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davmethodreport_principal_search_property_setpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/method/report_principal_search_property_set.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/method/report_principal_search_property_set.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/method/report_principal_search_property_set.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -54,13 +54,13 @@
</span><span class="cx"> if depth != "0":
</span><span class="cx"> log.error("Error in principal-search-property-set REPORT, Depth set to %s" % (depth,))
</span><span class="cx"> raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth,)))
</span><del>-
</del><ins>+
</ins><span class="cx"> # Get details from the resource
</span><span class="cx"> result = self.principalSearchPropertySet()
</span><span class="cx"> if result is None:
</span><span class="cx"> log.error("Error in principal-search-property-set REPORT not supported on: %s" % (self,))
</span><span class="cx"> raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Not allowed on this resource"))
</span><del>-
</del><ins>+
</ins><span class="cx"> yield Response(code=responsecode.OK, stream=MemoryStream(result.toxml()))
</span><span class="cx">
</span><span class="cx"> report_DAV__principal_search_property_set = deferredGenerator(report_DAV__principal_search_property_set)
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davnonepropspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/noneprops.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/noneprops.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/noneprops.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -48,28 +48,34 @@
</span><span class="cx"> NonePropertyStore.__singleton = object.__new__(clazz)
</span><span class="cx"> return NonePropertyStore.__singleton
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __init__(self, resource):
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def get(self, qname, uid=None):
</span><span class="cx"> raise HTTPError(StatusResponse(
</span><span class="cx"> responsecode.NOT_FOUND,
</span><span class="cx"> "No such property: %s" % (encodeXMLName(*qname),)
</span><span class="cx"> ))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def set(self, property, uid=None):
</span><span class="cx"> raise HTTPError(StatusResponse(
</span><span class="cx"> responsecode.FORBIDDEN,
</span><span class="cx"> "Permission denied for setting property: %s" % (property,)
</span><span class="cx"> ))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def delete(self, qname, uid=None):
</span><span class="cx"> # RFC 2518 Section 12.13.1 says that removal of
</span><span class="cx"> # non-existing property is not an error.
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def contains(self, qname, uid=None):
</span><span class="cx"> return False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def list(self, uid=None):
</span><span class="cx"> return ()
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/resource.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/resource.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/resource.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -148,8 +148,8 @@
</span><span class="cx"> (dav_namespace, "displayname"),
</span><span class="cx"> (dav_namespace, "supportedlock"),
</span><span class="cx"> (dav_namespace, "supported-report-set"), # RFC 3253, section 3.1.5
</span><del>- #(dav_namespace, "owner" ), # RFC 3744, section 5.1
- #(dav_namespace, "group" ), # RFC 3744, section 5.2
</del><ins>+ # (dav_namespace, "owner" ), # RFC 3744, section 5.1
+ # (dav_namespace, "group" ), # RFC 3744, section 5.2
</ins><span class="cx"> (dav_namespace, "supported-privilege-set"), # RFC 3744, section 5.3
</span><span class="cx"> (dav_namespace, "current-user-privilege-set"), # RFC 3744, section 5.4
</span><span class="cx"> (dav_namespace, "current-user-principal"), # RFC 5397, Section 3
</span><span class="lines">@@ -752,8 +752,8 @@
</span><span class="cx"> for name in names:
</span><span class="cx"> (names1 if name.rstrip("/").find("/") == -1 else namesDeep).append(name.rstrip("/"))
</span><span class="cx">
</span><del>- #children = []
- #yield self.findChildren("1", request, lambda x, y: children.append((x, y)), privileges=None, inherited_aces=None)
</del><ins>+ # children = []
+ # yield self.findChildren("1", request, lambda x, y: children.append((x, y)), privileges=None, inherited_aces=None)
</ins><span class="cx">
</span><span class="cx"> children = []
</span><span class="cx"> basepath = request.urlForResource(self)
</span><span class="lines">@@ -794,7 +794,7 @@
</span><span class="cx"> )[2].append((resource, url))
</span><span class="cx">
</span><span class="cx"> # Now determine whether each ace satisfies privileges
</span><del>- #print(aclmap)
</del><ins>+ # print(aclmap)
</ins><span class="cx"> for items in aclmap.itervalues():
</span><span class="cx"> checked = (yield self.checkACLPrivilege(
</span><span class="cx"> request, items[0], items[1], privileges, inherited_aces
</span><span class="lines">@@ -1237,7 +1237,7 @@
</span><span class="cx"> #
</span><span class="cx"> # Otherwise, we'd use this logic:
</span><span class="cx"> #
</span><del>- #elif old_ace.inherited:
</del><ins>+ # elif old_ace.inherited:
</ins><span class="cx"> # log.error("Attempt to overwrite inherited ace %r "
</span><span class="cx"> # "on resource %r" % (old_ace, self))
</span><span class="cx"> # returnValue((
</span><span class="lines">@@ -1273,8 +1273,7 @@
</span><span class="cx"> returnValue((element.dav_namespace, "no-ace-conflict"))
</span><span class="cx">
</span><span class="cx"> if ace.inherited:
</span><del>- log.error("Attempt to create inherited ace %r on resource %r"
- % (ace, self))
</del><ins>+ log.error("Attempt to create inherited ace %r on resource %r" % (ace, self))
</ins><span class="cx"> returnValue((element.dav_namespace, "no-ace-conflict"))
</span><span class="cx">
</span><span class="cx"> # Step 6
</span><span class="lines">@@ -1381,8 +1380,8 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> for resource, uri in resources:
</span><del>- acl = (yield
- resource.accessControlList(
</del><ins>+ acl = (
+ yield resource.accessControlList(
</ins><span class="cx"> request,
</span><span class="cx"> inherited_aces=inherited_aces
</span><span class="cx"> )
</span><span class="lines">@@ -1404,8 +1403,8 @@
</span><span class="cx"> ):
</span><span class="cx"> continue
</span><span class="cx">
</span><del>- match = (yield
- self.matchPrincipal(principal, ace.principal, request)
</del><ins>+ match = (
+ yield self.matchPrincipal(principal, ace.principal, request)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> if match:
</span><span class="lines">@@ -1525,8 +1524,8 @@
</span><span class="cx"> parent = (yield request.locateResource(parentURL))
</span><span class="cx">
</span><span class="cx"> if parent:
</span><del>- parent_acl = (yield
- parent.accessControlList(
</del><ins>+ parent_acl = (
+ yield parent.accessControlList(
</ins><span class="cx"> request, inheritance=True, expanding=True
</span><span class="cx"> )
</span><span class="cx"> )
</span><span class="lines">@@ -1989,15 +1988,15 @@
</span><span class="cx"> # First see if the ace's principal affects the principal
</span><span class="cx"> # being tested. FIXME: support the DAV:invert operation
</span><span class="cx">
</span><del>- match = (yield
- self.matchPrincipal(principal, ace.principal, request)
</del><ins>+ match = (
+ yield self.matchPrincipal(principal, ace.principal, request)
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> if match:
</span><span class="cx"> # Expand aggregate privileges
</span><span class="cx"> ps = []
</span><del>- supportedPrivs = (yield
- self.supportedPrivileges(request)
</del><ins>+ supportedPrivs = (
+ yield self.supportedPrivileges(request)
</ins><span class="cx"> )
</span><span class="cx"> for p in ace.privileges:
</span><span class="cx"> ps.extend(p.expandAggregate(supportedPrivs))
</span><span class="lines">@@ -2319,8 +2318,8 @@
</span><span class="cx">
</span><span class="cx"> def renderHTTP(self, request):
</span><span class="cx"> # FIXME: This is for testing with litmus; comment out when not in use
</span><del>- #litmus = request.headers.getRawHeaders("x-litmus")
- #if litmus: log.info("*** Litmus test: %s ***" % (litmus,))
</del><ins>+ # litmus = request.headers.getRawHeaders("x-litmus")
+ # if litmus: log.info("*** Litmus test: %s ***" % (litmus,))
</ins><span class="cx">
</span><span class="cx"> #
</span><span class="cx"> # If this is a collection and the URI doesn't end in "/", redirect.
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davstaticpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/static.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/static.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/static.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -8,10 +8,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -51,6 +51,8 @@
</span><span class="cx"> log.info("Setting of dead properties will not be allowed.")
</span><span class="cx"> from txweb2.dav.noneprops import NonePropertyStore as DeadPropertyStore
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class DAVFile (DAVResource, File):
</span><span class="cx"> """
</span><span class="cx"> WebDAV-accessible File resource.
</span><span class="lines">@@ -71,41 +73,48 @@
</span><span class="cx"> """
</span><span class="cx"> File.__init__(
</span><span class="cx"> self, path,
</span><del>- defaultType = defaultType,
- ignoredExts = (),
- processors = None,
- indexNames = indexNames,
</del><ins>+ defaultType=defaultType,
+ ignoredExts=(),
+ processors=None,
+ indexNames=indexNames,
</ins><span class="cx"> )
</span><span class="cx"> DAVResource.__init__(self, principalCollections=principalCollections)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __repr__(self):
</span><span class="cx"> return "<%s: %s>" % (self.__class__.__name__, self.fp.path)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> ##
</span><span class="cx"> # WebDAV
</span><span class="cx"> ##
</span><span class="cx">
</span><span class="cx"> def etag(self):
</span><del>- if not self.fp.exists(): return succeed(None)
</del><ins>+ if not self.fp.exists():
+ return succeed(None)
</ins><span class="cx"> if self.hasDeadProperty(TwistedGETContentMD5):
</span><span class="cx"> return succeed(http_headers.ETag(str(self.readDeadProperty(TwistedGETContentMD5))))
</span><span class="cx"> else:
</span><span class="cx"> return super(DAVFile, self).etag()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def davComplianceClasses(self):
</span><span class="cx"> return ("1", "access-control") # Add "2" when we have locking
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def deadProperties(self):
</span><span class="cx"> if not hasattr(self, "_dead_properties"):
</span><span class="cx"> self._dead_properties = DeadPropertyStore(self)
</span><span class="cx"> return self._dead_properties
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def isCollection(self):
</span><span class="cx"> """
</span><span class="cx"> See L{IDAVResource.isCollection}.
</span><span class="cx"> """
</span><span class="cx"> return self.fp.isdir()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> ##
</span><span class="cx"> # ACL
</span><span class="cx"> ##
</span><span class="lines">@@ -113,6 +122,7 @@
</span><span class="cx"> def supportedPrivileges(self, request):
</span><span class="cx"> return succeed(davPrivilegeSet)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> ##
</span><span class="cx"> # Quota
</span><span class="cx"> ##
</span><span class="lines">@@ -130,10 +140,10 @@
</span><span class="cx"> """
</span><span class="cx"> Recursively descend the directory tree rooted at top,
</span><span class="cx"> calling the callback function for each regular file
</span><del>-
</del><ins>+
</ins><span class="cx"> @param top: L{FilePath} for the directory to walk.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> total = 0
</span><span class="cx"> for f in top.listdir():
</span><span class="cx"> child = top.child(f)
</span><span class="lines">@@ -148,15 +158,16 @@
</span><span class="cx"> else:
</span><span class="cx"> # Unknown file type, print a message
</span><span class="cx"> pass
</span><del>-
</del><ins>+
</ins><span class="cx"> yield total
</span><del>-
</del><ins>+
</ins><span class="cx"> walktree = deferredGenerator(walktree)
</span><del>-
</del><ins>+
</ins><span class="cx"> return walktree(self.fp)
</span><span class="cx"> else:
</span><span class="cx"> return succeed(self.fp.getsize())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> ##
</span><span class="cx"> # Workarounds for issues with File
</span><span class="cx"> ##
</span><span class="lines">@@ -167,6 +178,7 @@
</span><span class="cx"> """
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def locateChild(self, req, segments):
</span><span class="cx"> """
</span><span class="cx"> See L{IResource}C{.locateChild}.
</span><span class="lines">@@ -178,7 +190,7 @@
</span><span class="cx"> return (child, segments[1:])
</span><span class="cx"> except InsecurePath:
</span><span class="cx"> raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Invalid URL path"))
</span><del>-
</del><ins>+
</ins><span class="cx"> # If we're not backed by a directory, we have no children.
</span><span class="cx"> # But check for existance first; we might be a collection resource
</span><span class="cx"> # that the request wants created.
</span><span class="lines">@@ -188,13 +200,14 @@
</span><span class="cx">
</span><span class="cx"> # OK, we need to return a child corresponding to the first segment
</span><span class="cx"> path = segments[0]
</span><del>-
</del><ins>+
</ins><span class="cx"> if path == "":
</span><span class="cx"> # Request is for a directory (collection) resource
</span><span class="cx"> return (self, ())
</span><span class="cx">
</span><span class="cx"> return (self.createSimilarFile(self.fp.child(path).path), segments[1:])
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def createSimilarFile(self, path):
</span><span class="cx"> return self.__class__(
</span><span class="cx"> path, defaultType=self.defaultType, indexNames=self.indexNames[:],
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtest__init__py"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/__init__.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/__init__.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/__init__.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_aclpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_acl.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_acl.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_acl.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -54,9 +54,13 @@
</span><span class="cx">
</span><span class="cx"> principalCollection = TestPrincipalsCollection(
</span><span class="cx"> "/principals/",
</span><del>- children={"users": TestPrincipalsCollection(
</del><ins>+ children={
+ "users": TestPrincipalsCollection(
</ins><span class="cx"> "/principals/users/",
</span><del>- children={"user01": userResource})})
</del><ins>+ children={"user01": userResource}
+ )
+ }
+ )
</ins><span class="cx">
</span><span class="cx"> rootResource = self.resource_class(
</span><span class="cx"> docroot, principalCollections=(principalCollection,))
</span><span class="lines">@@ -112,6 +116,7 @@
</span><span class="cx"> rmdir(self._docroot)
</span><span class="cx"> del self._docroot
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_COPY_MOVE_source(self):
</span><span class="cx"> """
</span><span class="cx"> Verify source access controls during COPY and MOVE.
</span><span class="lines">@@ -122,7 +127,7 @@
</span><span class="cx">
</span><span class="cx"> for src, status in (
</span><span class="cx"> ("nobind", responsecode.FORBIDDEN),
</span><del>- ("bind", responsecode.FORBIDDEN),
</del><ins>+ ("bind", responsecode.FORBIDDEN),
</ins><span class="cx"> ("unbind", responsecode.CREATED),
</span><span class="cx"> ):
</span><span class="cx"> src_path = os.path.join(self.docroot, "src_" + src)
</span><span class="lines">@@ -149,30 +154,31 @@
</span><span class="cx">
</span><span class="cx"> for method in ("COPY", "MOVE"):
</span><span class="cx"> for name, code in (
</span><del>- ("none" , {"COPY": responsecode.FORBIDDEN, "MOVE": status}[method]),
- ("read" , {"COPY": responsecode.CREATED, "MOVE": status}[method]),
- ("read-write" , {"COPY": responsecode.CREATED, "MOVE": status}[method]),
- ("unlock" , {"COPY": responsecode.FORBIDDEN, "MOVE": status}[method]),
- ("all" , {"COPY": responsecode.CREATED, "MOVE": status}[method]),
</del><ins>+ ("none", {"COPY": responsecode.FORBIDDEN, "MOVE": status}[method]),
+ ("read", {"COPY": responsecode.CREATED, "MOVE": status}[method]),
+ ("read-write" , {"COPY": responsecode.CREATED, "MOVE": status}[method]),
+ ("unlock", {"COPY": responsecode.FORBIDDEN, "MOVE": status}[method]),
+ ("all", {"COPY": responsecode.CREATED, "MOVE": status}[method]),
</ins><span class="cx"> ):
</span><span class="cx"> path = os.path.join(src_path, name)
</span><span class="cx"> uri = src_uri + "/" + name
</span><del>-
</del><ins>+
</ins><span class="cx"> request = SimpleRequest(self.site, method, uri)
</span><span class="cx"> request.headers.setHeader("destination", dst_uri)
</span><span class="cx"> _add_auth_header(request)
</span><del>-
</del><ins>+
</ins><span class="cx"> def test(response, code=code, path=path):
</span><span class="cx"> if os.path.isfile(dst_path):
</span><span class="cx"> os.remove(dst_path)
</span><del>-
</del><ins>+
</ins><span class="cx"> if response.code != code:
</span><span class="cx"> return self.oops(request, response, code, method, name)
</span><del>-
</del><ins>+
</ins><span class="cx"> yield (request, test)
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_COPY_MOVE_dest(self):
</span><span class="cx"> """
</span><span class="cx"> Verify destination access controls during COPY and MOVE.
</span><span class="lines">@@ -206,6 +212,7 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_DELETE(self):
</span><span class="cx"> """
</span><span class="cx"> Verify access controls during DELETE.
</span><span class="lines">@@ -232,6 +239,7 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_UNLOCK(self):
</span><span class="cx"> """
</span><span class="cx"> Verify access controls during UNLOCK of unowned lock.
</span><span class="lines">@@ -270,6 +278,7 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_PUT_exists(self):
</span><span class="cx"> """
</span><span class="cx"> Verify access controls during PUT of existing file.
</span><span class="lines">@@ -295,6 +304,7 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_PROPFIND(self):
</span><span class="cx"> """
</span><span class="cx"> Verify access controls during PROPFIND.
</span><span class="lines">@@ -331,6 +341,7 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_GET_REPORT(self):
</span><span class="cx"> """
</span><span class="cx"> Verify access controls during GET and REPORT.
</span><span class="lines">@@ -367,13 +378,14 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def oops(self, request, response, code, method, name):
</span><span class="cx"> def gotResponseData(doc):
</span><span class="cx"> if doc is None:
</span><span class="cx"> doc_xml = None
</span><span class="cx"> else:
</span><span class="cx"> doc_xml = doc.toxml()
</span><del>-
</del><ins>+
</ins><span class="cx"> def fail(acl):
</span><span class="cx"> self.fail("Incorrect status code %s (!= %s) for %s of resource %s with %s ACL: %s\nACL: %s"
</span><span class="cx"> % (response.code, code, method, request.uri, name, doc_xml, acl.toxml()))
</span><span class="lines">@@ -391,6 +403,8 @@
</span><span class="cx"> d.addCallback(gotResponseData)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def _add_auth_header(request):
</span><span class="cx"> request.headers.setHeader(
</span><span class="cx"> "authorization",
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_authpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_auth.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_auth.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_auth.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -51,8 +51,7 @@
</span><span class="cx"> self.credentialFactories = None
</span><span class="cx"> self.chanRequest = FakeChannel(secure)
</span><span class="cx">
</span><del>- wrapper = AuthenticationWrapper(None, None,
- wireEncryptedfactories, wireUnencryptedfactories, None)
</del><ins>+ wrapper = AuthenticationWrapper(None, None, wireEncryptedfactories, wireUnencryptedfactories, None)
</ins><span class="cx"> req = FakeRequest(True) # Connection is over SSL
</span><span class="cx"> wrapper.hook(req)
</span><span class="cx"> self.assertEquals(
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_copypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_copy.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_copy.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_copy.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -70,7 +70,8 @@
</span><span class="cx"> for filename in os.listdir(dst_path):
</span><span class="cx"> self.fail("COPY %s (depth=%r) shouldn't copy directory contents (eg. %s)" % (uri, depth, filename))
</span><span class="cx">
</span><del>- else: raise AssertionError("Unknown depth: %r" % (depth,))
</del><ins>+ else:
+ raise AssertionError("Unknown depth: %r" % (depth,))
</ins><span class="cx">
</span><span class="cx"> rmdir(dst_path)
</span><span class="cx">
</span><span class="lines">@@ -80,6 +81,7 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work(self, test))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_COPY_exists(self):
</span><span class="cx"> """
</span><span class="cx"> COPY to existing resource.
</span><span class="lines">@@ -94,6 +96,7 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work(self, test, overwrite=False))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_COPY_overwrite(self):
</span><span class="cx"> """
</span><span class="cx"> COPY to existing resource with overwrite header.
</span><span class="lines">@@ -110,6 +113,7 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work(self, test, overwrite=True))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_COPY_no_parent(self):
</span><span class="cx"> """
</span><span class="cx"> COPY to resource with no parent.
</span><span class="lines">@@ -124,13 +128,16 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work(self, test, dst=os.path.join(self.docroot, "elvislives!")))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def work(self, test, overwrite=None, dst=None, depths=("0", "infinity", None)):
</span><span class="cx"> if dst is None:
</span><span class="cx"> dst = os.path.join(self.docroot, "dst")
</span><span class="cx"> os.mkdir(dst)
</span><span class="cx">
</span><span class="cx"> for basename in os.listdir(self.docroot):
</span><del>- if basename == "dst": continue
</del><ins>+ if basename == "dst":
+ continue
</ins><span class="cx">
</span><span class="cx"> uri = urllib.quote("/" + basename)
</span><span class="cx"> path = os.path.join(self.docroot, basename)
</span><span class="lines">@@ -160,6 +167,8 @@
</span><span class="cx">
</span><span class="cx"> yield (request, do_test)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def sumFile(path):
</span><span class="cx"> m = md5()
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_deletepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_delete.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_delete.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_delete.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -58,7 +58,8 @@
</span><span class="cx"> path = os.path.join(self.docroot, filename)
</span><span class="cx"> uri = urllib.quote("/" + filename)
</span><span class="cx">
</span><del>- if os.path.isdir(path): uri = uri + "/"
</del><ins>+ if os.path.isdir(path):
+ uri = uri + "/"
</ins><span class="cx">
</span><span class="cx"> def do_test(response, path=path):
</span><span class="cx"> return check_result(response, path)
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_httppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_http.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_http.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_http.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -40,13 +40,14 @@
</span><span class="cx"> """
</span><span class="cx"> for ex_class in (IOError, OSError):
</span><span class="cx"> for exception, result in (
</span><del>- (ex_class(errno.EACCES, "Permission denied" ), responsecode.FORBIDDEN),
- (ex_class(errno.EPERM , "Permission denied" ), responsecode.FORBIDDEN),
</del><ins>+ (ex_class(errno.EACCES, "Permission denied"), responsecode.FORBIDDEN),
+ (ex_class(errno.EPERM , "Permission denied"), responsecode.FORBIDDEN),
</ins><span class="cx"> (ex_class(errno.ENOSPC, "No space available"), responsecode.INSUFFICIENT_STORAGE_SPACE),
</span><del>- (ex_class(errno.ENOENT, "No such file" ), responsecode.NOT_FOUND),
</del><ins>+ (ex_class(errno.ENOENT, "No such file"), responsecode.NOT_FOUND),
</ins><span class="cx"> ):
</span><span class="cx"> self._check_exception(exception, result)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_statusForFailure_HTTPError(self):
</span><span class="cx"> """
</span><span class="cx"> statusForFailure() for HTTPErrors
</span><span class="lines">@@ -55,6 +56,7 @@
</span><span class="cx"> self._check_exception(HTTPError(code), code)
</span><span class="cx"> self._check_exception(HTTPError(ErrorResponse(code, ("http://twistedmatrix.com/", "bar"))), code)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_statusForFailure_exception(self):
</span><span class="cx"> """
</span><span class="cx"> statusForFailure() for known/unknown exceptions
</span><span class="lines">@@ -74,6 +76,7 @@
</span><span class="cx"> else:
</span><span class="cx"> self.fail("Unknown exception should have re-raised.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _check_exception(self, exception, result):
</span><span class="cx"> try:
</span><span class="cx"> raise exception
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_lockpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_lock.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_lock.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_lock.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_mkcolpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_mkcol.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_mkcol.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_mkcol.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -60,6 +60,7 @@
</span><span class="cx">
</span><span class="cx"> return self.send(request, check_result)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_MKCOL_invalid_body(self):
</span><span class="cx"> """
</span><span class="cx"> MKCOL request with invalid request body
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_movepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_move.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_move.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_move.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -62,6 +62,7 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work(self, test))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_MOVE_exists(self):
</span><span class="cx"> """
</span><span class="cx"> MOVE to existing resource.
</span><span class="lines">@@ -76,6 +77,7 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work(self, test, overwrite=False))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_MOVE_overwrite(self):
</span><span class="cx"> """
</span><span class="cx"> MOVE to existing resource with overwrite header.
</span><span class="lines">@@ -90,6 +92,7 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work(self, test, overwrite=True))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_MOVE_no_parent(self):
</span><span class="cx"> """
</span><span class="cx"> MOVE to resource with no parent.
</span><span class="lines">@@ -104,5 +107,7 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work(self, test, dst=os.path.join(self.docroot, "elvislives!")))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def work(self, test, overwrite=None, dst=None):
</span><span class="cx"> return txweb2.dav.test.test_copy.work(self, test, overwrite, dst, depths=(None,))
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_optionspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_options.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_options.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_options.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -37,6 +37,7 @@
</span><span class="cx"> """
</span><span class="cx"> return self._test_level("1")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_DAV2(self):
</span><span class="cx"> """
</span><span class="cx"> DAV level 2
</span><span class="lines">@@ -51,12 +52,14 @@
</span><span class="cx"> """
</span><span class="cx"> return self._test_level("access-control")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _test_level(self, level):
</span><span class="cx"> def doTest(response):
</span><span class="cx"> response = IResponse(response)
</span><span class="cx">
</span><span class="cx"> dav = response.headers.getHeader("dav")
</span><del>- if not dav: self.fail("no DAV header: %s" % (response.headers,))
</del><ins>+ if not dav:
+ self.fail("no DAV header: %s" % (response.headers,))
</ins><span class="cx"> self.assertIn(level, dav, "no DAV level %s header" % (level,))
</span><span class="cx">
</span><span class="cx"> return response
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_proppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_prop.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_prop.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_prop.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -37,8 +37,8 @@
</span><span class="cx">
</span><span class="cx"> # Remove dynamic live properties that exist
</span><span class="cx"> dynamicLiveProperties = (
</span><del>- (dav_namespace, "quota-available-bytes" ),
- (dav_namespace, "quota-used-bytes" ),
</del><ins>+ (dav_namespace, "quota-available-bytes"),
+ (dav_namespace, "quota-used-bytes"),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -50,6 +50,7 @@
</span><span class="cx"> def liveProperties(self):
</span><span class="cx"> return [lookupElement(qname)() for qname in self.site.resource.liveProperties() if (qname[0] == dav_namespace) and qname not in dynamicLiveProperties]
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_PROPFIND_basic(self):
</span><span class="cx"> """
</span><span class="cx"> PROPFIND request
</span><span class="lines">@@ -115,6 +116,7 @@
</span><span class="cx">
</span><span class="cx"> return self.send(request, check_result)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_PROPFIND_list(self):
</span><span class="cx"> """
</span><span class="cx"> PROPFIND with allprop, propname
</span><span class="lines">@@ -196,6 +198,7 @@
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_PROPPATCH_basic(self):
</span><span class="cx"> """
</span><span class="cx"> PROPPATCH
</span><span class="lines">@@ -268,6 +271,7 @@
</span><span class="cx"> request.stream = MemoryStream(patch.toxml())
</span><span class="cx"> return self.send(request, check_patch_response)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_PROPPATCH_liveprop(self):
</span><span class="cx"> """
</span><span class="cx"> PROPPATCH on a live property
</span><span class="lines">@@ -277,6 +281,7 @@
</span><span class="cx">
</span><span class="cx"> return self._simple_PROPPATCH(patch, prop, responsecode.FORBIDDEN, "edit of live property")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_PROPPATCH_exists_not(self):
</span><span class="cx"> """
</span><span class="cx"> PROPPATCH remove a non-existant property
</span><span class="lines">@@ -286,6 +291,7 @@
</span><span class="cx">
</span><span class="cx"> return self._simple_PROPPATCH(patch, prop, responsecode.OK, "remove of non-existant property")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _simple_PROPPATCH(self, patch, prop, expected_code, what):
</span><span class="cx"> def check_result(response):
</span><span class="cx"> response = IResponse(response)
</span><span class="lines">@@ -320,6 +326,8 @@
</span><span class="cx"> request.stream = MemoryStream(patch.toxml())
</span><span class="cx"> return self.send(request, check_result)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class SpiffyProperty (davxml.WebDAVTextElement):
</span><span class="cx"> namespace = "http://twistedmatrix.com/ns/private/tests"
</span><span class="cx"> name = "spiffyproperty"
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_putpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_put.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_put.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_put.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -77,17 +77,20 @@
</span><span class="cx"> path = os.path.join(self.docroot, name)
</span><span class="cx">
</span><span class="cx"> # Can't really PUT something you can't read
</span><del>- if not os.path.isfile(path): continue
-
- def do_test(response): checkResult(response, path)
-
</del><ins>+ if not os.path.isfile(path):
+ continue
+
+ def do_test(response):
+ checkResult(response, path)
+
</ins><span class="cx"> request = SimpleRequest(self.site, "PUT", dst_uri)
</span><span class="cx"> request.stream = FileStream(file(path, "rb"))
</span><del>-
</del><ins>+
</ins><span class="cx"> yield (request, do_test)
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_PUT_again(self):
</span><span class="cx"> """
</span><span class="cx"> PUT on existing resource with If-None-Match header
</span><span class="lines">@@ -117,18 +120,19 @@
</span><span class="cx">
</span><span class="cx"> request = SimpleRequest(self.site, "PUT", dst_uri)
</span><span class="cx"> request.stream = FileStream(file(__file__, "rb"))
</span><del>-
</del><ins>+
</ins><span class="cx"> if code == responsecode.CREATED:
</span><span class="cx"> if os.path.isfile(dst_path):
</span><span class="cx"> os.remove(dst_path)
</span><span class="cx"> request.headers.setHeader("if-none-match", ("*",))
</span><span class="cx"> elif code == responsecode.PRECONDITION_FAILED:
</span><span class="cx"> request.headers.setHeader("if-none-match", ("*",))
</span><del>-
</del><ins>+
</ins><span class="cx"> yield (request, (checkResult, onError))
</span><span class="cx">
</span><span class="cx"> return serialize(self.send, work())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_PUT_no_parent(self):
</span><span class="cx"> """
</span><span class="cx"> PUT with no parent
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_quotapy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_quota.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_quota.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_quota.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -52,12 +52,16 @@
</span><span class="cx"> d.addCallback(_defer)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class QuotaEmpty(QuotaBase):
</span><span class="cx">
</span><span class="cx"> def test_Empty_Quota(self):
</span><span class="cx">
</span><span class="cx"> return self.checkQuota(0)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class QuotaPUT(QuotaBase):
</span><span class="cx">
</span><span class="cx"> def test_Quota_PUT(self):
</span><span class="lines">@@ -79,6 +83,8 @@
</span><span class="cx"> request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))
</span><span class="cx"> return self.send(request, checkResult)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class QuotaDELETE(QuotaBase):
</span><span class="cx">
</span><span class="cx"> def test_Quota_DELETE(self):
</span><span class="lines">@@ -115,6 +121,8 @@
</span><span class="cx"> request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))
</span><span class="cx"> return self.send(request, checkPUTResult)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class OverQuotaPUT(QuotaBase):
</span><span class="cx">
</span><span class="cx"> def test_Quota_PUT(self):
</span><span class="lines">@@ -138,6 +146,8 @@
</span><span class="cx"> request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))
</span><span class="cx"> return self.send(request, checkResult)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class QuotaOKAdjustment(QuotaBase):
</span><span class="cx">
</span><span class="cx"> def test_Quota_OK_Adjustment(self):
</span><span class="lines">@@ -169,6 +179,8 @@
</span><span class="cx"> request.stream = FileStream(file(os.path.join(os.path.dirname(__file__), "data", "quota_100.txt"), "rb"))
</span><span class="cx"> return self.send(request, checkPUTResult)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class QuotaBadAdjustment(QuotaBase):
</span><span class="cx">
</span><span class="cx"> def test_Quota_Bad_Adjustment(self):
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_reportpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_report.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_report.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_report.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span><span class="lines">@@ -50,6 +50,7 @@
</span><span class="cx">
</span><span class="cx"> return self.send(request, do_test)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_REPORT_unknown(self):
</span><span class="cx"> """
</span><span class="cx"> Unknown/bogus report type
</span><span class="lines">@@ -62,8 +63,9 @@
</span><span class="cx"> % (response.code,))
</span><span class="cx"> class GoofyReport (davxml.WebDAVUnknownElement):
</span><span class="cx"> namespace = "GOOFY:"
</span><del>- name = "goofy-report"
- def __init__(self): super(GoofyReport, self).__init__()
</del><ins>+ name = "goofy-report"
+ def __init__(self):
+ super(GoofyReport, self).__init__()
</ins><span class="cx">
</span><span class="cx"> request = SimpleRequest(self.site, "REPORT", "/")
</span><span class="cx"> request.stream = MemoryStream(GoofyReport().toxml())
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_report_expandpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_report_expand.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_report_expand.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_report_expand.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_resourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_resource.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_resource.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_resource.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -215,10 +215,11 @@
</span><span class="cx"> baduser.writeDeadProperty(TwistedPasswordProperty("badpass"))
</span><span class="cx">
</span><span class="cx"> rootresource = TestPrincipalsCollection("/", {
</span><del>- "users": TestResource("/users/",
- {"gooduser": gooduser,
- "baduser": baduser})
- })
</del><ins>+ "users": TestResource(
+ "/users/",
+ {"gooduser": gooduser,
+ "baduser": baduser})
+ })
</ins><span class="cx">
</span><span class="cx"> protected = TestResource(
</span><span class="cx"> "/protected", principalCollections=[rootresource])
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_staticpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_static.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_static.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_static.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -7,10 +7,10 @@
</span><span class="cx"> # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
</span><span class="cx"> # copies of the Software, and to permit persons to whom the Software is
</span><span class="cx"> # furnished to do so, subject to the following conditions:
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # The above copyright notice and this permission notice shall be included in all
</span><span class="cx"> # copies or substantial portions of the Software.
</span><del>-#
</del><ins>+#
</ins><span class="cx"> # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
</span><span class="cx"> # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
</span><span class="cx"> # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttest_xattrpropspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/test_xattrprops.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/test_xattrprops.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/test_xattrprops.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -24,6 +24,8 @@
</span><span class="cx"> else:
</span><span class="cx"> from xattr import xattr
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class ExtendedAttributesPropertyStoreTests(TestCase):
</span><span class="cx"> """
</span><span class="cx"> Tests for L{xattrPropertyStore}.
</span><span class="lines">@@ -31,6 +33,7 @@
</span><span class="cx"> if xattrPropertyStore is None:
</span><span class="cx"> skip = "xattr package missing, cannot test xattr property store"
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def setUp(self):
</span><span class="cx"> """
</span><span class="cx"> Create a resource and a xattr property store for it.
</span><span class="lines">@@ -71,6 +74,7 @@
</span><span class="cx"> # of the EPERM failure.
</span><span class="cx"> self.assertEquals(error.response.code, FORBIDDEN)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _missingTest(self, method):
</span><span class="cx"> # Remove access to the directory containing the file so that getting
</span><span class="cx"> # extended attributes from it fails with EPERM.
</span><span class="lines">@@ -99,6 +103,7 @@
</span><span class="cx"> """
</span><span class="cx"> self._forbiddenTest('get')
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_getMissing(self):
</span><span class="cx"> """
</span><span class="cx"> Test missing file.
</span><span class="lines">@@ -118,6 +123,7 @@
</span><span class="cx"> # Make sure that the status is NOT FOUND.
</span><span class="cx"> self.assertEquals(error.response.code, NOT_FOUND)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _makeValue(self, uid=None):
</span><span class="cx"> """
</span><span class="cx"> Create and return any old WebDAVDocument for use by the get tests.
</span><span class="lines">@@ -324,6 +330,7 @@
</span><span class="cx"> document = self._makeValue()
</span><span class="cx"> self.assertFalse(propertyStore.contains(document.root_element.qname()))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_list(self):
</span><span class="cx"> """
</span><span class="cx"> L{xattrPropertyStore.list} returns a C{list} of property names
</span><span class="lines">@@ -359,6 +366,7 @@
</span><span class="cx"> # of the EPERM failure.
</span><span class="cx"> self.assertEquals(error.response.code, FORBIDDEN)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_listMissing(self):
</span><span class="cx"> """
</span><span class="cx"> Test missing file.
</span><span class="lines">@@ -371,12 +379,13 @@
</span><span class="cx"> # Try to get a property from it - and fail.
</span><span class="cx"> self.assertEqual(propertyStore.list(), [])
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_get_uids(self):
</span><span class="cx"> """
</span><span class="cx"> L{xattrPropertyStore.get} accepts a L{WebDAVElement} and stores a
</span><span class="cx"> compressed XML document representing it in an extended attribute.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> for uid in (None, "123", "456",):
</span><span class="cx"> document = self._makeValue(uid)
</span><span class="cx"> self._setValue(document, document.toxml(), uid=uid)
</span><span class="lines">@@ -391,19 +400,20 @@
</span><span class="cx"> L{xattrPropertyStore.set} accepts a L{WebDAVElement} and stores a
</span><span class="cx"> compressed XML document representing it in an extended attribute.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> for uid in (None, "123", "456",):
</span><span class="cx"> document = self._makeValue(uid)
</span><span class="cx"> self.propertyStore.set(document.root_element, uid=uid)
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> decompress(self._getValue(document, uid)), document.root_element.toxml(pretty=False))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_delete_uids(self):
</span><span class="cx"> """
</span><span class="cx"> L{xattrPropertyStore.set} accepts a L{WebDAVElement} and stores a
</span><span class="cx"> compressed XML document representing it in an extended attribute.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> for delete_uid in (None, "123", "456",):
</span><span class="cx"> for uid in (None, "123", "456",):
</span><span class="cx"> document = self._makeValue(uid)
</span><span class="lines">@@ -416,7 +426,8 @@
</span><span class="cx"> document = self._makeValue(uid)
</span><span class="cx"> self.assertEquals(
</span><span class="cx"> decompress(self._getValue(document, uid)), document.root_element.toxml(pretty=False))
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def test_contains_uids(self):
</span><span class="cx"> """
</span><span class="cx"> L{xattrPropertyStore.contains} returns C{True} if the given property
</span><span class="lines">@@ -430,6 +441,7 @@
</span><span class="cx"> self.assertTrue(
</span><span class="cx"> self.propertyStore.contains(document.root_element.qname(), uid=uid))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_list_uids(self):
</span><span class="cx"> """
</span><span class="cx"> L{xattrPropertyStore.list} returns a C{list} of property names
</span><span class="lines">@@ -465,4 +477,3 @@
</span><span class="cx"> (u'bar', u'baz', "456"),
</span><span class="cx"> (u'moo', u'mar456', "456"),
</span><span class="cx"> ]))
</span><del>-
</del></span></pre></div>
<a id="CalendarServertrunktxweb2davtesttworequest_clientpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/test/tworequest_client.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/test/tworequest_client.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/test/tworequest_client.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -33,8 +33,8 @@
</span><span class="cx"> send("Content-Length: 100\r\n\r\n")
</span><span class="cx"> send("X" * 100)
</span><span class="cx">
</span><del>-#import time
-#time.sleep(5)
</del><ins>+# import time
+# time.sleep(5)
</ins><span class="cx"> print >> sys.stderr, ">> Getting data"
</span><span class="cx"> data = ''
</span><span class="cx"> while len(data) < 299999:
</span></span></pre></div>
<a id="CalendarServertrunktxweb2davxattrpropspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/dav/xattrprops.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/dav/xattrprops.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/dav/xattrprops.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -64,6 +64,8 @@
</span><span class="cx"> if hasattr(errno, "ENODATA"):
</span><span class="cx"> _ATTR_MISSING += (errno.ENODATA,)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class xattrPropertyStore (object):
</span><span class="cx"> """
</span><span class="cx">
</span><span class="lines">@@ -86,6 +88,7 @@
</span><span class="cx"> if sys.platform == "linux2":
</span><span class="cx"> deadPropertyXattrPrefix = "user."
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _encode(clazz, name, uid=None):
</span><span class="cx"> result = urllib.quote(encodeXMLName(*name), safe='{}:')
</span><span class="cx"> if uid:
</span><span class="lines">@@ -93,6 +96,7 @@
</span><span class="cx"> r = clazz.deadPropertyXattrPrefix + result
</span><span class="cx"> return r
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _decode(clazz, name):
</span><span class="cx"> name = urllib.unquote(name[len(clazz.deadPropertyXattrPrefix):])
</span><span class="cx">
</span><span class="lines">@@ -105,8 +109,8 @@
</span><span class="cx"> uid = None
</span><span class="cx"> else:
</span><span class="cx"> uid = name[:index1]
</span><del>- propnamespace = name[index1+1:index2]
- propname = name[index2+1:]
</del><ins>+ propnamespace = name[index1 + 1:index2]
+ propname = name[index2 + 1:]
</ins><span class="cx">
</span><span class="cx"> return (propnamespace, propname, uid)
</span><span class="cx">
</span><span class="lines">@@ -134,7 +138,7 @@
</span><span class="cx"> @return: A L{WebDAVDocument} representing the value associated with the
</span><span class="cx"> given property.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> try:
</span><span class="cx"> data = self.attrs.get(self._encode(qname, uid))
</span><span class="cx"> except KeyError:
</span><span class="lines">@@ -247,7 +251,7 @@
</span><span class="cx">
</span><span class="cx"> @return: C{True} if the property exists, C{False} otherwise.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> key = self._encode(qname, uid)
</span><span class="cx"> try:
</span><span class="cx"> self.attrs.get(key)
</span><span class="lines">@@ -274,7 +278,7 @@
</span><span class="cx"> @return: A C{list} of property names as two-tuples of namespace URI and
</span><span class="cx"> local name.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> prefix = self.deadPropertyXattrPrefix
</span><span class="cx"> try:
</span><span class="cx"> attrs = iter(self.attrs)
</span><span class="lines">@@ -292,7 +296,7 @@
</span><span class="cx"> if name.startswith(prefix)
</span><span class="cx"> ]
</span><span class="cx"> if filterByUID:
</span><del>- return [
</del><ins>+ return [
</ins><span class="cx"> (namespace, name)
</span><span class="cx"> for namespace, name, propuid in results
</span><span class="cx"> if propuid == uid
</span></span></pre></div>
<a id="CalendarServertrunktxweb2errorpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/error.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/error.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/error.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -186,10 +186,11 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def loadMessage(self, code):
</span><del>- tag = XMLString(('<t:transparent xmlns:t="http://twistedmatrix.com/'
- 'ns/twisted.web.template/0.1">') +
- ERROR_MESSAGES.get(code, "") +
- '</t:transparent>').load()[0]
</del><ins>+ tag = XMLString(
+ ('<t:transparent xmlns:t="http://twistedmatrix.com/'
+ 'ns/twisted.web.template/0.1">') +
+ ERROR_MESSAGES.get(code, "") +
+ '</t:transparent>').load()[0]
</ins><span class="cx"> return tag
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -234,7 +235,7 @@
</span><span class="cx"> subtype = 'error'
</span><span class="cx"> body = 'Error in default error handler:\n' + error[0].getTraceback()
</span><span class="cx">
</span><del>- ctype = http_headers.MimeType('text', subtype, {'charset':'utf-8'})
</del><ins>+ ctype = http_headers.MimeType('text', subtype, {'charset': 'utf-8'})
</ins><span class="cx"> response.headers.setHeader("content-type", ctype)
</span><span class="cx"> response.stream = stream.MemoryStream(body)
</span><span class="cx"> return response
</span><span class="lines">@@ -242,5 +243,4 @@
</span><span class="cx"> defaultErrorHandler.handleErrors = True
</span><span class="cx">
</span><span class="cx">
</span><del>-__all__ = ['defaultErrorHandler',]
-
</del><ins>+__all__ = ['defaultErrorHandler', ]
</ins></span></pre></div>
<a id="CalendarServertrunktxweb2fileuploadpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/fileupload.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/fileupload.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/fileupload.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -34,9 +34,9 @@
</span><span class="cx"> from txweb2 import http_headers
</span><span class="cx"> from cStringIO import StringIO
</span><span class="cx">
</span><del>-###################################
-##### Multipart MIME Reader #####
-###################################
</del><ins>+#
+# Multipart MIME Reader
+#
</ins><span class="cx">
</span><span class="cx"> class MimeFormatError(Exception):
</span><span class="cx"> pass
</span><span class="lines">@@ -52,21 +52,22 @@
</span><span class="cx"> def parseContentDispositionFormData(value):
</span><span class="cx"> match = cd_regexp.match(value)
</span><span class="cx"> if not match:
</span><del>- # Error parsing.
</del><ins>+ # Error parsing.
</ins><span class="cx"> raise ValueError("Unknown content-disposition format.")
</span><del>- name=match.group(1)
- filename=match.group(2)
</del><ins>+ name = match.group(1)
+ filename = match.group(2)
</ins><span class="cx"> return name, filename
</span><span class="cx">
</span><span class="cx">
</span><del>-#@defer.deferredGenerator
</del><ins>+
+# @defer.deferredGenerator
</ins><span class="cx"> def _readHeaders(stream):
</span><span class="cx"> """Read the MIME headers. Assumes we've just finished reading in the
</span><span class="cx"> boundary string."""
</span><span class="cx">
</span><span class="cx"> ctype = fieldname = filename = None
</span><span class="cx"> headers = []
</span><del>-
</del><ins>+
</ins><span class="cx"> # Now read headers
</span><span class="cx"> while 1:
</span><span class="cx"> line = stream.readline(size=1024)
</span><span class="lines">@@ -74,7 +75,7 @@
</span><span class="cx"> line = defer.waitForDeferred(line)
</span><span class="cx"> yield line
</span><span class="cx"> line = line.getResult()
</span><del>- #print("GOT", line)
</del><ins>+ # print("GOT", line)
</ins><span class="cx"> if not line.endswith('\r\n'):
</span><span class="cx"> if line == "":
</span><span class="cx"> raise MimeFormatError("Unexpected end of stream.")
</span><span class="lines">@@ -84,21 +85,21 @@
</span><span class="cx"> line = line[:-2] # strip \r\n
</span><span class="cx"> if line == "":
</span><span class="cx"> break # End of headers
</span><del>-
</del><ins>+
</ins><span class="cx"> parts = line.split(':', 1)
</span><span class="cx"> if len(parts) != 2:
</span><span class="cx"> raise MimeFormatError("Header did not have a :")
</span><span class="cx"> name, value = parts
</span><span class="cx"> name = name.lower()
</span><span class="cx"> headers.append((name, value))
</span><del>-
</del><ins>+
</ins><span class="cx"> if name == "content-type":
</span><span class="cx"> ctype = http_headers.parseContentType(http_headers.tokenize((value,), foldCase=False))
</span><span class="cx"> elif name == "content-disposition":
</span><span class="cx"> fieldname, filename = parseContentDispositionFormData(value)
</span><del>-
</del><ins>+
</ins><span class="cx"> if ctype is None:
</span><del>- ctype == http_headers.MimeType('application', 'octet-stream')
</del><ins>+ ctype = http_headers.MimeType('application', 'octet-stream')
</ins><span class="cx"> if fieldname is None:
</span><span class="cx"> raise MimeFormatError('Content-disposition invalid or omitted.')
</span><span class="cx">
</span><span class="lines">@@ -114,7 +115,7 @@
</span><span class="cx"> self.boundary = boundary
</span><span class="cx"> self.data = ''
</span><span class="cx"> self.deferred = defer.Deferred()
</span><del>-
</del><ins>+
</ins><span class="cx"> length = None # unknown
</span><span class="cx"> def read(self):
</span><span class="cx"> if self.stream is None:
</span><span class="lines">@@ -128,6 +129,7 @@
</span><span class="cx"> return newdata.addCallbacks(self._gotRead, self._gotError)
</span><span class="cx"> return self._gotRead(newdata)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _gotRead(self, newdata):
</span><span class="cx"> if not newdata:
</span><span class="cx"> raise MimeFormatError("Unexpected EOF")
</span><span class="lines">@@ -136,10 +138,10 @@
</span><span class="cx"> data = self.data
</span><span class="cx"> boundary = self.boundary
</span><span class="cx"> off = data.find(boundary)
</span><del>-
</del><ins>+
</ins><span class="cx"> if off == -1:
</span><span class="cx"> # No full boundary, check for the first character
</span><del>- off = data.rfind(boundary[0], max(0, len(data)-len(boundary)))
</del><ins>+ off = data.rfind(boundary[0], max(0, len(data) - len(boundary)))
</ins><span class="cx"> if off != -1:
</span><span class="cx"> # We could have a partial boundary, store it for next time
</span><span class="cx"> self.data = data[off:]
</span><span class="lines">@@ -148,10 +150,11 @@
</span><span class="cx"> self.data = ''
</span><span class="cx"> return data
</span><span class="cx"> else:
</span><del>- self.stream.pushback(data[off+len(boundary):])
</del><ins>+ self.stream.pushback(data[off + len(boundary):])
</ins><span class="cx"> self.stream = None
</span><span class="cx"> return data[:off]
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _gotError(self, err):
</span><span class="cx"> # Propogate error back to MultipartMimeStream also
</span><span class="cx"> if self.deferred is not None:
</span><span class="lines">@@ -159,26 +162,30 @@
</span><span class="cx"> self.deferred = None
</span><span class="cx"> deferred.errback(err)
</span><span class="cx"> return err
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def close(self):
</span><span class="cx"> # Assume error will be raised again and handled by MMS?
</span><span class="cx"> readAndDiscard(self).addErrback(lambda _: None)
</span><del>-
</del><ins>+
+
+
</ins><span class="cx"> class MultipartMimeStream(object):
</span><span class="cx"> implements(IStream)
</span><span class="cx"> def __init__(self, stream, boundary):
</span><span class="cx"> self.stream = BufferedStream(stream)
</span><del>- self.boundary = "--"+boundary
</del><ins>+ self.boundary = "--" + boundary
</ins><span class="cx"> self.first = True
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def read(self):
</span><span class="cx"> """
</span><span class="cx"> Return a deferred which will fire with a tuple of:
</span><span class="cx"> (fieldname, filename, ctype, dataStream)
</span><span class="cx"> or None when all done.
</span><del>-
</del><ins>+
</ins><span class="cx"> Format errors will be sent to the errback.
</span><del>-
</del><ins>+
</ins><span class="cx"> Returns None when all done.
</span><span class="cx">
</span><span class="cx"> IMPORTANT: you *must* exhaust dataStream returned by this call
</span><span class="lines">@@ -193,8 +200,9 @@
</span><span class="cx"> d.addCallback(self._gotHeaders)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _readFirstBoundary(self):
</span><del>- #print("_readFirstBoundary")
</del><ins>+ # print("_readFirstBoundary")
</ins><span class="cx"> line = self.stream.readline(size=1024)
</span><span class="cx"> if isinstance(line, defer.Deferred):
</span><span class="cx"> line = defer.waitForDeferred(line)
</span><span class="lines">@@ -202,20 +210,21 @@
</span><span class="cx"> line = line.getResult()
</span><span class="cx"> if line != self.boundary + '\r\n':
</span><span class="cx"> raise MimeFormatError("Extra data before first boundary: %r looking for: %r" % (line, self.boundary + '\r\n'))
</span><del>-
- self.boundary = "\r\n"+self.boundary
</del><ins>+
+ self.boundary = "\r\n" + self.boundary
</ins><span class="cx"> yield True
</span><span class="cx"> return
</span><span class="cx"> _readFirstBoundary = defer.deferredGenerator(_readFirstBoundary)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _readBoundaryLine(self):
</span><del>- #print("_readBoundaryLine")
</del><ins>+ # print("_readBoundaryLine")
</ins><span class="cx"> line = self.stream.readline(size=1024)
</span><span class="cx"> if isinstance(line, defer.Deferred):
</span><span class="cx"> line = defer.waitForDeferred(line)
</span><span class="cx"> yield line
</span><span class="cx"> line = line.getResult()
</span><del>-
</del><ins>+
</ins><span class="cx"> if line == "--\r\n":
</span><span class="cx"> # THE END!
</span><span class="cx"> yield False
</span><span class="lines">@@ -226,22 +235,25 @@
</span><span class="cx"> return
</span><span class="cx"> _readBoundaryLine = defer.deferredGenerator(_readBoundaryLine)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _doReadHeaders(self, morefields):
</span><del>- #print("_doReadHeaders", morefields)
</del><ins>+ # print("_doReadHeaders", morefields)
</ins><span class="cx"> if not morefields:
</span><span class="cx"> return None
</span><span class="cx"> return _readHeaders(self.stream)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _gotHeaders(self, headers):
</span><span class="cx"> if headers is None:
</span><span class="cx"> return None
</span><span class="cx"> bws = _BoundaryWatchingStream(self.stream, self.boundary)
</span><span class="cx"> self.deferred = bws.deferred
</span><del>- ret=list(headers)
</del><ins>+ ret = list(headers)
</ins><span class="cx"> ret.append(bws)
</span><span class="cx"> return tuple(ret)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def readIntoFile(stream, outFile, maxlen):
</span><span class="cx"> """Read the stream into a file, but not if it's longer than maxlen.
</span><span class="cx"> Returns Deferred which will be triggered on finish.
</span><span class="lines">@@ -249,29 +261,32 @@
</span><span class="cx"> curlen = [0]
</span><span class="cx"> def done(_):
</span><span class="cx"> return _
</span><ins>+
+
</ins><span class="cx"> def write(data):
</span><span class="cx"> curlen[0] += len(data)
</span><span class="cx"> if curlen[0] > maxlen:
</span><span class="cx"> raise MimeFormatError("Maximum length of %d bytes exceeded." %
</span><span class="cx"> maxlen)
</span><del>-
</del><ins>+
</ins><span class="cx"> outFile.write(data)
</span><span class="cx"> return readStream(stream, write).addBoth(done)
</span><span class="cx">
</span><del>-#@defer.deferredGenerator
</del><ins>+
+
+# @defer.deferredGenerator
</ins><span class="cx"> def parseMultipartFormData(stream, boundary,
</span><del>- maxMem=100*1024, maxFields=1024, maxSize=10*1024*1024):
</del><ins>+ maxMem=100 * 1024, maxFields=1024, maxSize=10 * 1024 * 1024):
</ins><span class="cx"> # If the stream length is known to be too large upfront, abort immediately
</span><del>-
</del><ins>+
</ins><span class="cx"> if stream.length is not None and stream.length > maxSize:
</span><del>- raise MimeFormatError("Maximum length of %d bytes exceeded." %
- maxSize)
-
</del><ins>+ raise MimeFormatError("Maximum length of %d bytes exceeded." % maxSize)
+
</ins><span class="cx"> mms = MultipartMimeStream(stream, boundary)
</span><span class="cx"> numFields = 0
</span><span class="cx"> args = {}
</span><span class="cx"> files = {}
</span><del>-
</del><ins>+
</ins><span class="cx"> while 1:
</span><span class="cx"> datas = mms.read()
</span><span class="cx"> if isinstance(datas, defer.Deferred):
</span><span class="lines">@@ -280,11 +295,11 @@
</span><span class="cx"> datas = datas.getResult()
</span><span class="cx"> if datas is None:
</span><span class="cx"> break
</span><del>-
- numFields+=1
</del><ins>+
+ numFields += 1
</ins><span class="cx"> if numFields == maxFields:
</span><del>- raise MimeFormatError("Maximum number of fields %d exceeded"%maxFields)
-
</del><ins>+ raise MimeFormatError("Maximum number of fields %d exceeded" % maxFields)
+
</ins><span class="cx"> # Parse data
</span><span class="cx"> fieldname, filename, ctype, stream = datas
</span><span class="cx"> if filename is None:
</span><span class="lines">@@ -311,29 +326,32 @@
</span><span class="cx"> maxSize -= outfile.tell()
</span><span class="cx"> outfile.seek(0)
</span><span class="cx"> files.setdefault(fieldname, []).append((filename, ctype, outfile))
</span><del>-
-
</del><ins>+
+
</ins><span class="cx"> yield args, files
</span><span class="cx"> return
</span><span class="cx"> parseMultipartFormData = defer.deferredGenerator(parseMultipartFormData)
</span><span class="cx">
</span><del>-###################################
-##### x-www-urlencoded reader #####
-###################################
</del><ins>+#
+# x-www-urlencoded reader
+#
</ins><span class="cx">
</span><span class="cx">
</span><del>-def parse_urlencoded_stream(input, maxMem=100*1024,
- keep_blank_values=False, strict_parsing=False):
</del><ins>+def parse_urlencoded_stream(
+ input, maxMem=100 * 1024,
+ keep_blank_values=False, strict_parsing=False
+):
+
</ins><span class="cx"> lastdata = ''
</span><del>- still_going=1
-
</del><ins>+ still_going = 1
+
</ins><span class="cx"> while still_going:
</span><span class="cx"> try:
</span><span class="cx"> yield input.wait
</span><span class="cx"> data = input.next()
</span><span class="cx"> except StopIteration:
</span><span class="cx"> pairs = [lastdata]
</span><del>- still_going=0
</del><ins>+ still_going = 0
</ins><span class="cx"> else:
</span><span class="cx"> maxMem -= len(data)
</span><span class="cx"> if maxMem < 0:
</span><span class="lines">@@ -341,13 +359,13 @@
</span><span class="cx"> maxMem)
</span><span class="cx"> pairs = str(data).split('&')
</span><span class="cx"> pairs[0] = lastdata + pairs[0]
</span><del>- lastdata=pairs.pop()
-
</del><ins>+ lastdata = pairs.pop()
+
</ins><span class="cx"> for name_value in pairs:
</span><span class="cx"> nv = name_value.split('=', 1)
</span><span class="cx"> if len(nv) != 2:
</span><span class="cx"> if strict_parsing:
</span><del>- raise MimeFormatError("bad query field: %s") % `name_value`
</del><ins>+ raise MimeFormatError("bad query field: %s") % repr(name_value)
</ins><span class="cx"> continue
</span><span class="cx"> if len(nv[1]) or keep_blank_values:
</span><span class="cx"> name = urllib.unquote(nv[0].replace('+', ' '))
</span><span class="lines">@@ -355,13 +373,13 @@
</span><span class="cx"> yield name, value
</span><span class="cx"> parse_urlencoded_stream = generatorToStream(parse_urlencoded_stream)
</span><span class="cx">
</span><del>-def parse_urlencoded(stream, maxMem=100*1024, maxFields=1024,
</del><ins>+def parse_urlencoded(stream, maxMem=100 * 1024, maxFields=1024,
</ins><span class="cx"> keep_blank_values=False, strict_parsing=False):
</span><span class="cx"> d = {}
</span><span class="cx"> numFields = 0
</span><span class="cx">
</span><del>- s=parse_urlencoded_stream(stream, maxMem, keep_blank_values, strict_parsing)
-
</del><ins>+ s = parse_urlencoded_stream(stream, maxMem, keep_blank_values, strict_parsing)
+
</ins><span class="cx"> while 1:
</span><span class="cx"> datas = s.read()
</span><span class="cx"> if isinstance(datas, defer.Deferred):
</span><span class="lines">@@ -371,11 +389,11 @@
</span><span class="cx"> if datas is None:
</span><span class="cx"> break
</span><span class="cx"> name, value = datas
</span><del>-
</del><ins>+
</ins><span class="cx"> numFields += 1
</span><span class="cx"> if numFields == maxFields:
</span><del>- raise MimeFormatError("Maximum number of fields %d exceeded"%maxFields)
-
</del><ins>+ raise MimeFormatError("Maximum number of fields %d exceeded" % maxFields)
+
</ins><span class="cx"> if name in d:
</span><span class="cx"> d[name].append(value)
</span><span class="cx"> else:
</span></span></pre></div>
<a id="CalendarServertrunktxweb2filtergzippy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/filter/gzip.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/filter/gzip.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/filter/gzip.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -15,11 +15,11 @@
</span><span class="cx"> # uh.. stuff
</span><span class="cx"> header += '\002\377'
</span><span class="cx"> yield header
</span><del>-
</del><ins>+
</ins><span class="cx"> compress = zlib.compressobj(compressLevel, zlib.DEFLATED, -zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, 0)
</span><span class="cx"> _compress = compress.compress
</span><span class="cx"> _crc32 = zlib.crc32
</span><del>-
</del><ins>+
</ins><span class="cx"> yield input.wait
</span><span class="cx"> for buf in input:
</span><span class="cx"> if len(buf) != 0:
</span><span class="lines">@@ -27,16 +27,16 @@
</span><span class="cx"> size += len(buf)
</span><span class="cx"> yield _compress(buf)
</span><span class="cx"> yield input.wait
</span><del>-
</del><ins>+
</ins><span class="cx"> yield compress.flush()
</span><span class="cx"> yield struct.pack('<LL', crc & 0xFFFFFFFFL, size & 0xFFFFFFFFL)
</span><del>-gzipStream=stream.generatorToStream(gzipStream)
</del><ins>+gzipStream = stream.generatorToStream(gzipStream)
</ins><span class="cx">
</span><span class="cx"> def deflateStream(input, compressLevel=6):
</span><span class="cx"> # NOTE: this produces RFC-conformant but some-browser-incompatible output.
</span><span class="cx"> # The RFC says that you're supposed to output zlib-format data, but many
</span><span class="cx"> # browsers expect raw deflate output. Luckily all those browsers support
</span><del>- # gzip, also, so they won't even see deflate output.
</del><ins>+ # gzip, also, so they won't even see deflate output.
</ins><span class="cx"> compress = zlib.compressobj(compressLevel, zlib.DEFLATED, zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, 0)
</span><span class="cx"> _compress = compress.compress
</span><span class="cx"> yield input.wait
</span><span class="lines">@@ -46,24 +46,24 @@
</span><span class="cx"> yield input.wait
</span><span class="cx">
</span><span class="cx"> yield compress.flush()
</span><del>-deflateStream=stream.generatorToStream(deflateStream)
</del><ins>+deflateStream = stream.generatorToStream(deflateStream)
</ins><span class="cx">
</span><span class="cx"> def gzipfilter(request, response):
</span><span class="cx"> if response.stream is None or response.headers.getHeader('content-encoding'):
</span><span class="cx"> # Empty stream, or already compressed.
</span><span class="cx"> return response
</span><del>-
</del><ins>+
</ins><span class="cx"> # FIXME: make this a more flexible matching scheme
</span><span class="cx"> mimetype = response.headers.getHeader('content-type')
</span><span class="cx"> if not mimetype or mimetype.mediaType != 'text':
</span><span class="cx"> return response
</span><del>-
</del><ins>+
</ins><span class="cx"> # Make sure to note we're going to return different content depending on
</span><span class="cx"> # the accept-encoding header.
</span><span class="cx"> vary = response.headers.getHeader('vary', [])
</span><span class="cx"> if 'accept-encoding' not in vary:
</span><del>- response.headers.setHeader('vary', vary+['accept-encoding'])
-
</del><ins>+ response.headers.setHeader('vary', vary + ['accept-encoding'])
+
</ins><span class="cx"> ae = request.headers.getHeader('accept-encoding', {})
</span><span class="cx"> # Always prefer gzip over deflate no matter what their q-values are.
</span><span class="cx"> if ae.get('gzip', 0):
</span><span class="lines">@@ -72,7 +72,7 @@
</span><span class="cx"> elif ae.get('deflate', 0):
</span><span class="cx"> response.stream = deflateStream(response.stream)
</span><span class="cx"> response.headers.setHeader('content-encoding', ['deflate'])
</span><del>-
</del><ins>+
</ins><span class="cx"> return response
</span><span class="cx">
</span><span class="cx"> __all__ = ['gzipfilter']
</span></span></pre></div>
<a id="CalendarServertrunktxweb2filterlocationpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/filter/location.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/filter/location.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/filter/location.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -16,13 +16,13 @@
</span><span class="cx"> # Check to see whether we have an absolute URI or not.
</span><span class="cx"> # If not, have the request turn it into an absolute URI.
</span><span class="cx"> #
</span><del>- (scheme, host, path, params, querystring, fragment) = urlparse.urlparse(location)
</del><ins>+ (scheme, _ignore_host, _ignore_path, _ignore_params, _ignore_querystring, _ignore_fragment) = urlparse.urlparse(location)
</ins><span class="cx">
</span><span class="cx"> if scheme == "":
</span><span class="cx"> uri = request.unparseURL(path=location)
</span><span class="cx"> else:
</span><span class="cx"> uri = location
</span><del>-
</del><ins>+
</ins><span class="cx"> response.headers.setHeader("location", uri)
</span><span class="cx">
</span><span class="cx"> return response
</span></span></pre></div>
<a id="CalendarServertrunktxweb2filterrangepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/filter/range.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/filter/range.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/filter/range.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -1,6 +1,7 @@
</span><span class="cx"> # -*- test-case-name: txweb2.test.test_stream -*-
</span><span class="cx">
</span><del>-import time, os
</del><ins>+import os
+import time
</ins><span class="cx">
</span><span class="cx"> from txweb2 import http, http_headers, responsecode, stream
</span><span class="cx">
</span><span class="lines">@@ -9,26 +10,30 @@
</span><span class="cx"> class UnsatisfiableRangeRequest(Exception):
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def canonicalizeRange((start, end), size):
</span><span class="cx"> """Return canonicalized (start, end) or raises UnsatisfiableRangeRequest
</span><span class="cx"> exception.
</span><span class="cx">
</span><span class="cx"> NOTE: end is the last byte *inclusive*, which is not the usual convention
</span><span class="cx"> in python! Be very careful! A range of 0,1 should return 2 bytes."""
</span><del>-
</del><ins>+
</ins><span class="cx"> # handle "-500" ranges
</span><span class="cx"> if start is None:
</span><del>- start = max(0, size-end)
</del><ins>+ start = max(0, size - end)
</ins><span class="cx"> end = None
</span><del>-
</del><ins>+
</ins><span class="cx"> if end is None or end >= size:
</span><span class="cx"> end = size - 1
</span><del>-
</del><ins>+
</ins><span class="cx"> if start >= size:
</span><span class="cx"> raise UnsatisfiableRangeRequest
</span><del>-
- return start,end
</del><span class="cx">
</span><ins>+ return start, end
+
+
+
</ins><span class="cx"> def makeUnsatisfiable(request, oldresponse):
</span><span class="cx"> if request.headers.hasHeader('if-range'):
</span><span class="cx"> return oldresponse # Return resource instead of error
</span><span class="lines">@@ -36,15 +41,19 @@
</span><span class="cx"> response.headers.setHeader("content-range", ('bytes', None, None, oldresponse.stream.length))
</span><span class="cx"> return response
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def makeSegment(inputStream, lastOffset, start, end):
</span><span class="cx"> offset = start - lastOffset
</span><span class="cx"> length = end + 1 - start
</span><del>-
</del><ins>+
</ins><span class="cx"> if offset != 0:
</span><span class="cx"> before, inputStream = inputStream.split(offset)
</span><span class="cx"> before.close()
</span><span class="cx"> return inputStream.split(length)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def rangefilter(request, oldresponse):
</span><span class="cx"> if oldresponse.stream is None:
</span><span class="cx"> return oldresponse
</span><span class="lines">@@ -53,24 +62,26 @@
</span><span class="cx"> # Does not deal with indeterminate length outputs
</span><span class="cx"> return oldresponse
</span><span class="cx">
</span><del>- oldresponse.headers.setHeader('accept-ranges',('bytes',))
-
</del><ins>+ oldresponse.headers.setHeader('accept-ranges', ('bytes',))
+
</ins><span class="cx"> rangespec = request.headers.getHeader('range')
</span><del>-
</del><ins>+
</ins><span class="cx"> # If we've got a range header and the If-Range header check passes, and
</span><span class="cx"> # the range type is bytes, do a partial response.
</span><del>- if (rangespec is not None and http.checkIfRange(request, oldresponse) and
- rangespec[0] == 'bytes'):
</del><ins>+ if (
+ rangespec is not None and http.checkIfRange(request, oldresponse) and
+ rangespec[0] == 'bytes'
+ ):
</ins><span class="cx"> # If it's a single range, return a simple response
</span><span class="cx"> if len(rangespec[1]) == 1:
</span><span class="cx"> try:
</span><del>- start,end = canonicalizeRange(rangespec[1][0], size)
</del><ins>+ start, end = canonicalizeRange(rangespec[1][0], size)
</ins><span class="cx"> except UnsatisfiableRangeRequest:
</span><span class="cx"> return makeUnsatisfiable(request, oldresponse)
</span><span class="cx">
</span><span class="cx"> response = http.Response(responsecode.PARTIAL_CONTENT, oldresponse.headers)
</span><del>- response.headers.setHeader('content-range',('bytes',start, end, size))
-
</del><ins>+ response.headers.setHeader('content-range', ('bytes', start, end, size))
+
</ins><span class="cx"> content, after = makeSegment(oldresponse.stream, 0, start, end)
</span><span class="cx"> after.close()
</span><span class="cx"> response.stream = content
</span><span class="lines">@@ -81,40 +92,44 @@
</span><span class="cx"> offsetList = []
</span><span class="cx"> for arange in rangespec[1]:
</span><span class="cx"> try:
</span><del>- start,end = canonicalizeRange(arange, size)
</del><ins>+ start, end = canonicalizeRange(arange, size)
</ins><span class="cx"> except UnsatisfiableRangeRequest:
</span><span class="cx"> continue
</span><span class="cx"> if start <= lastOffset:
</span><span class="cx"> # Stupid client asking for out-of-order or overlapping ranges, PUNT!
</span><span class="cx"> return oldresponse
</span><del>- offsetList.append((start,end))
</del><ins>+ offsetList.append((start, end))
</ins><span class="cx"> lastOffset = end
</span><span class="cx">
</span><span class="cx"> if not offsetList:
</span><span class="cx"> return makeUnsatisfiable(request, oldresponse)
</span><del>-
</del><ins>+
</ins><span class="cx"> content_type = oldresponse.headers.getRawHeaders('content-type')
</span><del>- boundary = "%x%x" % (int(time.time()*1000000), os.getpid())
</del><ins>+ boundary = "%x%x" % (int(time.time() * 1000000), os.getpid())
</ins><span class="cx"> response = http.Response(responsecode.PARTIAL_CONTENT, oldresponse.headers)
</span><del>-
- response.headers.setHeader('content-type',
</del><ins>+
+ response.headers.setHeader(
+ 'content-type',
</ins><span class="cx"> http_headers.MimeType('multipart', 'byteranges',
</span><del>- [('boundary', boundary)]))
</del><ins>+ [('boundary', boundary)])
+ )
</ins><span class="cx"> response.stream = out = stream.CompoundStream()
</span><del>-
-
</del><ins>+
+
</ins><span class="cx"> lastOffset = 0
</span><span class="cx"> origStream = oldresponse.stream
</span><span class="cx">
</span><span class="cx"> headerString = "\r\n--%s" % boundary
</span><span class="cx"> if len(content_type) == 1:
</span><del>- headerString+='\r\nContent-Type: %s' % content_type[0]
- headerString+="\r\nContent-Range: %s\r\n\r\n"
-
- for start,end in offsetList:
- out.addStream(headerString %
- http_headers.generateContentRange(('bytes', start, end, size)))
</del><ins>+ headerString += '\r\nContent-Type: %s' % content_type[0]
+ headerString += "\r\nContent-Range: %s\r\n\r\n"
</ins><span class="cx">
</span><ins>+ for start, end in offsetList:
+ out.addStream(
+ headerString %
+ http_headers.generateContentRange(('bytes', start, end, size))
+ )
+
</ins><span class="cx"> content, origStream = makeSegment(origStream, lastOffset, start, end)
</span><span class="cx"> lastOffset = end + 1
</span><span class="cx"> out.addStream(content)
</span><span class="lines">@@ -124,5 +139,5 @@
</span><span class="cx"> else:
</span><span class="cx"> return oldresponse
</span><span class="cx">
</span><del>-
</del><ins>+
</ins><span class="cx"> __all__ = ['rangefilter']
</span></span></pre></div>
<a id="CalendarServertrunktxweb2httppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/http.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/http.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/http.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -236,7 +236,8 @@
</span><span class="cx"> 'date', 'etag', 'content-location', 'expires',
</span><span class="cx"> 'cache-control', 'vary',
</span><span class="cx"> # Others:
</span><del>- 'server', 'proxy-authenticate', 'www-authenticate', 'warning'):
</del><ins>+ 'server', 'proxy-authenticate', 'www-authenticate', 'warning'
+ ):
</ins><span class="cx"> value = oldResponse.headers.getRawHeaders(header)
</span><span class="cx"> if value is not None:
</span><span class="cx"> headers.setRawHeaders(header, value)
</span></span></pre></div>
<a id="CalendarServertrunktxweb2http_headerspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/http_headers.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/http_headers.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/http_headers.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -243,7 +243,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-##### HTTP tokenizer
</del><ins>+# HTTP tokenizer
</ins><span class="cx"> class Token(str):
</span><span class="cx"> __slots__ = []
</span><span class="cx"> tokens = {}
</span><span class="lines">@@ -385,7 +385,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-##### parser utilities:
</del><ins>+# parser utilities:
</ins><span class="cx"> def checkSingleToken(tokens):
</span><span class="cx"> if len(tokens) != 1:
</span><span class="cx"> raise ValueError("Expected single token, not %s." % (tokens,))
</span><span class="lines">@@ -430,7 +430,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-##### Generation utilities
</del><ins>+# Generation utilities
</ins><span class="cx"> def quoteString(s):
</span><span class="cx"> """
</span><span class="cx"> Quote a string according to the rules for the I{quoted-string} production
</span><span class="lines">@@ -589,7 +589,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-##### Specific header parsers.
</del><ins>+# Specific header parsers.
</ins><span class="cx"> def parseAccept(field):
</span><span class="cx"> atype, args = parseArgs(field)
</span><span class="cx">
</span><span class="lines">@@ -863,7 +863,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-#### Header generators
</del><ins>+# Header generators
</ins><span class="cx"> def generateAccept(accept):
</span><span class="cx"> mimeType, q = accept
</span><span class="cx">
</span><span class="lines">@@ -1126,7 +1126,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-#### Cookies. Blech!
</del><ins>+# Cookies. Blech!
</ins><span class="cx"> class Cookie(object):
</span><span class="cx"> # __slots__ = ['name', 'value', 'path', 'domain', 'ports', 'expires', 'discard', 'secure', 'comment', 'commenturl', 'version']
</span><span class="cx">
</span><span class="lines">@@ -1501,7 +1501,7 @@
</span><span class="cx"> # MS definition uses lower case
</span><span class="cx"> return "t" if brief else "f"
</span><span class="cx">
</span><del>-##### Random stuff that looks useful.
</del><ins>+# Random stuff that looks useful.
</ins><span class="cx"> # def sortMimeQuality(s):
</span><span class="cx"> # def sorter(item1, item2):
</span><span class="cx"> # if item1[0] == '*':
</span><span class="lines">@@ -1716,24 +1716,24 @@
</span><span class="cx"> 'Cache-Control': (tokenize, listParser(parseCacheControl), dict),
</span><span class="cx"> 'Connection': (tokenize, filterTokens),
</span><span class="cx"> 'Date': (last, parseDateTime),
</span><del>-# 'Pragma': tokenize
-# 'Trailer': tokenize
</del><ins>+ # 'Pragma': tokenize
+ # 'Trailer': tokenize
</ins><span class="cx"> 'Transfer-Encoding': (tokenize, filterTokens),
</span><del>-# 'Upgrade': tokenize
-# 'Via': tokenize,stripComment
-# 'Warning': tokenize
</del><ins>+ # 'Upgrade': tokenize
+ # 'Via': tokenize,stripComment
+ # 'Warning': tokenize
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> generator_general_headers = {
</span><span class="cx"> 'Cache-Control': (iteritems, listGenerator(generateCacheControl), singleHeader),
</span><span class="cx"> 'Connection': (generateList, singleHeader),
</span><span class="cx"> 'Date': (generateDateTime, singleHeader),
</span><del>-# 'Pragma':
-# 'Trailer':
</del><ins>+ # 'Pragma':
+ # 'Trailer':
</ins><span class="cx"> 'Transfer-Encoding': (generateList, singleHeader),
</span><del>-# 'Upgrade':
-# 'Via':
-# 'Warning':
</del><ins>+ # 'Upgrade':
+ # 'Via':
+ # 'Warning':
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> parser_request_headers = {
</span><span class="lines">@@ -1753,7 +1753,7 @@
</span><span class="cx"> 'If-Unmodified-Since': (last, parseDateTime),
</span><span class="cx"> 'Max-Forwards': (last, int),
</span><span class="cx"> 'Prefer': (tokenize, listParser(parsePrefer), list),
</span><del>-# 'Proxy-Authorization': str, # what is "credentials"
</del><ins>+ # 'Proxy-Authorization': str, # what is "credentials"
</ins><span class="cx"> 'Range': (tokenize, parseRange),
</span><span class="cx"> 'Referer': (last, str), # TODO: URI object?
</span><span class="cx"> 'TE': (tokenize, listParser(parseAcceptQvalue), dict),
</span><span class="lines">@@ -1777,7 +1777,7 @@
</span><span class="cx"> 'If-Unmodified-Since': (generateDateTime, singleHeader),
</span><span class="cx"> 'Max-Forwards': (str, singleHeader),
</span><span class="cx"> 'Prefer': (listGenerator(generatePrefer), singleHeader),
</span><del>-# 'Proxy-Authorization': str, # what is "credentials"
</del><ins>+ # 'Proxy-Authorization': str, # what is "credentials"
</ins><span class="cx"> 'Range': (generateRange, singleHeader),
</span><span class="cx"> 'Referer': (str, singleHeader),
</span><span class="cx"> 'TE': (iteritems, listGenerator(generateAcceptQvalue), singleHeader),
</span><span class="lines">@@ -1789,7 +1789,7 @@
</span><span class="cx"> 'Age': (last, int),
</span><span class="cx"> 'ETag': (tokenize, ETag.parse),
</span><span class="cx"> 'Location': (last,), # TODO: URI object?
</span><del>-# 'Proxy-Authenticate'
</del><ins>+ # 'Proxy-Authenticate'
</ins><span class="cx"> 'Retry-After': (last, parseRetryAfter),
</span><span class="cx"> 'Server': (last,),
</span><span class="cx"> 'Set-Cookie': (parseSetCookie,),
</span><span class="lines">@@ -1804,7 +1804,7 @@
</span><span class="cx"> 'Age': (str, singleHeader),
</span><span class="cx"> 'ETag': (ETag.generate, singleHeader),
</span><span class="cx"> 'Location': (str, singleHeader),
</span><del>-# 'Proxy-Authenticate'
</del><ins>+ # 'Proxy-Authenticate'
</ins><span class="cx"> 'Retry-After': (generateRetryAfter, singleHeader),
</span><span class="cx"> 'Server': (str, singleHeader),
</span><span class="cx"> 'Set-Cookie': (generateSetCookie,),
</span><span class="lines">@@ -1825,7 +1825,7 @@
</span><span class="cx"> 'Content-Type': (lambda hdr: tokenize(hdr, foldCase=False), parseContentType),
</span><span class="cx"> 'Expires': (last, parseExpires),
</span><span class="cx"> 'Last-Modified': (last, parseDateTime),
</span><del>- }
</del><ins>+}
</ins><span class="cx">
</span><span class="cx"> generator_entity_headers = {
</span><span class="cx"> 'Allow': (generateList, singleHeader),
</span><span class="lines">@@ -1839,18 +1839,18 @@
</span><span class="cx"> 'Content-Type': (generateContentType, singleHeader),
</span><span class="cx"> 'Expires': (generateDateTime, singleHeader),
</span><span class="cx"> 'Last-Modified': (generateDateTime, singleHeader),
</span><del>- }
</del><ins>+}
</ins><span class="cx">
</span><span class="cx"> parser_dav_headers = {
</span><span class="cx"> 'Brief' : (last, parseBrief),
</span><span class="cx"> 'DAV' : (tokenize, list),
</span><span class="cx"> 'Depth' : (last, parseDepth),
</span><span class="cx"> 'Destination' : (last,), # TODO: URI object?
</span><del>- # 'If' : (),
- # 'Lock-Token' : (),
</del><ins>+ # 'If' : (),
+ # 'Lock-Token' : (),
</ins><span class="cx"> 'Overwrite' : (last, parseOverWrite),
</span><del>- # 'Status-URI' : (),
- # 'Timeout' : (),
</del><ins>+ # 'Status-URI' : (),
+ # 'Timeout' : (),
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> generator_dav_headers = {
</span><span class="lines">@@ -1858,11 +1858,11 @@
</span><span class="cx"> 'DAV' : (generateList, singleHeader),
</span><span class="cx"> 'Depth' : (singleHeader),
</span><span class="cx"> 'Destination' : (singleHeader),
</span><del>- # 'If' : (),
- # 'Lock-Token' : (),
</del><ins>+ # 'If' : (),
+ # 'Lock-Token' : (),
</ins><span class="cx"> 'Overwrite' : (),
</span><del>- # 'Status-URI' : (),
- # 'Timeout' : (),
</del><ins>+ # 'Status-URI' : (),
+ # 'Timeout' : (),
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> DefaultHTTPHandler.updateParsers(parser_general_headers)
</span></span></pre></div>
<a id="CalendarServertrunktxweb2iwebpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/iweb.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/iweb.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/iweb.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -46,11 +46,11 @@
</span><span class="cx">
</span><span class="cx"> @return: A 2-tuple of (resource, remaining-path-segments),
</span><span class="cx"> or a deferred which will fire the above.
</span><del>-
</del><ins>+
</ins><span class="cx"> Causes the object publishing machinery to continue on
</span><span class="cx"> with specified resource and segments, calling the
</span><span class="cx"> appropriate method on the specified resource.
</span><del>-
</del><ins>+
</ins><span class="cx"> If you return (self, L{server.StopTraversal}), this
</span><span class="cx"> instructs web2 to immediately stop the lookup stage,
</span><span class="cx"> and switch to the rendering stage, leaving the
</span><span class="lines">@@ -74,7 +74,7 @@
</span><span class="cx"> result = super(SpecialAdaptInterfaceClass, self).__call__(other, alternate)
</span><span class="cx"> if result is not alternate:
</span><span class="cx"> return result
</span><del>-
</del><ins>+
</ins><span class="cx"> result = IOldNevowResource(other, alternate)
</span><span class="cx"> if result is not alternate:
</span><span class="cx"> result = IResource(result)
</span><span class="lines">@@ -106,8 +106,10 @@
</span><span class="cx"> string instead of a response object.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class ICanHandleException(Interface):
</span><del>-
</del><ins>+
</ins><span class="cx"> # Shared interface with inevow.ICanHandleException
</span><span class="cx"> def renderHTTP_exception(request, failure):
</span><span class="cx"> """
</span><span class="lines">@@ -120,6 +122,7 @@
</span><span class="cx"> not replacing the page."""
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> # http.py interfaces
</span><span class="cx"> class IResponse(Interface):
</span><span class="cx"> """
</span><span class="lines">@@ -129,6 +132,8 @@
</span><span class="cx"> headers = Attribute("A http_headers.Headers instance of headers to send")
</span><span class="cx"> stream = Attribute("A stream.IByteStream of outgoing data, or else None.")
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class IRequest(Interface):
</span><span class="cx"> """
</span><span class="cx"> I'm a request for a web resource.
</span><span class="lines">@@ -137,21 +142,22 @@
</span><span class="cx"> method = Attribute("The HTTP method from the request line, e.g. GET")
</span><span class="cx"> uri = Attribute("The raw URI from the request line. May or may not include host.")
</span><span class="cx"> clientproto = Attribute("Protocol from the request line, e.g. HTTP/1.1")
</span><del>-
</del><ins>+
</ins><span class="cx"> headers = Attribute("A http_headers.Headers instance of incoming headers.")
</span><span class="cx"> stream = Attribute("A stream.IByteStream of incoming data.")
</span><del>-
</del><ins>+
</ins><span class="cx"> def writeResponse(response):
</span><span class="cx"> """
</span><span class="cx"> Write an IResponse object to the client.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> chanRequest = Attribute("The ChannelRequest. I wonder if this is public really?")
</span><span class="cx">
</span><span class="cx">
</span><del>-from twisted.web.iweb import IRequest as IOldRequest
</del><span class="cx">
</span><ins>+# from twisted.web.iweb import IRequest as IOldRequest
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> class IChanRequestCallbacks(Interface):
</span><span class="cx"> """
</span><span class="cx"> The bits that are required of a Request for interfacing with a
</span><span class="lines">@@ -161,7 +167,7 @@
</span><span class="cx"> def __init__(chanRequest, command, path, version, contentLength, inHeaders):
</span><span class="cx"> """
</span><span class="cx"> Create a new Request object.
</span><del>-
</del><ins>+
</ins><span class="cx"> @param chanRequest: the IChanRequest object creating this request
</span><span class="cx"> @param command: the HTTP command e.g. GET
</span><span class="cx"> @param path: the HTTP path e.g. /foo/bar.html
</span><span class="lines">@@ -175,38 +181,39 @@
</span><span class="cx"> to return a response. L{handleContentComplete} may or may not
</span><span class="cx"> have been called already.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def handleContentChunk(data):
</span><span class="cx"> """
</span><span class="cx"> Called when a piece of incoming data has been received.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def handleContentComplete():
</span><span class="cx"> """
</span><span class="cx"> Called when the incoming data stream is finished.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def connectionLost(reason):
</span><span class="cx"> """
</span><span class="cx"> Called if the connection was lost.
</span><span class="cx"> """
</span><del>-
-
</del><ins>+
+
+
</ins><span class="cx"> class IChanRequest(Interface):
</span><del>-
</del><ins>+
</ins><span class="cx"> def writeIntermediateResponse(code, headers=None):
</span><span class="cx"> """
</span><span class="cx"> Write a non-terminating response.
</span><del>-
</del><ins>+
</ins><span class="cx"> Intermediate responses cannot contain data.
</span><span class="cx"> If the channel does not support intermediate responses, do nothing.
</span><del>-
</del><ins>+
</ins><span class="cx"> @param code: The response code. Should be in the 1xx range.
</span><span class="cx"> @type code: int
</span><span class="cx"> @param headers: the headers to send in the response
</span><span class="cx"> @type headers: C{twisted.web.http_headers.Headers}
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def writeHeaders(code, headers):
</span><span class="cx"> """
</span><span class="cx"> Write a final response.
</span><span class="lines">@@ -218,7 +225,7 @@
</span><span class="cx"> necessary for the protocol.
</span><span class="cx"> @type headers: C{twisted.web.http_headers.Headers}
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def write(data):
</span><span class="cx"> """
</span><span class="cx"> Write some data.
</span><span class="lines">@@ -226,16 +233,16 @@
</span><span class="cx"> @param data: the data bytes
</span><span class="cx"> @type data: str
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def finish():
</span><span class="cx"> """
</span><span class="cx"> Finish the request, and clean up the connection if necessary.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def abortConnection():
</span><span class="cx"> """
</span><span class="cx"> Forcibly abort the connection without cleanly closing.
</span><del>-
</del><ins>+
</ins><span class="cx"> Use if, for example, you can't write all the data you promised.
</span><span class="cx"> """
</span><span class="cx">
</span><span class="lines">@@ -243,7 +250,7 @@
</span><span class="cx"> """
</span><span class="cx"> Register a producer with the standard API.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def unregisterProducer():
</span><span class="cx"> """
</span><span class="cx"> Unregister a producer.
</span><span class="lines">@@ -270,6 +277,7 @@
</span><span class="cx"> persistent = Attribute("""Whether this request supports HTTP connection persistence. May be set to False. Should not be set to other values.""")
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class ISite(Interface):
</span><span class="cx"> pass
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxweb2logpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/log.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/log.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/log.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -39,65 +39,73 @@
</span><span class="cx">
</span><span class="cx"> class _LogByteCounter(object):
</span><span class="cx"> implements(stream.IByteStream)
</span><del>-
</del><ins>+
</ins><span class="cx"> def __init__(self, stream, done):
</span><del>- self.stream=stream
- self.done=done
- self.len=0
-
- length=property(lambda self: self.stream.length)
-
</del><ins>+ self.stream = stream
+ self.done = done
+ self.len = 0
+
+ length = property(lambda self: self.stream.length)
+
</ins><span class="cx"> def _callback(self, data):
</span><span class="cx"> if data is None:
</span><span class="cx"> if self.done:
</span><del>- done=self.done; self.done=None
</del><ins>+ done = self.done
+ self.done = None
</ins><span class="cx"> done(True, self.len)
</span><span class="cx"> else:
</span><span class="cx"> self.len += len(data)
</span><span class="cx"> return data
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def read(self):
</span><span class="cx"> data = self.stream.read()
</span><span class="cx"> if isinstance(data, defer.Deferred):
</span><span class="cx"> return data.addCallback(self._callback)
</span><span class="cx"> return self._callback(data)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def close(self):
</span><span class="cx"> if self.done:
</span><del>- done=self.done; self.done=None
</del><ins>+ done = self.done
+ self.done = None
</ins><span class="cx"> done(False, self.len)
</span><span class="cx"> self.stream.close()
</span><span class="cx">
</span><del>-
</del><ins>+
+
</ins><span class="cx"> class ILogInfo(Interface):
</span><span class="cx"> """Auxilliary information about the response useful for logging."""
</span><del>-
- bytesSent=Attribute("Number of bytes sent.")
- responseCompleted=Attribute("Whether or not the response was completed.")
- secondsTaken=Attribute("Number of seconds taken to serve the request.")
- startTime=Attribute("Time at which the request started")
</del><span class="cx">
</span><del>-
</del><ins>+ bytesSent = Attribute("Number of bytes sent.")
+ responseCompleted = Attribute("Whether or not the response was completed.")
+ secondsTaken = Attribute("Number of seconds taken to serve the request.")
+ startTime = Attribute("Time at which the request started")
+
+
+
</ins><span class="cx"> class LogInfo(object):
</span><span class="cx"> implements(ILogInfo)
</span><span class="cx">
</span><del>- responseCompleted=None
- secondsTaken=None
- bytesSent=None
- startTime=None
</del><ins>+ responseCompleted = None
+ secondsTaken = None
+ bytesSent = None
+ startTime = None
</ins><span class="cx">
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def logFilter(request, response, startTime=None):
</span><span class="cx"> if startTime is None:
</span><span class="cx"> startTime = time.time()
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _log(success, length):
</span><del>- loginfo=LogInfo()
- loginfo.bytesSent=length
- loginfo.responseCompleted=success
- loginfo.secondsTaken=time.time()-startTime
</del><ins>+ loginfo = LogInfo()
+ loginfo.bytesSent = length
+ loginfo.responseCompleted = success
+ loginfo.secondsTaken = time.time() - startTime
</ins><span class="cx">
</span><del>- if length:
</del><ins>+ if length:
</ins><span class="cx"> request.timeStamp("t-resp-wr")
</span><span class="cx"> log.info(interface=iweb.IRequest, request=request, response=response,
</span><span class="cx"> loginfo=loginfo)
</span><span class="lines">@@ -106,7 +114,7 @@
</span><span class="cx">
</span><span class="cx"> request.timeStamp("t-resp-gen")
</span><span class="cx"> if response.stream:
</span><del>- response.stream=_LogByteCounter(response.stream, _log)
</del><ins>+ response.stream = _LogByteCounter(response.stream, _log)
</ins><span class="cx"> else:
</span><span class="cx"> _log(True, 0)
</span><span class="cx">
</span><span class="lines">@@ -137,6 +145,7 @@
</span><span class="cx"> def logMessage(self, message):
</span><span class="cx"> raise NotImplemented, 'You must provide an implementation.'
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def computeTimezoneForLog(self, tz):
</span><span class="cx"> if tz > 0:
</span><span class="cx"> neg = 1
</span><span class="lines">@@ -156,7 +165,7 @@
</span><span class="cx"> def logDateString(self, when):
</span><span class="cx"> logtime = time.localtime(when)
</span><span class="cx"> Y, M, D, h, m, s = logtime[:6]
</span><del>-
</del><ins>+
</ins><span class="cx"> if not time.daylight:
</span><span class="cx"> tz = self.tzForLog
</span><span class="cx"> if tz is None:
</span><span class="lines">@@ -171,6 +180,7 @@
</span><span class="cx"> return '%02d/%s/%02d:%02d:%02d:%02d %s' % (
</span><span class="cx"> D, monthname[M], Y, h, m, s, tz)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def emit(self, eventDict):
</span><span class="cx"> if eventDict.get('interface') is not iweb.IRequest:
</span><span class="cx"> return
</span><span class="lines">@@ -178,11 +188,11 @@
</span><span class="cx"> request = eventDict['request']
</span><span class="cx"> response = eventDict['response']
</span><span class="cx"> loginfo = eventDict['loginfo']
</span><del>- firstLine = '%s %s HTTP/%s' %(
</del><ins>+ firstLine = '%s %s HTTP/%s' % (
</ins><span class="cx"> request.method,
</span><span class="cx"> request.uri,
</span><span class="cx"> '.'.join([str(x) for x in request.clientproto]))
</span><del>-
</del><ins>+
</ins><span class="cx"> self.logMessage(
</span><span class="cx"> '%s - %s [%s] "%s" %s %d "%s" "%s"' % (
</span><span class="cx"> request.remoteAddr.host,
</span><span class="lines">@@ -195,38 +205,45 @@
</span><span class="cx"> loginfo.bytesSent,
</span><span class="cx"> request.headers.getHeader('referer', '-'),
</span><span class="cx"> request.headers.getHeader('user-agent', '-')
</span><del>- )
</del><span class="cx"> )
</span><ins>+ )
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def start(self):
</span><span class="cx"> """Start observing log events."""
</span><span class="cx"> # Use the root publisher to bypass log level filtering
</span><span class="cx"> log.publisher.addObserver(self.emit, filtered=False)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def stop(self):
</span><span class="cx"> """Stop observing log events."""
</span><span class="cx"> log.publisher.removeObserver(self.emit)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class FileAccessLoggingObserver(BaseCommonAccessLoggingObserver):
</span><span class="cx"> """I log requests to a single logfile
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def __init__(self, logpath):
</span><span class="cx"> self.logpath = logpath
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def logMessage(self, message):
</span><span class="cx"> self.f.write(message + '\n')
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def start(self):
</span><span class="cx"> super(FileAccessLoggingObserver, self).start()
</span><span class="cx"> self.f = open(self.logpath, 'a', 1)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def stop(self):
</span><span class="cx"> super(FileAccessLoggingObserver, self).stop()
</span><span class="cx"> self.f.close()
</span><span class="cx">
</span><del>-
</del><ins>+
+
</ins><span class="cx"> class DefaultCommonAccessLoggingObserver(BaseCommonAccessLoggingObserver):
</span><span class="cx"> """Log requests to default twisted logfile."""
</span><span class="cx"> def logMessage(self, message):
</span></span></pre></div>
<a id="CalendarServertrunktxweb2resourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/resource.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/resource.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/resource.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -55,6 +55,7 @@
</span><span class="cx"> )
</span><span class="cx"> return self._allowed_methods
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def checkPreconditions(self, request):
</span><span class="cx"> """
</span><span class="cx"> Checks all preconditions imposed by this resource upon a request made
</span><span class="lines">@@ -148,6 +149,7 @@
</span><span class="cx"> # """
</span><span class="cx"> # return server.doTrace(request)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def http_HEAD(self, request):
</span><span class="cx"> """
</span><span class="cx"> Respond to a HEAD request.
</span><span class="lines">@@ -156,6 +158,7 @@
</span><span class="cx"> """
</span><span class="cx"> return self.http_GET(request)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def http_GET(self, request):
</span><span class="cx"> """
</span><span class="cx"> Respond to a GET request.
</span><span class="lines">@@ -171,6 +174,7 @@
</span><span class="cx">
</span><span class="cx"> return self.render(request)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def render(self, request):
</span><span class="cx"> """
</span><span class="cx"> Subclasses should implement this method to do page rendering.
</span><span class="lines">@@ -216,6 +220,7 @@
</span><span class="cx">
</span><span class="cx"> return None, []
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def child_(self, request):
</span><span class="cx"> """
</span><span class="cx"> This method locates a child with a trailing C{"/"} in the URL.
</span><span class="lines">@@ -225,6 +230,7 @@
</span><span class="cx"> return self
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getChild(self, path):
</span><span class="cx"> """
</span><span class="cx"> Get a static child - when registered using L{putChild}.
</span><span class="lines">@@ -253,6 +259,7 @@
</span><span class="cx"> """
</span><span class="cx"> setattr(self, 'child_%s' % (path,), child)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def http_GET(self, request):
</span><span class="cx"> if self.addSlash and request.prepath[-1] != '':
</span><span class="cx"> # If this is a directory-ish resource...
</span><span class="lines">@@ -320,6 +327,7 @@
</span><span class="cx"> self._args = args
</span><span class="cx"> self._kwargs = kwargs
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def renderHTTP(self, request):
</span><span class="cx"> return http.RedirectResponse(
</span><span class="cx"> request.unparseURL(*self._args, **self._kwargs)
</span><span class="lines">@@ -338,6 +346,7 @@
</span><span class="cx"> def __init__(self, resource):
</span><span class="cx"> self.resource = resource
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def hook(self, request):
</span><span class="cx"> """
</span><span class="cx"> Override this method in order to do something before passing control on
</span><span class="lines">@@ -348,18 +357,21 @@
</span><span class="cx"> """
</span><span class="cx"> raise NotImplementedError()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def locateChild(self, request, segments):
</span><span class="cx"> x = self.hook(request)
</span><span class="cx"> if x is not None:
</span><span class="cx"> return x.addCallback(lambda data: (self.resource, segments))
</span><span class="cx"> return self.resource, segments
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def renderHTTP(self, request):
</span><span class="cx"> x = self.hook(request)
</span><span class="cx"> if x is not None:
</span><span class="cx"> return x.addCallback(lambda data: self.resource)
</span><span class="cx"> return self.resource
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getChild(self, name):
</span><span class="cx"> return self.resource.getChild(name)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxweb2responsecodepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/responsecode.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/responsecode.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/responsecode.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -23,57 +23,57 @@
</span><span class="cx"> #
</span><span class="cx"> ##
</span><span class="cx">
</span><del>-CONTINUE = 100
-SWITCHING = 101
</del><ins>+CONTINUE = 100
+SWITCHING = 101
</ins><span class="cx">
</span><del>-OK = 200
-CREATED = 201
-ACCEPTED = 202
-NON_AUTHORITATIVE_INFORMATION = 203
-NO_CONTENT = 204
-RESET_CONTENT = 205
-PARTIAL_CONTENT = 206
-MULTI_STATUS = 207
</del><ins>+OK = 200
+CREATED = 201
+ACCEPTED = 202
+NON_AUTHORITATIVE_INFORMATION = 203
+NO_CONTENT = 204
+RESET_CONTENT = 205
+PARTIAL_CONTENT = 206
+MULTI_STATUS = 207
</ins><span class="cx">
</span><del>-MULTIPLE_CHOICE = 300
-MOVED_PERMANENTLY = 301
-FOUND = 302
-SEE_OTHER = 303
-NOT_MODIFIED = 304
-USE_PROXY = 305
-TEMPORARY_REDIRECT = 307
</del><ins>+MULTIPLE_CHOICE = 300
+MOVED_PERMANENTLY = 301
+FOUND = 302
+SEE_OTHER = 303
+NOT_MODIFIED = 304
+USE_PROXY = 305
+TEMPORARY_REDIRECT = 307
</ins><span class="cx">
</span><del>-BAD_REQUEST = 400
-UNAUTHORIZED = 401
-PAYMENT_REQUIRED = 402
-FORBIDDEN = 403
-NOT_FOUND = 404
-NOT_ALLOWED = 405
-NOT_ACCEPTABLE = 406
-PROXY_AUTH_REQUIRED = 407
-REQUEST_TIMEOUT = 408
-CONFLICT = 409
-GONE = 410
-LENGTH_REQUIRED = 411
-PRECONDITION_FAILED = 412
-REQUEST_ENTITY_TOO_LARGE = 413
-REQUEST_URI_TOO_LONG = 414
-UNSUPPORTED_MEDIA_TYPE = 415
</del><ins>+BAD_REQUEST = 400
+UNAUTHORIZED = 401
+PAYMENT_REQUIRED = 402
+FORBIDDEN = 403
+NOT_FOUND = 404
+NOT_ALLOWED = 405
+NOT_ACCEPTABLE = 406
+PROXY_AUTH_REQUIRED = 407
+REQUEST_TIMEOUT = 408
+CONFLICT = 409
+GONE = 410
+LENGTH_REQUIRED = 411
+PRECONDITION_FAILED = 412
+REQUEST_ENTITY_TOO_LARGE = 413
+REQUEST_URI_TOO_LONG = 414
+UNSUPPORTED_MEDIA_TYPE = 415
</ins><span class="cx"> REQUESTED_RANGE_NOT_SATISFIABLE = 416
</span><del>-EXPECTATION_FAILED = 417
-UNPROCESSABLE_ENTITY = 422 # RFC 2518
-LOCKED = 423 # RFC 2518
-FAILED_DEPENDENCY = 424 # RFC 2518
</del><ins>+EXPECTATION_FAILED = 417
+UNPROCESSABLE_ENTITY = 422 # RFC 2518
+LOCKED = 423 # RFC 2518
+FAILED_DEPENDENCY = 424 # RFC 2518
</ins><span class="cx">
</span><del>-INTERNAL_SERVER_ERROR = 500
-NOT_IMPLEMENTED = 501
-BAD_GATEWAY = 502
-SERVICE_UNAVAILABLE = 503
-GATEWAY_TIMEOUT = 504
-HTTP_VERSION_NOT_SUPPORTED = 505
-LOOP_DETECTED = 506
-INSUFFICIENT_STORAGE_SPACE = 507
-NOT_EXTENDED = 510
</del><ins>+INTERNAL_SERVER_ERROR = 500
+NOT_IMPLEMENTED = 501
+BAD_GATEWAY = 502
+SERVICE_UNAVAILABLE = 503
+GATEWAY_TIMEOUT = 504
+HTTP_VERSION_NOT_SUPPORTED = 505
+LOOP_DETECTED = 506
+INSUFFICIENT_STORAGE_SPACE = 507
+NOT_EXTENDED = 510
</ins><span class="cx">
</span><span class="cx"> RESPONSES = {
</span><span class="cx"> # 100
</span><span class="lines">@@ -133,6 +133,6 @@
</span><span class="cx"> LOOP_DETECTED: "Loop In Linked or Bound Resource",
</span><span class="cx"> INSUFFICIENT_STORAGE_SPACE: "Insufficient Storage Space",
</span><span class="cx"> NOT_EXTENDED: "Not Extended"
</span><del>- }
</del><ins>+}
</ins><span class="cx">
</span><span class="cx"> # No __all__ necessary -- everything is exported
</span></span></pre></div>
<a id="CalendarServertrunktxweb2serverpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/server.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/server.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/server.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -29,7 +29,9 @@
</span><span class="cx"> """
</span><span class="cx"> from __future__ import print_function
</span><span class="cx">
</span><del>-import cgi, time, urlparse
</del><ins>+import cgi
+import time
+import urlparse
</ins><span class="cx"> from urllib import quote, unquote
</span><span class="cx"> from urlparse import urlsplit
</span><span class="cx"> import weakref
</span><span class="lines">@@ -67,12 +69,14 @@
</span><span class="cx"> http.checkPreconditions(request, response)
</span><span class="cx"> return response
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def doTrace(request):
</span><span class="cx"> request = iweb.IRequest(request)
</span><span class="cx"> txt = "%s %s HTTP/%d.%d\r\n" % (request.method, request.uri,
</span><span class="cx"> request.clientproto[0], request.clientproto[1])
</span><span class="cx">
</span><del>- l=[]
</del><ins>+ l = []
</ins><span class="cx"> for name, valuelist in request.headers.getAllRawHeaders():
</span><span class="cx"> for value in valuelist:
</span><span class="cx"> l.append("%s: %s\r\n" % (name, value))
</span><span class="lines">@@ -84,8 +88,9 @@
</span><span class="cx"> txt)
</span><span class="cx">
</span><span class="cx">
</span><del>-def parsePOSTData(request, maxMem=100*1024, maxFields=1024,
- maxSize=10*1024*1024):
</del><ins>+
+def parsePOSTData(request, maxMem=100 * 1024, maxFields=1024,
+ maxSize=10 * 1024 * 1024):
</ins><span class="cx"> """
</span><span class="cx"> Parse data of a POST request.
</span><span class="cx">
</span><span class="lines">@@ -110,22 +115,27 @@
</span><span class="cx"> if ctype is None:
</span><span class="cx"> return defer.succeed(None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def updateArgs(data):
</span><span class="cx"> args = data
</span><span class="cx"> request.args.update(args)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def updateArgsAndFiles(data):
</span><span class="cx"> args, files = data
</span><span class="cx"> request.args.update(args)
</span><span class="cx"> request.files.update(files)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def error(f):
</span><span class="cx"> f.trap(fileupload.MimeFormatError)
</span><span class="cx"> raise http.HTTPError(
</span><span class="cx"> http.StatusResponse(responsecode.BAD_REQUEST, str(f.value)))
</span><span class="cx">
</span><del>- if (ctype.mediaType == 'application'
- and ctype.mediaSubtype == 'x-www-form-urlencoded'):
</del><ins>+ if (
+ ctype.mediaType == 'application'
+ and ctype.mediaSubtype == 'x-www-form-urlencoded'
+ ):
</ins><span class="cx"> d = fileupload.parse_urlencoded(request.stream)
</span><span class="cx"> d.addCallbacks(updateArgs, error)
</span><span class="cx"> return d
</span><span class="lines">@@ -134,9 +144,9 @@
</span><span class="cx"> boundary = ctype.params.get('boundary')
</span><span class="cx"> if boundary is None:
</span><span class="cx"> return defer.fail(http.HTTPError(
</span><del>- http.StatusResponse(
- responsecode.BAD_REQUEST,
- "Boundary not specified in Content-Type.")))
</del><ins>+ http.StatusResponse(
+ responsecode.BAD_REQUEST,
+ "Boundary not specified in Content-Type.")))
</ins><span class="cx"> d = fileupload.parseMultipartFormData(request.stream, boundary,
</span><span class="cx"> maxMem, maxFields, maxSize)
</span><span class="cx"> d.addCallbacks(updateArgsAndFiles, error)
</span><span class="lines">@@ -149,6 +159,7 @@
</span><span class="cx"> ctype.mediaType, ctype.mediaSubtype))))
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class StopTraversal(object):
</span><span class="cx"> """
</span><span class="cx"> Indicates to Request._handleSegment that it should stop handling
</span><span class="lines">@@ -157,6 +168,7 @@
</span><span class="cx"> pass
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class Request(http.Request):
</span><span class="cx"> """
</span><span class="cx"> vars:
</span><span class="lines">@@ -195,10 +207,10 @@
</span><span class="cx">
</span><span class="cx"> self.timeStamps = [("t", time.time(),)]
</span><span class="cx">
</span><del>- if kw.has_key('site'):
</del><ins>+ if 'site' in kw:
</ins><span class="cx"> self.site = kw['site']
</span><span class="cx"> del kw['site']
</span><del>- if kw.has_key('prepathuri'):
</del><ins>+ if 'prepathuri' in kw:
</ins><span class="cx"> self._initialprepath = kw['prepathuri']
</span><span class="cx"> del kw['prepathuri']
</span><span class="cx">
</span><span class="lines">@@ -215,9 +227,11 @@
</span><span class="cx"> except AttributeError:
</span><span class="cx"> self.serverInstance = "Unknown"
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def timeStamp(self, tag):
</span><span class="cx"> self.timeStamps.append((tag, time.time(),))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def addResponseFilter(self, filter, atEnd=False, onlyOnce=False):
</span><span class="cx"> """
</span><span class="cx"> Add a response filter to this request.
</span><span class="lines">@@ -236,6 +250,7 @@
</span><span class="cx"> else:
</span><span class="cx"> self.responseFilters.insert(0, filter)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def unparseURL(self, scheme=None, host=None, port=None,
</span><span class="cx"> path=None, params=None, querystring=None, fragment=None):
</span><span class="cx"> """Turn the request path into a url string. For any pieces of
</span><span class="lines">@@ -243,13 +258,20 @@
</span><span class="cx"> request. The arguments have the same meaning as the same named
</span><span class="cx"> attributes of Request."""
</span><span class="cx">
</span><del>- if scheme is None: scheme = self.scheme
- if host is None: host = self.host
- if port is None: port = self.port
- if path is None: path = self.path
- if params is None: params = self.params
- if querystring is None: querystring = self.querystring
- if fragment is None: fragment = ''
</del><ins>+ if scheme is None:
+ scheme = self.scheme
+ if host is None:
+ host = self.host
+ if port is None:
+ port = self.port
+ if path is None:
+ path = self.path
+ if params is None:
+ params = self.params
+ if querystring is None:
+ querystring = self.querystring
+ if fragment is None:
+ fragment = ''
</ins><span class="cx">
</span><span class="cx"> if port == http.defaultPortForScheme.get(scheme, 0):
</span><span class="cx"> hostport = host
</span><span class="lines">@@ -260,6 +282,7 @@
</span><span class="cx"> scheme, hostport, path,
</span><span class="cx"> params, querystring, fragment))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _parseURL(self):
</span><span class="cx"> if self.uri[0] == '/':
</span><span class="cx"> # Can't use urlparse for request_uri because urlparse
</span><span class="lines">@@ -275,7 +298,7 @@
</span><span class="cx"> else:
</span><span class="cx"> # It is an absolute uri, use standard urlparse
</span><span class="cx"> (self.scheme, self.host, self.path,
</span><del>- self.params, self.querystring, fragment) = urlparse.urlparse(self.uri)
</del><ins>+ self.params, self.querystring, _ignore_fragment) = urlparse.urlparse(self.uri)
</ins><span class="cx">
</span><span class="cx"> if self.querystring:
</span><span class="cx"> self.args = cgi.parse_qs(self.querystring, True)
</span><span class="lines">@@ -298,8 +321,9 @@
</span><span class="cx"> else:
</span><span class="cx"> self.prepath = []
</span><span class="cx"> self.postpath = path
</span><del>- #print("_parseURL", self.uri, (self.uri, self.scheme, self.host, self.path, self.params, self.querystring))
</del><ins>+ # print("_parseURL", self.uri, (self.uri, self.scheme, self.host, self.path, self.params, self.querystring))
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def _schemeFromPort(self, port):
</span><span class="cx"> """
</span><span class="cx"> Try to determine the scheme matching the supplied server port. This is needed in case
</span><span class="lines">@@ -316,7 +340,7 @@
</span><span class="cx"> @rtype: C{bool}
</span><span class="cx"> """
</span><span class="cx">
</span><del>- #from twistedcaldav.config import config
</del><ins>+ # from twistedcaldav.config import config
</ins><span class="cx"> if hasattr(self.site, "EnableSSL") and self.site.EnableSSL:
</span><span class="cx"> if port == self.site.SSLPort:
</span><span class="cx"> return True
</span><span class="lines">@@ -325,6 +349,7 @@
</span><span class="cx">
</span><span class="cx"> return False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _fixupURLParts(self):
</span><span class="cx"> hostaddr, secure = self.chanRequest.getHostInfo()
</span><span class="cx"> if not self.scheme:
</span><span class="lines">@@ -343,7 +368,7 @@
</span><span class="cx"> # When no hostname specified anywhere, either raise an
</span><span class="cx"> # error, or use the interface hostname, depending on
</span><span class="cx"> # protocol version
</span><del>- if self.clientproto >= (1,1):
</del><ins>+ if self.clientproto >= (1, 1):
</ins><span class="cx"> raise http.HTTPError(responsecode.BAD_REQUEST)
</span><span class="cx"> self.host = hostaddr.host
</span><span class="cx"> self.port = hostaddr.port
</span><span class="lines">@@ -380,10 +405,12 @@
</span><span class="cx"> d.callback(None)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _processTimeStamp(self, res):
</span><span class="cx"> self.timeStamp("t-req-proc")
</span><span class="cx"> return res
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def preprocessRequest(self):
</span><span class="cx"> """Do any request processing that doesn't follow the normal
</span><span class="cx"> resource lookup procedure. "OPTIONS *" is handled here, for
</span><span class="lines">@@ -399,7 +426,7 @@
</span><span class="cx"> # Allow other methods to tunnel through using POST and a request header.
</span><span class="cx"> # See http://code.google.com/apis/gdata/docs/2.0/basics.html
</span><span class="cx"> if self.headers.hasHeader("X-HTTP-Method-Override"):
</span><del>- intendedMethod = self.headers.getRawHeaders("X-HTTP-Method-Override")[0];
</del><ins>+ intendedMethod = self.headers.getRawHeaders("X-HTTP-Method-Override")[0]
</ins><span class="cx"> if intendedMethod:
</span><span class="cx"> self.originalMethod = self.method
</span><span class="cx"> self.method = intendedMethod
</span><span class="lines">@@ -407,6 +434,7 @@
</span><span class="cx"> # This is where CONNECT would go if we wanted it
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _getChild(self, _, res, path, updatepaths=True):
</span><span class="cx"> """Call res.locateChild, and pass the result on to _handleSegment."""
</span><span class="cx">
</span><span class="lines">@@ -421,6 +449,7 @@
</span><span class="cx"> else:
</span><span class="cx"> return self._handleSegment(result, res, path, updatepaths)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _handleSegment(self, result, res, path, updatepaths):
</span><span class="cx"> """Handle the result of a locateChild call done in _getChild."""
</span><span class="cx">
</span><span class="lines">@@ -435,7 +464,7 @@
</span><span class="cx"> return newres.addCallback(
</span><span class="cx"> lambda actualRes: self._handleSegment(
</span><span class="cx"> (actualRes, newpath), res, path, updatepaths)
</span><del>- )
</del><ins>+ )
</ins><span class="cx">
</span><span class="cx"> if path:
</span><span class="cx"> url = quote("/" + "/".join(path))
</span><span class="lines">@@ -444,19 +473,19 @@
</span><span class="cx">
</span><span class="cx"> if newpath is StopTraversal:
</span><span class="cx"> # We need to rethink how to do this.
</span><del>- #if newres is res:
</del><ins>+ # if newres is res:
</ins><span class="cx"> return res
</span><del>- #else:
</del><ins>+ # else:
</ins><span class="cx"> # raise ValueError("locateChild must not return StopTraversal with a resource other than self.")
</span><span class="cx">
</span><span class="cx"> newres = iweb.IResource(newres)
</span><span class="cx"> if newres is res:
</span><del>- assert not newpath is path, "URL traversal cycle detected when attempting to locateChild %r from resource %r." % (path, res)
</del><ins>+ assert newpath is not path, "URL traversal cycle detected when attempting to locateChild %r from resource %r." % (path, res)
</ins><span class="cx"> assert len(newpath) < len(path), "Infinite loop impending..."
</span><span class="cx">
</span><span class="cx"> if updatepaths:
</span><span class="cx"> # We found a Resource... update the request.prepath and postpath
</span><del>- for x in xrange(len(path) - len(newpath)):
</del><ins>+ for _ in xrange(len(path) - len(newpath)):
</ins><span class="cx"> self.prepath.append(self.postpath.pop(0))
</span><span class="cx"> url = quote("/" + "/".join(self.prepath) + ("/" if self.prepath and self.prepath[-1] else ""))
</span><span class="cx"> self._rememberResource(newres, url)
</span><span class="lines">@@ -474,6 +503,7 @@
</span><span class="cx">
</span><span class="cx"> _urlsByResource = weakref.WeakKeyDictionary()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _rememberResource(self, resource, url):
</span><span class="cx"> """
</span><span class="cx"> Remember the URL of a visited resource.
</span><span class="lines">@@ -482,6 +512,7 @@
</span><span class="cx"> self._urlsByResource[resource] = url
</span><span class="cx"> return resource
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _forgetResource(self, resource, url):
</span><span class="cx"> """
</span><span class="cx"> Remember the URL of a visited resource.
</span><span class="lines">@@ -489,6 +520,7 @@
</span><span class="cx"> del self._resourcesByURL[url]
</span><span class="cx"> del self._urlsByResource[resource]
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def urlForResource(self, resource):
</span><span class="cx"> """
</span><span class="cx"> Looks up the URL of the given resource if this resource was found while
</span><span class="lines">@@ -512,6 +544,7 @@
</span><span class="cx"> raise NoURLForResourceError(resource)
</span><span class="cx"> return url
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def locateResource(self, url):
</span><span class="cx"> """
</span><span class="cx"> Looks up the resource with the given URL.
</span><span class="lines">@@ -531,7 +564,7 @@
</span><span class="cx"> #
</span><span class="cx"> # Parse the URL
</span><span class="cx"> #
</span><del>- (scheme, host, path, query, fragment) = urlsplit(url)
</del><ins>+ (_ignore_scheme, _ignore_host, path, query, fragment) = urlsplit(url)
</ins><span class="cx">
</span><span class="cx"> if query or fragment:
</span><span class="cx"> raise http.HTTPError(http.StatusResponse(
</span><span class="lines">@@ -574,6 +607,7 @@
</span><span class="cx"> d.addErrback(notFound)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def locateChildResource(self, parent, childName):
</span><span class="cx"> """
</span><span class="cx"> Looks up the child resource with the given name given the parent
</span><span class="lines">@@ -612,6 +646,7 @@
</span><span class="cx"> d.addErrback(notFound)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _processingFailed(self, reason):
</span><span class="cx"> if reason.check(http.HTTPError) is not None:
</span><span class="cx"> # If the exception was an HTTPError, leave it alone
</span><span class="lines">@@ -647,7 +682,7 @@
</span><span class="cx"> )
</span><span class="cx"> response = http.Response(
</span><span class="cx"> responsecode.INTERNAL_SERVER_ERROR,
</span><del>- {'content-type': http_headers.MimeType('text','html')},
</del><ins>+ {'content-type': http_headers.MimeType('text', 'html')},
</ins><span class="cx"> body
</span><span class="cx"> )
</span><span class="cx"> self.writeResponse(response)
</span><span class="lines">@@ -664,8 +699,10 @@
</span><span class="cx">
</span><span class="cx"> def _cbFinishRender(self, result):
</span><span class="cx"> def filterit(response, f):
</span><del>- if (hasattr(f, 'handleErrors') or
- (response.code >= 200 and response.code < 300)):
</del><ins>+ if (
+ hasattr(f, 'handleErrors') or
+ (response.code >= 200 and response.code < 300)
+ ):
</ins><span class="cx"> return f(self, response)
</span><span class="cx"> else:
</span><span class="cx"> return response
</span><span class="lines">@@ -688,6 +725,7 @@
</span><span class="cx">
</span><span class="cx"> raise TypeError("html is not a resource or a response")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def renderHTTP_exception(self, req, reason):
</span><span class="cx"> log.failure("Exception rendering request: {request}", reason, request=req)
</span><span class="cx">
</span><span class="lines">@@ -696,19 +734,23 @@
</span><span class="cx">
</span><span class="cx"> return http.Response(
</span><span class="cx"> responsecode.INTERNAL_SERVER_ERROR,
</span><del>- {'content-type': http_headers.MimeType('text','html')},
</del><ins>+ {'content-type': http_headers.MimeType('text', 'html')},
</ins><span class="cx"> body)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class Site(object):
</span><span class="cx"> def __init__(self, resource):
</span><span class="cx"> """Initialize.
</span><span class="cx"> """
</span><span class="cx"> self.resource = iweb.IResource(resource)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __call__(self, *args, **kwargs):
</span><span class="cx"> return Request(site=self, *args, **kwargs)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class NoURLForResourceError(RuntimeError):
</span><span class="cx"> def __init__(self, resource):
</span><span class="cx"> RuntimeError.__init__(self, "Resource %r has no URL in this request." % (resource,))
</span></span></pre></div>
<a id="CalendarServertrunktxweb2staticpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/static.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/static.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/static.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -29,8 +29,9 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # System Imports
</span><del>-import os, time
</del><ins>+import os
</ins><span class="cx"> import tempfile
</span><ins>+import time
</ins><span class="cx">
</span><span class="cx"> # Sibling Imports
</span><span class="cx"> from txweb2 import http_headers, resource
</span><span class="lines">@@ -53,48 +54,57 @@
</span><span class="cx"> """
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def lastModified(self):
</span><span class="cx"> """
</span><span class="cx"> @return: The last modified time of the resource if available, None otherwise.
</span><span class="cx"> """
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def creationDate(self):
</span><span class="cx"> """
</span><span class="cx"> @return: The creation date of the resource if available, None otherwise.
</span><span class="cx"> """
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def contentLength(self):
</span><span class="cx"> """
</span><span class="cx"> @return: The size in bytes of the resource if available, None otherwise.
</span><span class="cx"> """
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def contentType(self):
</span><span class="cx"> """
</span><span class="cx"> @return: The MIME type of the resource if available, None otherwise.
</span><span class="cx"> """
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def contentEncoding(self):
</span><span class="cx"> """
</span><span class="cx"> @return: The encoding of the resource if available, None otherwise.
</span><span class="cx"> """
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def displayName(self):
</span><span class="cx"> """
</span><span class="cx"> @return: The display name of the resource if available, None otherwise.
</span><span class="cx"> """
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def exists(self):
</span><span class="cx"> """
</span><span class="cx"> @return: True if the resource exists on the server, False otherwise.
</span><span class="cx"> """
</span><span class="cx"> return True
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class StaticRenderMixin(resource.RenderMixin, MetaDataMixin):
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -105,9 +115,9 @@
</span><span class="cx"> etag = (yield self.etag())
</span><span class="cx"> http.checkPreconditions(
</span><span class="cx"> request,
</span><del>- entityExists = self.exists(),
- etag = etag,
- lastModified = self.lastModified(),
</del><ins>+ entityExists=self.exists(),
+ etag=etag,
+ lastModified=self.lastModified(),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> # Check per-method preconditions
</span><span class="lines">@@ -155,23 +165,30 @@
</span><span class="cx"> self.type = http_headers.MimeType.fromString(type)
</span><span class="cx"> self.created_time = time.time()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def etag(self):
</span><span class="cx"> lastModified = self.lastModified()
</span><del>- return succeed(http_headers.ETag("%X-%X" % (lastModified, hash(self.data)),
- weak=(time.time() - lastModified <= 1)))
</del><ins>+ return succeed(http_headers.ETag(
+ "%X-%X" % (lastModified, hash(self.data)),
+ weak=(time.time() - lastModified <= 1)))
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def lastModified(self):
</span><span class="cx"> return self.creationDate()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def creationDate(self):
</span><span class="cx"> return self.created_time
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def contentLength(self):
</span><span class="cx"> return len(self.data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def contentType(self):
</span><span class="cx"> return self.type
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def render(self, req):
</span><span class="cx"> return http.Response(
</span><span class="cx"> responsecode.OK,
</span><span class="lines">@@ -179,6 +196,7 @@
</span><span class="cx"> stream=self.data)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class File(StaticRenderMixin):
</span><span class="cx"> """
</span><span class="cx"> File is a resource that represents a plain non-interpreted file
</span><span class="lines">@@ -207,7 +225,7 @@
</span><span class="cx"> contentEncodings = {
</span><span class="cx"> ".gz" : "gzip",
</span><span class="cx"> ".bz2": "bzip2"
</span><del>- }
</del><ins>+ }
</ins><span class="cx">
</span><span class="cx"> processors = {}
</span><span class="cx">
</span><span class="lines">@@ -233,23 +251,27 @@
</span><span class="cx"> self.processors = dict([
</span><span class="cx"> (key.lower(), value)
</span><span class="cx"> for key, value in processors.items()
</span><del>- ])
</del><ins>+ ])
</ins><span class="cx">
</span><span class="cx"> if indexNames is not None:
</span><span class="cx"> self.indexNames = indexNames
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def comparePath(self, path):
</span><del>-
</del><ins>+
</ins><span class="cx"> if isinstance(path, FilePath):
</span><span class="cx"> return path.path == self.fp.path
</span><span class="cx"> else:
</span><span class="cx"> return path == self.fp.path
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def exists(self):
</span><span class="cx"> return self.fp.exists()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def etag(self):
</span><del>- if not self.fp.exists(): return succeed(None)
</del><ins>+ if not self.fp.exists():
+ return succeed(None)
</ins><span class="cx">
</span><span class="cx"> st = self.fp.statinfo
</span><span class="cx">
</span><span class="lines">@@ -265,18 +287,21 @@
</span><span class="cx"> weak=weak
</span><span class="cx"> ))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def lastModified(self):
</span><span class="cx"> if self.fp.exists():
</span><span class="cx"> return self.fp.getmtime()
</span><span class="cx"> else:
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def creationDate(self):
</span><span class="cx"> if self.fp.exists():
</span><span class="cx"> return self.fp.getmtime()
</span><span class="cx"> else:
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def contentLength(self):
</span><span class="cx"> if self.fp.exists():
</span><span class="cx"> if self.fp.isfile():
</span><span class="lines">@@ -288,6 +313,7 @@
</span><span class="cx"> else:
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _initTypeAndEncoding(self):
</span><span class="cx"> self._type, self._encoding = getTypeAndEncoding(
</span><span class="cx"> self.fp.basename(),
</span><span class="lines">@@ -297,24 +323,29 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> # Handle cases not covered by getTypeAndEncoding()
</span><del>- if self.fp.isdir(): self._type = "httpd/unix-directory"
</del><ins>+ if self.fp.isdir():
+ self._type = "httpd/unix-directory"
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def contentType(self):
</span><span class="cx"> if not hasattr(self, "_type"):
</span><span class="cx"> self._initTypeAndEncoding()
</span><span class="cx"> return http_headers.MimeType.fromString(self._type)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def contentEncoding(self):
</span><span class="cx"> if not hasattr(self, "_encoding"):
</span><span class="cx"> self._initTypeAndEncoding()
</span><span class="cx"> return self._encoding
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def displayName(self):
</span><span class="cx"> if self.fp.exists():
</span><span class="cx"> return self.fp.basename()
</span><span class="cx"> else:
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def ignoreExt(self, ext):
</span><span class="cx"> """Ignore the given extension.
</span><span class="cx">
</span><span class="lines">@@ -331,6 +362,7 @@
</span><span class="cx"> """
</span><span class="cx"> self.putChildren[name] = child
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getChild(self, name):
</span><span class="cx"> """
</span><span class="cx"> Look up a child resource.
</span><span class="lines">@@ -340,7 +372,8 @@
</span><span class="cx"> return self
</span><span class="cx">
</span><span class="cx"> child = self.putChildren.get(name, None)
</span><del>- if child: return child
</del><ins>+ if child:
+ return child
</ins><span class="cx">
</span><span class="cx"> child_fp = self.fp.child(name)
</span><span class="cx"> if hasattr(self, "knownChildren"):
</span><span class="lines">@@ -351,6 +384,7 @@
</span><span class="cx"> else:
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def listChildren(self):
</span><span class="cx"> """
</span><span class="cx"> @return: a sequence of the names of all known children of this resource.
</span><span class="lines">@@ -361,19 +395,22 @@
</span><span class="cx"> self.knownChildren = set(children)
</span><span class="cx"> return children
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def locateChild(self, req, segments):
</span><span class="cx"> """
</span><span class="cx"> See L{IResource}C{.locateChild}.
</span><span class="cx"> """
</span><span class="cx"> # If getChild() finds a child resource, return it
</span><span class="cx"> child = self.getChild(segments[0])
</span><del>- if child is not None: return (child, segments[1:])
</del><ins>+ if child is not None:
+ return (child, segments[1:])
</ins><span class="cx">
</span><span class="cx"> # If we're not backed by a directory, we have no children.
</span><span class="cx"> # But check for existance first; we might be a collection resource
</span><span class="cx"> # that the request wants created.
</span><span class="cx"> self.fp.restat(False)
</span><del>- if self.fp.exists() and not self.fp.isdir(): return (None, ())
</del><ins>+ if self.fp.exists() and not self.fp.isdir():
+ return (None, ())
</ins><span class="cx">
</span><span class="cx"> # OK, we need to return a child corresponding to the first segment
</span><span class="cx"> path = segments[0]
</span><span class="lines">@@ -400,10 +437,12 @@
</span><span class="cx">
</span><span class="cx"> return self.createSimilarFile(fpath.path), segments[1:]
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def renderHTTP(self, req):
</span><span class="cx"> self.fp.changed()
</span><span class="cx"> return super(File, self).renderHTTP(req)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def render(self, req):
</span><span class="cx"> """You know what you doing."""
</span><span class="cx"> if not self.fp.exists():
</span><span class="lines">@@ -412,7 +451,7 @@
</span><span class="cx"> if self.fp.isdir():
</span><span class="cx"> if req.path[-1] != "/":
</span><span class="cx"> # Redirect to include trailing '/' in URI
</span><del>- return http.RedirectResponse(req.unparseURL(path=req.path+'/'))
</del><ins>+ return http.RedirectResponse(req.unparseURL(path=req.path + '/'))
</ins><span class="cx"> else:
</span><span class="cx"> ifp = self.fp.childSearchPreauth(*self.indexNames)
</span><span class="cx"> if ifp:
</span><span class="lines">@@ -450,11 +489,13 @@
</span><span class="cx">
</span><span class="cx"> return response
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def createSimilarFile(self, path):
</span><span class="cx"> return self.__class__(path, self.defaultType, self.ignoredExts,
</span><span class="cx"> self.processors, self.indexNames[:])
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class FileSaver(resource.PostableResource):
</span><span class="cx"> allowedTypes = (http_headers.MimeType('text', 'plain'),
</span><span class="cx"> http_headers.MimeType('text', 'html'),
</span><span class="lines">@@ -467,6 +508,7 @@
</span><span class="cx"> self.expectedFields = expectedFields
</span><span class="cx"> self.permissions = permissions
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def makeUniqueName(self, filename):
</span><span class="cx"> """Called when a unique filename is needed.
</span><span class="cx">
</span><span class="lines">@@ -478,6 +520,7 @@
</span><span class="cx">
</span><span class="cx"> return tempfile.mktemp(suffix=os.path.splitext(filename)[1], dir=self.destination)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def isSafeToWrite(self, filename, mimetype, filestream):
</span><span class="cx"> """Returns True if it's "safe" to write this file,
</span><span class="cx"> otherwise it raises an exception.
</span><span class="lines">@@ -493,6 +536,7 @@
</span><span class="cx">
</span><span class="cx"> return True
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def writeFile(self, filename, mimetype, fileobject):
</span><span class="cx"> """Does the I/O dirty work after it calls isSafeToWrite to make
</span><span class="cx"> sure it's safe to write this file.
</span><span class="lines">@@ -505,11 +549,12 @@
</span><span class="cx"> flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_BINARY", 0)
</span><span class="cx">
</span><span class="cx"> fileobject = os.fdopen(os.open(outname, flags, self.permissions), 'wb', 0)
</span><del>-
</del><ins>+
</ins><span class="cx"> stream.readIntoFile(filestream, fileobject)
</span><span class="cx">
</span><span class="cx"> return outname
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def render(self, req):
</span><span class="cx"> content = ["<html><body>"]
</span><span class="cx">
</span><span class="lines">@@ -560,12 +605,16 @@
</span><span class="cx"> def isDangerous(path):
</span><span class="cx"> return path == '..' or '/' in path or os.sep in path
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def addSlash(request):
</span><span class="cx"> return "http%s://%s%s/" % (
</span><span class="cx"> request.isSecure() and 's' or '',
</span><span class="cx"> request.getHeader("host"),
</span><span class="cx"> (request.uri.split('?')[0]))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def loadMimeTypes(mimetype_locations=['/etc/mime.types']):
</span><span class="cx"> """
</span><span class="cx"> Multiple file locations containing mime-types can be passed as a list.
</span><span class="lines">@@ -580,18 +629,18 @@
</span><span class="cx"> # usual suspects.
</span><span class="cx"> contentTypes.update(
</span><span class="cx"> {
</span><del>- '.conf': 'text/plain',
- '.diff': 'text/plain',
- '.exe': 'application/x-executable',
- '.flac': 'audio/x-flac',
- '.java': 'text/plain',
- '.ogg': 'application/ogg',
- '.oz': 'text/x-oz',
- '.swf': 'application/x-shockwave-flash',
- '.tgz': 'application/x-gtar',
- '.wml': 'text/vnd.wap.wml',
- '.xul': 'application/vnd.mozilla.xul+xml',
- '.py': 'text/plain',
</del><ins>+ '.conf': 'text/plain',
+ '.diff': 'text/plain',
+ '.exe': 'application/x-executable',
+ '.flac': 'audio/x-flac',
+ '.java': 'text/plain',
+ '.ogg': 'application/ogg',
+ '.oz': 'text/x-oz',
+ '.swf': 'application/x-shockwave-flash',
+ '.tgz': 'application/x-gtar',
+ '.wml': 'text/vnd.wap.wml',
+ '.xul': 'application/vnd.mozilla.xul+xml',
+ '.py': 'text/plain',
</ins><span class="cx"> '.patch': 'text/plain',
</span><span class="cx"> }
</span><span class="cx"> )
</span><span class="lines">@@ -603,10 +652,12 @@
</span><span class="cx">
</span><span class="cx"> return contentTypes
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def getTypeAndEncoding(filename, types, encodings, defaultType):
</span><span class="cx"> p, ext = os.path.splitext(filename)
</span><span class="cx"> ext = ext.lower()
</span><del>- if encodings.has_key(ext):
</del><ins>+ if ext in encodings:
</ins><span class="cx"> enc = encodings[ext]
</span><span class="cx"> ext = os.path.splitext(p)[1].lower()
</span><span class="cx"> else:
</span></span></pre></div>
<a id="CalendarServertrunktxweb2streampy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/stream.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/stream.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/stream.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -36,12 +36,12 @@
</span><span class="cx"> underlying resources and causes read to return None forevermore.
</span><span class="cx">
</span><span class="cx"> IByteStream adds a bit more to the API:
</span><del>-1) read is required to return objects conforming to the buffer interface.
</del><ins>+1) read is required to return objects conforming to the buffer interface.
</ins><span class="cx"> 2) .length, which may either an integer number of bytes remaining, or
</span><span class="cx"> None if unknown
</span><span class="cx"> 3) .split(position). Split takes a position, and splits the
</span><span class="cx"> stream in two pieces, returning the two new streams. Using the
</span><del>-original stream after calling split is not allowed.
</del><ins>+original stream after calling split is not allowed.
</ins><span class="cx">
</span><span class="cx"> There are two builtin source stream classes: FileStream and
</span><span class="cx"> MemoryStream. The first produces data from a file object, the second
</span><span class="lines">@@ -55,7 +55,10 @@
</span><span class="cx">
</span><span class="cx"> from __future__ import generators
</span><span class="cx">
</span><del>-import copy, os, types, sys
</del><ins>+import copy
+import os
+import types
+import sys
</ins><span class="cx"> from zope.interface import Interface, Attribute, implements
</span><span class="cx"> from twisted.internet.defer import Deferred
</span><span class="cx"> from twisted.internet import interfaces as ti_interfaces, defer, reactor, protocol, error as ti_error
</span><span class="lines">@@ -70,21 +73,23 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> # Python 2.4.2 (only) has a broken mmap that leaks a fd every time you call it.
</span><del>-if sys.version_info[0:3] != (2,4,2):
</del><ins>+if sys.version_info[0:3] != (2, 4, 2):
</ins><span class="cx"> try:
</span><span class="cx"> import mmap
</span><span class="cx"> except ImportError:
</span><span class="cx"> mmap = None
</span><span class="cx"> else:
</span><span class="cx"> mmap = None
</span><del>-
-##############################
-#### Interfaces ####
-##############################
</del><span class="cx">
</span><ins>+
+
+#
+# Interfaces
+#
+
</ins><span class="cx"> class IStream(Interface):
</span><span class="cx"> """A stream of arbitrary data."""
</span><del>-
</del><ins>+
</ins><span class="cx"> def read():
</span><span class="cx"> """Read some data.
</span><span class="cx">
</span><span class="lines">@@ -94,19 +99,21 @@
</span><span class="cx">
</span><span class="cx"> Errors may be indicated by exception or by a Deferred of a Failure.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> def close():
</span><span class="cx"> """Prematurely close. Should also cause further reads to
</span><span class="cx"> return None."""
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class IByteStream(IStream):
</span><span class="cx"> """A stream which is of bytes."""
</span><del>-
</del><ins>+
</ins><span class="cx"> length = Attribute("""How much data is in this stream. Can be None if unknown.""")
</span><del>-
</del><ins>+
</ins><span class="cx"> def read():
</span><span class="cx"> """Read some data.
</span><del>-
</del><ins>+
</ins><span class="cx"> Returns an object conforming to the buffer interface, or
</span><span class="cx"> if there is no more data available, returns None.
</span><span class="cx"> Can also return a Deferred resulting in one of the above.
</span><span class="lines">@@ -130,6 +137,8 @@
</span><span class="cx"> return None. Additionally, .length should be set to 0.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class ISendfileableStream(Interface):
</span><span class="cx"> def read(sendfile=False):
</span><span class="cx"> """
</span><span class="lines">@@ -139,21 +148,25 @@
</span><span class="cx">
</span><span class="cx"> If sendfile == True, returns either the above, or a SendfileBuffer.
</span><span class="cx"> """
</span><del>-
</del><ins>+
+
+
</ins><span class="cx"> class SimpleStream(object):
</span><span class="cx"> """Superclass of simple streams with a single buffer and a offset and length
</span><span class="cx"> into that buffer."""
</span><span class="cx"> implements(IByteStream)
</span><del>-
</del><ins>+
</ins><span class="cx"> length = None
</span><span class="cx"> start = None
</span><del>-
</del><ins>+
</ins><span class="cx"> def read(self):
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def close(self):
</span><span class="cx"> self.length = 0
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def split(self, point):
</span><span class="cx"> if self.length is not None:
</span><span class="cx"> if point > self.length:
</span><span class="lines">@@ -165,14 +178,14 @@
</span><span class="cx"> b.start += point
</span><span class="cx"> return (self, b)
</span><span class="cx">
</span><del>-##############################
-#### FileStream ####
-##############################
-
</del><ins>+#
+# FileStream
+#
+
</ins><span class="cx"> # maximum mmap size
</span><del>-MMAP_LIMIT = 4*1024*1024
</del><ins>+MMAP_LIMIT = 4 * 1024 * 1024
</ins><span class="cx"> # minimum mmap size
</span><del>-MMAP_THRESHOLD = 8*1024
</del><ins>+MMAP_THRESHOLD = 8 * 1024
</ins><span class="cx">
</span><span class="cx"> # maximum sendfile length
</span><span class="cx"> SENDFILE_LIMIT = 16777216
</span><span class="lines">@@ -184,7 +197,7 @@
</span><span class="cx"> Python's mmap call sucks and ommitted the "offset" argument for no
</span><span class="cx"> discernable reason. Replace this with a mmap module that has offset.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> offset = kwargs.get('offset', None)
</span><span class="cx"> if offset in [None, 0]:
</span><span class="cx"> if 'offset' in kwargs:
</span><span class="lines">@@ -193,6 +206,8 @@
</span><span class="cx"> raise mmap.error("mmap: Python sucks and does not support offset.")
</span><span class="cx"> return mmap.mmap(*args, **kwargs)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class FileStream(SimpleStream):
</span><span class="cx"> implements(ISendfileableStream)
</span><span class="cx"> """A stream that reads data from a file. File must be a normal
</span><span class="lines">@@ -213,7 +228,8 @@
</span><span class="cx"> else:
</span><span class="cx"> self.length = length
</span><span class="cx"> self.useMMap = useMMap
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def read(self, sendfile=False):
</span><span class="cx"> if self.f is None:
</span><span class="cx"> return None
</span><span class="lines">@@ -223,7 +239,7 @@
</span><span class="cx"> self.f = None
</span><span class="cx"> return None
</span><span class="cx">
</span><del>- #if sendfile and length > SENDFILE_THRESHOLD:
</del><ins>+ # if sendfile and length > SENDFILE_THRESHOLD:
</ins><span class="cx"> # # XXX: Yay using non-existent sendfile support!
</span><span class="cx"> # # FIXME: if we return a SendfileBuffer, and then sendfile
</span><span class="cx"> # # fails, then what? Or, what if file is too short?
</span><span class="lines">@@ -238,7 +254,7 @@
</span><span class="cx"> try:
</span><span class="cx"> res = mmapwrapper(self.f.fileno(), readSize,
</span><span class="cx"> access=mmap.ACCESS_READ, offset=self.start)
</span><del>- #madvise(res, MADV_SEQUENTIAL)
</del><ins>+ # madvise(res, MADV_SEQUENTIAL)
</ins><span class="cx"> self.length -= readSize
</span><span class="cx"> self.start += readSize
</span><span class="cx"> return res
</span><span class="lines">@@ -258,15 +274,16 @@
</span><span class="cx"> self.start += bytesRead
</span><span class="cx"> return b
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def close(self):
</span><span class="cx"> self.f = None
</span><span class="cx"> SimpleStream.close(self)
</span><span class="cx">
</span><span class="cx"> components.registerAdapter(FileStream, file, IByteStream)
</span><span class="cx">
</span><del>-##############################
-#### MemoryStream ####
-##############################
</del><ins>+#
+# MemoryStream
+#
</ins><span class="cx">
</span><span class="cx"> class MemoryStream(SimpleStream):
</span><span class="cx"> """A stream that reads data from a buffer object."""
</span><span class="lines">@@ -284,6 +301,7 @@
</span><span class="cx"> raise ValueError("len(mem) < start + length")
</span><span class="cx"> self.length = length
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def read(self):
</span><span class="cx"> if self.mem is None:
</span><span class="cx"> return None
</span><span class="lines">@@ -295,6 +313,7 @@
</span><span class="cx"> self.length = 0
</span><span class="cx"> return result
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def close(self):
</span><span class="cx"> self.mem = None
</span><span class="cx"> SimpleStream.close(self)
</span><span class="lines">@@ -302,23 +321,24 @@
</span><span class="cx"> components.registerAdapter(MemoryStream, str, IByteStream)
</span><span class="cx"> components.registerAdapter(MemoryStream, types.BufferType, IByteStream)
</span><span class="cx">
</span><del>-##############################
-#### CompoundStream ####
-##############################
</del><ins>+#
+# CompoundStream
+#
</ins><span class="cx">
</span><span class="cx"> class CompoundStream(object):
</span><span class="cx"> """A stream which is composed of many other streams.
</span><span class="cx">
</span><span class="cx"> Call addStream to add substreams.
</span><span class="cx"> """
</span><del>-
</del><ins>+
</ins><span class="cx"> implements(IByteStream, ISendfileableStream)
</span><span class="cx"> deferred = None
</span><span class="cx"> length = 0
</span><del>-
</del><ins>+
</ins><span class="cx"> def __init__(self, buckets=()):
</span><span class="cx"> self.buckets = [IByteStream(s) for s in buckets]
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def addStream(self, bucket):
</span><span class="cx"> """Add a stream to the output"""
</span><span class="cx"> bucket = IByteStream(bucket)
</span><span class="lines">@@ -329,13 +349,14 @@
</span><span class="cx"> else:
</span><span class="cx"> self.length += bucket.length
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def read(self, sendfile=False):
</span><span class="cx"> if self.deferred is not None:
</span><span class="cx"> raise RuntimeError("Call to read while read is already outstanding")
</span><span class="cx">
</span><span class="cx"> if not self.buckets:
</span><span class="cx"> return None
</span><del>-
</del><ins>+
</ins><span class="cx"> if sendfile and ISendfileableStream.providedBy(self.buckets[0]):
</span><span class="cx"> try:
</span><span class="cx"> result = self.buckets[0].read(sendfile)
</span><span class="lines">@@ -346,60 +367,64 @@
</span><span class="cx"> result = self.buckets[0].read()
</span><span class="cx"> except:
</span><span class="cx"> return self._gotFailure(Failure())
</span><del>-
</del><ins>+
</ins><span class="cx"> if isinstance(result, Deferred):
</span><span class="cx"> self.deferred = result
</span><span class="cx"> result.addCallbacks(self._gotRead, self._gotFailure, (sendfile,))
</span><span class="cx"> return result
</span><del>-
</del><ins>+
</ins><span class="cx"> return self._gotRead(result, sendfile)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _gotFailure(self, f):
</span><span class="cx"> self.deferred = None
</span><span class="cx"> del self.buckets[0]
</span><span class="cx"> self.close()
</span><span class="cx"> return f
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _gotRead(self, result, sendfile):
</span><span class="cx"> self.deferred = None
</span><span class="cx"> if result is None:
</span><span class="cx"> del self.buckets[0]
</span><span class="cx"> # Next bucket
</span><span class="cx"> return self.read(sendfile)
</span><del>-
</del><ins>+
</ins><span class="cx"> if self.length is not None:
</span><span class="cx"> self.length -= len(result)
</span><span class="cx"> return result
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def split(self, point):
</span><span class="cx"> num = 0
</span><span class="cx"> origPoint = point
</span><span class="cx"> for bucket in self.buckets:
</span><del>- num+=1
</del><ins>+ num += 1
</ins><span class="cx">
</span><span class="cx"> if point == 0:
</span><span class="cx"> b = CompoundStream()
</span><span class="cx"> b.buckets = self.buckets[num:]
</span><span class="cx"> del self.buckets[num:]
</span><del>- return self,b
-
</del><ins>+ return self, b
+
</ins><span class="cx"> if bucket.length is None:
</span><span class="cx"> # Indeterminate length bucket.
</span><span class="cx"> # give up and use fallback splitter.
</span><span class="cx"> return fallbackSplit(self, origPoint)
</span><del>-
</del><ins>+
</ins><span class="cx"> if point < bucket.length:
</span><del>- before,after = bucket.split(point)
</del><ins>+ before, after = bucket.split(point)
</ins><span class="cx"> b = CompoundStream()
</span><span class="cx"> b.buckets = self.buckets[num:]
</span><span class="cx"> b.buckets[0] = after
</span><del>-
- del self.buckets[num+1:]
</del><ins>+
+ del self.buckets[num + 1:]
</ins><span class="cx"> self.buckets[num] = before
</span><del>- return self,b
-
</del><ins>+ return self, b
+
</ins><span class="cx"> point -= bucket.length
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def close(self):
</span><span class="cx"> for bucket in self.buckets:
</span><span class="cx"> bucket.close()
</span><span class="lines">@@ -407,10 +432,11 @@
</span><span class="cx"> self.length = 0
</span><span class="cx">
</span><span class="cx">
</span><del>-##############################
-#### readStream ####
-##############################
</del><span class="cx">
</span><ins>+#
+# readStream
+#
+
</ins><span class="cx"> class _StreamReader(object):
</span><span class="cx"> """Process a stream's data using callbacks for data and stream finish."""
</span><span class="cx">
</span><span class="lines">@@ -419,12 +445,14 @@
</span><span class="cx"> self.gotDataCallback = gotDataCallback
</span><span class="cx"> self.result = Deferred()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def run(self):
</span><span class="cx"> # self.result may be del'd in _read()
</span><span class="cx"> result = self.result
</span><span class="cx"> self._read()
</span><span class="cx"> return result
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _read(self):
</span><span class="cx"> try:
</span><span class="cx"> result = self.stream.read()
</span><span class="lines">@@ -436,11 +464,13 @@
</span><span class="cx"> else:
</span><span class="cx"> self._gotData(result)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _gotError(self, failure):
</span><span class="cx"> result = self.result
</span><span class="cx"> del self.result, self.gotDataCallback, self.stream
</span><span class="cx"> result.errback(failure)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _gotData(self, data):
</span><span class="cx"> if data is None:
</span><span class="cx"> result = self.result
</span><span class="lines">@@ -454,6 +484,8 @@
</span><span class="cx"> return
</span><span class="cx"> reactor.callLater(0, self._read)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def readStream(stream, gotDataCallback):
</span><span class="cx"> """Pass a stream's data to a callback.
</span><span class="cx">
</span><span class="lines">@@ -464,6 +496,7 @@
</span><span class="cx"> return _StreamReader(stream, gotDataCallback).run()
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def readAndDiscard(stream):
</span><span class="cx"> """Read all the data from the given stream, and throw it out.
</span><span class="cx">
</span><span class="lines">@@ -471,6 +504,8 @@
</span><span class="cx"> """
</span><span class="cx"> return readStream(stream, lambda _: None)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def readIntoFile(stream, outFile):
</span><span class="cx"> """Read a stream and write it into a file.
</span><span class="cx">
</span><span class="lines">@@ -481,6 +516,8 @@
</span><span class="cx"> return _
</span><span class="cx"> return readStream(stream, outFile.write).addBoth(done)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def connectStream(inputStream, factory):
</span><span class="cx"> """Connect a protocol constructed from a factory to stream.
</span><span class="cx">
</span><span class="lines">@@ -498,21 +535,26 @@
</span><span class="cx"> lambda _: p.connectionLost(ti_error.ConnectionDone()), lambda _: p.connectionLost(_))
</span><span class="cx"> return out
</span><span class="cx">
</span><del>-##############################
-#### fallbackSplit ####
-##############################
</del><span class="cx">
</span><ins>+
+#
+# fallbackSplit
+#
+
</ins><span class="cx"> def fallbackSplit(stream, point):
</span><span class="cx"> after = PostTruncaterStream(stream, point)
</span><span class="cx"> before = TruncaterStream(stream, point, after)
</span><span class="cx"> return (before, after)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class TruncaterStream(object):
</span><span class="cx"> def __init__(self, stream, point, postTruncater):
</span><span class="cx"> self.stream = stream
</span><span class="cx"> self.length = point
</span><span class="cx"> self.postTruncater = postTruncater
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def read(self):
</span><span class="cx"> if self.length == 0:
</span><span class="cx"> if self.postTruncater is not None:
</span><span class="lines">@@ -521,13 +563,14 @@
</span><span class="cx"> postTruncater.sendInitialSegment(self.stream.read())
</span><span class="cx"> self.stream = None
</span><span class="cx"> return None
</span><del>-
</del><ins>+
</ins><span class="cx"> result = self.stream.read()
</span><span class="cx"> if isinstance(result, Deferred):
</span><span class="cx"> return result.addCallback(self._gotRead)
</span><span class="cx"> else:
</span><span class="cx"> return self._gotRead(result)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _gotRead(self, data):
</span><span class="cx"> if data is None:
</span><span class="cx"> raise ValueError("Ran out of data for a split of a indeterminate length source")
</span><span class="lines">@@ -544,7 +587,8 @@
</span><span class="cx"> postTruncater.sendInitialSegment(after)
</span><span class="cx"> self.stream = None
</span><span class="cx"> return before
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def split(self, point):
</span><span class="cx"> if point > self.length:
</span><span class="cx"> raise ValueError("split point (%d) > length (%d)" % (point, self.length))
</span><span class="lines">@@ -554,7 +598,8 @@
</span><span class="cx"> self.length = point
</span><span class="cx"> self.postTruncater = post
</span><span class="cx"> return self, trunc
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def close(self):
</span><span class="cx"> if self.postTruncater is not None:
</span><span class="cx"> self.postTruncater.notifyClosed(self)
</span><span class="lines">@@ -563,14 +608,15 @@
</span><span class="cx"> self.stream.close()
</span><span class="cx"> self.stream = None
</span><span class="cx"> self.length = 0
</span><del>-
</del><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class PostTruncaterStream(object):
</span><span class="cx"> deferred = None
</span><span class="cx"> sentInitialSegment = False
</span><span class="cx"> truncaterClosed = None
</span><span class="cx"> closed = False
</span><del>-
</del><ins>+
</ins><span class="cx"> length = None
</span><span class="cx"> def __init__(self, stream, point):
</span><span class="cx"> self.stream = stream
</span><span class="lines">@@ -578,6 +624,7 @@
</span><span class="cx"> if stream.length is not None:
</span><span class="cx"> self.length = stream.length - point
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def read(self):
</span><span class="cx"> if not self.sentInitialSegment:
</span><span class="cx"> self.sentInitialSegment = True
</span><span class="lines">@@ -585,12 +632,14 @@
</span><span class="cx"> readAndDiscard(self.truncaterClosed)
</span><span class="cx"> self.truncaterClosed = None
</span><span class="cx"> return self.deferred
</span><del>-
</del><ins>+
</ins><span class="cx"> return self.stream.read()
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def split(self, point):
</span><span class="cx"> return fallbackSplit(self, point)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def close(self):
</span><span class="cx"> self.closed = True
</span><span class="cx"> if self.truncaterClosed is not None:
</span><span class="lines">@@ -600,9 +649,10 @@
</span><span class="cx"> elif self.sentInitialSegment:
</span><span class="cx"> # first half already finished up
</span><span class="cx"> self.stream.close()
</span><del>-
</del><ins>+
</ins><span class="cx"> self.deferred = None
</span><del>-
</del><ins>+
+
</ins><span class="cx"> # Callbacks from TruncaterStream
</span><span class="cx"> def sendInitialSegment(self, data):
</span><span class="cx"> if self.closed:
</span><span class="lines">@@ -614,7 +664,8 @@
</span><span class="cx"> data.chainDeferred(self.deferred)
</span><span class="cx"> else:
</span><span class="cx"> self.deferred.callback(data)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def notifyClosed(self, truncater):
</span><span class="cx"> if self.closed:
</span><span class="cx"> # we are closed, have first half really close
</span><span class="lines">@@ -627,10 +678,12 @@
</span><span class="cx"> # Idle, store closed info.
</span><span class="cx"> self.truncaterClosed = truncater
</span><span class="cx">
</span><del>-########################################
-#### ProducerStream/StreamProducer ####
-########################################
-
</del><ins>+
+
+#
+# ProducerStream/StreamProducer
+#
+
</ins><span class="cx"> class ProducerStream(object):
</span><span class="cx"> """Turns producers into a IByteStream.
</span><span class="cx"> Thus, implements IConsumer and IByteStream."""
</span><span class="lines">@@ -642,13 +695,14 @@
</span><span class="cx"> producer = None
</span><span class="cx"> producerPaused = False
</span><span class="cx"> deferred = None
</span><del>-
</del><ins>+
</ins><span class="cx"> bufferSize = 5
</span><del>-
</del><ins>+
</ins><span class="cx"> def __init__(self, length=None):
</span><span class="cx"> self.buffer = []
</span><span class="cx"> self.length = length
</span><del>-
</del><ins>+
+
</ins><span class="cx"> # IByteStream implementation
</span><span class="cx"> def read(self):
</span><span class="cx"> if self.buffer:
</span><span class="lines">@@ -666,26 +720,29 @@
</span><span class="cx"> or self.producerPaused):
</span><span class="cx"> self.producerPaused = False
</span><span class="cx"> self.producer.resumeProducing()
</span><del>-
</del><ins>+
</ins><span class="cx"> return deferred
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def split(self, point):
</span><span class="cx"> return fallbackSplit(self, point)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def close(self):
</span><span class="cx"> """Called by reader of stream when it is done reading."""
</span><del>- self.buffer=[]
</del><ins>+ self.buffer = []
</ins><span class="cx"> self.closed = True
</span><span class="cx"> if self.producer is not None:
</span><span class="cx"> self.producer.stopProducing()
</span><span class="cx"> self.producer = None
</span><span class="cx"> self.deferred = None
</span><del>-
</del><ins>+
+
</ins><span class="cx"> # IConsumer implementation
</span><span class="cx"> def write(self, data):
</span><span class="cx"> if self.closed:
</span><span class="cx"> return
</span><del>-
</del><ins>+
</ins><span class="cx"> if self.deferred:
</span><span class="cx"> deferred = self.deferred
</span><span class="cx"> self.deferred = None
</span><span class="lines">@@ -697,6 +754,7 @@
</span><span class="cx"> self.producer.pauseProducing()
</span><span class="cx"> self.producerPaused = True
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def finish(self, failure=None):
</span><span class="cx"> """Called by producer when it is done.
</span><span class="cx">
</span><span class="lines">@@ -716,13 +774,14 @@
</span><span class="cx"> deferred.callback(None)
</span><span class="cx"> else:
</span><span class="cx"> if failure is not None:
</span><del>- self.failed = True
- self.failure = failure
-
</del><ins>+ self.failed = True
+ self.failure = failure
+
+
</ins><span class="cx"> def registerProducer(self, producer, streaming):
</span><span class="cx"> if self.producer is not None:
</span><span class="cx"> raise RuntimeError("Cannot register producer %s, because producer %s was never unregistered." % (producer, self.producer))
</span><del>-
</del><ins>+
</ins><span class="cx"> if self.closed:
</span><span class="cx"> producer.stopProducing()
</span><span class="cx"> else:
</span><span class="lines">@@ -731,9 +790,12 @@
</span><span class="cx"> if not streaming:
</span><span class="cx"> producer.resumeProducing()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def unregisterProducer(self):
</span><span class="cx"> self.producer = None
</span><del>-
</del><ins>+
+
+
</ins><span class="cx"> class StreamProducer(object):
</span><span class="cx"> """A push producer which gets its data by reading a stream."""
</span><span class="cx"> implements(ti_interfaces.IPushProducer)
</span><span class="lines">@@ -742,21 +804,23 @@
</span><span class="cx"> finishedCallback = None
</span><span class="cx"> paused = False
</span><span class="cx"> consumer = None
</span><del>-
</del><ins>+
</ins><span class="cx"> def __init__(self, stream, enforceStr=True):
</span><span class="cx"> self.stream = stream
</span><span class="cx"> self.enforceStr = enforceStr
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def beginProducing(self, consumer):
</span><span class="cx"> if self.stream is None:
</span><span class="cx"> return defer.succeed(None)
</span><del>-
</del><ins>+
</ins><span class="cx"> self.consumer = consumer
</span><span class="cx"> finishedCallback = self.finishedCallback = Deferred()
</span><span class="cx"> self.consumer.registerProducer(self, True)
</span><span class="cx"> self.resumeProducing()
</span><span class="cx"> return finishedCallback
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def resumeProducing(self):
</span><span class="cx"> self.paused = False
</span><span class="cx"> if self.deferred is not None:
</span><span class="lines">@@ -767,13 +831,14 @@
</span><span class="cx"> except:
</span><span class="cx"> self.stopProducing(Failure())
</span><span class="cx"> return
</span><del>-
</del><ins>+
</ins><span class="cx"> if isinstance(data, Deferred):
</span><span class="cx"> self.deferred = data
</span><span class="cx"> self.deferred.addCallbacks(self._doWrite, self.stopProducing)
</span><span class="cx"> else:
</span><span class="cx"> self._doWrite(data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _doWrite(self, data):
</span><span class="cx"> if self.consumer is None:
</span><span class="cx"> return
</span><span class="lines">@@ -785,19 +850,21 @@
</span><span class="cx"> self.finishedCallback.callback(None)
</span><span class="cx"> self.finishedCallback = self.deferred = self.consumer = self.stream = None
</span><span class="cx"> return
</span><del>-
</del><ins>+
</ins><span class="cx"> self.deferred = None
</span><span class="cx"> if self.enforceStr:
</span><span class="cx"> # XXX: sucks that we have to do this. make transport.write(buffer) work!
</span><span class="cx"> data = str(buffer(data))
</span><span class="cx"> self.consumer.write(data)
</span><del>-
</del><ins>+
</ins><span class="cx"> if not self.paused:
</span><span class="cx"> self.resumeProducing()
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def pauseProducing(self):
</span><span class="cx"> self.paused = True
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def stopProducing(self, failure=ti_error.ConnectionLost()):
</span><span class="cx"> if self.consumer is not None:
</span><span class="cx"> self.consumer.unregisterProducer()
</span><span class="lines">@@ -810,13 +877,15 @@
</span><span class="cx"> self.paused = True
</span><span class="cx"> if self.stream is not None:
</span><span class="cx"> self.stream.close()
</span><del>-
</del><ins>+
</ins><span class="cx"> self.finishedCallback = self.deferred = self.consumer = self.stream = None
</span><span class="cx">
</span><del>-##############################
-#### ProcessStreamer ####
-##############################
</del><span class="cx">
</span><ins>+
+#
+# ProcessStreamer
+#
+
</ins><span class="cx"> class _ProcessStreamerProtocol(protocol.ProcessProtocol):
</span><span class="cx">
</span><span class="cx"> def __init__(self, inputStream, outStream, errStream):
</span><span class="lines">@@ -824,39 +893,47 @@
</span><span class="cx"> self.outStream = outStream
</span><span class="cx"> self.errStream = errStream
</span><span class="cx"> self.resultDeferred = defer.Deferred()
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def connectionMade(self):
</span><span class="cx"> p = StreamProducer(self.inputStream)
</span><span class="cx"> # if the process stopped reading from the input stream,
</span><span class="cx"> # this is not an error condition, so it oughtn't result
</span><span class="cx"> # in a ConnectionLost() from the input stream:
</span><del>- p.stopProducing = lambda err=None: StreamProducer.stopProducing(p, err)
-
</del><ins>+ p.stopProducing = lambda err = None: StreamProducer.stopProducing(p, err)
+
</ins><span class="cx"> d = p.beginProducing(self.transport)
</span><span class="cx"> d.addCallbacks(lambda _: self.transport.closeStdin(),
</span><span class="cx"> self._inputError)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _inputError(self, f):
</span><span class="cx"> log.failure("Error in input stream for transport {transport}", f, transport=self.transport)
</span><span class="cx"> self.transport.closeStdin()
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def outReceived(self, data):
</span><span class="cx"> self.outStream.write(data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def errReceived(self, data):
</span><span class="cx"> self.errStream.write(data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def outConnectionLost(self):
</span><span class="cx"> self.outStream.finish()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def errConnectionLost(self):
</span><span class="cx"> self.errStream.finish()
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def processEnded(self, reason):
</span><span class="cx"> self.resultDeferred.errback(reason)
</span><span class="cx"> del self.resultDeferred
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class ProcessStreamer(object):
</span><span class="cx"> """Runs a process hooked up to streams.
</span><span class="cx">
</span><span class="lines">@@ -874,7 +951,8 @@
</span><span class="cx"> self._program = program
</span><span class="cx"> self._args = args
</span><span class="cx"> self._env = env
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def run(self):
</span><span class="cx"> """Run the process.
</span><span class="cx">
</span><span class="lines">@@ -886,29 +964,36 @@
</span><span class="cx"> del self._env
</span><span class="cx"> return self._protocol.resultDeferred.addErrback(lambda _: _.trap(ti_error.ProcessDone))
</span><span class="cx">
</span><del>-##############################
-#### generatorToStream ####
-##############################
</del><span class="cx">
</span><ins>+
+#
+# generatorToStream
+#
+
</ins><span class="cx"> class _StreamIterator(object):
</span><del>- done=False
</del><ins>+ done = False
</ins><span class="cx">
</span><span class="cx"> def __iter__(self):
</span><span class="cx"> return self
</span><ins>+
+
</ins><span class="cx"> def next(self):
</span><span class="cx"> if self.done:
</span><span class="cx"> raise StopIteration
</span><span class="cx"> return self.value
</span><del>- wait=object()
</del><ins>+ wait = object()
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class _IteratorStream(object):
</span><span class="cx"> length = None
</span><del>-
</del><ins>+
</ins><span class="cx"> def __init__(self, fun, stream, args, kwargs):
</span><del>- self._stream=stream
</del><ins>+ self._stream = stream
</ins><span class="cx"> self._streamIterator = _StreamIterator()
</span><span class="cx"> self._gen = fun(self._streamIterator, *args, **kwargs)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def read(self):
</span><span class="cx"> try:
</span><span class="cx"> val = self._gen.next()
</span><span class="lines">@@ -922,34 +1007,39 @@
</span><span class="cx"> else:
</span><span class="cx"> return self._gotRead(newdata)
</span><span class="cx"> return val
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def _gotRead(self, data):
</span><span class="cx"> if data is None:
</span><del>- self._streamIterator.done=True
</del><ins>+ self._streamIterator.done = True
</ins><span class="cx"> else:
</span><del>- self._streamIterator.value=data
</del><ins>+ self._streamIterator.value = data
</ins><span class="cx"> return self.read()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def close(self):
</span><span class="cx"> self._stream.close()
</span><span class="cx"> del self._gen, self._stream, self._streamIterator
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def split(self):
</span><span class="cx"> return fallbackSplit(self)
</span><del>-
</del><ins>+
+
+
</ins><span class="cx"> def generatorToStream(fun):
</span><span class="cx"> """Converts a generator function into a stream.
</span><del>-
</del><ins>+
</ins><span class="cx"> The function should take an iterator as its first argument,
</span><span class="cx"> which will be converted *from* a stream by this wrapper, and
</span><span class="cx"> yield items which are turned *into* the results from the
</span><span class="cx"> stream's 'read' call.
</span><del>-
</del><ins>+
</ins><span class="cx"> One important point: before every call to input.next(), you
</span><span class="cx"> *MUST* do a "yield input.wait" first. Yielding this magic value
</span><span class="cx"> takes care of ensuring that the input is not a deferred before
</span><span class="cx"> you see it.
</span><del>-
</del><ins>+
</ins><span class="cx"> >>> from txweb2 import stream
</span><span class="cx"> >>> from string import maketrans
</span><span class="cx"> >>> alphabet = 'abcdefghijklmnopqrstuvwxyz'
</span><span class="lines">@@ -974,61 +1064,67 @@
</span><span class="cx"> >>> evenMoreEncryptedStream = encrypt(encryptedStream, 13)
</span><span class="cx"> >>> evenMoreEncryptedStream.read()
</span><span class="cx"> 'SampleSampleSample'
</span><del>-
</del><ins>+
</ins><span class="cx"> """
</span><span class="cx"> def generatorToStream_inner(stream, *args, **kwargs):
</span><span class="cx"> return _IteratorStream(fun, stream, args, kwargs)
</span><span class="cx"> return generatorToStream_inner
</span><span class="cx">
</span><span class="cx">
</span><del>-##############################
-#### BufferedStream ####
-##############################
</del><span class="cx">
</span><ins>+#
+# BufferedStream
+#
+
</ins><span class="cx"> class BufferedStream(object):
</span><span class="cx"> """A stream which buffers its data to provide operations like
</span><span class="cx"> readline and readExactly."""
</span><del>-
</del><ins>+
</ins><span class="cx"> data = ""
</span><span class="cx"> def __init__(self, stream):
</span><span class="cx"> self.stream = stream
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _readUntil(self, f):
</span><span class="cx"> """Internal helper function which repeatedly calls f each time
</span><span class="cx"> after more data has been received, until it returns non-None."""
</span><span class="cx"> while True:
</span><span class="cx"> r = f()
</span><span class="cx"> if r is not None:
</span><del>- yield r; return
-
</del><ins>+ yield r
+ return
+
</ins><span class="cx"> newdata = self.stream.read()
</span><span class="cx"> if isinstance(newdata, defer.Deferred):
</span><span class="cx"> newdata = defer.waitForDeferred(newdata)
</span><del>- yield newdata; newdata = newdata.getResult()
-
</del><ins>+ yield newdata
+ newdata = newdata.getResult()
+
</ins><span class="cx"> if newdata is None:
</span><span class="cx"> # End Of File
</span><span class="cx"> newdata = self.data
</span><span class="cx"> self.data = ''
</span><del>- yield newdata; return
</del><ins>+ yield newdata
+ return
</ins><span class="cx"> self.data += str(newdata)
</span><span class="cx"> _readUntil = defer.deferredGenerator(_readUntil)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def readExactly(self, size=None):
</span><span class="cx"> """Read exactly size bytes of data, or, if size is None, read
</span><span class="cx"> the entire stream into a string."""
</span><span class="cx"> if size is not None and size < 0:
</span><span class="cx"> raise ValueError("readExactly: size cannot be negative: %s", size)
</span><del>-
</del><ins>+
</ins><span class="cx"> def gotdata():
</span><span class="cx"> data = self.data
</span><span class="cx"> if size is not None and len(data) >= size:
</span><del>- pre,post = data[:size], data[size:]
</del><ins>+ pre, post = data[:size], data[size:]
</ins><span class="cx"> self.data = post
</span><span class="cx"> return pre
</span><span class="cx"> return self._readUntil(gotdata)
</span><del>-
-
</del><ins>+
+
</ins><span class="cx"> def readline(self, delimiter='\r\n', size=None):
</span><span class="cx"> """
</span><span class="cx"> Read a line of data from the string, bounded by
</span><span class="lines">@@ -1040,7 +1136,7 @@
</span><span class="cx"> the next line will be returned together.
</span><span class="cx"> """
</span><span class="cx"> if size is not None and size < 0:
</span><del>- raise ValueError("readline: size cannot be negative: %s" % (size, ))
</del><ins>+ raise ValueError("readline: size cannot be negative: %s" % (size,))
</ins><span class="cx">
</span><span class="cx"> def gotdata():
</span><span class="cx"> data = self.data
</span><span class="lines">@@ -1055,18 +1151,20 @@
</span><span class="cx"> splitpoint = data.find(delimiter)
</span><span class="cx"> if splitpoint != -1:
</span><span class="cx"> splitpoint += len(delimiter)
</span><del>-
</del><ins>+
</ins><span class="cx"> if splitpoint != -1:
</span><span class="cx"> pre = data[:splitpoint]
</span><span class="cx"> self.data = data[splitpoint:]
</span><span class="cx"> return pre
</span><span class="cx"> return self._readUntil(gotdata)
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def pushback(self, pushed):
</span><span class="cx"> """Push data back into the buffer."""
</span><del>-
</del><ins>+
</ins><span class="cx"> self.data = pushed + self.data
</span><del>-
</del><ins>+
+
</ins><span class="cx"> def read(self):
</span><span class="cx"> data = self.data
</span><span class="cx"> if data:
</span><span class="lines">@@ -1074,17 +1172,18 @@
</span><span class="cx"> return data
</span><span class="cx"> return self.stream.read()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _len(self):
</span><span class="cx"> l = self.stream.length
</span><span class="cx"> if l is None:
</span><span class="cx"> return None
</span><span class="cx"> return l + len(self.data)
</span><del>-
</del><ins>+
</ins><span class="cx"> length = property(_len)
</span><del>-
</del><ins>+
</ins><span class="cx"> def split(self, offset):
</span><span class="cx"> off = offset - len(self.data)
</span><del>-
</del><ins>+
</ins><span class="cx"> pre, post = self.stream.split(max(0, off))
</span><span class="cx"> pre = BufferedStream(pre)
</span><span class="cx"> post = BufferedStream(post)
</span><span class="lines">@@ -1093,14 +1192,15 @@
</span><span class="cx"> post.data = self.data[-off:]
</span><span class="cx"> else:
</span><span class="cx"> pre.data = self.data
</span><del>-
</del><ins>+
</ins><span class="cx"> return pre, post
</span><span class="cx">
</span><del>-
-#########################
-#### MD5Stream ####
-#########################
</del><span class="cx">
</span><ins>+
+#
+# MD5Stream
+#
+
</ins><span class="cx"> class MD5Stream(SimpleStream):
</span><span class="cx"> """
</span><span class="cx"> An wrapper which computes the MD5 hash of the data read from the
</span></span></pre></div>
<a id="CalendarServertrunktxweb2test__init__py"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/test/__init__.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/test/__init__.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/test/__init__.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -6,4 +6,3 @@
</span><span class="cx"> txweb2.test: unittests for the Twext.Web2, Web Server Framework
</span><span class="cx">
</span><span class="cx"> """
</span><del>-
</del></span></pre></div>
<a id="CalendarServertrunktxweb2testsimple_clientpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/test/simple_client.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/test/simple_client.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/test/simple_client.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -1,4 +1,5 @@
</span><del>-import socket, sys
</del><ins>+import socket
+import sys
</ins><span class="cx">
</span><span class="cx"> test_type = sys.argv[1]
</span><span class="cx"> port = int(sys.argv[2])
</span><span class="lines">@@ -10,12 +11,12 @@
</span><span class="cx">
</span><span class="cx"> if socket_type == 'ssl':
</span><span class="cx"> s2 = socket.ssl(s)
</span><del>- send=s2.write
- recv=s2.read
</del><ins>+ send = s2.write
+ recv = s2.read
</ins><span class="cx"> else:
</span><del>- send=s.send
- recv=s.recv
-
</del><ins>+ send = s.send
+ recv = s.recv
+
</ins><span class="cx"> print >> sys.stderr, ">> Making %s request to port %d" % (socket_type, port)
</span><span class="cx">
</span><span class="cx"> send("GET /error HTTP/1.0\r\n")
</span><span class="lines">@@ -24,20 +25,20 @@
</span><span class="cx"> if test_type == "lingeringClose":
</span><span class="cx"> print >> sys.stderr, ">> Sending lots of data"
</span><span class="cx"> send("Content-Length: 1000000\r\n\r\n")
</span><del>- send("X"*1000000)
</del><ins>+ send("X" * 1000000)
</ins><span class="cx"> else:
</span><span class="cx"> send('\r\n')
</span><span class="cx">
</span><del>-#import time
-#time.sleep(5)
</del><ins>+# import time
+# time.sleep(5)
</ins><span class="cx"> print >> sys.stderr, ">> Getting data"
</span><del>-data=''
</del><ins>+data = ''
</ins><span class="cx"> while len(data) < 299999:
</span><span class="cx"> try:
</span><del>- x=recv(10000)
</del><ins>+ x = recv(10000)
</ins><span class="cx"> except:
</span><span class="cx"> break
</span><span class="cx"> if x == '':
</span><span class="cx"> break
</span><del>- data+=x
</del><ins>+ data += x
</ins><span class="cx"> sys.stdout.write(data)
</span></span></pre></div>
<a id="CalendarServertrunktxweb2testtest_clientpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/test/test_client.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/test/test_client.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/test/test_client.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -22,13 +22,16 @@
</span><span class="cx"> def dataReceived(self, data):
</span><span class="cx"> self.data += data
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def write(self, data):
</span><span class="cx"> self.transport.write(data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def connectionLost(self, reason):
</span><span class="cx"> self.done = True
</span><span class="cx"> self.transport.loseConnection()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def loseConnection(self):
</span><span class="cx"> self.done = True
</span><span class="cx"> self.transport.loseConnection()
</span><span class="lines">@@ -52,15 +55,20 @@
</span><span class="cx">
</span><span class="cx"> return cxn
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def writeToClient(self, cxn, data):
</span><span class="cx"> cxn.server.write(data)
</span><span class="cx"> self.iterate(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def writeLines(self, cxn, lines):
</span><span class="cx"> self.writeToClient(cxn, '\r\n'.join(lines))
</span><span class="cx">
</span><del>- def assertReceived(self, cxn, expectedStatus, expectedHeaders,
- expectedContent=None):
</del><ins>+
+ def assertReceived(
+ self, cxn, expectedStatus, expectedHeaders,
+ expectedContent=None
+ ):
</ins><span class="cx"> self.iterate(cxn)
</span><span class="cx">
</span><span class="cx"> headers, content = cxn.server.data.split('\r\n\r\n', 1)
</span><span class="lines">@@ -80,15 +88,18 @@
</span><span class="cx">
</span><span class="cx"> self.assertEquals(content, expectedContent)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def assertDone(self, cxn):
</span><span class="cx"> self.iterate(cxn)
</span><span class="cx"> self.assertEquals(cxn.server.done, True, 'Connection not closed.')
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def assertHeaders(self, resp, expectedHeaders):
</span><span class="cx"> headers = list(resp.headers.getAllRawHeaders())
</span><span class="cx"> headers.sort()
</span><span class="cx"> self.assertEquals(headers, expectedHeaders)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def checkResponse(self, resp, code, headers, length, data):
</span><span class="cx"> """
</span><span class="cx"> Assert various things about a response: http code, headers, stream
</span><span class="lines">@@ -130,6 +141,7 @@
</span><span class="cx">
</span><span class="cx"> return d.addCallback(lambda _: self.assertDone(cxn))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_delayedContent(self):
</span><span class="cx"> """
</span><span class="cx"> Make sure that the client returns the response object as soon as the
</span><span class="lines">@@ -164,6 +176,7 @@
</span><span class="cx">
</span><span class="cx"> return d.addCallback(lambda _: self.assertDone(cxn))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_prematurePipelining(self):
</span><span class="cx"> """
</span><span class="cx"> Ensure that submitting a second request before it's allowed results
</span><span class="lines">@@ -189,6 +202,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_userHeaders(self):
</span><span class="cx"> """
</span><span class="cx"> Make sure that headers get through in both directions.
</span><span class="lines">@@ -237,6 +251,7 @@
</span><span class="cx">
</span><span class="cx"> return d.addCallback(lambda _: self.assertDone(cxn))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_streamedUpload(self):
</span><span class="cx"> """
</span><span class="cx"> Make sure that sending request content works.
</span><span class="lines">@@ -260,6 +275,7 @@
</span><span class="cx">
</span><span class="cx"> return d.addCallback(lambda _: self.assertDone(cxn))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_sentHead(self):
</span><span class="cx"> """
</span><span class="cx"> Ensure that HEAD requests work, and return Content-Length.
</span><span class="lines">@@ -282,6 +298,7 @@
</span><span class="cx">
</span><span class="cx"> return d.addCallback(lambda _: self.assertDone(cxn))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_sentHeadKeepAlive(self):
</span><span class="cx"> """
</span><span class="cx"> Ensure that keepalive works right after a HEAD request.
</span><span class="lines">@@ -309,9 +326,9 @@
</span><span class="cx"> didIt[0] = second
</span><span class="cx">
</span><span class="cx"> if second:
</span><del>- keepAlive='close'
</del><ins>+ keepAlive = 'close'
</ins><span class="cx"> else:
</span><del>- keepAlive='Keep-Alive'
</del><ins>+ keepAlive = 'Keep-Alive'
</ins><span class="cx">
</span><span class="cx"> cxn.server.data = ''
</span><span class="cx">
</span><span class="lines">@@ -319,10 +336,10 @@
</span><span class="cx"> self.checkResponse, 200, [('Content-Length', ['5'])], 0, None)
</span><span class="cx">
</span><span class="cx"> self.assertReceived(cxn, 'HEAD / HTTP/1.1',
</span><del>- ['Connection: '+ keepAlive])
</del><ins>+ ['Connection: ' + keepAlive])
</ins><span class="cx">
</span><span class="cx"> self.writeLines(cxn, ('HTTP/1.1 200 OK',
</span><del>- 'Connection: '+ keepAlive,
</del><ins>+ 'Connection: ' + keepAlive,
</ins><span class="cx"> 'Content-Length: 5',
</span><span class="cx"> '\r\n'))
</span><span class="cx">
</span><span class="lines">@@ -332,6 +349,7 @@
</span><span class="cx">
</span><span class="cx"> return d.addCallback(lambda _: self.assertDone(cxn))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_chunkedUpload(self):
</span><span class="cx"> """
</span><span class="cx"> Ensure chunked data is correctly decoded on upload.
</span><span class="lines">@@ -385,6 +403,7 @@
</span><span class="cx">
</span><span class="cx"> return d.addCallback(lambda _: self.assertDone(cxn))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_serverIsntHttp(self):
</span><span class="cx"> """
</span><span class="cx"> Check that an error is returned if the server doesn't talk HTTP.
</span><span class="lines">@@ -435,6 +454,7 @@
</span><span class="cx"> self.writeLines(cxn, ('HTTP/1.1 200',
</span><span class="cx"> '\r\n'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_errorReadingRequestStream(self):
</span><span class="cx"> """
</span><span class="cx"> Ensure that stream errors are propagated to the response.
</span><span class="lines">@@ -492,4 +512,3 @@
</span><span class="cx"> return self.assertFailure(response.stream.read(), ValueError)
</span><span class="cx"> d.addCallback(cb)
</span><span class="cx"> return d
</span><del>-
</del></span></pre></div>
<a id="CalendarServertrunktxweb2testtest_fileuploadpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/test/test_fileupload.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/test/test_fileupload.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/test/test_fileupload.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> raise ValueError("len(mem) < start + length")
</span><span class="cx"> self.length = length
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def read(self):
</span><span class="cx"> if self.mem is None:
</span><span class="cx"> return None
</span><span class="lines">@@ -41,6 +42,7 @@
</span><span class="cx"> self.start += amtToRead
</span><span class="cx"> return result
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def close(self):
</span><span class="cx"> self.mem = None
</span><span class="cx"> stream.SimpleStream.close(self)
</span><span class="lines">@@ -58,21 +60,24 @@
</span><span class="cx"> d.addCallback(self._assertFailures, expected_error)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _assertFailures(self, failures, *expectedFailures):
</span><span class="cx"> for flag, failure in failures:
</span><span class="cx"> self.failUnlessEqual(flag, defer.FAILURE)
</span><span class="cx"> failure.trap(*expectedFailures)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def doTest(self, boundary, data, expected_args, expected_files):
</span><del>- #import time, gc, cgi, cStringIO
</del><ins>+ # import time, gc, cgi, cStringIO
</ins><span class="cx"> for bytes in range(1, 20):
</span><del>- #s = TestStream(data, maxReturn=bytes)
</del><ins>+ # s = TestStream(data, maxReturn=bytes)
</ins><span class="cx"> s = stream.IStream(data)
</span><del>- #t=time.time()
</del><ins>+ # t=time.time()
</ins><span class="cx"> d = waitForDeferred(fileupload.parseMultipartFormData(s, boundary))
</span><del>- yield d; args, files = d.getResult()
- #e=time.time()
- #print "%.2g"%(e-t)
</del><ins>+ yield d
+ args, files = d.getResult()
+ # e=time.time()
+ # print "%.2g"%(e-t)
</ins><span class="cx"> self.assertEquals(args, expected_args)
</span><span class="cx">
</span><span class="cx"> # Read file data back into memory to compare.
</span><span class="lines">@@ -81,17 +86,17 @@
</span><span class="cx"> out[name] = [(filename, ctype, f.read()) for (filename, ctype, f) in l]
</span><span class="cx"> self.assertEquals(out, expected_files)
</span><span class="cx">
</span><del>- #data=cStringIO.StringIO(data)
- #t=time.time()
- #d=cgi.parse_multipart(data, {'boundary':boundary})
- #e=time.time()
- #print "CGI: %.2g"%(e-t)
</del><ins>+ # data=cStringIO.StringIO(data)
+ # t=time.time()
+ # d=cgi.parse_multipart(data, {'boundary':boundary})
+ # e=time.time()
+ # print "CGI: %.2g"%(e-t)
</ins><span class="cx"> doTest = deferredGenerator(doTest)
</span><span class="cx">
</span><span class="cx"> def testNormalUpload(self):
</span><span class="cx"> return self.doTest(
</span><span class="cx"> '---------------------------155781040421463194511908194298',
</span><del>-"""-----------------------------155781040421463194511908194298\r
</del><ins>+ """-----------------------------155781040421463194511908194298\r
</ins><span class="cx"> Content-Disposition: form-data; name="foo"\r
</span><span class="cx"> \r
</span><span class="cx"> Foo Bar\r
</span><span class="lines">@@ -104,14 +109,15 @@
</span><span class="cx"> blah\r
</span><span class="cx"> -----------------------------155781040421463194511908194298--\r
</span><span class="cx"> """,
</span><del>- {'foo':['Foo Bar']},
- {'file':[('filename', MimeType('text', 'html'),
</del><ins>+ {'foo': ['Foo Bar']},
+ {'file': [('filename', MimeType('text', 'html'),
</ins><span class="cx"> "Contents of a file\nblah\nblah")]})
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testMultipleUpload(self):
</span><span class="cx"> return self.doTest(
</span><span class="cx"> 'xyz',
</span><del>-"""--xyz\r
</del><ins>+ """--xyz\r
</ins><span class="cx"> Content-Disposition: form-data; name="foo"\r
</span><span class="cx"> \r
</span><span class="cx"> Foo Bar\r
</span><span class="lines">@@ -131,15 +137,15 @@
</span><span class="cx"> bleh\r
</span><span class="cx"> --xyz--\r
</span><span class="cx"> """,
</span><del>- {'foo':['Foo Bar', 'Baz']},
- {'file':[('filename', MimeType('text', 'html'), "blah"),
- ('filename', MimeType('text', 'plain'), "bleh")]})
</del><ins>+ {'foo': ['Foo Bar', 'Baz']},
+ {'file': [('filename', MimeType('text', 'html'), "blah"),
+ ('filename', MimeType('text', 'plain'), "bleh")]})
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def testStupidFilename(self):
</span><span class="cx"> return self.doTest(
</span><span class="cx"> '----------0xKhTmLbOuNdArY',
</span><del>-"""------------0xKhTmLbOuNdArY\r
</del><ins>+ """------------0xKhTmLbOuNdArY\r
</ins><span class="cx"> Content-Disposition: form-data; name="file"; filename="foo"; name="foobar.txt"\r
</span><span class="cx"> Content-Type: text/plain\r
</span><span class="cx"> \r
</span><span class="lines">@@ -149,14 +155,14 @@
</span><span class="cx"> ------------0xKhTmLbOuNdArY--\r
</span><span class="cx"> """,
</span><span class="cx"> {},
</span><del>- {'file':[('foo"; name="foobar.txt', MimeType('text', 'plain'),
</del><ins>+ {'file': [('foo"; name="foobar.txt', MimeType('text', 'plain'),
</ins><span class="cx"> "Contents of a file\nblah\nblah")]})
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def testEmptyFilename(self):
</span><span class="cx"> return self.doTest(
</span><span class="cx"> 'curlPYafCMnsamUw9kSkJJkSen41sAV',
</span><del>-"""--curlPYafCMnsamUw9kSkJJkSen41sAV\r
</del><ins>+ """--curlPYafCMnsamUw9kSkJJkSen41sAV\r
</ins><span class="cx"> cONTENT-tYPE: application/octet-stream\r
</span><span class="cx"> cONTENT-dISPOSITION: FORM-DATA; NAME="foo"; FILENAME=""\r
</span><span class="cx"> \r
</span><span class="lines">@@ -164,7 +170,7 @@
</span><span class="cx"> --curlPYafCMnsamUw9kSkJJkSen41sAV--\r
</span><span class="cx"> """,
</span><span class="cx"> {},
</span><del>- {'foo':[('', MimeType('application', 'octet-stream'),
</del><ins>+ {'foo': [('', MimeType('application', 'octet-stream'),
</ins><span class="cx"> "qwertyuiop")]})
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -172,7 +178,7 @@
</span><span class="cx"> def testMissingContentDisposition(self):
</span><span class="cx"> return self.doTestError(
</span><span class="cx"> '----------0xKhTmLbOuNdArY',
</span><del>-"""------------0xKhTmLbOuNdArY\r
</del><ins>+ """------------0xKhTmLbOuNdArY\r
</ins><span class="cx"> Content-Type: text/html\r
</span><span class="cx"> \r
</span><span class="cx"> Blah blah I am a stupid webbrowser\r
</span><span class="lines">@@ -184,7 +190,7 @@
</span><span class="cx"> def testRandomData(self):
</span><span class="cx"> return self.doTestError(
</span><span class="cx"> 'boundary',
</span><del>-"""--sdkjsadjlfjlj skjsfdkljsd
</del><ins>+ """--sdkjsadjlfjlj skjsfdkljsd
</ins><span class="cx"> sfdkjsfdlkjhsfadklj sffkj""",
</span><span class="cx"> fileupload.MimeFormatError)
</span><span class="cx">
</span><span class="lines">@@ -275,16 +281,17 @@
</span><span class="cx"> for bytes in range(1, 20):
</span><span class="cx"> s = TestStream(data, maxReturn=bytes)
</span><span class="cx"> d = waitForDeferred(fileupload.parse_urlencoded(s))
</span><del>- yield d; args = d.getResult()
</del><ins>+ yield d
+ args = d.getResult()
</ins><span class="cx"> self.assertEquals(args, expected_args)
</span><span class="cx"> doTest = deferredGenerator(doTest)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_parseValid(self):
</span><del>- self.doTest("a=b&c=d&c=e", {'a':['b'], 'c':['d', 'e']})
- self.doTest("a=b&c=d&c=e", {'a':['b'], 'c':['d', 'e']})
- self.doTest("a=b+c%20d", {'a':['b c d']})
</del><ins>+ self.doTest("a=b&c=d&c=e", {'a': ['b'], 'c': ['d', 'e']})
+ self.doTest("a=b&c=d&c=e", {'a': ['b'], 'c': ['d', 'e']})
+ self.doTest("a=b+c%20d", {'a': ['b c d']})
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_parseInvalid(self):
</span><del>- self.doTest("a&b=c", {'b':['c']})
</del><ins>+ self.doTest("a&b=c", {'b': ['c']})
</ins></span></pre></div>
<a id="CalendarServertrunktxweb2testtest_httppy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/test/test_http.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/test/test_http.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/test/test_http.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -1,7 +1,9 @@
</span><span class="cx">
</span><span class="cx"> from __future__ import nested_scopes
</span><span class="cx">
</span><del>-import time, sys, os
</del><ins>+import os
+import sys
+import time
</ins><span class="cx">
</span><span class="cx"> from zope.interface import implements
</span><span class="cx">
</span><span class="lines">@@ -20,6 +22,7 @@
</span><span class="cx">
</span><span class="cx"> class RedirectResponseTestCase(unittest.TestCase):
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testTemporary(self):
</span><span class="cx"> """
</span><span class="cx"> Verify the "temporary" parameter sets the appropriate response code
</span><span class="lines">@@ -29,6 +32,8 @@
</span><span class="cx"> req = http.RedirectResponse("http://example.com/", temporary=True)
</span><span class="cx"> self.assertEquals(req.code, responsecode.TEMPORARY_REDIRECT)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class PreconditionTestCase(unittest.TestCase):
</span><span class="cx"> def checkPreconditions(self, request, response, expectedResult, expectedCode,
</span><span class="cx"> **kw):
</span><span class="lines">@@ -41,6 +46,7 @@
</span><span class="cx"> self.assertEquals(e.response.code, expectedCode)
</span><span class="cx"> self.assertEquals(preconditionsPass, expectedResult)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testWithoutHeaders(self):
</span><span class="cx"> request = http.Request(None, "GET", "/", "HTTP/1.1", 0, http_headers.Headers())
</span><span class="cx"> out_headers = http_headers.Headers()
</span><span class="lines">@@ -58,6 +64,7 @@
</span><span class="cx"> out_headers.setHeader("ETag", http_headers.ETag('foo'))
</span><span class="cx"> self.checkPreconditions(request, response, True, responsecode.OK)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testIfMatch(self):
</span><span class="cx"> request = http.Request(None, "GET", "/", "HTTP/1.1", 0, http_headers.Headers())
</span><span class="cx"> out_headers = http_headers.Headers()
</span><span class="lines">@@ -72,7 +79,7 @@
</span><span class="cx"> request.headers.setRawHeaders("If-Match", ('"frob"',))
</span><span class="cx"> self.checkPreconditions(request, response, False, responsecode.PRECONDITION_FAILED)
</span><span class="cx">
</span><del>- ## Actually set the ETag header
</del><ins>+ # Actually set the ETag header
</ins><span class="cx"> out_headers.setHeader("ETag", http_headers.ETag('foo'))
</span><span class="cx"> out_headers.setHeader("Last-Modified", 946771200) # Sun, 02 Jan 2000 00:00:00 GMT
</span><span class="cx">
</span><span class="lines">@@ -99,6 +106,7 @@
</span><span class="cx"> request.headers.setRawHeaders("If-Match", ('W/"foo"',))
</span><span class="cx"> self.checkPreconditions(request, response, False, responsecode.PRECONDITION_FAILED)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testIfUnmodifiedSince(self):
</span><span class="cx"> request = http.Request(None, "GET", "/", "HTTP/1.1", 0, http_headers.Headers())
</span><span class="cx"> out_headers = http_headers.Headers()
</span><span class="lines">@@ -149,9 +157,9 @@
</span><span class="cx"> self.checkPreconditions(request, response, False, responsecode.NOT_MODIFIED)
</span><span class="cx">
</span><span class="cx"> # With a non-GET method
</span><del>- request.method="PUT"
</del><ins>+ request.method = "PUT"
</ins><span class="cx"> self.checkPreconditions(request, response, False, responsecode.NOT_MODIFIED)
</span><del>- request.method="GET"
</del><ins>+ request.method = "GET"
</ins><span class="cx">
</span><span class="cx"> request.headers.setRawHeaders("If-Modified-Since", ('Sat, 01 Jan 2000 00:00:00 GMT',))
</span><span class="cx"> self.checkPreconditions(request, response, True, responsecode.OK)
</span><span class="lines">@@ -169,6 +177,7 @@
</span><span class="cx"> request.headers.setHeader("If-Modified-Since", time.time() + 500)
</span><span class="cx"> self.checkPreconditions(request, response, True, responsecode.OK)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testIfNoneMatch(self):
</span><span class="cx"> request = http.Request(None, "GET", "/", "HTTP/1.1", 0, http_headers.Headers())
</span><span class="cx"> out_headers = http_headers.Headers()
</span><span class="lines">@@ -182,24 +191,24 @@
</span><span class="cx">
</span><span class="cx"> # behavior of entityExists
</span><span class="cx"> request.headers.setRawHeaders("If-None-Match", ('*',))
</span><del>- request.method="PUT"
</del><ins>+ request.method = "PUT"
</ins><span class="cx"> self.checkPreconditions(request, response, False, responsecode.PRECONDITION_FAILED)
</span><del>- request.method="GET"
</del><ins>+ request.method = "GET"
</ins><span class="cx"> self.checkPreconditions(request, response, False, responsecode.NOT_MODIFIED)
</span><span class="cx"> self.checkPreconditions(request, response, True, responsecode.OK, entityExists=False)
</span><span class="cx">
</span><span class="cx"> # tag matches
</span><span class="cx"> request.headers.setRawHeaders("If-None-Match", ('"frob", "foo"',))
</span><del>- request.method="PUT"
</del><ins>+ request.method = "PUT"
</ins><span class="cx"> self.checkPreconditions(request, response, False, responsecode.PRECONDITION_FAILED)
</span><del>- request.method="GET"
</del><ins>+ request.method = "GET"
</ins><span class="cx"> self.checkPreconditions(request, response, False, responsecode.NOT_MODIFIED)
</span><span class="cx">
</span><span class="cx"> # now with IMS, also:
</span><span class="cx"> request.headers.setRawHeaders("If-Modified-Since", ('Mon, 03 Jan 2000 00:00:00 GMT',))
</span><del>- request.method="PUT"
</del><ins>+ request.method = "PUT"
</ins><span class="cx"> self.checkPreconditions(request, response, False, responsecode.PRECONDITION_FAILED)
</span><del>- request.method="GET"
</del><ins>+ request.method = "GET"
</ins><span class="cx"> self.checkPreconditions(request, response, False, responsecode.NOT_MODIFIED)
</span><span class="cx">
</span><span class="cx"> request.headers.setRawHeaders("If-Modified-Since", ('Sat, 01 Jan 2000 00:00:00 GMT',))
</span><span class="lines">@@ -232,15 +241,16 @@
</span><span class="cx"> self.checkPreconditions(request, response, False, responsecode.NOT_MODIFIED)
</span><span class="cx">
</span><span class="cx"> # Weak tags not okay for other methods
</span><del>- request.method="PUT"
</del><ins>+ request.method = "PUT"
</ins><span class="cx"> out_headers.setHeader("ETag", http_headers.ETag('foo', weak=True))
</span><span class="cx"> request.headers.setRawHeaders("If-None-Match", ('W/"foo"',))
</span><span class="cx"> self.checkPreconditions(request, response, True, responsecode.OK)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testNoResponse(self):
</span><span class="cx"> # Ensure that passing etag/lastModified arguments instead of response works.
</span><span class="cx"> request = http.Request(None, "GET", "/", "HTTP/1.1", 0, http_headers.Headers())
</span><del>- request.method="PUT"
</del><ins>+ request.method = "PUT"
</ins><span class="cx"> request.headers.setRawHeaders("If-None-Match", ('"foo"',))
</span><span class="cx">
</span><span class="cx"> self.checkPreconditions(request, None, True, responsecode.OK)
</span><span class="lines">@@ -249,10 +259,12 @@
</span><span class="cx"> lastModified=946771200)
</span><span class="cx">
</span><span class="cx"> # Make sure that, while you shoudn't do this, that it doesn't cause an error
</span><del>- request.method="GET"
</del><ins>+ request.method = "GET"
</ins><span class="cx"> self.checkPreconditions(request, None, False, responsecode.NOT_MODIFIED,
</span><span class="cx"> etag=http_headers.ETag('foo'))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class IfRangeTestCase(unittest.TestCase):
</span><span class="cx"> def testIfRange(self):
</span><span class="cx"> request = http.Request(None, "GET", "/", "HTTP/1.1", 0, http_headers.Headers())
</span><span class="lines">@@ -302,6 +314,7 @@
</span><span class="cx"> class LoopbackRelay(loopback.LoopbackRelay):
</span><span class="cx"> implements(interfaces.IProducer)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def pauseProducing(self):
</span><span class="cx"> self.paused = True
</span><span class="cx">
</span><span class="lines">@@ -330,6 +343,7 @@
</span><span class="cx"> return address.IPv4Address('TCP', 'localhost', 4321)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class TestRequestMixin(object):
</span><span class="cx"> def __init__(self, *args, **kwargs):
</span><span class="cx"> super(TestRequestMixin, self).__init__(*args, **kwargs)
</span><span class="lines">@@ -338,81 +352,105 @@
</span><span class="cx"> headers.sort()
</span><span class="cx"> self.cmds.append(('init', self.method, self.uri, self.clientproto, self.stream.length, tuple(headers)))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def process(self):
</span><span class="cx"> pass
</span><ins>+
+
</ins><span class="cx"> def handleContentChunk(self, data):
</span><span class="cx"> self.cmds.append(('contentChunk', data))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def handleContentComplete(self):
</span><span class="cx"> self.cmds.append(('contentComplete',))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def connectionLost(self, reason):
</span><span class="cx"> self.cmds.append(('connectionLost', reason))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _finished(self, x):
</span><span class="cx"> self._reallyFinished(x)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class TestRequest(TestRequestMixin, http.Request):
</span><span class="cx"> """
</span><span class="cx"> Stub request for testing.
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class TestSSLRedirectRequest(TestRequestMixin, SSLRedirectRequest):
</span><span class="cx"> """
</span><span class="cx"> Stub request for HSTS testing.
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class TestResponse(object):
</span><span class="cx"> implements(iweb.IResponse)
</span><span class="cx">
</span><span class="cx"> code = responsecode.OK
</span><span class="cx"> headers = None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def __init__(self):
</span><span class="cx"> self.headers = http_headers.Headers()
</span><span class="cx"> self.stream = stream.ProducerStream()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def write(self, data):
</span><span class="cx"> self.stream.write(data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def finish(self):
</span><span class="cx"> self.stream.finish()
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class TestClient(protocol.Protocol):
</span><span class="cx"> data = ""
</span><span class="cx"> done = False
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def dataReceived(self, data):
</span><del>- self.data+=data
</del><ins>+ self.data += data
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def write(self, data):
</span><span class="cx"> self.transport.write(data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def connectionLost(self, reason):
</span><span class="cx"> self.done = True
</span><span class="cx"> self.transport.loseConnection()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def loseConnection(self):
</span><span class="cx"> self.done = True
</span><span class="cx"> self.transport.loseConnection()
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class TestConnection:
</span><span class="cx"> def __init__(self):
</span><span class="cx"> self.requests = []
</span><span class="cx"> self.client = None
</span><span class="cx"> self.callLaters = []
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def fakeCallLater(self, secs, f):
</span><span class="cx"> assert secs == 0
</span><span class="cx"> self.callLaters.append(f)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class HTTPTests(unittest.TestCase):
</span><span class="cx">
</span><span class="cx"> requestClass = TestRequest
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def setUp(self):
</span><span class="cx"> super(HTTPTests, self).setUp()
</span><span class="cx">
</span><span class="lines">@@ -441,6 +479,7 @@
</span><span class="cx">
</span><span class="cx"> return cxn
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def iterate(self, cxn):
</span><span class="cx"> callLaters = cxn.callLaters
</span><span class="cx"> cxn.callLaters = []
</span><span class="lines">@@ -453,6 +492,7 @@
</span><span class="cx"> if cxn.clientToServer.shouldLose:
</span><span class="cx"> cxn.clientToServer.clearBuffer()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def compareResult(self, cxn, cmds, data):
</span><span class="cx"> self.iterate(cxn)
</span><span class="cx"> for receivedRequest, expectedCommands in map(None, cxn.requests, cmds):
</span><span class="lines">@@ -467,16 +507,20 @@
</span><span class="cx"> self.assertEquals(receivedRequest.cmds, sortedHeaderCommands)
</span><span class="cx"> self.assertEquals(cxn.client.data, data)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def assertDone(self, cxn, done=True):
</span><span class="cx"> self.iterate(cxn)
</span><span class="cx"> self.assertEquals(cxn.client.done, done)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class GracefulShutdownTestCase(HTTPTests):
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _callback(self, result):
</span><span class="cx"> self.callbackFired = True
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testAllConnectionsClosedWithoutConnectedChannels(self):
</span><span class="cx"> """
</span><span class="cx"> allConnectionsClosed( ) should fire right away if no connected channels
</span><span class="lines">@@ -487,6 +531,7 @@
</span><span class="cx"> factory.allConnectionsClosed().addCallback(self._callback)
</span><span class="cx"> self.assertTrue(self.callbackFired) # now!
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testallConnectionsClosedWithConnectedChannels(self):
</span><span class="cx"> """
</span><span class="cx"> allConnectionsClosed( ) should only fire after all connected channels
</span><span class="lines">@@ -511,11 +556,13 @@
</span><span class="cx"> self.assertTrue(self.callbackFired) # now!
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class CoreHTTPTestCase(HTTPTests):
</span><span class="cx"> # Note: these tests compare the client output using string
</span><span class="cx"> # matching. It is acceptable for this to change and break
</span><span class="cx"> # the test if you know what you are doing.
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testHTTP0_9(self, nouri=False):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cmds = [[]]
</span><span class="lines">@@ -528,7 +575,7 @@
</span><span class="cx"> # Second request which should not be handled
</span><span class="cx"> cxn.client.write("GET /two\r\n")
</span><span class="cx">
</span><del>- cmds[0] += [('init', 'GET', '/', (0,9), 0, ()), ('contentComplete',)]
</del><ins>+ cmds[0] += [('init', 'GET', '/', (0, 9), 0, ()), ('contentComplete',)]
</ins><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx">
</span><span class="cx"> response = TestResponse()
</span><span class="lines">@@ -547,9 +594,11 @@
</span><span class="cx">
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testHTTP0_9_nouri(self):
</span><span class="cx"> self.testHTTP0_9(True)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testHTTP1_0(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cmds = [[]]
</span><span class="lines">@@ -559,7 +608,7 @@
</span><span class="cx"> # Second request which should not be handled
</span><span class="cx"> cxn.client.write("GET /two HTTP/1.0\r\n\r\n")
</span><span class="cx">
</span><del>- cmds[0] += [('init', 'GET', '/', (1,0), 5,
</del><ins>+ cmds[0] += [('init', 'GET', '/', (1, 0), 5,
</ins><span class="cx"> (('Host', ['localhost']),)),
</span><span class="cx"> ('contentChunk', 'Input'),
</span><span class="cx"> ('contentComplete',)]
</span><span class="lines">@@ -582,6 +631,7 @@
</span><span class="cx">
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testHTTP1_0_keepalive(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cmds = [[]]
</span><span class="lines">@@ -592,14 +642,14 @@
</span><span class="cx"> # Third request shouldn't be handled
</span><span class="cx"> cxn.client.write("GET /three HTTP/1.0\r\n\r\n")
</span><span class="cx">
</span><del>- cmds[0] += [('init', 'GET', '/', (1,0), 5,
</del><ins>+ cmds[0] += [('init', 'GET', '/', (1, 0), 5,
</ins><span class="cx"> (('Host', ['localhost']),)),
</span><span class="cx"> ('contentChunk', 'Input'),
</span><span class="cx"> ('contentComplete',)]
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx">
</span><span class="cx"> response0 = TestResponse()
</span><del>- response0.headers.setRawHeaders("Content-Length", ("6", ))
</del><ins>+ response0.headers.setRawHeaders("Content-Length", ("6",))
</ins><span class="cx"> response0.headers.setRawHeaders("Yo", ("One", "Two"))
</span><span class="cx"> cxn.requests[0].writeResponse(response0)
</span><span class="cx"> response0.write("")
</span><span class="lines">@@ -615,13 +665,13 @@
</span><span class="cx">
</span><span class="cx"> # Now for second request:
</span><span class="cx"> cmds.append([])
</span><del>- cmds[1] += [('init', 'GET', '/two', (1,0), 0, ()),
</del><ins>+ cmds[1] += [('init', 'GET', '/two', (1, 0), 0, ()),
</ins><span class="cx"> ('contentComplete',)]
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> response1 = TestResponse()
</span><del>- response1.headers.setRawHeaders("Content-Length", ("0", ))
</del><ins>+ response1.headers.setRawHeaders("Content-Length", ("0",))
</ins><span class="cx"> cxn.requests[1].writeResponse(response1)
</span><span class="cx"> response1.write("")
</span><span class="cx">
</span><span class="lines">@@ -631,6 +681,7 @@
</span><span class="cx">
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testHTTP1_1_pipelining(self):
</span><span class="cx"> cxn = self.connect(maxPipeline=2)
</span><span class="cx"> cmds = []
</span><span class="lines">@@ -645,19 +696,19 @@
</span><span class="cx"> cxn.client.write("GET /four HTTP/1.1\r\nHost: localhost\r\n\r\n")
</span><span class="cx">
</span><span class="cx"> cmds.append([])
</span><del>- cmds[0] += [('init', 'GET', '/', (1,1), 5,
</del><ins>+ cmds[0] += [('init', 'GET', '/', (1, 1), 5,
</ins><span class="cx"> (('Host', ['localhost']),)),
</span><span class="cx"> ('contentChunk', 'Input'),
</span><span class="cx"> ('contentComplete',)]
</span><span class="cx"> cmds.append([])
</span><del>- cmds[1] += [('init', 'GET', '/two', (1,1), 0,
</del><ins>+ cmds[1] += [('init', 'GET', '/two', (1, 1), 0,
</ins><span class="cx"> (('Host', ['localhost']),)),
</span><span class="cx"> ('contentComplete',)]
</span><span class="cx">
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx">
</span><span class="cx"> response0 = TestResponse()
</span><del>- response0.headers.setRawHeaders("Content-Length", ("6", ))
</del><ins>+ response0.headers.setRawHeaders("Content-Length", ("6",))
</ins><span class="cx"> cxn.requests[0].writeResponse(response0)
</span><span class="cx"> response0.write("")
</span><span class="cx">
</span><span class="lines">@@ -672,7 +723,7 @@
</span><span class="cx">
</span><span class="cx"> # Now the third request gets read:
</span><span class="cx"> cmds.append([])
</span><del>- cmds[2] += [('init', 'GET', '/three', (1,1), 0,
</del><ins>+ cmds[2] += [('init', 'GET', '/three', (1, 1), 0,
</ins><span class="cx"> (('Host', ['localhost']),)),
</span><span class="cx"> ('contentComplete',)]
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="lines">@@ -680,7 +731,7 @@
</span><span class="cx"> # Let's write out the third request before the second.
</span><span class="cx"> # This should not cause anything to be written to the client.
</span><span class="cx"> response2 = TestResponse()
</span><del>- response2.headers.setRawHeaders("Content-Length", ("5", ))
</del><ins>+ response2.headers.setRawHeaders("Content-Length", ("5",))
</ins><span class="cx"> cxn.requests[2].writeResponse(response2)
</span><span class="cx">
</span><span class="cx"> response2.write("Three")
</span><span class="lines">@@ -689,7 +740,7 @@
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx">
</span><span class="cx"> response1 = TestResponse()
</span><del>- response1.headers.setRawHeaders("Content-Length", ("3", ))
</del><ins>+ response1.headers.setRawHeaders("Content-Length", ("3",))
</ins><span class="cx"> cxn.requests[1].writeResponse(response1)
</span><span class="cx"> response1.write("Two")
</span><span class="cx">
</span><span class="lines">@@ -700,7 +751,7 @@
</span><span class="cx">
</span><span class="cx"> # Fourth request shows up
</span><span class="cx"> cmds.append([])
</span><del>- cmds[3] += [('init', 'GET', '/four', (1,1), 0,
</del><ins>+ cmds[3] += [('init', 'GET', '/four', (1, 1), 0,
</ins><span class="cx"> (('Host', ['localhost']),)),
</span><span class="cx"> ('contentComplete',)]
</span><span class="cx"> data += "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nThree"
</span><span class="lines">@@ -718,20 +769,21 @@
</span><span class="cx"> cxn.client.loseConnection()
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testHTTP1_1_chunking(self, extraHeaders=""):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cmds = [[]]
</span><span class="cx"> data = ""
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\nHost: localhost\r\n\r\n5\r\nInput\r\n")
</span><span class="cx">
</span><del>- cmds[0] += [('init', 'GET', '/', (1,1), None,
</del><ins>+ cmds[0] += [('init', 'GET', '/', (1, 1), None,
</ins><span class="cx"> (('Host', ['localhost']),)),
</span><span class="cx"> ('contentChunk', 'Input')]
</span><span class="cx">
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx">
</span><span class="cx"> cxn.client.write("1; blahblahblah\r\na\r\n10\r\nabcdefghijklmnop\r\n")
</span><del>- cmds[0] += [('contentChunk', 'a'),('contentChunk', 'abcdefghijklmnop')]
</del><ins>+ cmds[0] += [('contentChunk', 'a'), ('contentChunk', 'abcdefghijklmnop')]
</ins><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx">
</span><span class="cx"> cxn.client.write("0\r\nRandom-Ignored-Trailer: foo\r\n\r\n")
</span><span class="lines">@@ -765,12 +817,13 @@
</span><span class="cx"> cxn.client.loseConnection()
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testHTTP1_1_expect_continue(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cmds = [[]]
</span><span class="cx"> data = ""
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\nContent-Length: 5\r\nHost: localhost\r\nExpect: 100-continue\r\n\r\n")
</span><del>- cmds[0] += [('init', 'GET', '/', (1,1), 5,
</del><ins>+ cmds[0] += [('init', 'GET', '/', (1, 1), 5,
</ins><span class="cx"> (('Expect', ['100-continue']), ('Host', ['localhost'])))]
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx">
</span><span class="lines">@@ -795,12 +848,13 @@
</span><span class="cx"> cxn.client.loseConnection()
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testHTTP1_1_expect_continue_early_reply(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cmds = [[]]
</span><span class="cx"> data = ""
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\nContent-Length: 5\r\nHost: localhost\r\nExpect: 100-continue\r\n\r\n")
</span><del>- cmds[0] += [('init', 'GET', '/', (1,1), 5,
</del><ins>+ cmds[0] += [('init', 'GET', '/', (1, 1), 5,
</ins><span class="cx"> (('Host', ['localhost']), ('Expect', ['100-continue'])))]
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx">
</span><span class="lines">@@ -817,13 +871,14 @@
</span><span class="cx"> cxn.client.loseConnection()
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testHeaderContinuation(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cmds = [[]]
</span><span class="cx"> data = ""
</span><span class="cx">
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\nHost: localhost\r\nFoo: yada\r\n yada\r\n\r\n")
</span><del>- cmds[0] += [('init', 'GET', '/', (1,1), 0,
</del><ins>+ cmds[0] += [('init', 'GET', '/', (1, 1), 0,
</ins><span class="cx"> (('Host', ['localhost']), ('Foo', ['yada yada']),)),
</span><span class="cx"> ('contentComplete',)]
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="lines">@@ -831,23 +886,26 @@
</span><span class="cx"> cxn.client.loseConnection()
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testTimeout_immediate(self):
</span><span class="cx"> # timeout 0 => timeout on first iterate call
</span><del>- cxn = self.connect(inputTimeOut = 0)
</del><ins>+ cxn = self.connect(inputTimeOut=0)
</ins><span class="cx"> return deferLater(reactor, 0, self.assertDone, cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testTimeout_inRequest(self):
</span><del>- cxn = self.connect(inputTimeOut = 0.3)
</del><ins>+ cxn = self.connect(inputTimeOut=0.3)
</ins><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\n")
</span><span class="cx"> return deferLater(reactor, 0.5, self.assertDone, cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testTimeout_betweenRequests(self):
</span><del>- cxn = self.connect(betweenRequestsTimeOut = 0.3)
</del><ins>+ cxn = self.connect(betweenRequestsTimeOut=0.3)
</ins><span class="cx"> cmds = [[]]
</span><span class="cx"> data = ""
</span><span class="cx">
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\n\r\n")
</span><del>- cmds[0] += [('init', 'GET', '/', (1,1), 0, ()),
</del><ins>+ cmds[0] += [('init', 'GET', '/', (1, 1), 0, ()),
</ins><span class="cx"> ('contentComplete',)]
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx">
</span><span class="lines">@@ -861,6 +919,7 @@
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx"> return deferLater(reactor, 0.5, self.assertDone, cxn) # Wait for timeout
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testTimeout_idleRequest(self):
</span><span class="cx"> cxn = self.connect(idleTimeOut=0.3)
</span><span class="cx"> cmds = [[]]
</span><span class="lines">@@ -873,6 +932,7 @@
</span><span class="cx">
</span><span class="cx"> return deferLater(reactor, 0.5, self.assertDone, cxn) # Wait for timeout
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testTimeout_abortRequest(self):
</span><span class="cx"> cxn = self.connect(allowPersistentConnections=False, closeTimeOut=0.3)
</span><span class="cx"> cxn.client.transport.loseConnection = lambda : None
</span><span class="lines">@@ -897,19 +957,20 @@
</span><span class="cx"> self.assertTrue(cxn.serverToClient.aborted)
</span><span class="cx"> return deferLater(reactor, 0.5, self.assertDone, cxn) # Wait for timeout
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testConnectionCloseRequested(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cmds = [[]]
</span><span class="cx"> data = ""
</span><span class="cx">
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\n\r\n")
</span><del>- cmds[0] += [('init', 'GET', '/', (1,1), 0, ()),
</del><ins>+ cmds[0] += [('init', 'GET', '/', (1, 1), 0, ()),
</ins><span class="cx"> ('contentComplete',)]
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx">
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\nConnection: close\r\n\r\n")
</span><span class="cx"> cmds.append([])
</span><del>- cmds[1] += [('init', 'GET', '/', (1,1), 0, ()),
</del><ins>+ cmds[1] += [('init', 'GET', '/', (1, 1), 0, ()),
</ins><span class="cx"> ('contentComplete',)]
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx">
</span><span class="lines">@@ -930,6 +991,7 @@
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testConnectionKeepAliveOff(self):
</span><span class="cx"> cxn = self.connect(allowPersistentConnections=False)
</span><span class="cx"> cmds = [[]]
</span><span class="lines">@@ -950,6 +1012,7 @@
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testExtraCRLFs(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cmds = [[]]
</span><span class="lines">@@ -957,7 +1020,7 @@
</span><span class="cx">
</span><span class="cx"> # Some broken clients (old IEs) send an extra CRLF after post
</span><span class="cx"> cxn.client.write("POST / HTTP/1.1\r\nContent-Length: 5\r\nHost: localhost\r\n\r\nInput\r\n")
</span><del>- cmds[0] += [('init', 'POST', '/', (1,1), 5,
</del><ins>+ cmds[0] += [('init', 'POST', '/', (1, 1), 5,
</ins><span class="cx"> (('Host', ['localhost']),)),
</span><span class="cx"> ('contentChunk', 'Input'),
</span><span class="cx"> ('contentComplete',)]
</span><span class="lines">@@ -966,20 +1029,21 @@
</span><span class="cx">
</span><span class="cx"> cxn.client.write("GET /two HTTP/1.1\r\n\r\n")
</span><span class="cx"> cmds.append([])
</span><del>- cmds[1] += [('init', 'GET', '/two', (1,1), 0, ()),
</del><ins>+ cmds[1] += [('init', 'GET', '/two', (1, 1), 0, ()),
</ins><span class="cx"> ('contentComplete',)]
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx">
</span><span class="cx"> cxn.client.loseConnection()
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testDisallowPersistentConnections(self):
</span><span class="cx"> cxn = self.connect(allowPersistentConnections=False)
</span><span class="cx"> cmds = [[]]
</span><span class="cx"> data = ""
</span><span class="cx">
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\nHost: localhost\r\n\r\nGET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
</span><del>- cmds[0] += [('init', 'GET', '/', (1,1), 0,
</del><ins>+ cmds[0] += [('init', 'GET', '/', (1, 1), 0,
</ins><span class="cx"> (('Host', ['localhost']),)),
</span><span class="cx"> ('contentComplete',)]
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="lines">@@ -991,6 +1055,7 @@
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testIgnoreBogusContentLength(self):
</span><span class="cx"> # Ensure that content-length is ignored when transfer-encoding
</span><span class="cx"> # is also specified.
</span><span class="lines">@@ -999,7 +1064,7 @@
</span><span class="cx"> data = ""
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\nContent-Length: 100\r\nTransfer-Encoding: chunked\r\nHost: localhost\r\n\r\n5\r\nInput\r\n")
</span><span class="cx">
</span><del>- cmds[0] += [('init', 'GET', '/', (1,1), None,
</del><ins>+ cmds[0] += [('init', 'GET', '/', (1, 1), None,
</ins><span class="cx"> (('Host', ['localhost']),)),
</span><span class="cx"> ('contentChunk', 'Input')]
</span><span class="cx">
</span><span class="lines">@@ -1018,56 +1083,67 @@
</span><span class="cx"> cxn.client.loseConnection()
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class ErrorTestCase(HTTPTests):
</span><ins>+
</ins><span class="cx"> def assertStartsWith(self, first, second, msg=None):
</span><span class="cx"> self.assert_(first.startswith(second), '%r.startswith(%r)' % (first, second))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def checkError(self, cxn, code):
</span><span class="cx"> self.iterate(cxn)
</span><del>- self.assertStartsWith(cxn.client.data, "HTTP/1.1 %d "%code)
</del><ins>+ self.assertStartsWith(cxn.client.data, "HTTP/1.1 %d " % code)
</ins><span class="cx"> self.assertIn("\r\nConnection: close\r\n", cxn.client.data)
</span><span class="cx"> # Ensure error messages have a defined content-length.
</span><span class="cx"> self.assertIn("\r\nContent-Length:", cxn.client.data)
</span><span class="cx"> self.assertDone(cxn)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testChunkingError1(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\nasdf\r\n")
</span><span class="cx">
</span><span class="cx"> self.checkError(cxn, 400)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testChunkingError2(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n1\r\nblahblah\r\n")
</span><span class="cx">
</span><span class="cx"> self.checkError(cxn, 400)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testChunkingError3(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n-1\r\nasdf\r\n")
</span><span class="cx">
</span><span class="cx"> self.checkError(cxn, 400)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testTooManyHeaders(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\n")
</span><del>- cxn.client.write("Foo: Bar\r\n"*5000)
</del><ins>+ cxn.client.write("Foo: Bar\r\n" * 5000)
</ins><span class="cx">
</span><span class="cx"> self.checkError(cxn, 400)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testLineTooLong(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\n")
</span><del>- cxn.client.write("Foo: "+("Bar"*10000))
</del><ins>+ cxn.client.write("Foo: " + ("Bar" * 10000))
</ins><span class="cx">
</span><span class="cx"> self.checkError(cxn, 400)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testLineTooLong2(self):
</span><span class="cx"> cxn = self.connect()
</span><del>- cxn.client.write("GET "+("/Bar")*10000 +" HTTP/1.1\r\n")
</del><ins>+ cxn.client.write("GET " + ("/Bar") * 10000 + " HTTP/1.1\r\n")
</ins><span class="cx">
</span><span class="cx"> self.checkError(cxn, 414)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testNoColon(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\n")
</span><span class="lines">@@ -1096,57 +1172,67 @@
</span><span class="cx">
</span><span class="cx"> self.checkError(cxn, 400)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testWrongProtocol(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cxn.client.write("GET / Foobar/1.0\r\n")
</span><span class="cx">
</span><span class="cx"> self.checkError(cxn, 400)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testBadProtocolVersion(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cxn.client.write("GET / HTTP/1\r\n")
</span><span class="cx">
</span><span class="cx"> self.checkError(cxn, 400)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testBadProtocolVersion2(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cxn.client.write("GET / HTTP/-1.0\r\n")
</span><span class="cx">
</span><span class="cx"> self.checkError(cxn, 400)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testWrongProtocolVersion(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cxn.client.write("GET / HTTP/2.0\r\n")
</span><span class="cx">
</span><span class="cx"> self.checkError(cxn, 505)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testUnsupportedTE(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\n")
</span><span class="cx"> cxn.client.write("Transfer-Encoding: blahblahblah, chunked\r\n\r\n")
</span><span class="cx"> self.checkError(cxn, 501)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testTEWithoutChunked(self):
</span><span class="cx"> cxn = self.connect()
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\n")
</span><span class="cx"> cxn.client.write("Transfer-Encoding: gzip\r\n\r\n")
</span><span class="cx"> self.checkError(cxn, 400)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class PipelinedErrorTestCase(ErrorTestCase):
</span><span class="cx"> # Make sure that even low level reading errors don't corrupt the data stream,
</span><span class="cx"> # but always wait until their turn to respond.
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def connect(self):
</span><span class="cx"> cxn = ErrorTestCase.connect(self)
</span><span class="cx"> cxn.client.write("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
</span><span class="cx">
</span><del>- cmds = [[('init', 'GET', '/', (1,1), 0,
</del><ins>+ cmds = [[('init', 'GET', '/', (1, 1), 0,
</ins><span class="cx"> (('Host', ['localhost']),)),
</span><del>- ('contentComplete', )]]
</del><ins>+ ('contentComplete',)]]
</ins><span class="cx"> data = ""
</span><span class="cx"> self.compareResult(cxn, cmds, data)
</span><span class="cx"> return cxn
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def checkError(self, cxn, code):
</span><span class="cx"> self.iterate(cxn)
</span><span class="cx"> self.assertEquals(cxn.client.data, '')
</span><span class="lines">@@ -1167,7 +1253,9 @@
</span><span class="cx"> ErrorTestCase.checkError(self, cxn, code)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class SimpleFactory(channel.HTTPFactory):
</span><ins>+
</ins><span class="cx"> def buildProtocol(self, addr):
</span><span class="cx"> # Do a bunch of crazy crap just so that the test case can know when the
</span><span class="cx"> # connection is done.
</span><span class="lines">@@ -1180,21 +1268,28 @@
</span><span class="cx"> self.conn = p
</span><span class="cx"> return p
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class SimpleRequest(http.Request):
</span><ins>+
</ins><span class="cx"> def process(self):
</span><span class="cx"> response = TestResponse()
</span><span class="cx"> if self.uri == "/error":
</span><del>- response.code=402
</del><ins>+ response.code = 402
</ins><span class="cx"> elif self.uri == "/forbidden":
</span><del>- response.code=403
</del><ins>+ response.code = 403
</ins><span class="cx"> else:
</span><del>- response.code=404
</del><ins>+ response.code = 404
</ins><span class="cx"> response.write("URI %s unrecognized." % self.uri)
</span><span class="cx"> response.finish()
</span><span class="cx"> self.writeResponse(response)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class AbstractServerTestMixin:
</span><ins>+
</ins><span class="cx"> type = None
</span><ins>+
</ins><span class="cx"> def testBasicWorkingness(self):
</span><span class="cx"> args = ('-u', util.sibpath(__file__, "simple_client.py"), "basic",
</span><span class="cx"> str(self.port), self.type)
</span><span class="lines">@@ -1202,12 +1297,14 @@
</span><span class="cx"> utils.getProcessOutputAndValue(sys.executable, args=args,
</span><span class="cx"> env=os.environ)
</span><span class="cx"> )
</span><del>- yield d; out,err,code = d.getResult()
</del><ins>+ yield d
+ out, err, code = d.getResult()
</ins><span class="cx">
</span><span class="cx"> self.assertEquals(code, 0, "Error output:\n%s" % (err,))
</span><span class="cx"> self.assertEquals(out, "HTTP/1.1 402 Payment Required\r\nContent-Length: 0\r\nConnection: close\r\n\r\n")
</span><span class="cx"> testBasicWorkingness = deferredGenerator(testBasicWorkingness)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testLingeringClose(self):
</span><span class="cx"> args = ('-u', util.sibpath(__file__, "simple_client.py"),
</span><span class="cx"> "lingeringClose", str(self.port), self.type)
</span><span class="lines">@@ -1215,15 +1312,20 @@
</span><span class="cx"> utils.getProcessOutputAndValue(sys.executable, args=args,
</span><span class="cx"> env=os.environ)
</span><span class="cx"> )
</span><del>- yield d; out,err,code = d.getResult()
</del><ins>+ yield d
+ out, err, code = d.getResult()
</ins><span class="cx"> self.assertEquals(code, 0, "Error output:\n%s" % (err,))
</span><span class="cx"> self.assertEquals(out, "HTTP/1.1 402 Payment Required\r\nContent-Length: 0\r\nConnection: close\r\n\r\n")
</span><span class="cx"> testLingeringClose = deferredGenerator(testLingeringClose)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class TCPServerTest(unittest.TestCase, AbstractServerTestMixin):
</span><ins>+
</ins><span class="cx"> type = 'tcp'
</span><ins>+
</ins><span class="cx"> def setUp(self):
</span><del>- factory=SimpleFactory(requestFactory=SimpleRequest)
</del><ins>+ factory = SimpleFactory(requestFactory=SimpleRequest)
</ins><span class="cx">
</span><span class="cx"> factory.testcase = self
</span><span class="cx"> self.factory = factory
</span><span class="lines">@@ -1232,6 +1334,7 @@
</span><span class="cx"> self.socket = reactor.listenTCP(0, factory)
</span><span class="cx"> self.port = self.socket.getHost().port
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def tearDown(self):
</span><span class="cx"> # Make sure the listening port is closed
</span><span class="cx"> d = defer.maybeDeferred(self.socket.stopListening)
</span><span class="lines">@@ -1260,7 +1363,7 @@
</span><span class="cx"> def setUp(self):
</span><span class="cx"> HTTPChannel.allowPersistentConnections = True
</span><span class="cx"> sCTX = ssl.DefaultOpenSSLContextFactory(certPath, certPath)
</span><del>- factory=SimpleFactory(requestFactory=SimpleRequest)
</del><ins>+ factory = SimpleFactory(requestFactory=SimpleRequest)
</ins><span class="cx">
</span><span class="cx"> factory.testcase = self
</span><span class="cx"> self.factory = factory
</span><span class="lines">@@ -1269,6 +1372,7 @@
</span><span class="cx"> self.socket = reactor.listenSSL(0, factory, sCTX)
</span><span class="cx"> self.port = self.socket.getHost().port
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def tearDown(self):
</span><span class="cx"> # Make sure the listening port is closed
</span><span class="cx"> d = defer.maybeDeferred(self.socket.stopListening)
</span><span class="lines">@@ -1279,6 +1383,7 @@
</span><span class="cx"> return self.connlost
</span><span class="cx"> return d.addCallback(finish)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def testLingeringClose(self):
</span><span class="cx"> return super(SSLServerTest, self).testLingeringClose()
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxweb2testtest_http_headerspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/test/test_http_headers.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/test/test_http_headers.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/test/test_http_headers.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -244,7 +244,7 @@
</span><span class="cx"> {'private': ['set-cookie', 'set-cookie2'],
</span><span class="cx"> 'no-cache': ['proxy-authenticate']},
</span><span class="cx"> ['private="Set-Cookie, Set-Cookie2"', 'no-cache="Proxy-Authenticate"']),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Cache-Control", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -252,7 +252,7 @@
</span><span class="cx"> table = (
</span><span class="cx"> ("close", ['close', ]),
</span><span class="cx"> ("close, foo-bar", ['close', 'foo-bar'])
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Connection", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -271,7 +271,7 @@
</span><span class="cx"> table = (
</span><span class="cx"> ('chunked', ['chunked']),
</span><span class="cx"> ('gzip, chunked', ['gzip', 'chunked'])
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Transfer-Encoding", table)
</span><span class="cx">
</span><span class="cx"> # def testUpgrade(self):
</span><span class="lines">@@ -305,13 +305,13 @@
</span><span class="cx"> http_headers.MimeType('text', 'html', (('level', '1'),)): 1.0,
</span><span class="cx"> http_headers.MimeType('*', '*'): 1.0}),
</span><span class="cx">
</span><del>- ("text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5",
</del><ins>+ ("text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5",
</ins><span class="cx"> {http_headers.MimeType('text', '*'): 0.3,
</span><span class="cx"> http_headers.MimeType('text', 'html'): 0.7,
</span><span class="cx"> http_headers.MimeType('text', 'html', (('level', '1'),)): 1.0,
</span><span class="cx"> http_headers.MimeType('text', 'html', (('level', '2'),)): 0.4,
</span><span class="cx"> http_headers.MimeType('*', '*'): 0.5}),
</span><del>- )
</del><ins>+ )
</ins><span class="cx">
</span><span class="cx"> self.runRoundtripTest("Accept", table)
</span><span class="cx">
</span><span class="lines">@@ -329,7 +329,7 @@
</span><span class="cx"> ("",
</span><span class="cx"> {'iso-8859-1': 1.0},
</span><span class="cx"> ["iso-8859-1"]), # Yes this is an actual change -- we'll say that's okay. :)
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Accept-Charset", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -347,7 +347,7 @@
</span><span class="cx"> ("gzip;q=1.0, identity;q=0.5, *;q=0",
</span><span class="cx"> {'gzip': 1.0, 'identity': 0.5, '*': 0},
</span><span class="cx"> ["gzip", "identity;q=0.5", "*;q=0"]),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Accept-Encoding", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -357,7 +357,7 @@
</span><span class="cx"> {'da': 1.0, 'en-gb': 0.8, 'en': 0.7}),
</span><span class="cx"> ("*",
</span><span class="cx"> {'*': 1}),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Accept-Language", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -369,7 +369,7 @@
</span><span class="cx"> ('Digest nonce="bar", realm="foo", username="baz", response="bax"',
</span><span class="cx"> ('digest', 'nonce="bar", realm="foo", username="baz", response="bax"'),
</span><span class="cx"> ['digest', 'nonce="bar"', 'realm="foo"', 'username="baz"', 'response="bax"'])
</span><del>- )
</del><ins>+ )
</ins><span class="cx">
</span><span class="cx"> self.runRoundtripTest("Authorization", table)
</span><span class="cx">
</span><span class="lines">@@ -381,10 +381,12 @@
</span><span class="cx"> ('name,"blah=value,"', [Cookie('name,"blah', 'value,"')]),
</span><span class="cx"> ('name,"blah = value," ', [Cookie('name,"blah', 'value,"')], ['name,"blah=value,"']),
</span><span class="cx"> ("`~!@#$%^&*()-_+[{]}\\|:'\",<.>/?=`~!@#$%^&*()-_+[{]}\\|:'\",<.>/?", [Cookie("`~!@#$%^&*()-_+[{]}\\|:'\",<.>/?", "`~!@#$%^&*()-_+[{]}\\|:'\",<.>/?")]),
</span><del>- ('name,"blah = value," ; name2=val2',
- [Cookie('name,"blah', 'value,"'), Cookie('name2', 'val2')],
- ['name,"blah=value,"', 'name2=val2']),
- )
</del><ins>+ (
+ 'name,"blah = value," ; name2=val2',
+ [Cookie('name,"blah', 'value,"'), Cookie('name2', 'val2')],
+ ['name,"blah=value,"', 'name2=val2']
+ ),
+ )
</ins><span class="cx"> self.runRoundtripTest("Cookie", table)
</span><span class="cx">
</span><span class="cx"> # newstyle RFC2965 Cookie
</span><span class="lines">@@ -399,7 +401,7 @@
</span><span class="cx"> ('$Version = 1, NAME = "qq\\"qq",Frob=boo',
</span><span class="cx"> [Cookie('name', 'qq"qq', version=1), Cookie('frob', 'boo', version=1)],
</span><span class="cx"> ['$Version="1";name="qq\\"qq";frob="boo"']),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Cookie", table2)
</span><span class="cx">
</span><span class="cx"> # Generate only!
</span><span class="lines">@@ -413,7 +415,7 @@
</span><span class="cx"> '$Version="1";name2="value2"'),
</span><span class="cx"> ([Cookie('name', 'qq"qq'), Cookie('name2', 'value2', version=1)],
</span><span class="cx"> '$Version="1";name="qq\\"qq";name2="value2"'),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> for row in table3:
</span><span class="cx"> self.assertEquals(generateHeader("Cookie", row[0]), [row[1], ])
</span><span class="cx">
</span><span class="lines">@@ -425,7 +427,7 @@
</span><span class="cx"> ('name,"blah = value, ; expires="Sun, 09 Sep 2001 01:46:40 GMT"',
</span><span class="cx"> [Cookie('name,"blah', 'value,', expires=1000000000)],
</span><span class="cx"> ['name,"blah=value,', 'expires=Sun, 09 Sep 2001 01:46:40 GMT']),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Set-Cookie", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -433,7 +435,7 @@
</span><span class="cx"> table = (
</span><span class="cx"> ('name="value"; Comment="YadaYada"; CommentURL="http://frobnotz/"; Discard; Domain="blah.blah"; Max-Age=10; Path="/foo"; Port="80,8080"; Secure; Version="1"',
</span><span class="cx"> [Cookie("name", "value", comment="YadaYada", commenturl="http://frobnotz/", discard=True, domain="blah.blah", expires=1000000000, path="/foo", ports=(80, 8080), secure=True, version=1)]),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Set-Cookie2", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -445,7 +447,7 @@
</span><span class="cx"> {'foobar': ('twiddle',)}),
</span><span class="cx"> ("foo=bar;a=b;c",
</span><span class="cx"> {'foo': ('bar', ('a', 'b'), ('c', None))})
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Expect", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -457,7 +459,7 @@
</span><span class="cx"> [("return", "representation", [])]),
</span><span class="cx"> ("return =minimal;arg1;arg2=val2",
</span><span class="cx"> [("return", "minimal", [("arg1", None), ("arg2", "val2")])]),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Prefer", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -473,10 +475,10 @@
</span><span class="cx"> table = (
</span><span class="cx"> ('"xyzzy"', [http_headers.ETag('xyzzy')]),
</span><span class="cx"> ('"xyzzy", "r2d2xxxx", "c3piozzzz"', [http_headers.ETag('xyzzy'),
</span><del>- http_headers.ETag('r2d2xxxx'),
- http_headers.ETag('c3piozzzz')]),
</del><ins>+ http_headers.ETag('r2d2xxxx'),
+ http_headers.ETag('c3piozzzz')]),
</ins><span class="cx"> ('*', ['*']),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("If-Match", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -486,7 +488,7 @@
</span><span class="cx"> table = (
</span><span class="cx"> ("Sun, 09 Sep 2001 01:46:40 GMT", 1000000000),
</span><span class="cx"> ("Sun, 09 Sep 2001 01:46:40 GMT; length=500", 1000000000, ["Sun, 09 Sep 2001 01:46:40 GMT"]),
</span><del>- )
</del><ins>+ )
</ins><span class="cx">
</span><span class="cx"> self.runRoundtripTest("If-Modified-Since", table)
</span><span class="cx">
</span><span class="lines">@@ -501,7 +503,7 @@
</span><span class="cx"> http_headers.ETag('r2d2xxxx', weak=True),
</span><span class="cx"> http_headers.ETag('c3piozzzz', weak=True)]),
</span><span class="cx"> ('*', ['*']),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("If-None-Match", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -511,7 +513,7 @@
</span><span class="cx"> ('W/"xyzzy"', http_headers.ETag('xyzzy', weak=True)),
</span><span class="cx"> ('W/"xyzzy"', http_headers.ETag('xyzzy', weak=True)),
</span><span class="cx"> ("Sun, 09 Sep 2001 01:46:40 GMT", 1000000000),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("If-Range", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -534,7 +536,7 @@
</span><span class="cx"> ("bytes=-500", ('bytes', [(None, 500), ])),
</span><span class="cx"> ("bytes=9500-", ('bytes', [(9500, None), ])),
</span><span class="cx"> ("bytes=0-0,-1", ('bytes', [(0, 0), (None, 1)])),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Range", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -548,7 +550,7 @@
</span><span class="cx"> ("deflate", {'deflate': 1}),
</span><span class="cx"> ("", {}),
</span><span class="cx"> ("trailers, deflate;q=0.5", {'trailers': 1, 'deflate': 0.5}),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("TE", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -571,7 +573,7 @@
</span><span class="cx"> ('"xyzzy"', http_headers.ETag('xyzzy')),
</span><span class="cx"> ('W/"xyzzy"', http_headers.ETag('xyzzy', weak=True)),
</span><span class="cx"> ('""', http_headers.ETag('')),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("ETag", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -589,7 +591,7 @@
</span><span class="cx"> table = (
</span><span class="cx"> ("Sun, 09 Sep 2001 01:46:40 GMT", 1000000000, ["10"]),
</span><span class="cx"> ("120", 999999990 + 120),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Retry-After", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -601,7 +603,7 @@
</span><span class="cx"> table = (
</span><span class="cx"> ("*", ["*"]),
</span><span class="cx"> ("Accept, Accept-Encoding", ["accept", "accept-encoding"], ["accept", "accept-encoding"])
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Vary", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -685,14 +687,14 @@
</span><span class="cx"> table = (
</span><span class="cx"> ("GET", ['GET', ]),
</span><span class="cx"> ("GET, HEAD, PUT", ['GET', 'HEAD', 'PUT']),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Allow", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def testContentEncoding(self):
</span><span class="cx"> table = (
</span><span class="cx"> ("gzip", ['gzip', ]),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Content-Encoding", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -700,7 +702,7 @@
</span><span class="cx"> table = (
</span><span class="cx"> ("da", ['da', ]),
</span><span class="cx"> ("mi, en", ['mi', 'en']),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Content-Language", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -729,7 +731,7 @@
</span><span class="cx"> ("bytes 734-1233/*", ("bytes", 734, 1233, None)),
</span><span class="cx"> ("bytes */1234", ("bytes", None, None, 1234)),
</span><span class="cx"> ("bytes */*", ("bytes", None, None, None))
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Content-Range", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -737,7 +739,7 @@
</span><span class="cx"> table = (
</span><span class="cx"> ("text/html;charset=iso-8859-4", http_headers.MimeType('text', 'html', (('charset', 'iso-8859-4'),))),
</span><span class="cx"> ("text/html", http_headers.MimeType('text', 'html')),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Content-Type", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -745,7 +747,7 @@
</span><span class="cx"> table = (
</span><span class="cx"> ("attachment;filename=foo.txt", http_headers.MimeDisposition('attachment', (('filename', 'foo.txt'),))),
</span><span class="cx"> ("inline", http_headers.MimeDisposition('inline')),
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> self.runRoundtripTest("Content-Disposition", table)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -837,12 +839,9 @@
</span><span class="cx"> """Test that various uses of the constructer are equal
</span><span class="cx"> """
</span><span class="cx">
</span><del>- kwargMime = http_headers.MimeDisposition('attachment',
- key='value')
- dictMime = http_headers.MimeDisposition('attachment',
- {'key': 'value'})
- tupleMime = http_headers.MimeDisposition('attachment',
- (('key', 'value'),))
</del><ins>+ kwargMime = http_headers.MimeDisposition('attachment', key='value')
+ dictMime = http_headers.MimeDisposition('attachment', {'key': 'value'})
+ tupleMime = http_headers.MimeDisposition('attachment', (('key', 'value'),))
</ins><span class="cx">
</span><span class="cx"> stringMime = http_headers.MimeDisposition.fromString('attachment;key=value')
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxweb2testtest_httpauthpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/test/test_httpauth.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/test/test_httpauth.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/test/test_httpauth.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -32,6 +32,7 @@
</span><span class="cx"> """
</span><span class="cx"> return nonce
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _fakeStaticTime():
</span><span class="cx"> """
</span><span class="cx"> Return a stable time
</span><span class="lines">@@ -77,8 +78,7 @@
</span><span class="cx"> Test acceptance of username/password in basic auth.
</span><span class="cx"> """
</span><span class="cx"> response = base64.encodestring('%s:%s' % (
</span><del>- self.username,
- self.password))
</del><ins>+ self.username, self.password))
</ins><span class="cx">
</span><span class="cx"> d = self.credentialFactory.decode(response, _trivial_GET)
</span><span class="cx"> return d.addCallback(
</span><span class="lines">@@ -90,8 +90,7 @@
</span><span class="cx"> Incorrect passwords cause auth to fail.
</span><span class="cx"> """
</span><span class="cx"> response = base64.encodestring('%s:%s' % (
</span><del>- self.username,
- 'incorrectPassword'))
</del><ins>+ self.username, 'incorrectPassword'))
</ins><span class="cx">
</span><span class="cx"> d = self.credentialFactory.decode(response, _trivial_GET)
</span><span class="cx"> return d.addCallback(
</span><span class="lines">@@ -103,8 +102,7 @@
</span><span class="cx"> Responses that have incorrect padding cause auth to fail.
</span><span class="cx"> """
</span><span class="cx"> response = base64.encodestring('%s:%s' % (
</span><del>- self.username,
- self.password))
</del><ins>+ self.username, self.password))
</ins><span class="cx">
</span><span class="cx"> response = response.strip('=')
</span><span class="cx">
</span><span class="lines">@@ -164,6 +162,7 @@
</span><span class="cx"> self.credentialFactory = digest.DigestCredentialFactory('md5',
</span><span class="cx"> 'test realm')
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getDigestResponse(self, challenge, ncount):
</span><span class="cx"> """
</span><span class="cx"> Calculate the response for the given challenge
</span><span class="lines">@@ -180,9 +179,10 @@
</span><span class="cx"> nonce,
</span><span class="cx"> cnonce),
</span><span class="cx"> algo, nonce, ncount, cnonce, qop, "GET", "/write/", None
</span><del>- )
</del><ins>+ )
</ins><span class="cx"> return expected
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_getChallenge(self):
</span><span class="cx"> """
</span><span class="cx"> Test that all the required fields exist in the challenge,
</span><span class="lines">@@ -194,8 +194,8 @@
</span><span class="cx"> self.assertEquals(challenge['qop'], 'auth')
</span><span class="cx"> self.assertEquals(challenge['realm'], 'test realm')
</span><span class="cx"> self.assertEquals(challenge['algorithm'], 'md5')
</span><del>- self.assertTrue(challenge.has_key("nonce"))
- self.assertTrue(challenge.has_key("opaque"))
</del><ins>+ self.assertTrue("nonce" in challenge)
+ self.assertTrue("opaque" in challenge)
</ins><span class="cx"> return d.addCallback(_test)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -266,6 +266,7 @@
</span><span class="cx"> _trivial_GET)
</span><span class="cx"> self.assertEquals(str(e), "Invalid response, no username given.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_noNonce(self):
</span><span class="cx"> """
</span><span class="cx"> Test that login fails when our response does not contain a nonce
</span><span class="lines">@@ -277,6 +278,7 @@
</span><span class="cx"> _trivial_GET)
</span><span class="cx"> self.assertEquals(str(e), "Invalid response, no nonce given.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_noOpaque(self):
</span><span class="cx"> """
</span><span class="cx"> Test that login fails when our response does not contain a nonce
</span><span class="lines">@@ -288,6 +290,7 @@
</span><span class="cx"> _trivial_GET)
</span><span class="cx"> self.assertEquals(str(e), "Invalid response, no opaque given.")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_checkHash(self):
</span><span class="cx"> """
</span><span class="cx"> Check that given a hash of the form 'username:realm:password'
</span><span class="lines">@@ -296,9 +299,9 @@
</span><span class="cx"> d = self._createAndDecodeChallenge()
</span><span class="cx"> def _test(creds):
</span><span class="cx"> self.failUnless(creds.checkHash(
</span><del>- md5('username:test realm:password').hexdigest()))
</del><ins>+ md5('username:test realm:password').hexdigest()))
</ins><span class="cx"> self.failIf(creds.checkHash(
</span><del>- md5('username:test realm:bogus').hexdigest()))
</del><ins>+ md5('username:test realm:bogus').hexdigest()))
</ins><span class="cx"> return d.addCallback(_test)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -319,7 +322,7 @@
</span><span class="cx"> clientAddress.host)
</span><span class="cx">
</span><span class="cx"> badOpaque = ('foo-%s' % (
</span><del>- 'nonce,clientip'.encode('base64').strip('\n'),))
</del><ins>+ 'nonce,clientip'.encode('base64').strip('\n'),))
</ins><span class="cx">
</span><span class="cx"> self.assertRaises(
</span><span class="cx"> error.LoginFailed,
</span><span class="lines">@@ -454,7 +457,7 @@
</span><span class="cx"> ("user", "realm", "password", "preHA1"),
</span><span class="cx"> (None, "realm", None, "preHA1"),
</span><span class="cx"> (None, None, "password", "preHA1"),
</span><del>- )
</del><ins>+ )
</ins><span class="cx">
</span><span class="cx"> for pszUsername, pszRealm, pszPassword, preHA1 in arguments:
</span><span class="cx"> self.assertRaises(
</span><span class="lines">@@ -467,7 +470,7 @@
</span><span class="cx"> "nonce",
</span><span class="cx"> "cnonce",
</span><span class="cx"> preHA1=preHA1
</span><del>- )
</del><ins>+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_noNewlineOpaque(self):
</span><span class="lines">@@ -500,6 +503,7 @@
</span><span class="cx"> self.username = username
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class TestAuthRealm(object):
</span><span class="cx"> """
</span><span class="cx"> Test realm that supports the IHTTPUser interface
</span><span class="lines">@@ -517,6 +521,7 @@
</span><span class="cx"> raise NotImplementedError("Only IHTTPUser interface is supported")
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class ProtectedResource(test_server.BaseTestResource):
</span><span class="cx"> """
</span><span class="cx"> A test resource for use with HTTPAuthWrapper that holds on to it's
</span><span class="lines">@@ -531,11 +536,13 @@
</span><span class="cx"> self.request = req
</span><span class="cx"> return super(ProtectedResource, self).render(req)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def locateChild(self, req, segments):
</span><span class="cx"> self.segments = segments
</span><span class="cx"> return super(ProtectedResource, self).locateChild(req, segments)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class NonAnonymousResource(test_server.BaseTestResource):
</span><span class="cx"> """
</span><span class="cx"> A resource that forces authentication by raising an
</span><span class="lines">@@ -558,6 +565,7 @@
</span><span class="cx"> return super(NonAnonymousResource, self).render(req)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class HTTPAuthResourceTest(test_server.BaseCase):
</span><span class="cx"> """
</span><span class="cx"> Tests for the HTTPAuthWrapper Resource
</span><span class="lines">@@ -580,6 +588,7 @@
</span><span class="cx"> self.protectedResource = ProtectedResource()
</span><span class="cx"> self.protectedResource.responseText = "You shouldn't see me."
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def tearDown(self):
</span><span class="cx"> """
</span><span class="cx"> Clean up by getting rid of the portal, credentialFactory, and
</span><span class="lines">@@ -589,6 +598,7 @@
</span><span class="cx"> del self.credFactory
</span><span class="cx"> del self.protectedResource
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_authenticatedRequest(self):
</span><span class="cx"> """
</span><span class="cx"> Test that after successful authentication the request provides
</span><span class="lines">@@ -627,6 +637,7 @@
</span><span class="cx"> d.addCallback(checkRequest)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_allowedMethods(self):
</span><span class="cx"> """
</span><span class="cx"> Test that unknown methods result in a 401 instead of a 405 when
</span><span class="lines">@@ -650,6 +661,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_unauthorizedResponse(self):
</span><span class="cx"> """
</span><span class="cx"> Test that a request with no credentials results in a
</span><span class="lines">@@ -678,6 +690,7 @@
</span><span class="cx">
</span><span class="cx"> return d.addCallback(makeDeepRequest)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_badCredentials(self):
</span><span class="cx"> """
</span><span class="cx"> Test that a request with bad credentials results in a valid
</span><span class="lines">@@ -700,6 +713,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_successfulLogin(self):
</span><span class="cx"> """
</span><span class="cx"> Test that a request with good credentials results in the
</span><span class="lines">@@ -721,6 +735,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_wrongScheme(self):
</span><span class="cx"> """
</span><span class="cx"> Test that a request with credentials for a scheme that is not
</span><span class="lines">@@ -743,6 +758,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_multipleWWWAuthenticateSchemes(self):
</span><span class="cx"> """
</span><span class="cx"> Test that our unauthorized response can contain challenges for
</span><span class="lines">@@ -764,6 +780,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_authorizationAgainstMultipleSchemes(self):
</span><span class="cx"> """
</span><span class="cx"> Test that we can successfully authenticate when presented
</span><span class="lines">@@ -774,7 +791,7 @@
</span><span class="cx"> self.protectedResource,
</span><span class="cx"> (basic.BasicCredentialFactory('test realm'),
</span><span class="cx"> FakeDigestCredentialFactory('md5', 'test realm')),
</span><del>- self.portal,
</del><ins>+ self.portal,
</ins><span class="cx"> interfaces=(IHTTPUser,))
</span><span class="cx">
</span><span class="cx"> def respondBasic(ign):
</span><span class="lines">@@ -782,9 +799,8 @@
</span><span class="cx">
</span><span class="cx"> d = self.assertResponse((root, 'http://localhost/',
</span><span class="cx"> {'authorization':
</span><del>- ('basic', credentials)}),
- (200,
- {}, None))
</del><ins>+ ('basic', credentials)}),
+ (200, {}, None))
</ins><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><span class="lines">@@ -805,6 +821,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_wrappedResourceGetsFullSegments(self):
</span><span class="cx"> """
</span><span class="cx"> Test that the wrapped resource gets all the URL segments in it's
</span><span class="lines">@@ -833,6 +850,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_invalidCredentials(self):
</span><span class="cx"> """
</span><span class="cx"> Malformed or otherwise invalid credentials (as determined by
</span><span class="lines">@@ -854,6 +872,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_anonymousAuthentication(self):
</span><span class="cx"> """
</span><span class="cx"> If our portal has a credentials checker for IAnonymous credentials
</span><span class="lines">@@ -883,10 +902,11 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_forceAuthentication(self):
</span><span class="cx"> """
</span><span class="cx"> Test that if an HTTPError with an Unauthorized status code is raised
</span><del>- from within our protected resource, we add the WWW-Authenticate
</del><ins>+ from within our protected resource, we add the WWW-Authenticate
</ins><span class="cx"> headers if they do not already exist.
</span><span class="cx"> """
</span><span class="cx"> self.portal.registerChecker(checkers.AllowAnonymousAccess())
</span><span class="lines">@@ -897,7 +917,7 @@
</span><span class="cx"> root = wrapper.HTTPAuthResource(nonAnonResource,
</span><span class="cx"> [self.credFactory],
</span><span class="cx"> self.portal,
</span><del>- interfaces = (IHTTPUser,))
</del><ins>+ interfaces=(IHTTPUser,))
</ins><span class="cx">
</span><span class="cx"> def _tryAuthenticate(result):
</span><span class="cx"> credentials = base64.encodestring('username:password')
</span><span class="lines">@@ -923,6 +943,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_responseFilterDoesntClobberHeaders(self):
</span><span class="cx"> """
</span><span class="cx"> Test that if an UNAUTHORIZED response is returned and
</span><span class="lines">@@ -937,7 +958,7 @@
</span><span class="cx"> root = wrapper.HTTPAuthResource(nonAnonResource,
</span><span class="cx"> [self.credFactory],
</span><span class="cx"> self.portal,
</span><del>- interfaces = (IHTTPUser,))
</del><ins>+ interfaces=(IHTTPUser,))
</ins><span class="cx">
</span><span class="cx"> d = self.assertResponse(
</span><span class="cx"> (root, 'http://localhost/',
</span><span class="lines">@@ -949,6 +970,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_renderHTTP(self):
</span><span class="cx"> """
</span><span class="cx"> Test that if the renderHTTP method is ever called we authenticate
</span><span class="lines">@@ -960,7 +982,7 @@
</span><span class="cx"> root = wrapper.HTTPAuthResource(self.protectedResource,
</span><span class="cx"> [self.credFactory],
</span><span class="cx"> self.portal,
</span><del>- interfaces = (IHTTPUser,))
</del><ins>+ interfaces=(IHTTPUser,))
</ins><span class="cx">
</span><span class="cx"> request = SimpleRequest(None, "GET", "/")
</span><span class="cx"> request.prepath = ['']
</span></span></pre></div>
<a id="CalendarServertrunktxweb2testtest_logpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/test/test_log.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/test/test_log.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/test/test_log.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -16,6 +16,8 @@
</span><span class="cx"> def logMessage(self, message):
</span><span class="cx"> self.messages.append(message)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class SetDateWrapperResource(WrapperResource):
</span><span class="cx"> """
</span><span class="cx"> A resource wrapper which sets the date header.
</span><span class="lines">@@ -28,6 +30,8 @@
</span><span class="cx">
</span><span class="cx"> req.addResponseFilter(_filter, atEnd=True)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class NoneStreamResource(Resource):
</span><span class="cx"> """
</span><span class="cx"> A basic empty resource.
</span><span class="lines">@@ -35,6 +39,8 @@
</span><span class="cx"> def render(self, req):
</span><span class="cx"> return Response(200)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class TestLogging(BaseCase):
</span><span class="cx"> def setUp(self):
</span><span class="cx"> self.patch(theLogPublisher, "observers", [])
</span><span class="lines">@@ -47,6 +53,7 @@
</span><span class="cx">
</span><span class="cx"> self.root = SetDateWrapperResource(LogWrapperResource(self.resrc))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def assertLogged(self, **expected):
</span><span class="cx"> """
</span><span class="cx"> Check that logged messages matches expected format.
</span><span class="lines">@@ -84,6 +91,7 @@
</span><span class="cx"> else:
</span><span class="cx"> self.assertEquals(len(messages), 0, "len(%r) != 0" % (messages, ))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_logSimpleRequest(self):
</span><span class="cx"> """
</span><span class="cx"> Check the log for a simple request.
</span><span class="lines">@@ -100,6 +108,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_logErrors(self):
</span><span class="cx"> """
</span><span class="cx"> Test the error log.
</span><span class="lines">@@ -130,6 +139,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_logNoneResponseStream(self):
</span><span class="cx"> """
</span><span class="cx"> Test the log of an empty resource.
</span><span class="lines">@@ -145,4 +155,3 @@
</span><span class="cx"> d.addCallback(_cbCheckLog)
</span><span class="cx">
</span><span class="cx"> return d
</span><del>-
</del></span></pre></div>
<a id="CalendarServertrunktxweb2testtest_resourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/test/test_resource.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/test/test_resource.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/test/test_resource.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -22,6 +22,8 @@
</span><span class="cx"> class PreconditionError (Exception):
</span><span class="cx"> "Precondition Failure"
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class TestResource (RenderMixin):
</span><span class="cx"> implements(IResource)
</span><span class="cx">
</span><span class="lines">@@ -38,12 +40,15 @@
</span><span class="cx"> def preconditions_BLEARGH(self, request):
</span><span class="cx"> raise PreconditionError()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def precondition_HUCKHUCKBLORP(self, request):
</span><span class="cx"> return fail(None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def preconditions_SWEETHOOKUPS(self, request):
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def preconditions_HOOKUPS(self, request):
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><span class="lines">@@ -54,11 +59,15 @@
</span><span class="cx"> response.stream = MemoryStream(self.renderOutput)
</span><span class="cx"> return response
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> def generateResponse(method):
</span><span class="cx"> resource = TestResource()
</span><span class="cx"> method = getattr(resource, "http_" + method)
</span><span class="cx"> return method(SimpleRequest(Site(resource), method, "/"))
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class RenderMixInTestCase (unittest.TestCase):
</span><span class="cx"> """
</span><span class="cx"> Test RenderMixin.
</span><span class="lines">@@ -130,6 +139,7 @@
</span><span class="cx"> d = resource.renderHTTP(request)
</span><span class="cx"> d.addCallback(checkResponse)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_OPTIONS_status(self):
</span><span class="cx"> """
</span><span class="cx"> RenderMixin.http_OPTIONS()
</span><span class="lines">@@ -138,6 +148,7 @@
</span><span class="cx"> response = generateResponse("OPTIONS")
</span><span class="cx"> self.assertEquals(response.code, responsecode.OK)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_OPTIONS_allow(self):
</span><span class="cx"> """
</span><span class="cx"> RenderMixin.http_OPTIONS()
</span><span class="lines">@@ -149,6 +160,7 @@
</span><span class="cx"> self._my_allowed_methods
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_TRACE_status(self):
</span><span class="cx"> """
</span><span class="cx"> RenderMixin.http_TRACE()
</span><span class="lines">@@ -177,6 +189,7 @@
</span><span class="cx"> response = generateResponse("HEAD")
</span><span class="cx"> self.assertEquals(response.code, responsecode.OK)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_HEAD_body(self):
</span><span class="cx"> """
</span><span class="cx"> RenderMixin.http_HEAD()
</span><span class="lines">@@ -198,6 +211,7 @@
</span><span class="cx"> response = generateResponse("GET")
</span><span class="cx"> self.assertEquals(response.code, responsecode.OK)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_GET_body(self):
</span><span class="cx"> """
</span><span class="cx"> RenderMixin.http_GET()
</span><span class="lines">@@ -209,6 +223,8 @@
</span><span class="cx"> TestResource.renderOutput
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class ResourceTestCase (unittest.TestCase):
</span><span class="cx"> """
</span><span class="cx"> Test Resource.
</span><span class="lines">@@ -226,6 +242,8 @@
</span><span class="cx"> raise NotImplementedError()
</span><span class="cx"> test_child_nonsense.todo = "Someone should write this test"
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class PostableResourceTestCase (unittest.TestCase):
</span><span class="cx"> """
</span><span class="cx"> Test PostableResource.
</span><span class="lines">@@ -234,6 +252,8 @@
</span><span class="cx"> raise NotImplementedError()
</span><span class="cx"> test_POST.todo = "Someone should write this test"
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class LeafResourceTestCase (unittest.TestCase):
</span><span class="cx"> """
</span><span class="cx"> Test LeafResource.
</span><span class="lines">@@ -249,6 +269,8 @@
</span><span class="cx"> self.assertEquals(child, resource)
</span><span class="cx"> self.assertEquals(segments, StopTraversal)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class WrapperResourceTestCase (unittest.TestCase):
</span><span class="cx"> """
</span><span class="cx"> Test WrapperResource.
</span></span></pre></div>
<a id="CalendarServertrunktxweb2testtest_serverpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/test/test_server.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/test/test_server.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/test/test_server.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @implementer(iweb.IResource)
</span><span class="cx"> class ResourceAdapter(object):
</span><span class="cx"> """
</span><span class="lines">@@ -56,6 +57,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> @implementer(iweb.IOldNevowResource)
</span><span class="cx"> class OldResourceAdapter(object):
</span><span class="cx"> """
</span><span class="lines">@@ -99,7 +101,8 @@
</span><span class="cx"> IResource returns the same object.
</span><span class="cx"> """
</span><span class="cx"> @implementer(iweb.IResource)
</span><del>- class Resource(object): ""
</del><ins>+ class Resource(object):
+ ""
</ins><span class="cx"> resource = Resource()
</span><span class="cx"> self.assertIdentical(iweb.IResource(resource), resource)
</span><span class="cx">
</span><span class="lines">@@ -123,7 +126,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def __init__(self, site, method, prepath, uri, length=None,
</span><del>- headers=None, version=(1,1), content=None):
</del><ins>+ headers=None, version=(1, 1), content=None):
</ins><span class="cx"> self.producer = None
</span><span class="cx"> self.site = site
</span><span class="cx"> self.method = method
</span><span class="lines">@@ -152,39 +155,49 @@
</span><span class="cx"> self.data = ''
</span><span class="cx"> self.deferredFinish = defer.Deferred()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def writeIntermediateResponse(code, headers=None):
</span><span class="cx"> pass
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def writeHeaders(self, code, headers):
</span><span class="cx"> self.responseHeaders = headers
</span><span class="cx"> self.code = code
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def write(self, data):
</span><span class="cx"> self.data += data
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def finish(self, failed=False):
</span><span class="cx"> result = self.code, self.responseHeaders, self.data, failed
</span><span class="cx"> self.finished = True
</span><span class="cx"> self.deferredFinish.callback(result)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def abortConnection(self):
</span><span class="cx"> self.finish(failed=True)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def registerProducer(self, producer, streaming):
</span><span class="cx"> if self.producer is not None:
</span><span class="cx"> raise ValueError("Producer still set: " + repr(self.producer))
</span><span class="cx"> self.producer = producer
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def unregisterProducer(self):
</span><span class="cx"> self.producer = None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getHostInfo(self):
</span><span class="cx"> return self.hostInfo
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getRemoteHost(self):
</span><span class="cx"> return self.remoteHost
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class BaseTestResource(resource.Resource):
</span><span class="cx"> responseCode = 200
</span><span class="cx"> responseText = 'This is a fake resource.'
</span><span class="lines">@@ -199,18 +212,22 @@
</span><span class="cx"> for i in children:
</span><span class="cx"> self.putChild(i[0], i[1])
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def render(self, req):
</span><span class="cx"> return http.Response(self.responseCode, headers=self.responseHeaders,
</span><span class="cx"> stream=self.responseStream())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def responseStream(self):
</span><span class="cx"> return stream.MemoryStream(self.responseText)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class MyRenderError(Exception):
</span><span class="cx"> ""
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class ErrorWithProducerResource(BaseTestResource):
</span><span class="cx">
</span><span class="cx"> addSlash = True
</span><span class="lines">@@ -225,7 +242,6 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-
</del><span class="cx"> _unset = object()
</span><span class="cx"> class BaseCase(unittest.TestCase):
</span><span class="cx"> """
</span><span class="lines">@@ -241,6 +257,7 @@
</span><span class="cx"> site = server.Site(root)
</span><span class="cx"> return TestChanRequest(site, method, prepath, uri, length, headers, version, content)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def getResponseFor(self, root, uri, headers={},
</span><span class="cx"> method=None, version=None, prepath='', content=None, length=_unset):
</span><span class="cx"> if not isinstance(headers, http_headers.Headers):
</span><span class="lines">@@ -260,6 +277,7 @@
</span><span class="cx"> cr.request.process()
</span><span class="cx"> return cr.deferredFinish
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def assertResponse(self, request_data, expected_response, failure=False):
</span><span class="cx"> """
</span><span class="cx"> @type request_data: C{tuple}
</span><span class="lines">@@ -276,6 +294,7 @@
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _cbGotResponse(self, (code, headers, data, failed), expected_response, expectedfailure=False):
</span><span class="cx"> expected_code, expected_headers, expected_data = expected_response
</span><span class="cx"> self.assertEquals(code, expected_code)
</span><span class="lines">@@ -286,6 +305,7 @@
</span><span class="cx"> self.assertEquals(failed, expectedfailure)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class ErrorHandlingTest(BaseCase):
</span><span class="cx"> """
</span><span class="cx"> Tests for error handling.
</span><span class="lines">@@ -299,10 +319,10 @@
</span><span class="cx"> root = ErrorWithProducerResource()
</span><span class="cx"> site = server.Site(root)
</span><span class="cx"> tcr = TestChanRequest(site, "GET", "/", "http://localhost/")
</span><del>- request = server.Request(tcr, "GET", "/", (1, 1),
- 0, http_headers.Headers(
- {"host": "localhost"}),
- site=site)
</del><ins>+ request = server.Request(
+ tcr, "GET", "/", (1, 1),
+ 0, http_headers.Headers({"host": "localhost"}),
+ site=site)
</ins><span class="cx"> proc = request.process()
</span><span class="cx"> done = []
</span><span class="cx"> proc.addBoth(done.append)
</span><span class="lines">@@ -336,40 +356,47 @@
</span><span class="cx"> f.responseText = 'Remote Addr: %r' % req.remoteAddr.host
</span><span class="cx"> return f
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def setUp(self):
</span><span class="cx"> self.root = self.SampleTestResource()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_root(self):
</span><span class="cx"> return self.assertResponse(
</span><span class="cx"> (self.root, 'http://host/'),
</span><span class="cx"> (200, {}, 'This is a fake resource.'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_validChild(self):
</span><span class="cx"> return self.assertResponse(
</span><span class="cx"> (self.root, 'http://host/validChild'),
</span><span class="cx"> (200, {}, 'This is a valid child resource.'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_invalidChild(self):
</span><span class="cx"> return self.assertResponse(
</span><span class="cx"> (self.root, 'http://host/invalidChild'),
</span><span class="cx"> (404, {}, None))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_remoteAddrExposure(self):
</span><span class="cx"> return self.assertResponse(
</span><span class="cx"> (self.root, 'http://host/remoteAddr'),
</span><span class="cx"> (200, {}, "Remote Addr: 'remotehost'"))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_leafresource(self):
</span><span class="cx"> class TestResource(resource.LeafResource):
</span><span class="cx"> def render(self, req):
</span><span class="cx"> return http.Response(stream="prepath:%s postpath:%s" % (
</span><del>- req.prepath,
- req.postpath))
</del><ins>+ req.prepath,
+ req.postpath))
</ins><span class="cx">
</span><span class="cx"> return self.assertResponse(
</span><span class="cx"> (TestResource(), 'http://host/consumed/path/segments'),
</span><span class="cx"> (200, {}, "prepath:[] postpath:['consumed', 'path', 'segments']"))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_redirectResource(self):
</span><span class="cx"> """
</span><span class="cx"> Make sure a redirect response has the correct status and Location header.
</span><span class="lines">@@ -384,6 +411,7 @@
</span><span class="cx"> (redirectResource, 'http://localhost/'),
</span><span class="cx"> (301, {'location': 'https://localhost/foo?bar=baz'}, None))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_redirectResourceWithSchemeRemapping(self):
</span><span class="cx"> """
</span><span class="cx"> Make sure a redirect response has the correct status and Location header, when
</span><span class="lines">@@ -396,7 +424,7 @@
</span><span class="cx"> site.SSLPort = 8443
</span><span class="cx"> site.BindSSLPorts = []
</span><span class="cx"> return TestChanRequest(site, method, prepath, uri, length, headers, version, content)
</span><del>-
</del><ins>+
</ins><span class="cx"> self.patch(self, "chanrequest", chanrequest2)
</span><span class="cx">
</span><span class="cx"> redirectResource = resource.RedirectResource(path='/foo')
</span><span class="lines">@@ -405,6 +433,7 @@
</span><span class="cx"> (redirectResource, 'http://localhost:8443/'),
</span><span class="cx"> (301, {'location': 'https://localhost:8443/foo'}, None))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_redirectResourceWithoutSchemeRemapping(self):
</span><span class="cx"> """
</span><span class="cx"> Make sure a redirect response has the correct status and Location header, when
</span><span class="lines">@@ -417,7 +446,7 @@
</span><span class="cx"> site.SSLPort = 8443
</span><span class="cx"> site.BindSSLPorts = []
</span><span class="cx"> return TestChanRequest(site, method, prepath, uri, length, headers, version, content)
</span><del>-
</del><ins>+
</ins><span class="cx"> self.patch(self, "chanrequest", chanrequest2)
</span><span class="cx">
</span><span class="cx"> redirectResource = resource.RedirectResource(path='/foo')
</span><span class="lines">@@ -426,6 +455,7 @@
</span><span class="cx"> (redirectResource, 'http://localhost:8008/'),
</span><span class="cx"> (301, {'location': 'http://localhost:8008/foo'}, None))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_redirectResourceWithoutSSLSchemeRemapping(self):
</span><span class="cx"> """
</span><span class="cx"> Make sure a redirect response has the correct status and Location header, when
</span><span class="lines">@@ -438,7 +468,7 @@
</span><span class="cx"> site.SSLPort = 8443
</span><span class="cx"> site.BindSSLPorts = []
</span><span class="cx"> return TestChanRequest(site, method, prepath, uri, length, headers, version, content)
</span><del>-
</del><ins>+
</ins><span class="cx"> self.patch(self, "chanrequest", chanrequest2)
</span><span class="cx">
</span><span class="cx"> redirectResource = resource.RedirectResource(path='/foo')
</span><span class="lines">@@ -448,30 +478,36 @@
</span><span class="cx"> (301, {'location': 'http://localhost:8443/foo'}, None))
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class URLParsingTest(BaseCase):
</span><span class="cx"> class TestResource(resource.LeafResource):
</span><span class="cx"> def render(self, req):
</span><del>- return http.Response(stream="Host:%s, Path:%s"%(req.host, req.path))
</del><ins>+ return http.Response(stream="Host:%s, Path:%s" % (req.host, req.path))
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def setUp(self):
</span><span class="cx"> self.root = self.TestResource()
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_normal(self):
</span><span class="cx"> return self.assertResponse(
</span><del>- (self.root, '/path', {'Host':'host'}),
</del><ins>+ (self.root, '/path', {'Host': 'host'}),
</ins><span class="cx"> (200, {}, 'Host:host, Path:/path'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_fullurl(self):
</span><span class="cx"> return self.assertResponse(
</span><span class="cx"> (self.root, 'http://host/path'),
</span><span class="cx"> (200, {}, 'Host:host, Path:/path'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_strangepath(self):
</span><span class="cx"> # Ensure that the double slashes don't confuse it
</span><span class="cx"> return self.assertResponse(
</span><del>- (self.root, '//path', {'Host':'host'}),
</del><ins>+ (self.root, '//path', {'Host': 'host'}),
</ins><span class="cx"> (200, {}, 'Host:host, Path://path'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_strangepathfull(self):
</span><span class="cx"> return self.assertResponse(
</span><span class="cx"> (self.root, 'http://host//path'),
</span><span class="lines">@@ -481,7 +517,7 @@
</span><span class="cx">
</span><span class="cx"> class TestDeferredRendering(BaseCase):
</span><span class="cx"> class ResourceWithDeferreds(BaseTestResource):
</span><del>- addSlash=True
</del><ins>+ addSlash = True
</ins><span class="cx"> responseText = 'I should be wrapped in a Deferred.'
</span><span class="cx"> def render(self, req):
</span><span class="cx"> d = defer.Deferred()
</span><span class="lines">@@ -494,11 +530,13 @@
</span><span class="cx"> reactor.callLater(0, d.callback, BaseTestResource())
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_deferredRootResource(self):
</span><span class="cx"> return self.assertResponse(
</span><span class="cx"> (self.ResourceWithDeferreds(), 'http://host/'),
</span><span class="cx"> (200, {}, 'I should be wrapped in a Deferred.'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_deferredChild(self):
</span><span class="cx"> return self.assertResponse(
</span><span class="cx"> (self.ResourceWithDeferreds(), 'http://host/deferred'),
</span><span class="lines">@@ -521,6 +559,7 @@
</span><span class="cx"> ))
</span><span class="cx"> return defer.DeferredList(ds, fireOnOneErrback=True)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_hostRedirect(self):
</span><span class="cx"> ds = []
</span><span class="cx"> for url1, url2 in (
</span><span class="lines">@@ -533,6 +572,7 @@
</span><span class="cx"> ))
</span><span class="cx"> return defer.DeferredList(ds, fireOnOneErrback=True)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_pathRedirect(self):
</span><span class="cx"> root = BaseTestResource()
</span><span class="cx"> redirect = resource.RedirectResource(path="/other")
</span><span class="lines">@@ -555,6 +595,7 @@
</span><span class="cx"> def __init__(self, test):
</span><span class="cx"> self.test = test
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def render(self, request):
</span><span class="cx"> self.test.assertEquals(request.urlForResource(self), self.expectedURI)
</span><span class="cx"> return 201
</span><span class="lines">@@ -589,12 +630,13 @@
</span><span class="cx">
</span><span class="cx"> for uri in (foo.expectedURI, bar.expectedURI, baz.expectedURI):
</span><span class="cx"> ds.append(self.assertResponse(
</span><del>- (root, uri, {'Host':'host'}),
</del><ins>+ (root, uri, {'Host': 'host'}),
</ins><span class="cx"> (201, {}, None),
</span><span class="cx"> ))
</span><span class="cx">
</span><span class="cx"> return defer.DeferredList(ds, fireOnOneErrback=True)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_urlEncoding(self):
</span><span class="cx"> """
</span><span class="cx"> Test to make sure that URL encoding is working.
</span><span class="lines">@@ -608,10 +650,11 @@
</span><span class="cx"> root.putChild("foo bar", child)
</span><span class="cx">
</span><span class="cx"> return self.assertResponse(
</span><del>- (root, child.expectedURI, {'Host':'host'}),
</del><ins>+ (root, child.expectedURI, {'Host': 'host'}),
</ins><span class="cx"> (201, {}, None)
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_locateResource(self):
</span><span class="cx"> """
</span><span class="cx"> Test urlForResource() on resource looked up via a locateResource() call.
</span><span class="lines">@@ -629,6 +672,7 @@
</span><span class="cx"> d.addCallback(gotResource)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_unknownResource(self):
</span><span class="cx"> """
</span><span class="cx"> Test urlForResource() on unknown resource.
</span><span class="lines">@@ -639,6 +683,7 @@
</span><span class="cx">
</span><span class="cx"> self.assertRaises(server.NoURLForResourceError, request.urlForResource, child)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_locateChildResource(self):
</span><span class="cx"> """
</span><span class="cx"> Test urlForResource() on deeply nested resource looked up via
</span><span class="lines">@@ -677,6 +722,7 @@
</span><span class="cx"> d.addCallback(gotResource)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_deferredLocateChild(self):
</span><span class="cx"> """
</span><span class="cx"> Test deferred value from locateChild()
</span><span class="lines">@@ -742,11 +788,13 @@
</span><span class="cx"> ctype = http_headers.MimeType('application', 'x-www-form-urlencoded')
</span><span class="cx"> content = "key=value&multiple=two+words&multiple=more%20words"
</span><span class="cx"> root = resource.Resource()
</span><del>- request = SimpleRequest(server.Site(root), "GET", "/",
- http_headers.Headers({'content-type': ctype}), content)
</del><ins>+ request = SimpleRequest(
+ server.Site(root), "GET", "/",
+ http_headers.Headers({'content-type': ctype}), content)
</ins><span class="cx"> def cb(ign):
</span><span class="cx"> self.assertEquals(request.files, {})
</span><del>- self.assertEquals(request.args,
</del><ins>+ self.assertEquals(
+ request.args,
</ins><span class="cx"> {'multiple': ['two words', 'more words'], 'key': ['value']})
</span><span class="cx"> return server.parsePOSTData(request).addCallback(cb)
</span><span class="cx">
</span><span class="lines">@@ -758,7 +806,7 @@
</span><span class="cx"> """
</span><span class="cx"> ctype = http_headers.MimeType('multipart', 'form-data',
</span><span class="cx"> (('boundary', '---weeboundary'),))
</span><del>- content="""-----weeboundary\r
</del><ins>+ content = """-----weeboundary\r
</ins><span class="cx"> Content-Disposition: form-data; name="FileNameOne"; filename="myfilename"\r
</span><span class="cx"> Content-Type: text/html\r
</span><span class="cx"> \r
</span><span class="lines">@@ -766,13 +814,15 @@
</span><span class="cx"> -----weeboundary--\r
</span><span class="cx"> """
</span><span class="cx"> root = resource.Resource()
</span><del>- request = SimpleRequest(server.Site(root), "GET", "/",
- http_headers.Headers({'content-type': ctype}), content)
</del><ins>+ request = SimpleRequest(
+ server.Site(root), "GET", "/",
+ http_headers.Headers({'content-type': ctype}), content)
</ins><span class="cx"> def cb(ign):
</span><span class="cx"> self.assertEquals(request.args, {})
</span><span class="cx"> self.assertEquals(request.files.keys(), ['FileNameOne'])
</span><del>- self.assertEquals(request.files.values()[0][0][:2],
- ('myfilename', http_headers.MimeType('text', 'html', {})))
</del><ins>+ self.assertEquals(
+ request.files.values()[0][0][:2],
+ ('myfilename', http_headers.MimeType('text', 'html', {})))
</ins><span class="cx"> f = request.files.values()[0][0][2]
</span><span class="cx"> self.assertEquals(f.read(), "my great content wooo")
</span><span class="cx"> return server.parsePOSTData(request).addCallback(cb)
</span><span class="lines">@@ -784,7 +834,7 @@
</span><span class="cx"> C{http.HTTPError}.
</span><span class="cx"> """
</span><span class="cx"> ctype = http_headers.MimeType('multipart', 'form-data')
</span><del>- content="""-----weeboundary\r
</del><ins>+ content = """-----weeboundary\r
</ins><span class="cx"> Content-Disposition: form-data; name="FileNameOne"; filename="myfilename"\r
</span><span class="cx"> Content-Type: text/html\r
</span><span class="cx"> \r
</span><span class="lines">@@ -792,10 +842,10 @@
</span><span class="cx"> -----weeboundary--\r
</span><span class="cx"> """
</span><span class="cx"> root = resource.Resource()
</span><del>- request = SimpleRequest(server.Site(root), "GET", "/",
- http_headers.Headers({'content-type': ctype}), content)
- return self.assertFailure(server.parsePOSTData(request),
- http.HTTPError)
</del><ins>+ request = SimpleRequest(
+ server.Site(root), "GET", "/",
+ http_headers.Headers({'content-type': ctype}), content)
+ return self.assertFailure(server.parsePOSTData(request), http.HTTPError)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_wrongContentType(self):
</span><span class="lines">@@ -805,21 +855,21 @@
</span><span class="cx"> ctype = http_headers.MimeType('application', 'foobar')
</span><span class="cx"> content = "key=value&multiple=two+words&multiple=more%20words"
</span><span class="cx"> root = resource.Resource()
</span><del>- request = SimpleRequest(server.Site(root), "GET", "/",
- http_headers.Headers({'content-type': ctype}), content)
- return self.assertFailure(server.parsePOSTData(request),
- http.HTTPError)
</del><ins>+ request = SimpleRequest(
+ server.Site(root), "GET", "/",
+ http_headers.Headers({'content-type': ctype}), content)
+ return self.assertFailure(server.parsePOSTData(request), http.HTTPError)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_mimeParsingError(self):
</span><span class="cx"> """
</span><span class="cx"> A malformed content should result in a C{http.HTTPError}.
</span><del>-
</del><ins>+
</ins><span class="cx"> The tested content has an invalid closing boundary.
</span><span class="cx"> """
</span><span class="cx"> ctype = http_headers.MimeType('multipart', 'form-data',
</span><span class="cx"> (('boundary', '---weeboundary'),))
</span><del>- content="""-----weeboundary\r
</del><ins>+ content = """-----weeboundary\r
</ins><span class="cx"> Content-Disposition: form-data; name="FileNameOne"; filename="myfilename"\r
</span><span class="cx"> Content-Type: text/html\r
</span><span class="cx"> \r
</span><span class="lines">@@ -827,10 +877,10 @@
</span><span class="cx"> -----weeoundary--\r
</span><span class="cx"> """
</span><span class="cx"> root = resource.Resource()
</span><del>- request = SimpleRequest(server.Site(root), "GET", "/",
- http_headers.Headers({'content-type': ctype}), content)
- return self.assertFailure(server.parsePOSTData(request),
- http.HTTPError)
</del><ins>+ request = SimpleRequest(
+ server.Site(root), "GET", "/",
+ http_headers.Headers({'content-type': ctype}), content)
+ return self.assertFailure(server.parsePOSTData(request), http.HTTPError)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_multipartMaxMem(self):
</span><span class="lines">@@ -840,7 +890,7 @@
</span><span class="cx"> """
</span><span class="cx"> ctype = http_headers.MimeType('multipart', 'form-data',
</span><span class="cx"> (('boundary', '---weeboundary'),))
</span><del>- content="""-----weeboundary\r
</del><ins>+ content = """-----weeboundary\r
</ins><span class="cx"> Content-Disposition: form-data; name="FileNameOne"\r
</span><span class="cx"> Content-Type: text/html\r
</span><span class="cx"> \r
</span><span class="lines">@@ -849,12 +899,15 @@
</span><span class="cx"> -----weeboundary--\r
</span><span class="cx"> """
</span><span class="cx"> root = resource.Resource()
</span><del>- request = SimpleRequest(server.Site(root), "GET", "/",
- http_headers.Headers({'content-type': ctype}), content)
</del><ins>+ request = SimpleRequest(
+ server.Site(root), "GET", "/",
+ http_headers.Headers({'content-type': ctype}), content)
</ins><span class="cx"> def cb(res):
</span><del>- self.assertEquals(res.response.description,
</del><ins>+ self.assertEquals(
+ res.response.description,
</ins><span class="cx"> "Maximum length of 10 bytes exceeded.")
</span><del>- return self.assertFailure(server.parsePOSTData(request, maxMem=10),
</del><ins>+ return self.assertFailure(
+ server.parsePOSTData(request, maxMem=10),
</ins><span class="cx"> http.HTTPError).addCallback(cb)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -865,7 +918,7 @@
</span><span class="cx"> """
</span><span class="cx"> ctype = http_headers.MimeType('multipart', 'form-data',
</span><span class="cx"> (('boundary', '---weeboundary'),))
</span><del>- content="""-----weeboundary\r
</del><ins>+ content = """-----weeboundary\r
</ins><span class="cx"> Content-Disposition: form-data; name="FileNameOne"; filename="myfilename"\r
</span><span class="cx"> Content-Type: text/html\r
</span><span class="cx"> \r
</span><span class="lines">@@ -874,12 +927,15 @@
</span><span class="cx"> -----weeboundary--\r
</span><span class="cx"> """
</span><span class="cx"> root = resource.Resource()
</span><del>- request = SimpleRequest(server.Site(root), "GET", "/",
- http_headers.Headers({'content-type': ctype}), content)
</del><ins>+ request = SimpleRequest(
+ server.Site(root), "GET", "/",
+ http_headers.Headers({'content-type': ctype}), content)
</ins><span class="cx"> def cb(res):
</span><del>- self.assertEquals(res.response.description,
</del><ins>+ self.assertEquals(
+ res.response.description,
</ins><span class="cx"> "Maximum length of 10 bytes exceeded.")
</span><del>- return self.assertFailure(server.parsePOSTData(request, maxSize=10),
</del><ins>+ return self.assertFailure(
+ server.parsePOSTData(request, maxSize=10),
</ins><span class="cx"> http.HTTPError).addCallback(cb)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -911,12 +967,15 @@
</span><span class="cx"> -----xyz--\r
</span><span class="cx"> """
</span><span class="cx"> root = resource.Resource()
</span><del>- request = SimpleRequest(server.Site(root), "GET", "/",
- http_headers.Headers({'content-type': ctype}), content)
</del><ins>+ request = SimpleRequest(
+ server.Site(root), "GET", "/",
+ http_headers.Headers({'content-type': ctype}), content)
</ins><span class="cx"> def cb(res):
</span><del>- self.assertEquals(res.response.description,
</del><ins>+ self.assertEquals(
+ res.response.description,
</ins><span class="cx"> "Maximum number of fields 3 exceeded")
</span><del>- return self.assertFailure(server.parsePOSTData(request, maxFields=3),
</del><ins>+ return self.assertFailure(
+ server.parsePOSTData(request, maxFields=3),
</ins><span class="cx"> http.HTTPError).addCallback(cb)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -929,13 +988,12 @@
</span><span class="cx"> (('boundary', '---weeboundary'),))
</span><span class="cx"> # XXX: maybe this is not a good example
</span><span class="cx"> # parseContentDispositionFormData could handle this problem
</span><del>- content="""-----weeboundary\r
</del><ins>+ content = """-----weeboundary\r
</ins><span class="cx"> Content-Disposition: form-data; name="FileNameOne"; filename="myfilename and invalid data \r
</span><span class="cx"> -----weeboundary--\r
</span><span class="cx"> """
</span><span class="cx"> root = resource.Resource()
</span><del>- request = SimpleRequest(server.Site(root), "GET", "/",
- http_headers.Headers({'content-type': ctype}), content)
- return self.assertFailure(server.parsePOSTData(request),
- ValueError)
-
</del><ins>+ request = SimpleRequest(
+ server.Site(root), "GET", "/",
+ http_headers.Headers({'content-type': ctype}), content)
+ return self.assertFailure(server.parsePOSTData(request), ValueError)
</ins></span></pre></div>
<a id="CalendarServertrunktxweb2testtest_staticpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/test/test_static.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/test/test_static.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/test/test_static.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -20,6 +20,7 @@
</span><span class="cx"> self.text = "Hello, World\n"
</span><span class="cx"> self.data = static.Data(self.text, "text/plain")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_dataState(self):
</span><span class="cx"> """
</span><span class="cx"> Test the internal state of the Data object
</span><span class="lines">@@ -67,11 +68,13 @@
</span><span class="cx"> self.tempdir = self.mktemp()
</span><span class="cx"> os.mkdir(self.tempdir)
</span><span class="cx">
</span><del>- self.root = static.FileSaver(self.tempdir,
- expectedFields=['FileNameOne'],
- maxBytes=16)
</del><ins>+ self.root = static.FileSaver(
+ self.tempdir,
+ expectedFields=['FileNameOne'],
+ maxBytes=16)
</ins><span class="cx"> self.root.addSlash = True
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def uploadFile(self, fieldname, filename, mimetype, content, resrc=None,
</span><span class="cx"> host='foo', path='/'):
</span><span class="cx"> if not resrc:
</span><span class="lines">@@ -80,12 +83,12 @@
</span><span class="cx"> ctype = http_headers.MimeType('multipart', 'form-data',
</span><span class="cx"> (('boundary', '---weeboundary'),))
</span><span class="cx">
</span><del>- return self.getResponseFor(resrc, '/',
- headers={'host': 'foo',
- 'content-type': ctype },
- length=len(content),
- method='POST',
- content="""-----weeboundary\r
</del><ins>+ return self.getResponseFor(
+ resrc, '/',
+ headers={'host': 'foo', 'content-type': ctype},
+ length=len(content),
+ method='POST',
+ content="""-----weeboundary\r
</ins><span class="cx"> Content-Disposition: form-data; name="%s"; filename="%s"\r
</span><span class="cx"> Content-Type: %s\r
</span><span class="cx"> \r
</span><span class="lines">@@ -93,6 +96,7 @@
</span><span class="cx"> -----weeboundary--\r
</span><span class="cx"> """ % (fieldname, filename, mimetype, content))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _CbAssertInResponse(self, (code, headers, data, failed),
</span><span class="cx"> expected_response, expectedFailure=False):
</span><span class="cx">
</span><span class="lines">@@ -107,36 +111,43 @@
</span><span class="cx">
</span><span class="cx"> self.assertEquals(failed, expectedFailure)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def fileNameFromResponse(self, response):
</span><del>- (code, headers, data, failure) = response
- return data[data.index('Saved file')+11:data.index('<br />')]
</del><ins>+ (_ignore_code, _ignore_headers, data, _ignore_failure) = response
+ return data[data.index('Saved file') + 11:data.index('<br />')]
</ins><span class="cx">
</span><ins>+
</ins><span class="cx"> def assertInResponse(self, response, expected_response, failure=False):
</span><span class="cx"> d = response
</span><span class="cx"> d.addCallback(self._CbAssertInResponse, expected_response, failure)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_enforcesMaxBytes(self):
</span><span class="cx"> return self.assertInResponse(
</span><del>- self.uploadFile('FileNameOne', 'myfilename', 'text/html', 'X'*32),
</del><ins>+ self.uploadFile('FileNameOne', 'myfilename', 'text/html', 'X' * 32),
</ins><span class="cx"> (200, {}, 'exceeds maximum length'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_enforcesMimeType(self):
</span><span class="cx"> return self.assertInResponse(
</span><span class="cx"> self.uploadFile('FileNameOne', 'myfilename',
</span><span class="cx"> 'application/x-python', 'X'),
</span><span class="cx"> (200, {}, 'type not allowed'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_invalidField(self):
</span><span class="cx"> return self.assertInResponse(
</span><span class="cx"> self.uploadFile('NotARealField', 'myfilename', 'text/html', 'X'),
</span><span class="cx"> (200, {}, 'not a valid field'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_reportFileSave(self):
</span><span class="cx"> return self.assertInResponse(
</span><span class="cx"> self.uploadFile('FileNameOne', 'myfilename', 'text/plain', 'X'),
</span><span class="cx"> (200, {}, 'Saved file'))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_compareFileContents(self):
</span><span class="cx"> def gotFname(fname):
</span><span class="cx"> contents = file(fname, 'rb').read()
</span></span></pre></div>
<a id="CalendarServertrunktxweb2testtest_streampy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txweb2/test/test_stream.py (14119 => 14120)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txweb2/test/test_stream.py        2014-10-29 02:35:02 UTC (rev 14119)
+++ CalendarServer/trunk/txweb2/test/test_stream.py        2014-10-29 16:28:01 UTC (rev 14120)
</span><span class="lines">@@ -5,7 +5,9 @@
</span><span class="cx"> Tests for the stream implementations in L{txweb2}.
</span><span class="cx"> """
</span><span class="cx">
</span><del>-import tempfile, sys, os
</del><ins>+import os
+import sys
+import tempfile
</ins><span class="cx">
</span><span class="cx"> from zope.interface import implements
</span><span class="cx">
</span><span class="lines">@@ -24,6 +26,7 @@
</span><span class="cx"> raise TypeError("%s doesn't conform to the buffer interface" % (data,))
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class SimpleStreamTests:
</span><span class="cx"> text = '1234567890'
</span><span class="cx"> def test_split(self):
</span><span class="lines">@@ -48,6 +51,7 @@
</span><span class="cx"> self.assertEquals(bufstr(b.read()), self.text[point + 2:8])
</span><span class="cx"> self.assertEquals(b.read(), None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_read(self):
</span><span class="cx"> s = self.makeStream()
</span><span class="cx"> self.assertEquals(s.length, len(self.text))
</span><span class="lines">@@ -66,10 +70,13 @@
</span><span class="cx"> self.assertEquals(s.read(), None)
</span><span class="cx"> self.assertEquals(s.length, 0)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class FileStreamTest(SimpleStreamTests, unittest.TestCase):
</span><span class="cx"> def makeStream(self, *args, **kw):
</span><span class="cx"> return stream.FileStream(self.f, *args, **kw)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def setUp(self):
</span><span class="cx"> """
</span><span class="cx"> Create a file containing C{self.text} to be streamed.
</span><span class="lines">@@ -79,6 +86,7 @@
</span><span class="cx"> f.seek(0, 0)
</span><span class="cx"> self.f = f
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_close(self):
</span><span class="cx"> s = self.makeStream()
</span><span class="cx"> s.close()
</span><span class="lines">@@ -88,6 +96,7 @@
</span><span class="cx"> # would raise exception if f is closed
</span><span class="cx"> self.f.seek(0, 0)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_read2(self):
</span><span class="cx"> s = self.makeStream(0)
</span><span class="cx"> s.CHUNK_SIZE = 6
</span><span class="lines">@@ -108,6 +117,8 @@
</span><span class="cx"> self.assertEquals(bufstr(s.read()), self.text)
</span><span class="cx"> self.assertRaises(RuntimeError, s.read) # ran out of data
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class MMapFileStreamTest(SimpleStreamTests, unittest.TestCase):
</span><span class="cx"> text = SimpleStreamTests.text
</span><span class="cx"> text = text * (stream.MMAP_THRESHOLD // len(text) + 1)
</span><span class="lines">@@ -115,6 +126,7 @@
</span><span class="cx"> def makeStream(self, *args, **kw):
</span><span class="cx"> return stream.FileStream(self.f, *args, **kw)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def setUp(self):
</span><span class="cx"> """
</span><span class="cx"> Create a file containing C{self.text}, which should be long enough to
</span><span class="lines">@@ -125,6 +137,7 @@
</span><span class="cx"> f.seek(0, 0)
</span><span class="cx"> self.f = f
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_mmapwrapper(self):
</span><span class="cx"> self.assertRaises(TypeError, stream.mmapwrapper)
</span><span class="cx"> self.assertRaises(TypeError, stream.mmapwrapper, offset=0)
</span><span class="lines">@@ -133,15 +146,19 @@
</span><span class="cx"> if not stream.mmap:
</span><span class="cx"> test_mmapwrapper.skip = 'mmap not supported here'
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class MemoryStreamTest(SimpleStreamTests, unittest.TestCase):
</span><span class="cx"> def makeStream(self, *args, **kw):
</span><span class="cx"> return stream.MemoryStream(self.text, *args, **kw)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_close(self):
</span><span class="cx"> s = self.makeStream()
</span><span class="cx"> s.close()
</span><span class="cx"> self.assertEquals(s.length, 0)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_read2(self):
</span><span class="cx"> self.assertRaises(ValueError, self.makeStream, 0, 20)
</span><span class="cx">
</span><span class="lines">@@ -174,46 +191,54 @@
</span><span class="cx"> s = stream.MemoryStream(self.data)
</span><span class="cx"> self.s = stream.BufferedStream(s)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _cbGotData(self, data, expected):
</span><span class="cx"> self.assertEqual(data, expected)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_readline(self):
</span><span class="cx"> """Test that readline reads a line."""
</span><span class="cx"> d = self.s.readline()
</span><span class="cx"> d.addCallback(self._cbGotData, 'I was angry with my friend:\r\n')
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_readlineWithSize(self):
</span><span class="cx"> """Test the size argument to readline"""
</span><span class="cx"> d = self.s.readline(size=5)
</span><span class="cx"> d.addCallback(self._cbGotData, 'I was')
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_readlineWithBigSize(self):
</span><span class="cx"> """Test the size argument when it's bigger than the length of the line."""
</span><span class="cx"> d = self.s.readline(size=40)
</span><span class="cx"> d.addCallback(self._cbGotData, 'I was angry with my friend:\r\n')
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_readlineWithZero(self):
</span><span class="cx"> """Test readline with size = 0."""
</span><span class="cx"> d = self.s.readline(size=0)
</span><span class="cx"> d.addCallback(self._cbGotData, '')
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_readlineFinished(self):
</span><span class="cx"> """Test readline on a finished stream."""
</span><span class="cx"> nolines = len(self.data.split('\r\n'))
</span><del>- for i in range(nolines):
</del><ins>+ for _ in range(nolines):
</ins><span class="cx"> self.s.readline()
</span><span class="cx"> d = self.s.readline()
</span><span class="cx"> d.addCallback(self._cbGotData, '')
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_readlineNegSize(self):
</span><span class="cx"> """Ensure that readline with a negative size raises an exception."""
</span><span class="cx"> self.assertRaises(ValueError, self.s.readline, size=-1)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_readlineSizeInDelimiter(self):
</span><span class="cx"> """
</span><span class="cx"> Test behavior of readline when size falls inside the
</span><span class="lines">@@ -224,12 +249,14 @@
</span><span class="cx"> d.addCallback(lambda _: self.s.readline())
</span><span class="cx"> d.addCallback(self._cbGotData, "\nI told my wrath, my wrath did end.\r\n")
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_readExactly(self):
</span><span class="cx"> """Make sure readExactly with no arg reads all the data."""
</span><span class="cx"> d = self.s.readExactly()
</span><span class="cx"> d.addCallback(self._cbGotData, self.data)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_readExactlyLimited(self):
</span><span class="cx"> """
</span><span class="cx"> Test readExactly with a number.
</span><span class="lines">@@ -238,6 +265,7 @@
</span><span class="cx"> d.addCallback(self._cbGotData, self.data[:10])
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_readExactlyBig(self):
</span><span class="cx"> """
</span><span class="cx"> Test readExactly with a number larger than the size of the
</span><span class="lines">@@ -247,6 +275,7 @@
</span><span class="cx"> d.addCallback(self._cbGotData, self.data)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_read(self):
</span><span class="cx"> """
</span><span class="cx"> Make sure read() also functions. (note that this test uses
</span><span class="lines">@@ -255,6 +284,8 @@
</span><span class="cx"> """
</span><span class="cx"> self.assertEqual(str(self.s.read()), self.data)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class TestStreamer:
</span><span class="cx"> implements(stream.IStream, stream.IByteStream)
</span><span class="cx">
</span><span class="lines">@@ -266,17 +297,22 @@
</span><span class="cx"> def __init__(self, list):
</span><span class="cx"> self.list = list
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def read(self):
</span><span class="cx"> self.readCalled += 1
</span><span class="cx"> if self.list:
</span><span class="cx"> return self.list.pop(0)
</span><span class="cx"> return None
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def close(self):
</span><span class="cx"> self.closeCalled += 1
</span><span class="cx"> self.list = []
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class FallbackSplitTest(unittest.TestCase):
</span><ins>+
</ins><span class="cx"> def test_split(self):
</span><span class="cx"> s = TestStreamer(['abcd', defer.succeed('efgh'), 'ijkl'])
</span><span class="cx"> left, right = stream.fallbackSplit(s, 5)
</span><span class="lines">@@ -287,6 +323,7 @@
</span><span class="cx"> d.addCallback(self._cbSplit, left, right)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _cbSplit(self, result, left, right):
</span><span class="cx"> self.assertEquals(bufstr(result), 'e')
</span><span class="cx"> self.assertEquals(left.read(), None)
</span><span class="lines">@@ -295,6 +332,7 @@
</span><span class="cx"> self.assertEquals(bufstr(right.read()), 'ijkl')
</span><span class="cx"> self.assertEquals(right.read(), None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_split2(self):
</span><span class="cx"> s = TestStreamer(['abcd', defer.succeed('efgh'), 'ijkl'])
</span><span class="cx"> left, right = stream.fallbackSplit(s, 4)
</span><span class="lines">@@ -309,6 +347,7 @@
</span><span class="cx"> self.assertEquals(bufstr(right.read()), 'ijkl')
</span><span class="cx"> self.assertEquals(right.read(), None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_splitsplit(self):
</span><span class="cx"> s = TestStreamer(['abcd', defer.succeed('efgh'), 'ijkl'])
</span><span class="cx"> left, right = stream.fallbackSplit(s, 5)
</span><span class="lines">@@ -329,6 +368,7 @@
</span><span class="cx"> self.assertEquals(bufstr(right.read()), 'ijkl')
</span><span class="cx"> self.assertEquals(right.read(), None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_closeboth(self):
</span><span class="cx"> s = TestStreamer(['abcd', defer.succeed('efgh'), 'ijkl'])
</span><span class="cx"> left, right = stream.fallbackSplit(s, 5)
</span><span class="lines">@@ -340,6 +380,7 @@
</span><span class="cx"> self.assertEquals(s.readCalled, 0)
</span><span class="cx"> self.assertEquals(s.closeCalled, 1)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_closeboth_rev(self):
</span><span class="cx"> s = TestStreamer(['abcd', defer.succeed('efgh'), 'ijkl'])
</span><span class="cx"> left, right = stream.fallbackSplit(s, 5)
</span><span class="lines">@@ -351,6 +392,7 @@
</span><span class="cx"> self.assertEquals(s.readCalled, 0)
</span><span class="cx"> self.assertEquals(s.closeCalled, 1)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_closeleft(self):
</span><span class="cx"> s = TestStreamer(['abcd', defer.succeed('efgh'), 'ijkl'])
</span><span class="cx"> left, right = stream.fallbackSplit(s, 5)
</span><span class="lines">@@ -359,11 +401,13 @@
</span><span class="cx"> d.addCallback(self._cbCloseleft, right)
</span><span class="cx"> return d
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _cbCloseleft(self, result, right):
</span><span class="cx"> self.assertEquals(bufstr(result), 'fgh')
</span><span class="cx"> self.assertEquals(bufstr(right.read()), 'ijkl')
</span><span class="cx"> self.assertEquals(right.read(), None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_closeright(self):
</span><span class="cx"> s = TestStreamer(['abcd', defer.succeed('efgh'), 'ijkl'])
</span><span class="cx"> left, right = stream.fallbackSplit(s, 3)
</span><span class="lines">@@ -375,11 +419,13 @@
</span><span class="cx"> self.assertEquals(s.closeCalled, 1)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class ProcessStreamerTest(unittest.TestCase):
</span><span class="cx">
</span><span class="cx"> if interfaces.IReactorProcess(reactor, None) is None:
</span><span class="cx"> skip = "Platform lacks spawnProcess support, can't test process streaming."
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def runCode(self, code, inputStream=None):
</span><span class="cx"> if inputStream is None:
</span><span class="cx"> inputStream = stream.MemoryStream("")
</span><span class="lines">@@ -387,6 +433,7 @@
</span><span class="cx"> [sys.executable, "-u", "-c", code],
</span><span class="cx"> os.environ)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_output(self):
</span><span class="cx"> p = self.runCode("import sys\nfor i in range(100): sys.stdout.write('x' * 1000)")
</span><span class="cx"> l = []
</span><span class="lines">@@ -396,6 +443,7 @@
</span><span class="cx"> d2 = p.run()
</span><span class="cx"> return d.addCallback(verify).addCallback(lambda _: d2)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_errouput(self):
</span><span class="cx"> p = self.runCode("import sys\nfor i in range(100): sys.stderr.write('x' * 1000)")
</span><span class="cx"> l = []
</span><span class="lines">@@ -405,6 +453,7 @@
</span><span class="cx"> p.run()
</span><span class="cx"> return d.addCallback(verify)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_input(self):
</span><span class="cx"> p = self.runCode("import sys\nsys.stdout.write(sys.stdin.read())",
</span><span class="cx"> "hello world")
</span><span class="lines">@@ -416,6 +465,7 @@
</span><span class="cx"> return d2
</span><span class="cx"> return d.addCallback(verify)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_badexit(self):
</span><span class="cx"> p = self.runCode("raise ValueError")
</span><span class="cx"> l = []
</span><span class="lines">@@ -426,6 +476,7 @@
</span><span class="cx"> self.assert_(p.errStream.closed)
</span><span class="cx"> return p.run().addErrback(lambda _: _.trap(ProcessTerminated) and l.append(1)).addCallback(verify)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_inputerror(self):
</span><span class="cx"> p = self.runCode("import sys\nsys.stdout.write(sys.stdin.read())",
</span><span class="cx"> TestStreamer(["hello", defer.fail(ZeroDivisionError())]))
</span><span class="lines">@@ -440,6 +491,7 @@
</span><span class="cx"> self.assertEqual(len(excs), 1)
</span><span class="cx"> return d.addCallback(verify).addCallback(cbVerified)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_processclosedinput(self):
</span><span class="cx"> p = self.runCode("import sys; sys.stdout.write(sys.stdin.read(3));" +
</span><span class="cx"> "sys.stdin.close(); sys.stdout.write('def')",
</span><span class="lines">@@ -452,6 +504,7 @@
</span><span class="cx"> return d.addCallback(verify).addCallback(lambda _: d2)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class AdapterTestCase(unittest.TestCase):
</span><span class="cx">
</span><span class="cx"> def test_adapt(self):
</span><span class="lines">@@ -465,14 +518,17 @@
</span><span class="cx"> self.assertEquals(s.read(), None)
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class ReadStreamTestCase(unittest.TestCase):
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_pull(self):
</span><span class="cx"> l = []
</span><span class="cx"> s = TestStreamer(['abcd', defer.succeed('efgh'), 'ijkl'])
</span><span class="cx"> return stream.readStream(s, l.append).addCallback(
</span><span class="cx"> lambda _: self.assertEquals(l, ["abcd", "efgh", "ijkl"]))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_pullFailure(self):
</span><span class="cx"> l = []
</span><span class="cx"> s = TestStreamer(['abcd', defer.fail(RuntimeError()), 'ijkl'])
</span><span class="lines">@@ -481,12 +537,15 @@
</span><span class="cx"> self.assertEquals(l, ["abcd"])
</span><span class="cx"> return stream.readStream(s, l.append).addErrback(test)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_pullException(self):
</span><span class="cx"> class Failer:
</span><del>- def read(self): raise RuntimeError
</del><ins>+ def read(self):
+ raise RuntimeError
</ins><span class="cx"> return stream.readStream(Failer(), lambda _: None).addErrback(
</span><span class="cx"> lambda _: _.trap(RuntimeError))
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_processingException(self):
</span><span class="cx"> s = TestStreamer(['abcd', defer.succeed('efgh'), 'ijkl'])
</span><span class="cx"> return stream.readStream(s, lambda x: 1 / 0).addErrback(
</span><span class="lines">@@ -508,6 +567,7 @@
</span><span class="cx"> return d
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class CompoundStreamTest:
</span><span class="cx"> """
</span><span class="cx"> CompoundStream lets you combine many streams into one continuous stream.
</span><span class="lines">@@ -583,6 +643,7 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class AsynchronousDummyStream(object):
</span><span class="cx"> """
</span><span class="cx"> An L{IByteStream} implementation which always returns a
</span><span class="lines">@@ -592,14 +653,18 @@
</span><span class="cx"> def __init__(self):
</span><span class="cx"> self._readResults = []
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def read(self):
</span><span class="cx"> result = defer.Deferred()
</span><span class="cx"> self._readResults.append(result)
</span><span class="cx"> return result
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def _write(self, bytes):
</span><span class="cx"> self._readResults.pop(0).callback(bytes)
</span><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class MD5StreamTest(unittest.TestCase):
</span><span class="cx"> """
</span><span class="cx"> Tests for L{stream.MD5Stream}.
</span><span class="lines">@@ -622,6 +687,7 @@
</span><span class="cx">
</span><span class="cx"> self.assertEquals(self.digest, md5Stream.getMD5())
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_asynchronous(self):
</span><span class="cx"> """
</span><span class="cx"> L{stream.MD5Stream} also supports L{IByteStream} providers which return
</span><span class="lines">@@ -648,6 +714,7 @@
</span><span class="cx">
</span><span class="cx"> return result
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_getMD5FailsBeforeClose(self):
</span><span class="cx"> """
</span><span class="cx"> L{stream.MD5Stream.getMD5} raises L{RuntimeError} if called before
</span><span class="lines">@@ -657,6 +724,7 @@
</span><span class="cx"> md5Stream = stream.MD5Stream(dataStream)
</span><span class="cx"> self.assertRaises(RuntimeError, md5Stream.getMD5)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_initializationFailsWithoutStream(self):
</span><span class="cx"> """
</span><span class="cx"> L{stream.MD5Stream.__init__} raises L{ValueError} if passed C{None} as
</span><span class="lines">@@ -664,6 +732,7 @@
</span><span class="cx"> """
</span><span class="cx"> self.assertRaises(ValueError, stream.MD5Stream, None)
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> def test_readAfterClose(self):
</span><span class="cx"> """
</span><span class="cx"> L{stream.MD5Stream.read} raises L{RuntimeError} if called after
</span></span></pre>
</div>
</div>
</body>
</html>