Skip to content

Instantly share code, notes, and snippets.

@leetschau
Created April 20, 2025 07:09
Show Gist options
  • Save leetschau/25c37cd63df6dba7c52194e62a08dcad to your computer and use it in GitHub Desktop.
Save leetschau/25c37cd63df6dba7c52194e62a08dcad to your computer and use it in GitHub Desktop.

Revisions

  1. leetschau created this gist Apr 20, 2025.
    135 changes: 135 additions & 0 deletions sharing-server.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,135 @@
    #!/usr/bin/env python3

    ## This script starts a Web server providing texts and files sharing function.
    ## See the usage with `-h` option.

    import os
    import sys
    import argparse
    import urllib.parse
    from http.server import HTTPServer, BaseHTTPRequestHandler
    from html import escape

    text_log = []

    class SimpleHandler(BaseHTTPRequestHandler):
    def do_GET(self):
    if self.path == '/':
    self.respond(200, "text/html; charset=utf-8", self.render_main_page())

    elif self.path.startswith("/files/"):
    filename = os.path.basename(urllib.parse.unquote(self.path[len("/files/"):]))
    file_path = os.path.join(self.server.directory, filename)
    if os.path.isfile(file_path):
    with open(file_path, "rb") as f:
    data = f.read()
    self.send_response(200)
    self.send_header("Content-Disposition", f"attachment; filename*=UTF-8''{urllib.parse.quote(filename)}")
    self.send_header("Content-Type", "application/octet-stream")
    self.send_header("Content-Length", str(len(data)))
    self.end_headers()
    self.wfile.write(data)
    else:
    self.send_error(404, "File not found")

    else:
    self.send_error(404)

    def do_POST(self):
    content_type = self.headers.get("Content-Type", "")
    content_length = int(self.headers.get("Content-Length", "0"))
    body = self.rfile.read(content_length)

    if self.path == "/upload" and "multipart/form-data" in content_type:
    boundary = content_type.split("boundary=")[-1].encode()
    parts = body.split(b"--" + boundary)
    for part in parts:
    if b"filename=" in part:
    header_data, file_data = part.split(b"\r\n\r\n", 1)
    file_data = file_data.rstrip(b"\r\n--")
    disposition = [line for line in header_data.split(b"\r\n") if b"Content-Disposition" in line][0]
    disposition_str = disposition.decode("utf-8", errors="replace")
    filename_enc = disposition_str.split("filename=")[-1].strip().strip('"')
    filename = os.path.basename(filename_enc)
    if filename:
    with open(os.path.join(self.server.directory, filename), "wb") as f:
    f.write(file_data)
    self.redirect("/")

    elif self.path == "/text":
    fields = urllib.parse.parse_qs(body.decode('utf-8', errors='replace'))
    text = fields.get("text", [""])[0]
    text_log.append(text)
    print("Received text:", text)
    self.redirect("/")

    else:
    self.send_error(400)

    def render_main_page(self):
    file_list_html = "".join(
    f"<li><a href='/files/{urllib.parse.quote(f)}'>{escape(f)}</a></li>"
    for f in os.listdir(self.server.directory)
    if os.path.isfile(os.path.join(self.server.directory, f))
    )
    log_html = escape("\n".join(text_log))
    directory_display = escape(self.server.directory)

    return f"""
    <html><head><meta charset="utf-8"></head><body>
    <h1>File & Text Server</h1>
    <h2>Upload File</h2>
    <form method="POST" action="/upload" enctype="multipart/form-data">
    <input type="file" name="file">
    <input type="submit" value="Upload">
    </form>
    <h2>Files at {directory_display}</h2>
    <ul>{file_list_html}</ul>
    <h2>Send Text</h2>
    <form method="POST" action="/text">
    <textarea name="text" rows="4" cols="50"></textarea><br>
    <input type="submit" value="Send">
    </form>
    <h2>Text Log</h2>
    <pre>{log_html}</pre>
    </body></html>
    """

    def respond(self, status, content_type, content):
    content_bytes = content.encode("utf-8")
    self.send_response(status)
    self.send_header("Content-Type", content_type)
    self.send_header("Content-Length", str(len(content_bytes)))
    self.end_headers()
    self.wfile.write(content_bytes)

    def redirect(self, location):
    self.send_response(303)
    self.send_header("Location", location)
    self.end_headers()

    def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--dir", default=".", help="Directory to serve files from (default: current directory)")
    parser.add_argument("--port", type=int, default=7123, help="Port to listen on (default: 7123)")
    args = parser.parse_args()

    directory = os.path.abspath(args.dir)
    if not os.path.isdir(directory):
    print("Directory does not exist:", directory)
    sys.exit(1)

    server = HTTPServer(('', args.port), SimpleHandler)
    server.directory = directory

    print(f"Serving at http://localhost:{args.port}")
    print(f"Sharing directory: {directory}")
    server.serve_forever()

    if __name__ == "__main__":
    main()