r46802 - Merge addSlash-8169-3: Fix twisted.web.static.addSlash
hawkowl-TA+aISz0psMTMxyoc4vAAJOcrHinNvQL0E9HWUfgJXw@public.gmane.org
| Newsgroups | gmane.comp.python.twisted.commits |
|---|---|
| Message-ID | <[email protected]> |
Author: hawkowl
Date: Mon Feb 15 17:56:37 2016
New Revision: 46802
Added:
trunk/twisted/web/topfiles/8169.bugfix
trunk/twisted/web/topfiles/8169.removal
Modified:
trunk/twisted/web/static.py
trunk/twisted/web/test/requesthelper.py
trunk/twisted/web/test/test_static.py
Log:
Merge addSlash-8169-3: Fix twisted.web.static.addSlash
Author: hawkowl
Reviewer: adiroiban
Fixes: #8169
Modified: trunk/twisted/web/static.py
==============================================================================
--- trunk/twisted/web/static.py (original)
+++ trunk/twisted/web/static.py Mon Feb 15 17:56:37 2016
@@ -29,6 +29,9 @@
from twisted.internet import abstract, interfaces
from twisted.python.util import InsensitiveDict
from twisted.python.runtime import platformType
+from twisted.python.url import URL
+from twisted.python.versions import Version
+from twisted.python.deprecate import deprecated
if _PY3:
from urllib.parse import quote, unquote
@@ -61,22 +64,37 @@
render_HEAD = render_GET
+
+@deprecated(Version("Twisted", 16, 0, 0))
def addSlash(request):
- qs = ''
- qindex = request.uri.find('?')
- if qindex != -1:
- qs = request.uri[qindex:]
-
- return "http%s://%s%s/%s" % (
- request.isSecure() and 's' or '',
- request.getHeader("host"),
- (request.uri.split('?')[0]),
- qs)
+ """
+ Add a trailing slash to C{request}'s URI. Deprecated, do not use.
+ """
+ return _addSlash(request)
+
+
+
+def _addSlash(request):
+ """
+ Add a trailing slash to C{request}'s URI.
+
+ @param request: The incoming request to add the ending slash to.
+ @type request: An object conforming to L{twisted.web.iweb.IRequest}
+
+ @return: A URI with a trailing slash, with query and fragment preserved.
+ @rtype: L{bytes}
+ """
+ url = URL.fromText(request.uri.decode('ascii'))
+ # Add an empty path segment at the end, so that it adds a trailing slash
+ url = url.replace(path=list(url.path) + [u""])
+ return url.asText().encode('ascii')
+
+
class Redirect(resource.Resource):
def __init__(self, request):
resource.Resource.__init__(self)
- self.url = addSlash(request)
+ self.url = _addSlash(request)
def render(self, request):
return redirectTo(self.url, request)
@@ -610,7 +628,7 @@
def redirect(self, request):
- return redirectTo(addSlash(request), request)
+ return redirectTo(_addSlash(request), request)
def listNames(self):
Modified: trunk/twisted/web/test/requesthelper.py
==============================================================================
--- trunk/twisted/web/test/requesthelper.py (original)
+++ trunk/twisted/web/test/requesthelper.py Mon Feb 15 17:56:37 2016
@@ -23,6 +23,7 @@
from twisted.web.http_headers import Headers
from twisted.web.resource import Resource
from twisted.web.server import NOT_DONE_YET, Session, Site
+from twisted.web._responses import FOUND
class DummyChannel:
@@ -73,7 +74,7 @@
class DummyRequest(object):
"""
- Represents a dummy or fake request.
+ Represents a dummy or fake request. See L{twisted.web.server.Request}.
@ivar _finishedDeferreds: C{None} or a C{list} of L{Deferreds} which will
be called back with C{None} when C{finish} is called or which will be
@@ -98,11 +99,13 @@
method = b'GET'
client = None
+
def registerProducer(self, prod,s):
self.go = 1
while self.go:
prod.resumeProducing()
+
def unregisterProducer(self):
self.go = 0
@@ -123,6 +126,7 @@
self._serverName = b"dummy"
self.clientproto = b"HTTP/1.0"
+
def getAllHeaders(self):
"""
Return dictionary mapping the names of all received headers to the last
@@ -139,6 +143,7 @@
headers[k.lower()] = v[-1]
return headers
+
def getHeader(self, name):
"""
Retrieve the value of a request header.
@@ -158,6 +163,7 @@
"""
self.responseHeaders.addRawHeader(name, value)
+
def getSession(self):
if self.session:
return self.session
@@ -191,6 +197,7 @@
raise TypeError("write() only accepts bytes")
self.written.append(data)
+
def notifyFinish(self):
"""
Return a L{Deferred} which is called back with C{None} when the request
@@ -277,6 +284,7 @@
"""
return IPv4Address('TCP', '127.0.0.1', 80)
+
def setHost(self, host, port, ssl=0):
"""
Change the host and port the request thinks it's using.
@@ -299,6 +307,7 @@
hostHeader = host + b":" + intToBytes(port)
self.requestHeaders.addRawHeader(b"host", hostHeader)
+
def getClient(self):
"""
Get the client's IP address, if it has one.
@@ -308,6 +317,16 @@
"""
return self.getClientIP()
+
+ def redirect(self, url):
+ """
+ Utility function that does a redirect.
+
+ The request should have finish() called after this.
+ """
+ self.setResponseCode(FOUND)
+ self.setHeader(b"location", url)
+
DummyRequest.getClient = deprecated(
Version("Twisted", 15, 0, 0),
"Twisted Names to resolve hostnames")(DummyRequest.getClient)
Modified: trunk/twisted/web/test/test_static.py
==============================================================================
--- trunk/twisted/web/test/test_static.py (original)
+++ trunk/twisted/web/test/test_static.py Mon Feb 15 17:56:37 2016
@@ -25,6 +25,7 @@
from twisted.web.server import UnsupportedMethod
from twisted.web.test.requesthelper import DummyRequest
from twisted.web.test._util import _render
+from twisted.web._responses import FOUND
class StaticDataTests(TestCase):
@@ -382,6 +383,26 @@
return d
+ def test_directoryWithoutTrailingSlashRedirects(self):
+ """
+ A request for a path which is a directory but does not have a trailing
+ slash will be redirected to a URL which does have a slash by L{File}.
+ """
+ base = FilePath(self.mktemp())
+ base.makedirs()
+ base.child('folder').makedirs()
+ file = static.File(base.path)
+
+ request = DummyRequest([b"folder"])
+ request.uri = b"http://dummy/folder#baz?foo=bar"
+ child = resource.getChildForRequest(file, request)
+
+ self.successResultOf(self._render(child, request))
+ self.assertEqual(request.responseCode, FOUND)
+ self.assertEqual(request.responseHeaders.getRawHeaders(b"location"),
+ [b"http://dummy/folder/#baz?foo=bar"])
+
+
def _makeFilePathWithStringIO(self):
"""
Create a L{File} that when opened for reading, returns a L{StringIO}.
@@ -1689,3 +1710,19 @@
args, _, _, defaults = inspect.getargspec(static.loadMimeTypes)
defaultInit = defaults[args.index("init")]
self.assertIdentical(defaultInit, mimetypes.init)
+
+
+class StaticDeprecationTests(TestCase):
+
+ def test_addSlashDeprecated(self):
+ """
+ L{twisted.web.static.addSlash} is deprecated.
+ """
+ from twisted.web.static import addSlash
+
+ addSlash(DummyRequest([b'']))
+
+ warnings = self.flushWarnings([self.test_addSlashDeprecated])
+ self.assertEqual(len(warnings), 1)
+ self.assertEqual(warnings[0]['message'],
+ "twisted.web.static.addSlash was deprecated in Twisted 16.0.0")