stoneman_41
stoneman_41

Reputation: 400

How do I spawn a HTTP Server as a service, with the ability to stop it when necessary?

I am working on an application which requires endpoints to be configured to receive data. Because of this, I need to temporarily spawn up a REST HTTP server as part of the application, with a few endpoints, do computations while this server is up (and receiving data), and then stop the REST server after the calculations are over, and then finish the Python script.

However, there doesn't seem to be an easy way to just temporarily have a server up like that through Python, where it needs to be up before a section, and stopped immediately after so the program can terminate.

Upvotes: 2

Views: 1394

Answers (1)

Lex Scarisbrick
Lex Scarisbrick

Reputation: 1570

As mangoHero1 noted, Python comes with SimpleHTTPServer, which can spawn HTTP servers on the fly, in the same process. To have the HTTP client and server in the same process, you'd need to use threading. That would look something like this answer:

https://stackoverflow.com/a/14089457/6084928

I had a very similar use-case to yours and ended up writing an on-demand HTTP server specifically for testing that's based on SimpleHTTPServer. It works backwards from how you might expect a normal HTTP server to work. It accepts any requests, responds based on queued responses, and can later be interrogated as to what the requests look like. Perhaps it can work for your use-case.

https://pypi.python.org/pypi/spoof/

Example usage:

import unittest
import spoof
import thing

class TestThing(unittest.TestCase):
  httpd = None
  httpd6 = None

  @classmethod
  def setUpClass(cls):
    # X509 certificates can be expensive to generate, so it should be done
    # infrequently.  Also, creating a new HTTP server instance with a new
    # port number for each and every test can starve a system of available
    # TCP/IP ports.  Because of this, creating an `HTTPServer` instance
    # should also be done infrequently, unless the port number is static.
    sslContext = spoof.SSLContext.selfSigned()
    cls.httpd = spoof.HTTPServer(sslContext=sslContext)
    cls.httpd.start()
    # IPv6-only, if needed; `HTTPServer` also accepts IPv6 addresses
    cls.httpd6 = spoof.HTTPServer6(sslContext=sslContext)
    cls.httpd6.start()

  @classmethod
  def tearDownClass(cls):
    cls.httpd.stop()
    cls.httpd6.stop()
    cls.httpd = None
    cls.httpd6 = None

  def setUp(self):
    # Calling `reset()` suffices to sanitize the HTTP server environment.
    self.httpd.reset()
    self.httpd.debug = False
    self.thing = thing.Thing(self.httpd.address, self.httpd.port)
    # or
    self.altThing = thing.AltThing(self.httpd.url)

  def tearDown(self):
    self.thing = None
    self.altThing = None

  def test_thingUsingSpoof(self):
    response1 = [200, [('Content-Type', 'application/json')], '{"id": 1111}']
    response2 = [200, [('Content-Type', 'application/json')], '{"id": 2222}']
    self.httpd.queueResponse(response1)
    self.httpd.queueResponse(response2)
    # HTTP debug logging, if needed
    self.httpd.debug = True
    self.thing.requiringTwoJSONresponses()
    lastRequest = self.httpd.requests[-1]
    expectedContent = '{"action": "rename", "old": 1111, "new": 2222}'
    self.assertEquals(expectedContent, lastRequest.content)

Upvotes: 1

Related Questions