44import contextlib
55import io
66import os
7+ import socket
8+ import socketserver
79import sys
10+ import tempfile
811import threading
912import time
1013import unittest
1114import unittest .mock
15+
16+ from http .server import HTTPServer
1217from wsgiref .simple_server import make_server , WSGIRequestHandler , WSGIServer
18+
1319try :
1420 import ssl
1521except ImportError : # pragma: no cover
@@ -70,42 +76,51 @@ def run_once(loop):
7076 loop .run_forever ()
7177
7278
73- @contextlib .contextmanager
74- def run_test_server (* , host = '127.0.0.1' , port = 0 , use_ssl = False ):
79+ class SilentWSGIRequestHandler (WSGIRequestHandler ):
7580
76- class SilentWSGIRequestHandler (WSGIRequestHandler ):
77- def get_stderr (self ):
78- return io .StringIO ()
81+ def get_stderr (self ):
82+ return io .StringIO ()
7983
80- def log_message (self , format , * args ):
81- pass
84+ def log_message (self , format , * args ):
85+ pass
8286
83- class SilentWSGIServer (WSGIServer ):
84- def handle_error (self , request , client_address ):
87+
88+ class SilentWSGIServer (WSGIServer ):
89+
90+ def handle_error (self , request , client_address ):
91+ pass
92+
93+
94+ class SSLWSGIServerMixin :
95+
96+ def finish_request (self , request , client_address ):
97+ # The relative location of our test directory (which
98+ # contains the ssl key and certificate files) differs
99+ # between the stdlib and stand-alone asyncio.
100+ # Prefer our own if we can find it.
101+ here = os .path .join (os .path .dirname (__file__ ), '..' , 'tests' )
102+ if not os .path .isdir (here ):
103+ here = os .path .join (os .path .dirname (os .__file__ ),
104+ 'test' , 'test_asyncio' )
105+ keyfile = os .path .join (here , 'ssl_key.pem' )
106+ certfile = os .path .join (here , 'ssl_cert.pem' )
107+ ssock = ssl .wrap_socket (request ,
108+ keyfile = keyfile ,
109+ certfile = certfile ,
110+ server_side = True )
111+ try :
112+ self .RequestHandlerClass (ssock , client_address , self )
113+ ssock .close ()
114+ except OSError :
115+ # maybe socket has been closed by peer
85116 pass
86117
87- class SSLWSGIServer (SilentWSGIServer ):
88- def finish_request (self , request , client_address ):
89- # The relative location of our test directory (which
90- # contains the ssl key and certificate files) differs
91- # between the stdlib and stand-alone asyncio.
92- # Prefer our own if we can find it.
93- here = os .path .join (os .path .dirname (__file__ ), '..' , 'tests' )
94- if not os .path .isdir (here ):
95- here = os .path .join (os .path .dirname (os .__file__ ),
96- 'test' , 'test_asyncio' )
97- keyfile = os .path .join (here , 'ssl_key.pem' )
98- certfile = os .path .join (here , 'ssl_cert.pem' )
99- ssock = ssl .wrap_socket (request ,
100- keyfile = keyfile ,
101- certfile = certfile ,
102- server_side = True )
103- try :
104- self .RequestHandlerClass (ssock , client_address , self )
105- ssock .close ()
106- except OSError :
107- # maybe socket has been closed by peer
108- pass
118+
119+ class SSLWSGIServer (SSLWSGIServerMixin , SilentWSGIServer ):
120+ pass
121+
122+
123+ def _run_test_server (* , address , use_ssl = False , server_cls , server_ssl_cls ):
109124
110125 def app (environ , start_response ):
111126 status = '200 OK'
@@ -115,9 +130,9 @@ def app(environ, start_response):
115130
116131 # Run the test WSGI server in a separate thread in order not to
117132 # interfere with event handling in the main thread
118- server_class = SSLWSGIServer if use_ssl else SilentWSGIServer
119- httpd = make_server ( host , port , app ,
120- server_class , SilentWSGIRequestHandler )
133+ server_class = server_ssl_cls if use_ssl else server_cls
134+ httpd = server_class ( address , SilentWSGIRequestHandler )
135+ httpd . set_app ( app )
121136 httpd .address = httpd .server_address
122137 server_thread = threading .Thread (target = httpd .serve_forever )
123138 server_thread .start ()
@@ -129,6 +144,75 @@ def app(environ, start_response):
129144 server_thread .join ()
130145
131146
147+ if hasattr (socket , 'AF_UNIX' ):
148+
149+ class UnixHTTPServer (socketserver .UnixStreamServer , HTTPServer ):
150+
151+ def server_bind (self ):
152+ socketserver .UnixStreamServer .server_bind (self )
153+ self .server_name = '127.0.0.1'
154+ self .server_port = 80
155+
156+
157+ class UnixWSGIServer (UnixHTTPServer , WSGIServer ):
158+
159+ def server_bind (self ):
160+ UnixHTTPServer .server_bind (self )
161+ self .setup_environ ()
162+
163+ def get_request (self ):
164+ request , client_addr = super ().get_request ()
165+ # Code in the stdlib expects that get_request
166+ # will return a socket and a tuple (host, port).
167+ # However, this isn't true for UNIX sockets,
168+ # as the second return value will be a path;
169+ # hence we return some fake data sufficient
170+ # to get the tests going
171+ return request , ('127.0.0.1' , '' )
172+
173+
174+ class SilentUnixWSGIServer (UnixWSGIServer ):
175+
176+ def handle_error (self , request , client_address ):
177+ pass
178+
179+
180+ class UnixSSLWSGIServer (SSLWSGIServerMixin , SilentUnixWSGIServer ):
181+ pass
182+
183+
184+ def gen_unix_socket_path ():
185+ with tempfile .NamedTemporaryFile () as file :
186+ return file .name
187+
188+
189+ @contextlib .contextmanager
190+ def unix_socket_path ():
191+ path = gen_unix_socket_path ()
192+ try :
193+ yield path
194+ finally :
195+ try :
196+ os .unlink (path )
197+ except OSError :
198+ pass
199+
200+
201+ @contextlib .contextmanager
202+ def run_test_unix_server (* , use_ssl = False ):
203+ with unix_socket_path () as path :
204+ yield from _run_test_server (address = path , use_ssl = use_ssl ,
205+ server_cls = SilentUnixWSGIServer ,
206+ server_ssl_cls = UnixSSLWSGIServer )
207+
208+
209+ @contextlib .contextmanager
210+ def run_test_server (* , host = '127.0.0.1' , port = 0 , use_ssl = False ):
211+ yield from _run_test_server (address = (host , port ), use_ssl = use_ssl ,
212+ server_cls = SilentWSGIServer ,
213+ server_ssl_cls = SSLWSGIServer )
214+
215+
132216def make_test_protocol (base ):
133217 dct = {}
134218 for name in dir (base ):
@@ -275,5 +359,6 @@ def _process_events(self, event_list):
275359 def _write_to_self (self ):
276360 pass
277361
362+
278363def MockCallback (** kwargs ):
279364 return unittest .mock .Mock (spec = ['__call__' ], ** kwargs )
0 commit comments