Go to the documentation of this file.00001
00002 """
00003 This module contains some helpers to deal with the real http
00004 world.
00005 """
00006
00007 import threading
00008 import logging
00009 import select
00010 import socket
00011 import time
00012 import os
00013
00014 import six
00015 import webob
00016 from six.moves import http_client
00017 from waitress.server import TcpWSGIServer
00018
00019
00020 def get_free_port():
00021 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
00022 s.bind(('', 0))
00023 ip, port = s.getsockname()
00024 s.close()
00025 ip = os.environ.get('WEBTEST_SERVER_BIND', '127.0.0.1')
00026 return ip, port
00027
00028
00029 def check_server(host, port, path_info='/', timeout=3, retries=30):
00030 """Perform a request until the server reply"""
00031 if retries < 0:
00032 return 0
00033 time.sleep(.3)
00034 for i in range(retries):
00035 try:
00036 conn = http_client.HTTPConnection(host, int(port), timeout=timeout)
00037 conn.request('GET', path_info)
00038 res = conn.getresponse()
00039 return res.status
00040 except (socket.error, http_client.HTTPException):
00041 time.sleep(.3)
00042 return 0
00043
00044
00045 class StopableWSGIServer(TcpWSGIServer):
00046 """StopableWSGIServer is a TcpWSGIServer which run in a separated thread.
00047 This allow to use tools like casperjs or selenium.
00048
00049 Server instance have an ``application_url`` attribute formated with the
00050 server host and port.
00051 """
00052
00053 was_shutdown = False
00054
00055 def __init__(self, application, *args, **kwargs):
00056 super(StopableWSGIServer, self).__init__(self.wrapper, *args, **kwargs)
00057 self.runner = None
00058 self.test_app = application
00059 self.application_url = 'http://%s:%s/' % (self.adj.host, self.adj.port)
00060
00061 def wrapper(self, environ, start_response):
00062 """Wrap the wsgi application to override some path:
00063
00064 ``/__application__``: allow to ping the server.
00065
00066 ``/__file__?__file__={path}``: serve the file found at ``path``
00067 """
00068 if '__file__' in environ['PATH_INFO']:
00069 req = webob.Request(environ)
00070 resp = webob.Response()
00071 resp.content_type = 'text/html; charset=UTF-8'
00072 filename = req.params.get('__file__')
00073 if os.path.isfile(filename):
00074 body = open(filename, 'rb').read()
00075 body = body.replace(six.b('http://localhost/'),
00076 six.b('http://%s/' % req.host))
00077 resp.body = body
00078 else:
00079 resp.status = '404 Not Found'
00080 return resp(environ, start_response)
00081 elif '__application__' in environ['PATH_INFO']:
00082 return webob.Response('server started')(environ, start_response)
00083 return self.test_app(environ, start_response)
00084
00085 def run(self):
00086 """Run the server"""
00087 try:
00088 self.asyncore.loop(.5, map=self._map)
00089 except select.error:
00090 if not self.was_shutdown:
00091 raise
00092
00093 def shutdown(self):
00094 """Shutdown the server"""
00095
00096 self.was_shutdown = True
00097 self.logger.setLevel(logging.FATAL)
00098 while self._map:
00099 triggers = list(self._map.values())
00100 for trigger in triggers:
00101 trigger.handle_close()
00102 self.maintenance(0)
00103 self.task_dispatcher.shutdown()
00104 return True
00105
00106 @classmethod
00107 def create(cls, application, **kwargs):
00108 """Start a server to serve ``application``. Return a server
00109 instance."""
00110 host, port = get_free_port()
00111 if 'port' not in kwargs:
00112 kwargs['port'] = port
00113 if 'host' not in kwargs:
00114 kwargs['host'] = host
00115 if 'expose_tracebacks' not in kwargs:
00116 kwargs['expose_tracebacks'] = True
00117 server = cls(application, **kwargs)
00118 server.runner = threading.Thread(target=server.run)
00119 server.runner.daemon = True
00120 server.runner.start()
00121 return server
00122
00123 def wait(self, retries=30):
00124 """Wait until the server is started"""
00125 running = check_server(self.adj.host, self.adj.port,
00126 '/__application__', retries=retries)
00127 if running:
00128 return True
00129 try:
00130 self.shutdown()
00131 finally:
00132 return False