Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
iostream_test.py1422 linesDownload Raw Back to test
1from tornado.concurrent import Future
2from tornado import gen
3from tornado import netutil
4from tornado.ioloop import IOLoop
5from tornado.iostream import (
6    IOStream,
7    SSLIOStream,
8    PipeIOStream,
9    StreamClosedError,
10    _StreamBuffer,
11)
12from tornado.httpclient import AsyncHTTPClient, HTTPResponse
13from tornado.httputil import HTTPHeaders
14from tornado.locks import Condition, Event
15from tornado.log import gen_log
16from tornado.netutil import ssl_options_to_context, ssl_wrap_socket
17from tornado.platform.asyncio import AddThreadSelectorEventLoop
18from tornado.tcpserver import TCPServer
19from tornado.testing import (
20    AsyncHTTPTestCase,
21    AsyncHTTPSTestCase,
22    AsyncTestCase,
23    bind_unused_port,
24    ExpectLog,
25    gen_test,
26)
27from tornado.test.util import (
28    skipIfNonUnix,
29    refusing_port,
30    ignore_deprecation,
31    abstract_base_test,
32)
33from tornado.web import RequestHandler, Application
34import asyncio
35import errno
36import hashlib
37import logging
38import os
39import platform
40import random
41import socket
42import ssl
43import typing
44from unittest import mock
45import unittest
46
47
48def _server_ssl_options():
49    return dict(
50        certfile=os.path.join(os.path.dirname(__file__), "test.crt"),
51        keyfile=os.path.join(os.path.dirname(__file__), "test.key"),
52    )
53
54
55class HelloHandler(RequestHandler):
56    def get(self):
57        self.write("Hello")
58
59
60@abstract_base_test
61class TestIOStreamWebMixin(AsyncTestCase):
62    # We want to run these tests with both AsyncHTTPTestCase and AsyncHTTPSTestCase,
63    # but this leads to some tricky inheritance situations. We want this class's
64    # get_app, but the test classes's get_http_port and fetch. There's no way to make
65    # the method resolution order to do what we want in all cases, so the current
66    # state is that that AsyncHTTP(S)TestCase must be the first base class of the
67    # final class, and that class must define a get_app method that calls mixin_get_app.
68    #
69    # Alternatives include defining this class in a factory that can change the base class
70    # or refactoring to use composition instead of inheritance for the http components.
71    def _make_client_iostream(self):
72        raise NotImplementedError()
73
74    def mixin_get_app(self):
75        return Application([("/", HelloHandler)])
76
77    def get_http_port(self) -> int:
78        raise NotImplementedError()
79
80    def fetch(
81        self, path: str, raise_error: bool = False, **kwargs: typing.Any
82    ) -> HTTPResponse:
83        # To be filled in by mixing in AsyncHTTPTestCase or AsyncHTTPSTestCase
84        raise NotImplementedError()
85
86    def test_connection_closed(self):
87        # When a server sends a response and then closes the connection,
88        # the client must be allowed to read the data before the IOStream
89        # closes itself.  Epoll reports closed connections with a separate
90        # EPOLLRDHUP event delivered at the same time as the read event,
91        # while kqueue reports them as a second read/write event with an EOF
92        # flag.
93        if (
94            AsyncHTTPClient.configured_class().__name__.endswith("CurlAsyncHTTPClient")
95            and platform.system() == "Darwin"
96        ):
97            # It's possible that this is Tornado's fault, either in AsyncIOLoop or in
98            # CurlAsyncHTTPClient, but we've also seen this kind of issue in libcurl itself
99            # (especially a long time ago). The error is tied to the use of Apple's
100            # SecureTransport instead of OpenSSL.
101            self.skipTest("libcurl doesn't handle closed connections cleanly on macOS")
102        response = self.fetch("/", headers={"Connection": "close"})
103        response.rethrow()
104
105    @gen_test
106    def test_read_until_close(self):
107        stream = self._make_client_iostream()
108        yield stream.connect(("127.0.0.1", self.get_http_port()))
109        stream.write(b"GET / HTTP/1.0\r\n\r\n")
110
111        data = yield stream.read_until_close()
112        self.assertTrue(data.startswith(b"HTTP/1.1 200"))
113        self.assertTrue(data.endswith(b"Hello"))
114
115    @gen_test
116    def test_read_zero_bytes(self):
117        self.stream = self._make_client_iostream()
118        yield self.stream.connect(("127.0.0.1", self.get_http_port()))
119        self.stream.write(b"GET / HTTP/1.0\r\n\r\n")
120
121        # normal read
122        data = yield self.stream.read_bytes(9)
123        self.assertEqual(data, b"HTTP/1.1 ")
124
125        # zero bytes
126        data = yield self.stream.read_bytes(0)
127        self.assertEqual(data, b"")
128
129        # another normal read
130        data = yield self.stream.read_bytes(3)
131        self.assertEqual(data, b"200")
132
133        self.stream.close()
134
135    @gen_test
136    def test_write_while_connecting(self):
137        stream = self._make_client_iostream()
138        connect_fut = stream.connect(("127.0.0.1", self.get_http_port()))
139        # unlike the previous tests, try to write before the connection
140        # is complete.
141        write_fut = stream.write(b"GET / HTTP/1.0\r\nConnection: close\r\n\r\n")
142        self.assertFalse(connect_fut.done())
143
144        # connect will always complete before write.
145        it = gen.WaitIterator(connect_fut, write_fut)
146        resolved_order = []
147        while not it.done():
148            yield it.next()
149            resolved_order.append(it.current_future)
150        self.assertEqual(resolved_order, [connect_fut, write_fut])
151
152        data = yield stream.read_until_close()
153        self.assertTrue(data.endswith(b"Hello"))
154
155        stream.close()
156
157    @gen_test
158    def test_future_interface(self):
159        """Basic test of IOStream's ability to return Futures."""
160        stream = self._make_client_iostream()
161        connect_result = yield stream.connect(("127.0.0.1", self.get_http_port()))
162        self.assertIs(connect_result, stream)
163        yield stream.write(b"GET / HTTP/1.0\r\n\r\n")
164        first_line = yield stream.read_until(b"\r\n")
165        self.assertEqual(first_line, b"HTTP/1.1 200 OK\r\n")
166        # callback=None is equivalent to no callback.
167        header_data = yield stream.read_until(b"\r\n\r\n")
168        headers = HTTPHeaders.parse(header_data.decode("latin1"))
169        content_length = int(headers["Content-Length"])
170        body = yield stream.read_bytes(content_length)
171        self.assertEqual(body, b"Hello")
172        stream.close()
173
174    @gen_test
175    def test_future_close_while_reading(self):
176        stream = self._make_client_iostream()
177        yield stream.connect(("127.0.0.1", self.get_http_port()))
178        yield stream.write(b"GET / HTTP/1.0\r\n\r\n")
179        with self.assertRaises(StreamClosedError):
180            yield stream.read_bytes(1024 * 1024)
181        stream.close()
182
183    @gen_test
184    def test_future_read_until_close(self):
185        # Ensure that the data comes through before the StreamClosedError.
186        stream = self._make_client_iostream()
187        yield stream.connect(("127.0.0.1", self.get_http_port()))
188        yield stream.write(b"GET / HTTP/1.0\r\nConnection: close\r\n\r\n")
189        yield stream.read_until(b"\r\n\r\n")
190        body = yield stream.read_until_close()
191        self.assertEqual(body, b"Hello")
192
193        # Nothing else to read; the error comes immediately without waiting
194        # for yield.
195        with self.assertRaises(StreamClosedError):
196            stream.read_bytes(1)
197
198
199@abstract_base_test
200class TestReadWriteMixin(AsyncTestCase):
201    # Tests where one stream reads and the other writes.
202    # These should work for BaseIOStream implementations.
203
204    def make_iostream_pair(self, **kwargs):
205        raise NotImplementedError
206
207    def iostream_pair(self, **kwargs):
208        """Like make_iostream_pair, but called by ``async with``.
209
210        In py37 this becomes simpler with contextlib.asynccontextmanager.
211        """
212
213        class IOStreamPairContext:
214            def __init__(self, test, kwargs):
215                self.test = test
216                self.kwargs = kwargs
217
218            async def __aenter__(self):
219                self.pair = await self.test.make_iostream_pair(**self.kwargs)
220                return self.pair
221
222            async def __aexit__(self, typ, value, tb):
223                for s in self.pair:
224                    s.close()
225
226        return IOStreamPairContext(self, kwargs)
227
228    @gen_test
229    def test_write_zero_bytes(self):
230        # Attempting to write zero bytes should run the callback without
231        # going into an infinite loop.
232        rs, ws = yield self.make_iostream_pair()
233        yield ws.write(b"")
234        ws.close()
235        rs.close()
236
237    @gen_test
238    def test_future_delayed_close_callback(self):
239        # Same as test_delayed_close_callback, but with the future interface.
240        rs, ws = yield self.make_iostream_pair()
241
242        try:
243            ws.write(b"12")
244            chunks = []
245            chunks.append((yield rs.read_bytes(1)))
246            ws.close()
247            chunks.append((yield rs.read_bytes(1)))
248            self.assertEqual(chunks, [b"1", b"2"])
249        finally:
250            ws.close()
251            rs.close()
252
253    @gen_test
254    def test_close_buffered_data(self):
255        # Similar to the previous test, but with data stored in the OS's
256        # socket buffers instead of the IOStream's read buffer.  Out-of-band
257        # close notifications must be delayed until all data has been
258        # drained into the IOStream buffer. (epoll used to use out-of-band
259        # close events with EPOLLRDHUP, but no longer)
260        #
261        # This depends on the read_chunk_size being smaller than the
262        # OS socket buffer, so make it small.
263        rs, ws = yield self.make_iostream_pair(read_chunk_size=256)
264        try:
265            ws.write(b"A" * 512)
266            data = yield rs.read_bytes(256)
267            self.assertEqual(b"A" * 256, data)
268            ws.close()
269            # Allow the close to propagate to the `rs` side of the
270            # connection.  Using add_callback instead of add_timeout
271            # doesn't seem to work, even with multiple iterations
272            yield gen.sleep(0.01)
273            data = yield rs.read_bytes(256)
274            self.assertEqual(b"A" * 256, data)
275        finally:
276            ws.close()
277            rs.close()
278
279    @gen_test
280    def test_read_until_close_after_close(self):
281        # Similar to test_delayed_close_callback, but read_until_close takes
282        # a separate code path so test it separately.
283        rs, ws = yield self.make_iostream_pair()
284        try:
285            ws.write(b"1234")
286            # Read one byte to make sure the client has received the data.
287            # It won't run the close callback as long as there is more buffered
288            # data that could satisfy a later read.
289            data = yield rs.read_bytes(1)
290            ws.close()
291            self.assertEqual(data, b"1")
292            data = yield rs.read_until_close()
293            self.assertEqual(data, b"234")
294        finally:
295            ws.close()
296            rs.close()
297
298    @gen_test
299    def test_large_read_until(self):
300        # Performance test: read_until used to have a quadratic component
301        # so a read_until of 4MB would take 8 seconds; now it takes 0.25
302        # seconds.
303        rs, ws = yield self.make_iostream_pair()
304        try:
305            # This test fails on pypy with ssl.  I think it's because
306            # pypy's gc defeats moves objects, breaking the
307            # "frozen write buffer" assumption.
308            if (
309                isinstance(rs, SSLIOStream)
310                and platform.python_implementation() == "PyPy"
311            ):
312                raise unittest.SkipTest("pypy gc causes problems with openssl")
313            NUM_KB = 4096
314            for i in range(NUM_KB):
315                ws.write(b"A" * 1024)
316            ws.write(b"\r\n")
317            data = yield rs.read_until(b"\r\n")
318            self.assertEqual(len(data), NUM_KB * 1024 + 2)
319        finally:
320            ws.close()
321            rs.close()
322
323    @gen_test
324    async def test_read_until_with_close_after_second_packet(self):
325        # This is a regression test for a regression in Tornado 6.0
326        # (maybe 6.0.3?) reported in
327        # https://github.com/tornadoweb/tornado/issues/2717
328        #
329        # The data arrives in two chunks; the stream is closed at the
330        # same time that the second chunk is received. If the second
331        # chunk is larger than the first, it works, but when this bug
332        # existed it would fail if the second chunk were smaller than
333        # the first. This is due to the optimization that the
334        # read_until condition is only checked when the buffer doubles
335        # in size
336        async with self.iostream_pair() as (rs, ws):
337            rf = asyncio.ensure_future(rs.read_until(b"done"))
338            # We need to wait for the read_until to actually start. On
339            # windows that's tricky because the selector runs in
340            # another thread; sleeping is the simplest way.
341            await asyncio.sleep(0.1)
342            await ws.write(b"x" * 2048)
343            ws.write(b"done")
344            ws.close()
345            await rf
346
347    @gen_test
348    async def test_read_until_unsatisfied_after_close(self):
349        # If a stream is closed while reading, it raises
350        # StreamClosedError instead of UnsatisfiableReadError (the
351        # latter should only be raised when byte limits are reached).
352        # The particular scenario tested here comes from #2717.
353        async with self.iostream_pair() as (rs, ws):
354            rf = asyncio.ensure_future(rs.read_until(b"done"))
355            await ws.write(b"x" * 2048)
356            ws.write(b"foo")
357            ws.close()
358            with self.assertRaises(StreamClosedError):
359                await rf
360
361    @gen_test
362    def test_close_callback_with_pending_read(self):
363        # Regression test for a bug that was introduced in 2.3
364        # where the IOStream._close_callback would never be called
365        # if there were pending reads.
366        OK = b"OK\r\n"
367        rs, ws = yield self.make_iostream_pair()
368        event = Event()
369        rs.set_close_callback(event.set)
370        try:
371            ws.write(OK)
372            res = yield rs.read_until(b"\r\n")
373            self.assertEqual(res, OK)
374
375            ws.close()
376            rs.read_until(b"\r\n")
377            # If _close_callback (self.stop) is not called,
378            # an AssertionError: Async operation timed out after 5 seconds
379            # will be raised.
380            yield event.wait()
381        finally:
382            ws.close()
383            rs.close()
384
385    @gen_test
386    def test_future_close_callback(self):
387        # Regression test for interaction between the Future read interfaces
388        # and IOStream._maybe_add_error_listener.
389        rs, ws = yield self.make_iostream_pair()
390        closed = [False]
391        cond = Condition()
392
393        def close_callback():
394            closed[0] = True
395            cond.notify()
396
397        rs.set_close_callback(close_callback)
398        try:
399            ws.write(b"a")
400            res = yield rs.read_bytes(1)
401            self.assertEqual(res, b"a")
402            self.assertFalse(closed[0])
403            ws.close()
404            yield cond.wait()
405            self.assertTrue(closed[0])
406        finally:
407            rs.close()
408            ws.close()
409
410    @gen_test
411    def test_write_memoryview(self):
412        rs, ws = yield self.make_iostream_pair()
413        try:
414            fut = rs.read_bytes(4)
415            ws.write(memoryview(b"hello"))
416            data = yield fut
417            self.assertEqual(data, b"hell")
418        finally:
419            ws.close()
420            rs.close()
421
422    @gen_test
423    def test_read_bytes_partial(self):
424        rs, ws = yield self.make_iostream_pair()
425        try:
426            # Ask for more than is available with partial=True
427            fut = rs.read_bytes(50, partial=True)
428            ws.write(b"hello")
429            data = yield fut
430            self.assertEqual(data, b"hello")
431
432            # Ask for less than what is available; num_bytes is still
433            # respected.
434            fut = rs.read_bytes(3, partial=True)
435            ws.write(b"world")
436            data = yield fut
437            self.assertEqual(data, b"wor")
438
439            # Partial reads won't return an empty string, but read_bytes(0)
440            # will.
441            data = yield rs.read_bytes(0, partial=True)
442            self.assertEqual(data, b"")
443        finally:
444            ws.close()
445            rs.close()
446
447    @gen_test
448    def test_read_until_max_bytes(self):
449        rs, ws = yield self.make_iostream_pair()
450        closed = Event()
451        rs.set_close_callback(closed.set)
452        try:
453            # Extra room under the limit
454            fut = rs.read_until(b"def", max_bytes=50)
455            ws.write(b"abcdef")
456            data = yield fut
457            self.assertEqual(data, b"abcdef")
458
459            # Just enough space
460            fut = rs.read_until(b"def", max_bytes=6)
461            ws.write(b"abcdef")
462            data = yield fut
463            self.assertEqual(data, b"abcdef")
464
465            # Not enough space, but we don't know it until all we can do is
466            # log a warning and close the connection.
467            with ExpectLog(gen_log, "Unsatisfiable read", level=logging.INFO):
468                fut = rs.read_until(b"def", max_bytes=5)
469                ws.write(b"123456")
470                yield closed.wait()
471        finally:
472            ws.close()
473            rs.close()
474
475    @gen_test
476    def test_read_until_max_bytes_inline(self):
477        rs, ws = yield self.make_iostream_pair()
478        closed = Event()
479        rs.set_close_callback(closed.set)
480        try:
481            # Similar to the error case in the previous test, but the
482            # ws writes first so rs reads are satisfied
483            # inline.  For consistency with the out-of-line case, we
484            # do not raise the error synchronously.
485            ws.write(b"123456")
486            with ExpectLog(gen_log, "Unsatisfiable read", level=logging.INFO):
487                with self.assertRaises(StreamClosedError):
488                    yield rs.read_until(b"def", max_bytes=5)
489            yield closed.wait()
490        finally:
491            ws.close()
492            rs.close()
493
494    @gen_test
495    def test_read_until_max_bytes_ignores_extra(self):
496        rs, ws = yield self.make_iostream_pair()
497        closed = Event()
498        rs.set_close_callback(closed.set)
499        try:
500            # Even though data that matches arrives the same packet that
501            # puts us over the limit, we fail the request because it was not
502            # found within the limit.
503            ws.write(b"abcdef")
504            with ExpectLog(gen_log, "Unsatisfiable read", level=logging.INFO):
505                rs.read_until(b"def", max_bytes=5)
506                yield closed.wait()
507        finally:
508            ws.close()
509            rs.close()
510
511    @gen_test
512    def test_read_until_regex_max_bytes(self):
513        rs, ws = yield self.make_iostream_pair()
514        closed = Event()
515        rs.set_close_callback(closed.set)
516        try:
517            # Extra room under the limit
518            fut = rs.read_until_regex(b"def", max_bytes=50)
519            ws.write(b"abcdef")
520            data = yield fut
521            self.assertEqual(data, b"abcdef")
522
523            # Just enough space
524            fut = rs.read_until_regex(b"def", max_bytes=6)
525            ws.write(b"abcdef")
526            data = yield fut
527            self.assertEqual(data, b"abcdef")
528
529            # Not enough space, but we don't know it until all we can do is
530            # log a warning and close the connection.
531            with ExpectLog(gen_log, "Unsatisfiable read", level=logging.INFO):
532                rs.read_until_regex(b"def", max_bytes=5)
533                ws.write(b"123456")
534                yield closed.wait()
535        finally:
536            ws.close()
537            rs.close()
538
539    @gen_test
540    def test_read_until_regex_max_bytes_inline(self):
541        rs, ws = yield self.make_iostream_pair()
542        closed = Event()
543        rs.set_close_callback(closed.set)
544        try:
545            # Similar to the error case in the previous test, but the
546            # ws writes first so rs reads are satisfied
547            # inline.  For consistency with the out-of-line case, we
548            # do not raise the error synchronously.
549            ws.write(b"123456")
550            with ExpectLog(gen_log, "Unsatisfiable read", level=logging.INFO):
551                rs.read_until_regex(b"def", max_bytes=5)
552                yield closed.wait()
553        finally:
554            ws.close()
555            rs.close()
556
557    @gen_test
558    def test_read_until_regex_max_bytes_ignores_extra(self):
559        rs, ws = yield self.make_iostream_pair()
560        closed = Event()
561        rs.set_close_callback(closed.set)
562        try:
563            # Even though data that matches arrives the same packet that
564            # puts us over the limit, we fail the request because it was not
565            # found within the limit.
566            ws.write(b"abcdef")
567            with ExpectLog(gen_log, "Unsatisfiable read", level=logging.INFO):
568                rs.read_until_regex(b"def", max_bytes=5)
569                yield closed.wait()
570        finally:
571            ws.close()
572            rs.close()
573
574    @gen_test
575    def test_small_reads_from_large_buffer(self):
576        # 10KB buffer size, 100KB available to read.
577        # Read 1KB at a time and make sure that the buffer is not eagerly
578        # filled.
579        rs, ws = yield self.make_iostream_pair(max_buffer_size=10 * 1024)
580        try:
581            ws.write(b"a" * 1024 * 100)
582            for i in range(100):
583                data = yield rs.read_bytes(1024)
584                self.assertEqual(data, b"a" * 1024)
585        finally:
586            ws.close()
587            rs.close()
588
589    @gen_test
590    def test_small_read_untils_from_large_buffer(self):
591        # 10KB buffer size, 100KB available to read.
592        # Read 1KB at a time and make sure that the buffer is not eagerly
593        # filled.
594        rs, ws = yield self.make_iostream_pair(max_buffer_size=10 * 1024)
595        try:
596            ws.write((b"a" * 1023 + b"\n") * 100)
597            for i in range(100):
598                data = yield rs.read_until(b"\n", max_bytes=4096)
599                self.assertEqual(data, b"a" * 1023 + b"\n")
600        finally:
601            ws.close()
602            rs.close()
603
604    @gen_test
605    def test_flow_control(self):
606        MB = 1024 * 1024
607        rs, ws = yield self.make_iostream_pair(max_buffer_size=5 * MB)
608        try:
609            # Client writes more than the rs will accept.
610            ws.write(b"a" * 10 * MB)
611            # The rs pauses while reading.
612            yield rs.read_bytes(MB)
613            yield gen.sleep(0.1)
614            # The ws's writes have been blocked; the rs can
615            # continue to read gradually.
616            for i in range(9):
617                yield rs.read_bytes(MB)
618        finally:
619            rs.close()
620            ws.close()
621
622    @gen_test
623    def test_read_into(self):
624        rs, ws = yield self.make_iostream_pair()
625
626        def sleep_some():
627            self.io_loop.run_sync(lambda: gen.sleep(0.05))
628
629        try:
630            buf = bytearray(10)
631            fut = rs.read_into(buf)
632            ws.write(b"hello")
633            yield gen.sleep(0.05)
634            self.assertTrue(rs.reading())
635            ws.write(b"world!!")
636            data = yield fut
637            self.assertFalse(rs.reading())
638            self.assertEqual(data, 10)
639            self.assertEqual(bytes(buf), b"helloworld")
640
641            # Existing buffer is fed into user buffer
642            fut = rs.read_into(buf)
643            yield gen.sleep(0.05)
644            self.assertTrue(rs.reading())
645            ws.write(b"1234567890")
646            data = yield fut
647            self.assertFalse(rs.reading())
648            self.assertEqual(data, 10)
649            self.assertEqual(bytes(buf), b"!!12345678")
650
651            # Existing buffer can satisfy read immediately
652            buf = bytearray(4)
653            ws.write(b"abcdefghi")
654            data = yield rs.read_into(buf)
655            self.assertEqual(data, 4)
656            self.assertEqual(bytes(buf), b"90ab")
657
658            data = yield rs.read_bytes(7)
659            self.assertEqual(data, b"cdefghi")
660        finally:
661            ws.close()
662            rs.close()
663
664    @gen_test
665    def test_read_into_partial(self):
666        rs, ws = yield self.make_iostream_pair()
667
668        try:
669            # Partial read
670            buf = bytearray(10)
671            fut = rs.read_into(buf, partial=True)
672            ws.write(b"hello")
673            data = yield fut
674            self.assertFalse(rs.reading())
675            self.assertEqual(data, 5)
676            self.assertEqual(bytes(buf), b"hello\0\0\0\0\0")
677
678            # Full read despite partial=True
679            ws.write(b"world!1234567890")
680            data = yield rs.read_into(buf, partial=True)
681            self.assertEqual(data, 10)
682            self.assertEqual(bytes(buf), b"world!1234")
683
684            # Existing buffer can satisfy read immediately
685            data = yield rs.read_into(buf, partial=True)
686            self.assertEqual(data, 6)
687            self.assertEqual(bytes(buf), b"5678901234")
688
689        finally:
690            ws.close()
691            rs.close()
692
693    @gen_test
694    def test_read_into_zero_bytes(self):
695        rs, ws = yield self.make_iostream_pair()
696        try:
697            buf = bytearray()
698            fut = rs.read_into(buf)
699            self.assertEqual(fut.result(), 0)
700        finally:
701            ws.close()
702            rs.close()
703
704    @gen_test
705    def test_many_mixed_reads(self):
706        # Stress buffer handling when going back and forth between
707        # read_bytes() (using an internal buffer) and read_into()
708        # (using a user-allocated buffer).
709        r = random.Random(42)
710        nbytes = 1000000
711        rs, ws = yield self.make_iostream_pair()
712
713        produce_hash = hashlib.sha1()
714        consume_hash = hashlib.sha1()
715
716        @gen.coroutine
717        def produce():
718            remaining = nbytes
719            while remaining > 0:
720                size = r.randint(1, min(1000, remaining))
721                data = os.urandom(size)
722                produce_hash.update(data)
723                yield ws.write(data)
724                remaining -= size
725            assert remaining == 0
726
727        @gen.coroutine
728        def consume():
729            remaining = nbytes
730            while remaining > 0:
731                if r.random() > 0.5:
732                    # read_bytes()
733                    size = r.randint(1, min(1000, remaining))
734                    data = yield rs.read_bytes(size)
735                    consume_hash.update(data)
736                    remaining -= size
737                else:
738                    # read_into()
739                    size = r.randint(1, min(1000, remaining))
740                    buf = bytearray(size)
741                    n = yield rs.read_into(buf)
742                    assert n == size
743                    consume_hash.update(buf)
744                    remaining -= size
745            assert remaining == 0
746
747        try:
748            yield [produce(), consume()]
749            assert produce_hash.hexdigest() == consume_hash.hexdigest()
750        finally:
751            ws.close()
752            rs.close()
753
754
755@abstract_base_test
756class TestIOStreamMixin(TestReadWriteMixin):
757    def _make_server_iostream(self, connection, **kwargs):
758        raise NotImplementedError()
759
760    def _make_client_iostream(self, connection, **kwargs):
761        raise NotImplementedError()
762
763    @gen.coroutine
764    def make_iostream_pair(self, **kwargs):
765        listener, port = bind_unused_port()
766        server_stream_fut = Future()  # type: Future[IOStream]
767
768        def accept_callback(connection, address):
769            server_stream_fut.set_result(
770                self._make_server_iostream(connection, **kwargs)
771            )
772
773        netutil.add_accept_handler(listener, accept_callback)
774        client_stream = self._make_client_iostream(socket.socket(), **kwargs)
775        connect_fut = client_stream.connect(("127.0.0.1", port))
776        server_stream, client_stream = yield [server_stream_fut, connect_fut]
777        self.io_loop.remove_handler(listener.fileno())
778        listener.close()
779        raise gen.Return((server_stream, client_stream))
780
781    @gen_test
782    def test_connection_refused(self):
783        # When a connection is refused, the connect callback should not
784        # be run.  (The kqueue IOLoop used to behave differently from the
785        # epoll IOLoop in this respect)
786        cleanup_func, port = refusing_port()
787        self.addCleanup(cleanup_func)
788        stream = IOStream(socket.socket())
789
790        stream.set_close_callback(self.stop)
791        # log messages vary by platform and ioloop implementation
792        with ExpectLog(gen_log, ".*", required=False):
793            with self.assertRaises(StreamClosedError):
794                yield stream.connect(("127.0.0.1", port))
795
796        self.assertTrue(isinstance(stream.error, ConnectionRefusedError), stream.error)
797
798    @gen_test
799    def test_gaierror(self):
800        # Test that IOStream sets its exc_info on getaddrinfo error.
801        # It's difficult to reliably trigger a getaddrinfo error;
802        # some resolvers own't even return errors for malformed names,
803        # so we mock it instead. If IOStream changes to call a Resolver
804        # before sock.connect, the mock target will need to change too.
805        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
806        stream = IOStream(s)
807        stream.set_close_callback(self.stop)
808        with mock.patch(
809            "socket.socket.connect", side_effect=socket.gaierror(errno.EIO, "boom")
810        ):
811            with self.assertRaises(StreamClosedError):
812                yield stream.connect(("localhost", 80))
813            self.assertTrue(isinstance(stream.error, socket.gaierror))
814
815    @gen_test
816    def test_read_until_close_with_error(self):
817        server, client = yield self.make_iostream_pair()
818        try:
819            with mock.patch(
820                "tornado.iostream.BaseIOStream._try_inline_read",
821                side_effect=IOError("boom"),
822            ):
823                with self.assertRaisesRegex(IOError, "boom"):
824                    client.read_until_close()
825        finally:
826            server.close()
827            client.close()
828
829    @skipIfNonUnix
830    @gen_test
831    def test_inline_read_error(self):
832        # An error on an inline read is raised without logging (on the
833        # assumption that it will eventually be noticed or logged further
834        # up the stack).
835        #
836        # This test is posix-only because windows os.close() doesn't work
837        # on socket FDs, but we can't close the socket object normally
838        # because we won't get the error we want if the socket knows
839        # it's closed.
840        #
841        # This test is also disabled when the
842        # AddThreadSelectorEventLoop is used, because a race between
843        # this thread closing the socket and the selector thread
844        # calling the select system call can make this test flaky.
845        # This event loop implementation is normally only used on
846        # windows, making this check redundant with skipIfNonUnix, but
847        # we sometimes enable it on other platforms for testing.
848        io_loop = IOLoop.current()
849        if isinstance(
850            io_loop.selector_loop,  # type: ignore[attr-defined]
851            AddThreadSelectorEventLoop,
852        ):
853            self.skipTest("AddThreadSelectorEventLoop not supported")
854        server, client = yield self.make_iostream_pair()
855        try:
856            os.close(server.socket.fileno())
857            with self.assertRaises(socket.error):
858                server.read_bytes(1)
859        finally:
860            server.close()
861            client.close()
862
863    @gen_test
864    def test_async_read_error_logging(self):
865        # Socket errors on asynchronous reads should be logged (but only
866        # once).
867        server, client = yield self.make_iostream_pair()
868        closed = Event()
869        server.set_close_callback(closed.set)
870        try:
871            # Start a read that will be fulfilled asynchronously.
872            server.read_bytes(1)
873            client.write(b"a")
874            # Stub out read_from_fd to make it fail.
875
876            def fake_read_from_fd():
877                os.close(server.socket.fileno())
878                server.__class__.read_from_fd(server)
879
880            server.read_from_fd = fake_read_from_fd
881            # This log message is from _handle_read (not read_from_fd).
882            with ExpectLog(gen_log, "error on read"):
883                yield closed.wait()
884        finally:
885            server.close()
886            client.close()
887
888    @gen_test
889    def test_future_write(self):
890        """
891        Test that write() Futures are never orphaned.
892        """
893        # Run concurrent writers that will write enough bytes so as to
894        # clog the socket buffer and accumulate bytes in our write buffer.
895        m, n = 5000, 1000
896        nproducers = 10
897        total_bytes = m * n * nproducers
898        server, client = yield self.make_iostream_pair(max_buffer_size=total_bytes)
899
900        @gen.coroutine
901        def produce():
902            data = b"x" * m
903            for i in range(n):
904                yield server.write(data)
905
906        @gen.coroutine
907        def consume():
908            nread = 0
909            while nread < total_bytes:
910                res = yield client.read_bytes(m)
911                nread += len(res)
912
913        try:
914            yield [produce() for i in range(nproducers)] + [consume()]
915        finally:
916            server.close()
917            client.close()
918
919
920class TestIOStreamWebHTTP(AsyncHTTPTestCase, TestIOStreamWebMixin):
921    def _make_client_iostream(self):
922        return IOStream(socket.socket())
923
924    def get_app(self):
925        return self.mixin_get_app()
926
927
928class TestIOStreamWebHTTPS(AsyncHTTPSTestCase, TestIOStreamWebMixin):
929    def _make_client_iostream(self):
930        return SSLIOStream(socket.socket(), ssl_options=dict(cert_reqs=ssl.CERT_NONE))
931
932    def get_app(self):
933        return self.mixin_get_app()
934
935
936class TestIOStream(TestIOStreamMixin):
937    def _make_server_iostream(self, connection, **kwargs):
938        return IOStream(connection, **kwargs)
939
940    def _make_client_iostream(self, connection, **kwargs):
941        return IOStream(connection, **kwargs)
942
943
944class TestIOStreamSSL(TestIOStreamMixin):
945    def _make_server_iostream(self, connection, **kwargs):
946        ssl_ctx = ssl_options_to_context(_server_ssl_options(), server_side=True)
947        connection = ssl_ctx.wrap_socket(
948            connection,
949            server_side=True,
950            do_handshake_on_connect=False,
951        )
952        return SSLIOStream(connection, **kwargs)
953
954    def _make_client_iostream(self, connection, **kwargs):
955        return SSLIOStream(
956            connection, ssl_options=dict(cert_reqs=ssl.CERT_NONE), **kwargs
957        )
958
959
960# This will run some tests that are basically redundant but it's the
961# simplest way to make sure that it works to pass an SSLContext
962# instead of an ssl_options dict to the SSLIOStream constructor.
963class TestIOStreamSSLContext(TestIOStreamMixin):
964    def _make_server_iostream(self, connection, **kwargs):
965        context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
966        context.load_cert_chain(
967            os.path.join(os.path.dirname(__file__), "test.crt"),
968            os.path.join(os.path.dirname(__file__), "test.key"),
969        )
970        connection = ssl_wrap_socket(
971            connection, context, server_side=True, do_handshake_on_connect=False
972        )
973        return SSLIOStream(connection, **kwargs)
974
975    def _make_client_iostream(self, connection, **kwargs):
976        context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
977        context.check_hostname = False
978        context.verify_mode = ssl.CERT_NONE
979        return SSLIOStream(connection, ssl_options=context, **kwargs)
980
981
982class TestIOStreamStartTLS(AsyncTestCase):
983    def setUp(self):
984        try:
985            super().setUp()
986            self.listener, self.port = bind_unused_port()
987            self.server_stream = None
988            self.server_accepted = Future()  # type: Future[None]
989            netutil.add_accept_handler(self.listener, self.accept)
990            self.client_stream = IOStream(
991                socket.socket()
992            )  # type: typing.Optional[IOStream]
993            self.io_loop.add_future(
994                self.client_stream.connect(("127.0.0.1", self.port)), self.stop
995            )
996            self.wait()
997            self.io_loop.add_future(self.server_accepted, self.stop)
998            self.wait()
999        except Exception as e:
1000            print(e)
1001            raise
1002
1003    def tearDown(self):
1004        if self.server_stream is not None:
1005            self.server_stream.close()
1006        if self.client_stream is not None:
1007            self.client_stream.close()
1008        self.io_loop.remove_handler(self.listener.fileno())
1009        self.listener.close()
1010        super().tearDown()
1011
1012    def accept(self, connection, address):
1013        if self.server_stream is not None:
1014            self.fail("should only get one connection")
1015        self.server_stream = IOStream(connection)
1016        self.server_accepted.set_result(None)
1017
1018    @gen.coroutine
1019    def client_send_line(self, line):
1020        assert self.client_stream is not None
1021        self.client_stream.write(line)
1022        assert self.server_stream is not None
1023        recv_line = yield self.server_stream.read_until(b"\r\n")
1024        self.assertEqual(line, recv_line)
1025
1026    @gen.coroutine
1027    def server_send_line(self, line):
1028        assert self.server_stream is not None
1029        self.server_stream.write(line)
1030        assert self.client_stream is not None
1031        recv_line = yield self.client_stream.read_until(b"\r\n")
1032        self.assertEqual(line, recv_line)
1033
1034    def client_start_tls(self, ssl_options=None, server_hostname=None):
1035        assert self.client_stream is not None
1036        client_stream = self.client_stream
1037        self.client_stream = None
1038        return client_stream.start_tls(False, ssl_options, server_hostname)
1039
1040    def server_start_tls(self, ssl_options=None):
1041        assert self.server_stream is not None
1042        server_stream = self.server_stream
1043        self.server_stream = None
1044        return server_stream.start_tls(True, ssl_options)
1045
1046    @gen_test
1047    def test_start_tls_smtp(self):
1048        # This flow is simplified from RFC 3207 section 5.
1049        # We don't really need all of this, but it helps to make sure
1050        # that after realistic back-and-forth traffic the buffers end up
1051        # in a sane state.
1052        yield self.server_send_line(b"220 mail.example.com ready\r\n")
1053        yield self.client_send_line(b"EHLO mail.example.com\r\n")
1054        yield self.server_send_line(b"250-mail.example.com welcome\r\n")
1055        yield self.server_send_line(b"250 STARTTLS\r\n")
1056        yield self.client_send_line(b"STARTTLS\r\n")
1057        yield self.server_send_line(b"220 Go ahead\r\n")
1058        client_future = self.client_start_tls(dict(cert_reqs=ssl.CERT_NONE))
1059        server_future = self.server_start_tls(_server_ssl_options())
1060        self.client_stream = yield client_future
1061        self.server_stream = yield server_future
1062        self.assertTrue(isinstance(self.client_stream, SSLIOStream))
1063        self.assertTrue(isinstance(self.server_stream, SSLIOStream))
1064        yield self.client_send_line(b"EHLO mail.example.com\r\n")
1065        yield self.server_send_line(b"250 mail.example.com welcome\r\n")
1066
1067    @gen_test
1068    def test_handshake_fail(self):
1069        server_future = self.server_start_tls(_server_ssl_options())
1070        # Certificates are verified with the default configuration.
1071        with ExpectLog(gen_log, "SSL Error"):
1072            client_future = self.client_start_tls(server_hostname="localhost")
1073            with self.assertRaises(ssl.SSLError):
1074                yield client_future
1075            with self.assertRaises((ssl.SSLError, socket.error)):
1076                yield server_future
1077
1078    @gen_test
1079    def test_check_hostname(self):
1080        # Test that server_hostname parameter to start_tls is being used.
1081        server_future = self.server_start_tls(_server_ssl_options())
1082        with ExpectLog(gen_log, "SSL Error"):
1083            client_future = self.client_start_tls(
1084                ssl.create_default_context(), server_hostname="127.0.0.1"
1085            )
1086            with self.assertRaises(ssl.SSLError):
1087                # The client fails to connect with an SSL error.
1088                yield client_future
1089            with self.assertRaises(Exception):
1090                # The server fails to connect, but the exact error is unspecified.
1091                yield server_future
1092
1093    @gen_test
1094    def test_typed_memoryview(self):
1095        # Test support of memoryviews with an item size greater than 1 byte.
1096        buf = memoryview(bytes(80)).cast("L")
1097        assert self.server_stream is not None
1098        yield self.server_stream.write(buf)
1099        assert self.client_stream is not None
1100        # This will timeout if the calculation of the buffer size is incorrect
1101        recv = yield self.client_stream.read_bytes(buf.nbytes)
1102        self.assertEqual(bytes(recv), bytes(buf))
1103
1104
1105class WaitForHandshakeTest(AsyncTestCase):
1106    @gen.coroutine
1107    def connect_to_server(self, server_cls):
1108        server = client = None
1109        try:
1110            sock, port = bind_unused_port()
1111            server = server_cls(ssl_options=_server_ssl_options())
1112            server.add_socket(sock)
1113
1114            ssl_ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
1115            ssl_ctx.check_hostname = False
1116            ssl_ctx.verify_mode = ssl.CERT_NONE
1117            # These tests fail with ConnectionAbortedErrors with TLS
1118            # 1.3 on windows python 3.7.4 (which includes an upgrade
1119            # to openssl 1.1.c. Other platforms might be affected with
1120            # newer openssl too). Disable it until we figure out
1121            # what's up.
1122            # Update 2021-12-28: Still happening with Python 3.10 on
1123            # Windows. OP_NO_TLSv1_3 now raises a DeprecationWarning.
1124            with ignore_deprecation():
1125                ssl_ctx.options |= getattr(ssl, "OP_NO_TLSv1_3", 0)
1126                client = SSLIOStream(socket.socket(), ssl_options=ssl_ctx)
1127            yield client.connect(("127.0.0.1", port))
1128            self.assertIsNotNone(client.socket.cipher())
1129        finally:
1130            if server is not None:
1131                server.stop()
1132            if client is not None:
1133                client.close()
1134
1135    @gen_test
1136    def test_wait_for_handshake_future(self):
1137        test = self
1138        handshake_future = Future()  # type: Future[None]
1139
1140        class TestServer(TCPServer):
1141            def handle_stream(self, stream, address):
1142                test.assertIsNone(stream.socket.cipher())
1143                test.io_loop.spawn_callback(self.handle_connection, stream)
1144
1145            @gen.coroutine
1146            def handle_connection(self, stream):
1147                yield stream.wait_for_handshake()
1148                handshake_future.set_result(None)
1149
1150        yield self.connect_to_server(TestServer)
1151        yield handshake_future
1152
1153    @gen_test
1154    def test_wait_for_handshake_already_waiting_error(self):
1155        test = self
1156        handshake_future = Future()  # type: Future[None]
1157
1158        class TestServer(TCPServer):
1159            @gen.coroutine
1160            def handle_stream(self, stream, address):
1161                fut = stream.wait_for_handshake()
1162                test.assertRaises(RuntimeError, stream.wait_for_handshake)
1163                yield fut
1164
1165                handshake_future.set_result(None)
1166
1167        yield self.connect_to_server(TestServer)
1168        yield handshake_future
1169
1170    @gen_test
1171    def test_wait_for_handshake_already_connected(self):
1172        handshake_future = Future()  # type: Future[None]
1173
1174        class TestServer(TCPServer):
1175            @gen.coroutine
1176            def handle_stream(self, stream, address):
1177                yield stream.wait_for_handshake()
1178                yield stream.wait_for_handshake()
1179                handshake_future.set_result(None)
1180
1181        yield self.connect_to_server(TestServer)
1182        yield handshake_future
1183
1184
1185class TestIOStreamCheckHostname(AsyncTestCase):
1186    # This test ensures that hostname checks are working correctly after
1187    # #3337 revealed that we have no test coverage in this area, and we
1188    # removed a manual hostname check that was needed only for very old
1189    # versions of python.
1190    def setUp(self):
1191        super().setUp()
1192        self.listener, self.port = bind_unused_port()
1193
1194        def accept_callback(connection, address):
1195            ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
1196            ssl_ctx.load_cert_chain(
1197                os.path.join(os.path.dirname(__file__), "test.crt"),
1198                os.path.join(os.path.dirname(__file__), "test.key"),
1199            )
1200            connection = ssl_ctx.wrap_socket(

Showing the first 1,200 of 1422 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai