Created
February 15, 2024 14:47
-
-
Save ge9/d914e228bcbbbdf0b27ae5142fc79ed0 to your computer and use it in GitHub Desktop.
webdav proxy with a secret folder
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
#Usage: python test.py 127.0.0.100 18080 secret_folder_name 127.0.0.200:8080 /some/folder/ user password | |
import sys | |
import httpx | |
import uvicorn | |
import re | |
import urllib.parse | |
from starlette.applications import Starlette | |
from starlette.requests import Request | |
from starlette.responses import StreamingResponse, Response | |
from starlette.background import BackgroundTask | |
from starlette.routing import Route | |
SERVER_HOST = sys.argv[1] | |
SERVER_PORT = sys.argv[2] | |
SPECIAL_FOLDER = sys.argv[3] | |
RCLONE_SERVER_URL = 'http://'+sys.argv[4]+sys.argv[5] | |
RCLONE_USERNAME = sys.argv[6] | |
RCLONE_PASSWORD = sys.argv[7] | |
auth = (RCLONE_USERNAME, RCLONE_PASSWORD) | |
# HTTPクライアントの設定 | |
client = httpx.AsyncClient(timeout=30.0, base_url=RCLONE_SERVER_URL+"/", auth=auth) | |
async def _reverse_proxy(request: Request): | |
# request.url.pathだと特殊文字でバグる(#記号の後が消されるなど | |
path=urllib.parse.quote(re.sub(r'^http://[^/]+', '', str(request.url))) | |
print(path) | |
if not path.startswith("/"+SPECIAL_FOLDER): | |
# SPECIAL_FOLDERの外にアクセスした場合、何も表示しない | |
# 404とかを返すとSPECIAL_FOLDERの中身さえもWindowsで見れなくなるので見かけは正常応答 | |
return Response(content=b'Not found', status_code=200) | |
new_path = path[len("/"+SPECIAL_FOLDER) + 1:] # フォルダ名を取り除く | |
headers = {key.lower(): value for (key, value) in request.headers.items() if key.lower() not in ['host']} | |
print(request.method) | |
# MOVEリクエストの場合、Destinationヘッダーを変更 | |
if request.method == 'MOVE': | |
destination = headers.get('destination', '') | |
if "/"+SPECIAL_FOLDER in destination: | |
new_destination = destination.split("/"+SPECIAL_FOLDER+"/", 1)[1] | |
headers['destination'] = f'{RCLONE_SERVER_URL}/{new_destination}' | |
url = httpx.URL(path=new_path, query=request.url.query.encode("utf-8")) | |
rp_req = client.build_request(request.method, url, | |
headers=headers, | |
content=await request.body()) | |
resp = await client.send(rp_req, stream=True) | |
filtered_headers = {k: v for k, v in resp.headers.items() if k.lower() not in ['content-length', 'content-encoding', 'transfer-encoding', 'connection']} | |
print(filtered_headers) | |
print(resp.status_code) | |
if request.method == 'PROPFIND' and 200 <= resp.status_code < 300: | |
content = await resp.aread() | |
# SPECIAL_FOLDERの名前に付け替える。(クライアントによってはちゃんとやらなくても動いたりする) | |
# xml.etree.ElementTreeみたいなのを使う方法だと<ns0:href>みたいにns0とかいうのが付けられてしまい、Windowsではうまくいかなかった。 | |
t = content.decode('utf-8') | |
t2 = t.replace('<D:href>'+sys.argv[5], '<D:href>' + "/"+SPECIAL_FOLDER+"/") | |
respcontent = t2.encode('utf-8') | |
# print(respcontent) | |
return Response(content=respcontent, status_code=resp.status_code, headers=dict(filtered_headers)) | |
# それ以外ならそのまま応答を返す | |
return StreamingResponse( | |
resp.aiter_raw(), | |
status_code=resp.status_code, | |
headers=resp.headers, | |
background=BackgroundTask(resp.aclose) | |
) | |
routes = [Route("/{path:path}", _reverse_proxy, methods=['GET', 'POST', 'PUT', 'LOCK', 'UNLOCK', 'MOVE', 'MKCOL', 'DELETE', 'OPTIONS', 'PROPPATCH', 'PROPFIND'])] | |
app = Starlette(routes=routes) | |
if __name__ == '__main__': | |
uvicorn.run(app, host=SERVER_HOST, port=int(SERVER_PORT)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment