Created
April 20, 2025 07:09
-
-
Save leetschau/25c37cd63df6dba7c52194e62a08dcad to your computer and use it in GitHub Desktop.
A standalone (and naive) texts and files sharing web server
This file contains hidden or 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 | |
## 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() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment