Files
calculet-npu-research-archive/scripts/lfs-host-proxy.py
T

126 lines
4.0 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import http.client
import shutil
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import urlsplit, urlunsplit
HOP_BY_HOP = {
"connection",
"keep-alive",
"proxy-authenticate",
"proxy-authorization",
"proxy-connection",
"te",
"trailers",
"transfer-encoding",
"upgrade",
}
class GiteaProxy(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
target_host = "192.168.10.248"
def do_GET(self):
self._forward()
def do_HEAD(self):
self._forward(send_body=False)
def do_POST(self):
self._forward()
def do_PUT(self):
self._forward()
def _forward(self, send_body=True):
parsed = urlsplit(self.path)
path = urlunsplit(("", "", parsed.path or "/", parsed.query, ""))
print(
f"{self.command} content-length={self.headers.get('Content-Length')} "
f"transfer-encoding={self.headers.get('Transfer-Encoding')}",
flush=True,
)
upstream = http.client.HTTPConnection(self.target_host, 80, timeout=3600)
upstream.putrequest(
self.command,
path,
skip_host=True,
skip_accept_encoding=True,
)
for name, value in self.headers.items():
lower_name = name.lower()
if lower_name in HOP_BY_HOP:
continue
upstream.putheader(name, "gitea" if lower_name == "host" else value)
transfer_encoding = self.headers.get("Transfer-Encoding", "").lower()
if transfer_encoding == "chunked":
upstream.putheader("Transfer-Encoding", "chunked")
upstream.endheaders()
if transfer_encoding == "chunked":
self._forward_chunked_body(upstream)
else:
content_length = int(self.headers.get("Content-Length", "0"))
remaining = content_length
while remaining:
chunk = self.rfile.read(min(1024 * 1024, remaining))
if not chunk:
raise ConnectionError("request body ended early")
upstream.send(chunk)
remaining -= len(chunk)
response = upstream.getresponse()
self.send_response(response.status, response.reason)
for name, value in response.getheaders():
if name.lower() not in HOP_BY_HOP:
self.send_header(name, value)
self.send_header("Connection", "close")
self.end_headers()
self.close_connection = True
if send_body:
shutil.copyfileobj(response, self.wfile, 1024 * 1024)
upstream.close()
def _forward_chunked_body(self, upstream):
while True:
size_line = self.rfile.readline()
if not size_line:
raise ConnectionError("chunked request ended before size line")
upstream.send(size_line)
chunk_size = int(size_line.split(b";", 1)[0].strip(), 16)
if chunk_size == 0:
while True:
trailer_line = self.rfile.readline()
upstream.send(trailer_line)
if trailer_line in (b"\r\n", b"\n", b""):
return
remaining = chunk_size + 2
while remaining:
data = self.rfile.read(min(1024 * 1024, remaining))
if not data:
raise ConnectionError("chunked request ended inside data")
upstream.send(data)
remaining -= len(data)
def log_message(self, fmt, *args):
print("%s - %s" % (self.address_string(), fmt % args), flush=True)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--port", type=int, default=18765)
args = parser.parse_args()
server = ThreadingHTTPServer(("127.0.0.1", args.port), GiteaProxy)
print(f"Listening on 127.0.0.1:{args.port}", flush=True)
server.serve_forever()
if __name__ == "__main__":
main()