{"id":98,"date":"2011-08-04T09:17:34","date_gmt":"2011-08-04T07:17:34","guid":{"rendered":"http:\/\/blog.gocept.com\/?p=98"},"modified":"2016-10-27T09:38:43","modified_gmt":"2016-10-27T07:38:43","slug":"shutting-down-an-httpserver","status":"publish","type":"post","link":"https:\/\/blog.gocept.com\/2011\/08\/04\/shutting-down-an-httpserver\/","title":{"rendered":"Shutting down an HTTPServer"},"content":{"rendered":"

For integration tests it can be helpful to have a fake HTTP server whose behaviour the tests can control. All necessary building blocks are even included<\/a> in Python standard library. However, the BaseHTTPServer is surprisingly hard to shut down properly, so that it gives up the socket and everything.<\/p>\n

While working on gocept.selenium<\/a>, we came up with some code that does the trick (together with Jan-Wijbrand Kolman and Jan-Jaap Driessen).<\/p>\n

class HTTPServer(BaseHTTPServer.HTTPServer):\r\n\r\n\u00a0\u00a0\u00a0 _continue = True\r\n\r\n\u00a0\u00a0\u00a0 def serve_until_shutdown(self):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 while self._continue:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.handle_request()\r\n\r\n\u00a0\u00a0\u00a0 def shutdown(self):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self._continue = False\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # We fire a last request at the server in order to take it out of the\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # while loop in `self.serve_until_shutdown`.\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 try:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 urllib2.urlopen(\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 'http:\/\/%s:%s\/' % (self.server_name, self.server_port))\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 except urllib2.URLError:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # If the server is already shut down, we receive a socket error,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # which we ignore.\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pass\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.server_close()<\/pre>\n

You might use this in a zope.testrunner layer like this:<\/p>\n

class SilentRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):\r\n\r\n\u00a0\u00a0\u00a0 def log_message(self, format, *args):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pass\r\n\r\n\r\nclass HTTPServerLayer(object):\r\n\r\n\u00a0\u00a0\u00a0 host = 'localhost'\r\n\r\n\u00a0\u00a0\u00a0 def setUp(self):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.server = None\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.port = random.randint(30000, 40000)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.start_server()\r\n\r\n\u00a0\u00a0\u00a0 def start_server(self):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.server = HTTPServer((self.host, self.port), SilentRequestHandler)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.server_thread = threading.Thread(\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 target=self.server.serve_until_shutdown)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.server_thread.daemon = True\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.server_thread.start()\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # Kludge: Wait a little as it sometimes takes a while to get the server\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # started.\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 time.sleep(0.25)\r\n\r\n\u00a0\u00a0\u00a0 def stop_server(self):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if self.server is None:\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.server.shutdown()\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.server_thread.join()\r\n\r\n\u00a0\u00a0\u00a0 def tearDown(self):\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 self.stop_server()<\/pre>\n

 <\/p>\n

 <\/p>\n","protected":false},"excerpt":{"rendered":"

For integration tests it can be helpful to have a fake HTTP server whose behaviour the tests can control. All necessary building blocks are even included in Python standard library. However, the BaseHTTPServer is surprisingly hard to shut down properly, so that it gives up the socket and everything. While working on gocept.selenium, we came … Continue reading “Shutting down an HTTPServer”<\/span><\/a><\/p>\n","protected":false},"author":10315341,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_newsletter_tier_id":0,"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[10221],"tags":[832,111899,12,72208],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_likes_enabled":true,"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pFP3y-1A","jetpack-related-posts":[{"id":1262,"url":"https:\/\/blog.gocept.com\/2013\/04\/17\/running-tests-using-gocept-selenium-on-travis-ci\/","url_meta":{"origin":98,"position":0},"title":"Running tests using gocept.selenium on Travis-CI","author":"Michael Howitz","date":"April 17, 2013","format":false,"excerpt":"Travis-CI\u00a0is a\u00a0free hosted continuous integration platform for the open source community. It has a good integration with Github, so each push to a project runs the tests \u00a0of the project. gocept.selenium\u00a0is a python package our company has developed as a test-friendly Python API for Selenium\u00a0which allows to run tests in\u2026","rel":"","context":"In "en"","block_context":{"text":"en","link":"https:\/\/blog.gocept.com\/category\/en\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":127,"url":"https:\/\/blog.gocept.com\/2012\/01\/30\/sprint-fruits-gocept-exttest-and-gocept-package\/","url_meta":{"origin":98,"position":1},"title":"Sprint fruits: gocept.exttest and gocept.package","author":"","date":"January 30, 2012","format":false,"excerpt":"The whole company spent three days in Kloster Dr\u00fcbeck sprinting on internal tools and topics. We overhauled our workflow for generating invoices and identified steps that we could automate. We polished and released gocept.exttest, which integrates for example JavaScript unittest to Python's unittest framework. In a nutshell, it allows you\u2026","rel":"","context":"In "en"","block_context":{"text":"en","link":"https:\/\/blog.gocept.com\/category\/en\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2012\/01\/6754824273_981f3a0c34.jpg?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":381,"url":"https:\/\/blog.gocept.com\/2013\/01\/16\/news-from-the-toolbox-gocept-selenium-and-our-plans-for-its-future\/","url_meta":{"origin":98,"position":2},"title":"News from the toolbox: gocept.selenium and our plans for its future","author":"","date":"January 16, 2013","format":false,"excerpt":"For a couple of years, we at gocept have been developing a Python library, gocept.selenium, whose goal it is to integrate testing web sites in real browsers with the Python unittest framework. There exist a number of approaches to doing this; when first starting real-browser tests, we opted for using\u2026","rel":"","context":"In "en"","block_context":{"text":"en","link":"https:\/\/blog.gocept.com\/category\/en\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1873,"url":"https:\/\/blog.gocept.com\/2016\/10\/26\/towards-restrictedpython-3\/","url_meta":{"origin":98,"position":3},"title":"Towards RestrictedPython 3","author":"Michael Howitz","date":"October 26, 2016","format":false,"excerpt":"The biggest blocker to port Zope to Python 3 is\u00a0RestrictedPython. What is RestrictedPython? It is a library used by Zope to restrict Python code at instruction level to a bare minimum of trusted functionality. It\u00a0parses and filters the code for not\u00a0allowed constructs (such as\u00a0open()) and adds wrappers around\u00a0each access on\u2026","rel":"","context":"In "en"","block_context":{"text":"en","link":"https:\/\/blog.gocept.com\/category\/en\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2016\/10\/zope-is-not-dead.jpg?fit=1200%2C658&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2016\/10\/zope-is-not-dead.jpg?fit=1200%2C658&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2016\/10\/zope-is-not-dead.jpg?fit=1200%2C658&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2016\/10\/zope-is-not-dead.jpg?fit=1200%2C658&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2016\/10\/zope-is-not-dead.jpg?fit=1200%2C658&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":3350,"url":"https:\/\/blog.gocept.com\/2019\/11\/13\/union-cms-released-on-python-3\/","url_meta":{"origin":98,"position":4},"title":"union.cms released on Python 3","author":"Michael Howitz","date":"November 13, 2019","format":false,"excerpt":"union.cms is a content management system which was once developed on Zope 2. It was one of the early adopters of the Five technology aka using Zope 3 components in Zope 2. Now it is one of the proud early adopters of Zope 4 on Python 3. It is used\u2026","rel":"","context":"In "en"","block_context":{"text":"en","link":"https:\/\/blog.gocept.com\/category\/en\/"},"img":{"alt_text":"Green tree python","src":"https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2019\/11\/green-tree-python-1312700.jpg?fit=1200%2C863&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2019\/11\/green-tree-python-1312700.jpg?fit=1200%2C863&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2019\/11\/green-tree-python-1312700.jpg?fit=1200%2C863&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2019\/11\/green-tree-python-1312700.jpg?fit=1200%2C863&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2019\/11\/green-tree-python-1312700.jpg?fit=1200%2C863&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":2890,"url":"https:\/\/blog.gocept.com\/2017\/10\/24\/zope4-errorhandling\/","url_meta":{"origin":98,"position":5},"title":"Catching and rendering exceptions","author":"Michael Howitz","date":"October 24, 2017","format":false,"excerpt":"Error handling in Zope 4","rel":"","context":"In "en"","block_context":{"text":"en","link":"https:\/\/blog.gocept.com\/category\/en\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/posts\/98"}],"collection":[{"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/users\/10315341"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/comments?post=98"}],"version-history":[{"count":3,"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/posts\/98\/revisions"}],"predecessor-version":[{"id":2101,"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/posts\/98\/revisions\/2101"}],"wp:attachment":[{"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/media?parent=98"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/categories?post=98"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/tags?post=98"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}