simple_httpclient_test.py
Go to the documentation of this file.
00001 from __future__ import absolute_import, division, with_statement
00002 
00003 import collections
00004 from contextlib import closing
00005 import gzip
00006 import logging
00007 import re
00008 import socket
00009 
00010 from tornado.httpclient import AsyncHTTPClient
00011 from tornado.httputil import HTTPHeaders
00012 from tornado.ioloop import IOLoop
00013 from tornado.simple_httpclient import SimpleAsyncHTTPClient, _DEFAULT_CA_CERTS
00014 from tornado.test.httpclient_test import HTTPClientCommonTestCase, ChunkHandler, CountdownHandler, HelloWorldHandler
00015 from tornado.testing import AsyncHTTPTestCase, AsyncTestCase, LogTrapTestCase
00016 from tornado.util import b
00017 from tornado.web import RequestHandler, Application, asynchronous, url
00018 
00019 
00020 class SimpleHTTPClientCommonTestCase(HTTPClientCommonTestCase):
00021     def get_http_client(self):
00022         client = SimpleAsyncHTTPClient(io_loop=self.io_loop,
00023                                        force_instance=True)
00024         self.assertTrue(isinstance(client, SimpleAsyncHTTPClient))
00025         return client
00026 
00027 # Remove the base class from our namespace so the unittest module doesn't
00028 # try to run it again.
00029 del HTTPClientCommonTestCase
00030 
00031 
00032 class TriggerHandler(RequestHandler):
00033     def initialize(self, queue, wake_callback):
00034         self.queue = queue
00035         self.wake_callback = wake_callback
00036 
00037     @asynchronous
00038     def get(self):
00039         logging.info("queuing trigger")
00040         self.queue.append(self.finish)
00041         if self.get_argument("wake", "true") == "true":
00042             self.wake_callback()
00043 
00044 
00045 class HangHandler(RequestHandler):
00046     @asynchronous
00047     def get(self):
00048         pass
00049 
00050 
00051 class ContentLengthHandler(RequestHandler):
00052     def get(self):
00053         self.set_header("Content-Length", self.get_argument("value"))
00054         self.write("ok")
00055 
00056 
00057 class HeadHandler(RequestHandler):
00058     def head(self):
00059         self.set_header("Content-Length", "7")
00060 
00061 
00062 class OptionsHandler(RequestHandler):
00063     def options(self):
00064         self.set_header("Access-Control-Allow-Origin", "*")
00065         self.write("ok")
00066 
00067 
00068 class NoContentHandler(RequestHandler):
00069     def get(self):
00070         if self.get_argument("error", None):
00071             self.set_header("Content-Length", "7")
00072         self.set_status(204)
00073 
00074 
00075 class SeeOther303PostHandler(RequestHandler):
00076     def post(self):
00077         assert self.request.body == b("blah")
00078         self.set_header("Location", "/303_get")
00079         self.set_status(303)
00080 
00081 
00082 class SeeOther303GetHandler(RequestHandler):
00083     def get(self):
00084         assert not self.request.body
00085         self.write("ok")
00086 
00087 
00088 class HostEchoHandler(RequestHandler):
00089     def get(self):
00090         self.write(self.request.headers["Host"])
00091 
00092 
00093 class SimpleHTTPClientTestCase(AsyncHTTPTestCase, LogTrapTestCase):
00094     def setUp(self):
00095         super(SimpleHTTPClientTestCase, self).setUp()
00096         self.http_client = SimpleAsyncHTTPClient(self.io_loop)
00097 
00098     def get_app(self):
00099         # callable objects to finish pending /trigger requests
00100         self.triggers = collections.deque()
00101         return Application([
00102             url("/trigger", TriggerHandler, dict(queue=self.triggers,
00103                                                  wake_callback=self.stop)),
00104             url("/chunk", ChunkHandler),
00105             url("/countdown/([0-9]+)", CountdownHandler, name="countdown"),
00106             url("/hang", HangHandler),
00107             url("/hello", HelloWorldHandler),
00108             url("/content_length", ContentLengthHandler),
00109             url("/head", HeadHandler),
00110             url("/options", OptionsHandler),
00111             url("/no_content", NoContentHandler),
00112             url("/303_post", SeeOther303PostHandler),
00113             url("/303_get", SeeOther303GetHandler),
00114             url("/host_echo", HostEchoHandler),
00115             ], gzip=True)
00116 
00117     def test_singleton(self):
00118         # Class "constructor" reuses objects on the same IOLoop
00119         self.assertTrue(SimpleAsyncHTTPClient(self.io_loop) is
00120                         SimpleAsyncHTTPClient(self.io_loop))
00121         # unless force_instance is used
00122         self.assertTrue(SimpleAsyncHTTPClient(self.io_loop) is not
00123                         SimpleAsyncHTTPClient(self.io_loop,
00124                                               force_instance=True))
00125         # different IOLoops use different objects
00126         io_loop2 = IOLoop()
00127         self.assertTrue(SimpleAsyncHTTPClient(self.io_loop) is not
00128                         SimpleAsyncHTTPClient(io_loop2))
00129 
00130     def test_connection_limit(self):
00131         client = SimpleAsyncHTTPClient(self.io_loop, max_clients=2,
00132                                        force_instance=True)
00133         self.assertEqual(client.max_clients, 2)
00134         seen = []
00135         # Send 4 requests.  Two can be sent immediately, while the others
00136         # will be queued
00137         for i in range(4):
00138             client.fetch(self.get_url("/trigger"),
00139                          lambda response, i=i: (seen.append(i), self.stop()))
00140         self.wait(condition=lambda: len(self.triggers) == 2)
00141         self.assertEqual(len(client.queue), 2)
00142 
00143         # Finish the first two requests and let the next two through
00144         self.triggers.popleft()()
00145         self.triggers.popleft()()
00146         self.wait(condition=lambda: (len(self.triggers) == 2 and
00147                                      len(seen) == 2))
00148         self.assertEqual(set(seen), set([0, 1]))
00149         self.assertEqual(len(client.queue), 0)
00150 
00151         # Finish all the pending requests
00152         self.triggers.popleft()()
00153         self.triggers.popleft()()
00154         self.wait(condition=lambda: len(seen) == 4)
00155         self.assertEqual(set(seen), set([0, 1, 2, 3]))
00156         self.assertEqual(len(self.triggers), 0)
00157 
00158     def test_redirect_connection_limit(self):
00159         # following redirects should not consume additional connections
00160         client = SimpleAsyncHTTPClient(self.io_loop, max_clients=1,
00161                                        force_instance=True)
00162         client.fetch(self.get_url('/countdown/3'), self.stop,
00163                      max_redirects=3)
00164         response = self.wait()
00165         response.rethrow()
00166 
00167     def test_default_certificates_exist(self):
00168         open(_DEFAULT_CA_CERTS).close()
00169 
00170     def test_gzip(self):
00171         # All the tests in this file should be using gzip, but this test
00172         # ensures that it is in fact getting compressed.
00173         # Setting Accept-Encoding manually bypasses the client's
00174         # decompression so we can see the raw data.
00175         response = self.fetch("/chunk", use_gzip=False,
00176                               headers={"Accept-Encoding": "gzip"})
00177         self.assertEqual(response.headers["Content-Encoding"], "gzip")
00178         self.assertNotEqual(response.body, b("asdfqwer"))
00179         # Our test data gets bigger when gzipped.  Oops.  :)
00180         self.assertEqual(len(response.body), 34)
00181         f = gzip.GzipFile(mode="r", fileobj=response.buffer)
00182         self.assertEqual(f.read(), b("asdfqwer"))
00183 
00184     def test_max_redirects(self):
00185         response = self.fetch("/countdown/5", max_redirects=3)
00186         self.assertEqual(302, response.code)
00187         # We requested 5, followed three redirects for 4, 3, 2, then the last
00188         # unfollowed redirect is to 1.
00189         self.assertTrue(response.request.url.endswith("/countdown/5"))
00190         self.assertTrue(response.effective_url.endswith("/countdown/2"))
00191         self.assertTrue(response.headers["Location"].endswith("/countdown/1"))
00192 
00193     def test_header_reuse(self):
00194         # Apps may reuse a headers object if they are only passing in constant
00195         # headers like user-agent.  The header object should not be modified.
00196         headers = HTTPHeaders({'User-Agent': 'Foo'})
00197         self.fetch("/hello", headers=headers)
00198         self.assertEqual(list(headers.get_all()), [('User-Agent', 'Foo')])
00199 
00200     def test_303_redirect(self):
00201         response = self.fetch("/303_post", method="POST", body="blah")
00202         self.assertEqual(200, response.code)
00203         self.assertTrue(response.request.url.endswith("/303_post"))
00204         self.assertTrue(response.effective_url.endswith("/303_get"))
00205         #request is the original request, is a POST still
00206         self.assertEqual("POST", response.request.method)
00207 
00208     def test_request_timeout(self):
00209         response = self.fetch('/trigger?wake=false', request_timeout=0.1)
00210         self.assertEqual(response.code, 599)
00211         self.assertTrue(0.099 < response.request_time < 0.11, response.request_time)
00212         self.assertEqual(str(response.error), "HTTP 599: Timeout")
00213         # trigger the hanging request to let it clean up after itself
00214         self.triggers.popleft()()
00215 
00216     def test_ipv6(self):
00217         if not socket.has_ipv6:
00218             # python compiled without ipv6 support, so skip this test
00219             return
00220         try:
00221             self.http_server.listen(self.get_http_port(), address='::1')
00222         except socket.gaierror, e:
00223             if e.args[0] == socket.EAI_ADDRFAMILY:
00224                 # python supports ipv6, but it's not configured on the network
00225                 # interface, so skip this test.
00226                 return
00227             raise
00228         url = self.get_url("/hello").replace("localhost", "[::1]")
00229 
00230         # ipv6 is currently disabled by default and must be explicitly requested
00231         self.http_client.fetch(url, self.stop)
00232         response = self.wait()
00233         self.assertEqual(response.code, 599)
00234 
00235         self.http_client.fetch(url, self.stop, allow_ipv6=True)
00236         response = self.wait()
00237         self.assertEqual(response.body, b("Hello world!"))
00238 
00239     def test_multiple_content_length_accepted(self):
00240         response = self.fetch("/content_length?value=2,2")
00241         self.assertEqual(response.body, b("ok"))
00242         response = self.fetch("/content_length?value=2,%202,2")
00243         self.assertEqual(response.body, b("ok"))
00244 
00245         response = self.fetch("/content_length?value=2,4")
00246         self.assertEqual(response.code, 599)
00247         response = self.fetch("/content_length?value=2,%202,3")
00248         self.assertEqual(response.code, 599)
00249 
00250     def test_head_request(self):
00251         response = self.fetch("/head", method="HEAD")
00252         self.assertEqual(response.code, 200)
00253         self.assertEqual(response.headers["content-length"], "7")
00254         self.assertFalse(response.body)
00255 
00256     def test_options_request(self):
00257         response = self.fetch("/options", method="OPTIONS")
00258         self.assertEqual(response.code, 200)
00259         self.assertEqual(response.headers["content-length"], "2")
00260         self.assertEqual(response.headers["access-control-allow-origin"], "*")
00261         self.assertEqual(response.body, b("ok"))
00262 
00263     def test_no_content(self):
00264         response = self.fetch("/no_content")
00265         self.assertEqual(response.code, 204)
00266         # 204 status doesn't need a content-length, but tornado will
00267         # add a zero content-length anyway.
00268         self.assertEqual(response.headers["Content-length"], "0")
00269 
00270         # 204 status with non-zero content length is malformed
00271         response = self.fetch("/no_content?error=1")
00272         self.assertEqual(response.code, 599)
00273 
00274     def test_host_header(self):
00275         host_re = re.compile(b("^localhost:[0-9]+$"))
00276         response = self.fetch("/host_echo")
00277         self.assertTrue(host_re.match(response.body))
00278 
00279         url = self.get_url("/host_echo").replace("http://", "http://me:secret@")
00280         self.http_client.fetch(url, self.stop)
00281         response = self.wait()
00282         self.assertTrue(host_re.match(response.body), response.body)
00283 
00284 
00285 class CreateAsyncHTTPClientTestCase(AsyncTestCase, LogTrapTestCase):
00286     def setUp(self):
00287         super(CreateAsyncHTTPClientTestCase, self).setUp()
00288         self.saved = AsyncHTTPClient._save_configuration()
00289 
00290     def tearDown(self):
00291         AsyncHTTPClient._restore_configuration(self.saved)
00292         super(CreateAsyncHTTPClientTestCase, self).tearDown()
00293 
00294     def test_max_clients(self):
00295         # The max_clients argument is tricky because it was originally
00296         # allowed to be passed positionally; newer arguments are keyword-only.
00297         AsyncHTTPClient.configure(SimpleAsyncHTTPClient)
00298         with closing(AsyncHTTPClient(
00299                 self.io_loop, force_instance=True)) as client:
00300             self.assertEqual(client.max_clients, 10)
00301         with closing(AsyncHTTPClient(
00302                 self.io_loop, 11, force_instance=True)) as client:
00303             self.assertEqual(client.max_clients, 11)
00304         with closing(AsyncHTTPClient(
00305                 self.io_loop, max_clients=11, force_instance=True)) as client:
00306             self.assertEqual(client.max_clients, 11)
00307 
00308         # Now configure max_clients statically and try overriding it
00309         # with each way max_clients can be passed
00310         AsyncHTTPClient.configure(SimpleAsyncHTTPClient, max_clients=12)
00311         with closing(AsyncHTTPClient(
00312                 self.io_loop, force_instance=True)) as client:
00313             self.assertEqual(client.max_clients, 12)
00314         with closing(AsyncHTTPClient(
00315                 self.io_loop, max_clients=13, force_instance=True)) as client:
00316             self.assertEqual(client.max_clients, 13)
00317         with closing(AsyncHTTPClient(
00318                 self.io_loop, max_clients=14, force_instance=True)) as client:
00319             self.assertEqual(client.max_clients, 14)


rosbridge_server
Author(s): Jonathan Mace
autogenerated on Thu Jan 2 2014 11:53:55