2

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.

stoneman_41
  • 370
  • 2
  • 12
  • why dont you make a server that forwards data to your script in some fashion? separate the server from the code and have the server running. More details will probably be helpful. – PYA Aug 11 '17 at 21:13
  • python comes shipped with `SimpleHTTPServer` no? – Mangohero1 Aug 11 '17 at 21:13
  • @pyjg The issue is the code I'm writing is supposed to run altogether in one Python script. Yes it is possible to turn it into a bash script and run the server separately, but that's unfortunately not within the specs I am operating within. – stoneman_41 Aug 11 '17 at 21:18
  • Not sure I understand the use-case here. Is this for development only, testing, or as part of a live, running application you need to spawn HTTP servers on demand? – Lex Scarisbrick Aug 11 '17 at 21:18
  • @mangoHero1 Yeah, but does that provide easy access to start/stop a server on the fly, like I mentioned? – stoneman_41 Aug 11 '17 at 21:18
  • 1
    @LexScarisbrick This is part of a testing framework, in which we can simply run this Python script and get results as mentioned. – stoneman_41 Aug 11 '17 at 21:19
  • @stoneman_41what exactly will the server do? – PYA Aug 11 '17 at 21:24
  • @pyjg It has one `POST` endpoint, and is collecting a lot of data (which is used in the calculations I've described). – stoneman_41 Aug 11 '17 at 21:25
  • @stoneman_41 how is it collecting data? – PYA Aug 11 '17 at 21:26
  • @pyjg A separate service (that is also spawn up by the original Python script) is generating it and will be making a `POST` request to this server I am trying to create. If your proposal involves changing this structure, I unfortunately can't do that because it is part of a robust testing framework. – stoneman_41 Aug 11 '17 at 21:31

1 Answers1

1

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)
Lex Scarisbrick
  • 1,540
  • 1
  • 24
  • 31