-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.py
More file actions
82 lines (66 loc) · 2.88 KB
/
Copy pathserver.py
File metadata and controls
82 lines (66 loc) · 2.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#!/usr/bin/env python3
from http.server import BaseHTTPRequestHandler, HTTPServer
import subprocess, urllib.parse, shlex, os, threading, queue
class MyHandler(BaseHTTPRequestHandler):
def do_POST(self):
content_length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(content_length)
# Decode URL path and split into command parts
raw_path = urllib.parse.unquote(self.path.lstrip("/"))
cmd_parts = shlex.split(raw_path)
# Normalize first element to basename (safe fallback)
cmd_parts[0] = os.path.basename(cmd_parts[0])
print("Executing:", cmd_parts)
try:
process = subprocess.Popen(
cmd_parts,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
# Write stdin data and close it so the process sees EOF
if body:
process.stdin.write(body)
process.stdin.close()
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Transfer-Encoding", "chunked")
self.end_headers()
# Merge stdout and stderr into one queue via reader threads,
# so both streams are forwarded live and neither can block the other
line_queue = queue.Queue()
def reader(stream, prefix):
for line in stream:
line_queue.put(prefix + line if prefix else line)
line_queue.put(None) # sentinel: this stream is done
t_out = threading.Thread(target=reader, args=(process.stdout, b""))
t_err = threading.Thread(target=reader, args=(process.stderr, b"[stderr] "))
t_out.start()
t_err.start()
done_count = 0
while done_count < 2:
chunk = line_queue.get()
if chunk is None:
done_count += 1
continue
self.wfile.write(f"{len(chunk):X}\r\n".encode())
self.wfile.write(chunk)
self.wfile.write(b"\r\n")
self.wfile.flush()
t_out.join()
t_err.join()
process.wait()
# Terminate chunked transfer
self.wfile.write(b"0\r\n\r\n")
self.wfile.flush()
except FileNotFoundError:
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.end_headers()
self.wfile.write(f"Error: command not found: {cmd_parts[0]}\n".encode())
def log_message(self, format, *args):
print(f"[{self.address_string()}] {format % args}")
if __name__ == "__main__":
server = HTTPServer(("0.0.0.0", 8000), MyHandler)
print("Serving on 0.0.0.0:8000")
server.serve_forever()