-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added first version of normalize_path_middleware #1118
Changes from 3 commits
8c44b21
0506abc
8f8b974
7990b9e
764781e
5e389af
b5bde12
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import asyncio | ||
|
||
# from aiohttp.web_exceptions import HTTPMovedPermanently, HTTPNotFound | ||
from aiohttp.web_urldispatcher import SystemRoute | ||
|
||
|
||
@asyncio.coroutine | ||
def normalize_path_middleware(app, handler): | ||
""" | ||
Middleware that normalizes the path of a request. By normalizing it means: | ||
|
||
- Add a trailing slash to the path. | ||
- Double slashes are replaced by one. | ||
""" | ||
|
||
@asyncio.coroutine | ||
def middleware(request): | ||
|
||
router = request.app.router | ||
if not isinstance(request.match_info.route, SystemRoute): | ||
resp = yield from handler(request) | ||
|
||
elif not request.path.endswith('/'): | ||
request = request.clone(path=request.path + '/') | ||
match_info = yield from router.resolve(request) | ||
resp = yield from match_info.handler(request) | ||
|
||
else: | ||
resp = yield from handler(request) | ||
|
||
return resp | ||
|
||
return middleware |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,7 @@ | |
from . import hdrs | ||
from .helpers import reify, sentinel | ||
from .protocol import Response as ResponseImpl | ||
from .protocol import HttpVersion10, HttpVersion11 | ||
from .protocol import HttpVersion10, HttpVersion11, RawRequestMessage | ||
from .streams import EOF_MARKER | ||
|
||
__all__ = ( | ||
|
@@ -381,6 +381,33 @@ def post(self): | |
self._post = MultiDictProxy(out) | ||
return self._post | ||
|
||
def clone(self, *, method=None, path=None, headers=None, raw_headers=None): | ||
""" | ||
Creates and returns a new instance of Request object. If no parameters | ||
are given, an exact copy is returned. If a parameter is not passed, it | ||
will reuse the one from the current request object. | ||
|
||
:param method: str http method | ||
:param path: str url path to use | ||
:param headers: CIMultidictProxy or compatible containing the headers. | ||
:param raw_headers: tuple of two element tuples containing the headers | ||
as bytearrays. | ||
""" | ||
|
||
message = RawRequestMessage( | ||
method or self.method, path or self.path, self.version, | ||
headers or self.headers, raw_headers or self.raw_headers, | ||
self.keep_alive, None) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oohh. Our very low level API is really bad. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was wondering the same... Same with the headers and raw_headers. Why do we need to send both replicated? It would be cleaner to just send the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahhh I see... Still feels bit redundant |
||
|
||
return Request( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's keep |
||
self._app, | ||
message, | ||
self._payload, | ||
self._transport, | ||
self._reader, | ||
self._writer, | ||
secure_proxy_ssl_header=self._secure_proxy_ssl_header) | ||
|
||
def copy(self): | ||
raise NotImplementedError | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,12 @@ | ||
[pep8] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did you add this option? 79 lines is default value. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. People can have configurations at user home level. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm. Ok, make sense. |
||
max-line-length=80 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please keep 79: https://www.python.org/dev/peps/pep-0008/#id19 |
||
|
||
[easy_install] | ||
zip_ok = false | ||
|
||
[flake8] | ||
ignore = N801,N802,N803,E226 | ||
max-line-length=80 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 79 |
||
|
||
[tool:pytest] | ||
timeout = 5 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import asyncio | ||
|
||
from aiohttp import middlewares, web | ||
|
||
|
||
class TestNormalizePathMiddleware: | ||
|
||
@asyncio.coroutine | ||
def test_add_trailing_when_necessary(self, create_app_and_client): | ||
app, client = yield from create_app_and_client() | ||
app.middlewares.append(middlewares.normalize_path_middleware) | ||
app.router.add_route( | ||
'GET', '/resource1', lambda x: web.Response(text="OK")) | ||
app.router.add_route( | ||
'GET', '/resource2/', lambda x: web.Response(text="OK")) | ||
|
||
resp = yield from client.get('/resource1') | ||
assert resp.status == 200 | ||
|
||
resp = yield from client.get('/resource1/') | ||
assert resp.status == 404 | ||
|
||
resp = yield from client.get('/resource2') | ||
assert resp.status == 200 | ||
|
||
resp = yield from client.get('/resource2/') | ||
assert resp.status == 200 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't use autodoc markup