Last active
March 24, 2018 00:10
-
-
Save wecsam/25e131f885c65f982d3fe2abef509660 to your computer and use it in GitHub Desktop.
Serves stuff from STDIN over HTTP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse, io, socketserver, sys, threading | |
class StringServer(socketserver.BaseRequestHandler): | |
# These headers will be sent with every HTTP reply. | |
HEADERS = ( | |
b"HTTP/1.1 200 OK\r\n" | |
b"Content-Type: application/octet-stream\r\n" | |
) | |
# If True, then data that is received will be forwarded to STDOUT. | |
forward_to_stdout = False | |
# The value of this buffer will be sent in the body of every reply. | |
_body = io.BytesIO() | |
_body_protection = threading.Lock() | |
# We need a way to signal accumulate_forever to stop. | |
_accumulate_stop = threading.Event() | |
# Just send the request to STDOUT and then send the reply. | |
def handle(self): | |
request = self.request.recv(8192) | |
if self.forward_to_stdout: | |
sys.stdout.write(request.decode("UTF-8")) | |
with self._body_protection: | |
response = self._body.getvalue() | |
self.request.sendall( | |
self.HEADERS + | |
b"Content-Length: " + str(len(response)).encode("UTF-8") + | |
b"\r\n\r\n" + | |
response | |
) | |
# Start this method in a thread to move bytes from STDIN to the body buffer | |
# until accumulate_stop is called. | |
@classmethod | |
def accumulate_start(cls): | |
def accumulate_forever(): | |
while not cls._accumulate_stop.is_set(): | |
b = sys.stdin.readline().encode("UTF-8") | |
with cls._body_protection: | |
cls._body.write(b) | |
threading.Thread( | |
target=accumulate_forever, | |
name="Body Accumulator" | |
).start() | |
@classmethod | |
def accumulate_stop(cls): | |
cls._accumulate_stop.set() | |
sys.stderr.write("Press Enter to exit: ") | |
def serve(port, verbose=0): | |
StringServer.forward_to_stdout = verbose >= 2 | |
try: | |
with socketserver.TCPServer( | |
("", port), | |
StringServer | |
) as server: | |
if verbose >= 1: | |
sys.stderr.write( | |
"Listening on 0.0.0.0:{}...\n".format(port) | |
) | |
sys.stderr.write("Press Ctrl+C at the end of the input.\n") | |
# Start another thread to read input from STDIN. | |
StringServer.accumulate_start() | |
# Start handling connections. | |
try: | |
server.serve_forever() | |
except KeyboardInterrupt: | |
# Ctrl+C was pressed. | |
sys.stderr.write( | |
"Caught termination signal. Server stopped.\n" | |
) | |
finally: | |
# Stop the thread that was started above. | |
StringServer.accumulate_stop() | |
except OSError as e: | |
sys.stderr.write("Error: {}\n".format(e)) | |
def main(): | |
# Parse the command-line arguments. | |
argparser = argparse.ArgumentParser( | |
description= | |
"This script serves everything from STDIN over HTTP. Just launch " | |
"it and then pipe or type some data into STDIN. Until Ctrl+C is " | |
"pressed, every TCP connection request to the specified port " | |
"will be answered with an HTTP response where the body contains " | |
"everything that has been received on STDIN so far. The data " | |
"that is received from the remote host in each TCP connection " | |
"is ignored." | |
) | |
argparser.add_argument( | |
"-p", "--port", | |
type=int, | |
default=8080, | |
help="the port on which to listen" | |
) | |
argparser.add_argument("-v", "--verbose", default=0, action="count") | |
arguments = argparser.parse_args() | |
# Create a socketserver to handle connections. | |
serve(arguments.port, arguments.verbose) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment