00001
00002
00003 from __future__ import absolute_import, division, with_statement
00004
00005 import base64
00006 import binascii
00007 from contextlib import closing
00008 import functools
00009
00010 from tornado.escape import utf8
00011 from tornado.httpclient import AsyncHTTPClient
00012 from tornado.iostream import IOStream
00013 from tornado import netutil
00014 from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase, get_unused_port
00015 from tornado.util import b, bytes_type
00016 from tornado.web import Application, RequestHandler, url
00017
00018
00019 class HelloWorldHandler(RequestHandler):
00020 def get(self):
00021 name = self.get_argument("name", "world")
00022 self.set_header("Content-Type", "text/plain")
00023 self.finish("Hello %s!" % name)
00024
00025
00026 class PostHandler(RequestHandler):
00027 def post(self):
00028 self.finish("Post arg1: %s, arg2: %s" % (
00029 self.get_argument("arg1"), self.get_argument("arg2")))
00030
00031
00032 class ChunkHandler(RequestHandler):
00033 def get(self):
00034 self.write("asdf")
00035 self.flush()
00036 self.write("qwer")
00037
00038
00039 class AuthHandler(RequestHandler):
00040 def get(self):
00041 self.finish(self.request.headers["Authorization"])
00042
00043
00044 class CountdownHandler(RequestHandler):
00045 def get(self, count):
00046 count = int(count)
00047 if count > 0:
00048 self.redirect(self.reverse_url("countdown", count - 1))
00049 else:
00050 self.write("Zero")
00051
00052
00053 class EchoPostHandler(RequestHandler):
00054 def post(self):
00055 self.write(self.request.body)
00056
00057
00058
00059
00060
00061
00062 class HTTPClientCommonTestCase(AsyncHTTPTestCase, LogTrapTestCase):
00063 def get_http_client(self):
00064 """Returns AsyncHTTPClient instance. May be overridden in subclass."""
00065 return AsyncHTTPClient(io_loop=self.io_loop)
00066
00067 def get_app(self):
00068 return Application([
00069 url("/hello", HelloWorldHandler),
00070 url("/post", PostHandler),
00071 url("/chunk", ChunkHandler),
00072 url("/auth", AuthHandler),
00073 url("/countdown/([0-9]+)", CountdownHandler, name="countdown"),
00074 url("/echopost", EchoPostHandler),
00075 ], gzip=True)
00076
00077 def setUp(self):
00078 super(HTTPClientCommonTestCase, self).setUp()
00079
00080 self.http_client = self.get_http_client()
00081
00082 def test_hello_world(self):
00083 response = self.fetch("/hello")
00084 self.assertEqual(response.code, 200)
00085 self.assertEqual(response.headers["Content-Type"], "text/plain")
00086 self.assertEqual(response.body, b("Hello world!"))
00087 self.assertEqual(int(response.request_time), 0)
00088
00089 response = self.fetch("/hello?name=Ben")
00090 self.assertEqual(response.body, b("Hello Ben!"))
00091
00092 def test_streaming_callback(self):
00093
00094 chunks = []
00095 response = self.fetch("/hello",
00096 streaming_callback=chunks.append)
00097
00098 self.assertEqual(chunks, [b("Hello world!")])
00099 self.assertFalse(response.body)
00100
00101 def test_post(self):
00102 response = self.fetch("/post", method="POST",
00103 body="arg1=foo&arg2=bar")
00104 self.assertEqual(response.code, 200)
00105 self.assertEqual(response.body, b("Post arg1: foo, arg2: bar"))
00106
00107 def test_chunked(self):
00108 response = self.fetch("/chunk")
00109 self.assertEqual(response.body, b("asdfqwer"))
00110
00111 chunks = []
00112 response = self.fetch("/chunk",
00113 streaming_callback=chunks.append)
00114 self.assertEqual(chunks, [b("asdf"), b("qwer")])
00115 self.assertFalse(response.body)
00116
00117 def test_chunked_close(self):
00118
00119
00120 port = get_unused_port()
00121 (sock,) = netutil.bind_sockets(port, address="127.0.0.1")
00122 with closing(sock):
00123 def write_response(stream, request_data):
00124 stream.write(b("""\
00125 HTTP/1.1 200 OK
00126 Transfer-Encoding: chunked
00127
00128 1
00129 1
00130 1
00131 2
00132 0
00133
00134 """).replace(b("\n"), b("\r\n")), callback=stream.close)
00135
00136 def accept_callback(conn, address):
00137
00138
00139 stream = IOStream(conn, io_loop=self.io_loop)
00140 stream.read_until(b("\r\n\r\n"),
00141 functools.partial(write_response, stream))
00142 netutil.add_accept_handler(sock, accept_callback, self.io_loop)
00143 self.http_client.fetch("http://127.0.0.1:%d/" % port, self.stop)
00144 resp = self.wait()
00145 resp.rethrow()
00146 self.assertEqual(resp.body, b("12"))
00147
00148 def test_basic_auth(self):
00149 self.assertEqual(self.fetch("/auth", auth_username="Aladdin",
00150 auth_password="open sesame").body,
00151 b("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="))
00152
00153 def test_follow_redirect(self):
00154 response = self.fetch("/countdown/2", follow_redirects=False)
00155 self.assertEqual(302, response.code)
00156 self.assertTrue(response.headers["Location"].endswith("/countdown/1"))
00157
00158 response = self.fetch("/countdown/2")
00159 self.assertEqual(200, response.code)
00160 self.assertTrue(response.effective_url.endswith("/countdown/0"))
00161 self.assertEqual(b("Zero"), response.body)
00162
00163 def test_credentials_in_url(self):
00164 url = self.get_url("/auth").replace("http://", "http://me:secret@")
00165 self.http_client.fetch(url, self.stop)
00166 response = self.wait()
00167 self.assertEqual(b("Basic ") + base64.b64encode(b("me:secret")),
00168 response.body)
00169
00170 def test_body_encoding(self):
00171 unicode_body = u"\xe9"
00172 byte_body = binascii.a2b_hex(b("e9"))
00173
00174
00175 response = self.fetch("/echopost", method="POST", body=unicode_body,
00176 headers={"Content-Type": "application/blah"})
00177 self.assertEqual(response.headers["Content-Length"], "2")
00178 self.assertEqual(response.body, utf8(unicode_body))
00179
00180
00181 response = self.fetch("/echopost", method="POST",
00182 body=byte_body,
00183 headers={"Content-Type": "application/blah"})
00184 self.assertEqual(response.headers["Content-Length"], "1")
00185 self.assertEqual(response.body, byte_body)
00186
00187
00188
00189 response = self.fetch("/echopost", method="POST", body=byte_body,
00190 headers={"Content-Type": "application/blah"},
00191 user_agent=u"foo")
00192 self.assertEqual(response.headers["Content-Length"], "1")
00193 self.assertEqual(response.body, byte_body)
00194
00195 def test_types(self):
00196 response = self.fetch("/hello")
00197 self.assertEqual(type(response.body), bytes_type)
00198 self.assertEqual(type(response.headers["Content-Type"]), str)
00199 self.assertEqual(type(response.code), int)
00200 self.assertEqual(type(response.effective_url), str)