http-echo 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. #!/usr/local/bin/python3.8
  2. """
  3. A Simple Python HTTP server that echos the request in the response.
  4. """
  5. import socket
  6. import argparse
  7. from six.moves.urllib import parse
  8. import email.message
  9. try:
  10. from email.generator import BytesGenerator
  11. except ImportError:
  12. # BBB Python 2 compatibility
  13. from email.generator import Generator as BytesGenerator
  14. from six.moves import BaseHTTPServer
  15. parser = argparse.ArgumentParser(
  16. description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter
  17. )
  18. parser.add_argument(
  19. "--address",
  20. "-a",
  21. default="0.0.0.0",
  22. help="Hostname or IP address to accept requests on.",
  23. )
  24. parser.add_argument(
  25. "--port",
  26. "-p",
  27. help="Port to accept requests on. "
  28. "If not specified, use the first available port after 8000.",
  29. )
  30. class EchoHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  31. """
  32. A Simple Python HTTP server that echos the request in the response.
  33. """
  34. def do_GET(self):
  35. """
  36. Echo a request without a body.
  37. """
  38. message = self.get_message()
  39. self.send_head()
  40. BytesGenerator(self.wfile).flatten(message, unixfrom=False)
  41. do_HEAD = do_GET
  42. do_OPTIONS = do_GET
  43. do_DELETE = do_GET
  44. def do_POST(self):
  45. """
  46. Echo a request with a body.
  47. """
  48. message = self.get_message()
  49. message.set_payload(self.rfile.read(int(self.headers["Content-Length"])))
  50. self.send_head()
  51. BytesGenerator(self.wfile).flatten(message, unixfrom=False)
  52. do_PUT = do_POST
  53. do_PATCH = do_POST
  54. def send_head(self):
  55. """
  56. Send all the basic, required headers.
  57. """
  58. self.send_response(200)
  59. self.send_header("Content-Type", "text/rfc822-headers; charset=UTF-8")
  60. self.send_header("Last-Modified", self.date_time_string())
  61. self.end_headers()
  62. def get_message(self):
  63. """
  64. Assemble the basic message including query parameters.
  65. """
  66. message = email.message.Message()
  67. message["Method"] = self.command
  68. message["Path"] = self.path
  69. server_url = parse.SplitResult(
  70. "http",
  71. "{0}:{1}".format(self.server.server_name, self.server.server_port),
  72. "",
  73. "",
  74. "",
  75. )
  76. request_url = parse.urlsplit(server_url.geturl() + self.path)
  77. for header, value in parse.parse_qs(request_url.query).items():
  78. message.add_header(header, value[0])
  79. return message
  80. def main(args=None, default_port=8000):
  81. """
  82. Run the echo HTTP server.
  83. """
  84. args = parser.parse_args(args)
  85. port = args.port
  86. if port is None:
  87. port = default_port
  88. bound = False
  89. while not bound:
  90. try:
  91. httpd = BaseHTTPServer.HTTPServer(
  92. (args.address, port), EchoHTTPRequestHandler
  93. )
  94. except socket.error:
  95. port += 1
  96. if port > 65535:
  97. raise ValueError("No available port found")
  98. else:
  99. bound = True
  100. else:
  101. httpd = BaseHTTPServer.HTTPServer(
  102. (args.address, int(port)), EchoHTTPRequestHandler
  103. )
  104. print("Echoing HTTP at http://{0}:{1} ...".format(args.address, port))
  105. httpd.serve_forever()
  106. if __name__ == "__main__":
  107. main()