routing_test.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  2. # not use this file except in compliance with the License. You may obtain
  3. # a copy of the License at
  4. #
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. #
  7. # Unless required by applicable law or agreed to in writing, software
  8. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  9. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  10. # License for the specific language governing permissions and limitations
  11. # under the License.
  12. from __future__ import absolute_import, division, print_function
  13. from tornado.httputil import HTTPHeaders, HTTPMessageDelegate, HTTPServerConnectionDelegate, ResponseStartLine # noqa: E501
  14. from tornado.routing import HostMatches, PathMatches, ReversibleRouter, Router, Rule, RuleRouter
  15. from tornado.testing import AsyncHTTPTestCase
  16. from tornado.web import Application, HTTPError, RequestHandler
  17. from tornado.wsgi import WSGIContainer
  18. class BasicRouter(Router):
  19. def find_handler(self, request, **kwargs):
  20. class MessageDelegate(HTTPMessageDelegate):
  21. def __init__(self, connection):
  22. self.connection = connection
  23. def finish(self):
  24. self.connection.write_headers(
  25. ResponseStartLine("HTTP/1.1", 200, "OK"),
  26. HTTPHeaders({"Content-Length": "2"}),
  27. b"OK"
  28. )
  29. self.connection.finish()
  30. return MessageDelegate(request.connection)
  31. class BasicRouterTestCase(AsyncHTTPTestCase):
  32. def get_app(self):
  33. return BasicRouter()
  34. def test_basic_router(self):
  35. response = self.fetch("/any_request")
  36. self.assertEqual(response.body, b"OK")
  37. resources = {}
  38. class GetResource(RequestHandler):
  39. def get(self, path):
  40. if path not in resources:
  41. raise HTTPError(404)
  42. self.finish(resources[path])
  43. class PostResource(RequestHandler):
  44. def post(self, path):
  45. resources[path] = self.request.body
  46. class HTTPMethodRouter(Router):
  47. def __init__(self, app):
  48. self.app = app
  49. def find_handler(self, request, **kwargs):
  50. handler = GetResource if request.method == "GET" else PostResource
  51. return self.app.get_handler_delegate(request, handler, path_args=[request.path])
  52. class HTTPMethodRouterTestCase(AsyncHTTPTestCase):
  53. def get_app(self):
  54. return HTTPMethodRouter(Application())
  55. def test_http_method_router(self):
  56. response = self.fetch("/post_resource", method="POST", body="data")
  57. self.assertEqual(response.code, 200)
  58. response = self.fetch("/get_resource")
  59. self.assertEqual(response.code, 404)
  60. response = self.fetch("/post_resource")
  61. self.assertEqual(response.code, 200)
  62. self.assertEqual(response.body, b"data")
  63. def _get_named_handler(handler_name):
  64. class Handler(RequestHandler):
  65. def get(self, *args, **kwargs):
  66. if self.application.settings.get("app_name") is not None:
  67. self.write(self.application.settings["app_name"] + ": ")
  68. self.finish(handler_name + ": " + self.reverse_url(handler_name))
  69. return Handler
  70. FirstHandler = _get_named_handler("first_handler")
  71. SecondHandler = _get_named_handler("second_handler")
  72. class CustomRouter(ReversibleRouter):
  73. def __init__(self):
  74. super(CustomRouter, self).__init__()
  75. self.routes = {}
  76. def add_routes(self, routes):
  77. self.routes.update(routes)
  78. def find_handler(self, request, **kwargs):
  79. if request.path in self.routes:
  80. app, handler = self.routes[request.path]
  81. return app.get_handler_delegate(request, handler)
  82. def reverse_url(self, name, *args):
  83. handler_path = '/' + name
  84. return handler_path if handler_path in self.routes else None
  85. class CustomRouterTestCase(AsyncHTTPTestCase):
  86. def get_app(self):
  87. class CustomApplication(Application):
  88. def reverse_url(self, name, *args):
  89. return router.reverse_url(name, *args)
  90. router = CustomRouter()
  91. app1 = CustomApplication(app_name="app1")
  92. app2 = CustomApplication(app_name="app2")
  93. router.add_routes({
  94. "/first_handler": (app1, FirstHandler),
  95. "/second_handler": (app2, SecondHandler),
  96. "/first_handler_second_app": (app2, FirstHandler),
  97. })
  98. return router
  99. def test_custom_router(self):
  100. response = self.fetch("/first_handler")
  101. self.assertEqual(response.body, b"app1: first_handler: /first_handler")
  102. response = self.fetch("/second_handler")
  103. self.assertEqual(response.body, b"app2: second_handler: /second_handler")
  104. response = self.fetch("/first_handler_second_app")
  105. self.assertEqual(response.body, b"app2: first_handler: /first_handler")
  106. class ConnectionDelegate(HTTPServerConnectionDelegate):
  107. def start_request(self, server_conn, request_conn):
  108. class MessageDelegate(HTTPMessageDelegate):
  109. def __init__(self, connection):
  110. self.connection = connection
  111. def finish(self):
  112. response_body = b"OK"
  113. self.connection.write_headers(
  114. ResponseStartLine("HTTP/1.1", 200, "OK"),
  115. HTTPHeaders({"Content-Length": str(len(response_body))}))
  116. self.connection.write(response_body)
  117. self.connection.finish()
  118. return MessageDelegate(request_conn)
  119. class RuleRouterTest(AsyncHTTPTestCase):
  120. def get_app(self):
  121. app = Application()
  122. def request_callable(request):
  123. request.connection.write_headers(
  124. ResponseStartLine("HTTP/1.1", 200, "OK"),
  125. HTTPHeaders({"Content-Length": "2"}))
  126. request.connection.write(b"OK")
  127. request.connection.finish()
  128. router = CustomRouter()
  129. router.add_routes({
  130. "/nested_handler": (app, _get_named_handler("nested_handler"))
  131. })
  132. app.add_handlers(".*", [
  133. (HostMatches("www.example.com"), [
  134. (PathMatches("/first_handler"),
  135. "tornado.test.routing_test.SecondHandler", {}, "second_handler")
  136. ]),
  137. Rule(PathMatches("/.*handler"), router),
  138. Rule(PathMatches("/first_handler"), FirstHandler, name="first_handler"),
  139. Rule(PathMatches("/request_callable"), request_callable),
  140. ("/connection_delegate", ConnectionDelegate())
  141. ])
  142. return app
  143. def test_rule_based_router(self):
  144. response = self.fetch("/first_handler")
  145. self.assertEqual(response.body, b"first_handler: /first_handler")
  146. response = self.fetch("/first_handler", headers={'Host': 'www.example.com'})
  147. self.assertEqual(response.body, b"second_handler: /first_handler")
  148. response = self.fetch("/nested_handler")
  149. self.assertEqual(response.body, b"nested_handler: /nested_handler")
  150. response = self.fetch("/nested_not_found_handler")
  151. self.assertEqual(response.code, 404)
  152. response = self.fetch("/connection_delegate")
  153. self.assertEqual(response.body, b"OK")
  154. response = self.fetch("/request_callable")
  155. self.assertEqual(response.body, b"OK")
  156. response = self.fetch("/404")
  157. self.assertEqual(response.code, 404)
  158. class WSGIContainerTestCase(AsyncHTTPTestCase):
  159. def get_app(self):
  160. wsgi_app = WSGIContainer(self.wsgi_app)
  161. class Handler(RequestHandler):
  162. def get(self, *args, **kwargs):
  163. self.finish(self.reverse_url("tornado"))
  164. return RuleRouter([
  165. (PathMatches("/tornado.*"), Application([(r"/tornado/test", Handler, {}, "tornado")])),
  166. (PathMatches("/wsgi"), wsgi_app),
  167. ])
  168. def wsgi_app(self, environ, start_response):
  169. start_response("200 OK", [])
  170. return [b"WSGI"]
  171. def test_wsgi_container(self):
  172. response = self.fetch("/tornado/test")
  173. self.assertEqual(response.body, b"/tornado/test")
  174. response = self.fetch("/wsgi")
  175. self.assertEqual(response.body, b"WSGI")
  176. def test_delegate_not_found(self):
  177. response = self.fetch("/404")
  178. self.assertEqual(response.code, 404)