Skip to content

Instantly share code, notes, and snippets.

@ge9
Created February 15, 2024 14:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ge9/d914e228bcbbbdf0b27ae5142fc79ed0 to your computer and use it in GitHub Desktop.
Save ge9/d914e228bcbbbdf0b27ae5142fc79ed0 to your computer and use it in GitHub Desktop.
webdav proxy with a secret folder
#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