add a py sdk example for a proxy backend

This commit is contained in:
Kenneth Bingham 2025-01-28 18:00:25 -05:00
parent 141d219ef7
commit 26a3d802e7
No known key found for this signature in database
GPG Key ID: 31709281860130B6

View File

@ -0,0 +1,153 @@
import argparse
import atexit
import logging
import sys
import urllib.parse
import requests
from flask import Flask, Response, request
from waitress import serve
from zrok.model import ShareRequest, Share
from zrok.overview import EnvironmentAndResources, Overview
import zrok
# Setup logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
app = Flask(__name__)
target_url = None
zrok_opts = {}
bindPort = 18081
# List of hop-by-hop headers that should not be returned to the viewer
HOP_BY_HOP_HEADERS = {
'connection',
'keep-alive',
'proxy-authenticate',
'proxy-authorization',
'te',
'trailers',
'transfer-encoding',
'upgrade'
}
@app.route('/', defaults={'path': ''}, methods=['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH'])
@app.route('/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH'])
def proxy(path):
global target_url
logger.info(f"Incoming {request.method} request to {request.path}")
logger.info(f"Headers: {dict(request.headers)}")
# Forward the request to target URL
full_url = urllib.parse.urljoin(target_url, request.path)
logger.info(f"Forwarding to: {full_url}")
# Copy request headers, excluding hop-by-hop headers
headers = {k: v for k, v in request.headers.items() if k.lower() not in HOP_BY_HOP_HEADERS and k.lower() != 'host'}
try:
response = requests.request(
method=request.method,
url=full_url,
headers=headers,
data=request.get_data(),
stream=True
)
logger.info(f"Response status: {response.status_code}")
logger.info(f"Response headers: {dict(response.headers)}")
# Filter out hop-by-hop headers from the response
filtered_headers = {k: v for k, v in response.headers.items() if k.lower() not in HOP_BY_HOP_HEADERS}
return Response(
response.iter_content(chunk_size=8192),
status=response.status_code,
headers=filtered_headers
)
except Exception as e:
logger.error(f"Proxy error: {str(e)}", exc_info=True)
return str(e), 502
@zrok.decor.zrok(opts=zrok_opts)
def run_proxy():
# the port is only used to integrate zrok with frameworks that expect a "hostname:port" combo
serve(app, port=bindPort)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Start a zrok proxy server')
parser.add_argument('target_url', help='Target URL to proxy requests to')
parser.add_argument('-n', '--unique-name', help='Unique name for the proxy instance')
args = parser.parse_args()
target_url = args.target_url
logger.info("=== Starting proxy server ===")
logger.info(f"Target URL: {target_url}")
logger.info(f"Logging level: {logger.getEffectiveLevel()}")
root = zrok.environment.root.Load()
my_env = EnvironmentAndResources(
environment=None,
shares=[]
)
overview = Overview.create(root=root)
for env_stuff in overview.environments:
if env_stuff.environment.z_id == root.env.ZitiIdentity:
my_env = EnvironmentAndResources(
environment=env_stuff.environment,
shares=env_stuff.shares
)
break
if my_env:
logger.debug(
f"Found environment in overview with Ziti identity "
f"matching local environment: {my_env.environment.z_id}"
)
else:
logger.error("No matching environment found")
sys.exit(1)
existing_reserved_share = None
for share in my_env.shares:
if share.token == args.unique_name:
existing_reserved_share = share
break
if existing_reserved_share:
logger.debug(f"Found existing share with token: {existing_reserved_share.token}")
shr = Share(Token=existing_reserved_share.token, FrontendEndpoints=[existing_reserved_share.frontend_endpoint])
else:
logger.debug(f"No existing share found with token: {args.unique_name}")
share_request = ShareRequest(
BackendMode=zrok.model.PROXY_BACKEND_MODE,
ShareMode=zrok.model.PUBLIC_SHARE_MODE,
Frontends=['public'],
Target="http-proxy",
Reserved=True
)
if args.unique_name:
share_request.UniqueName = args.unique_name
shr = zrok.share.CreateShare(root=root, request=share_request)
def cleanup():
zrok.share.ReleaseReservedShare(root=root, shr=shr)
logger.info(f"Share {shr.Token} released")
if not args.unique_name:
atexit.register(cleanup)
zrok_opts['cfg'] = zrok.decor.Opts(root=root, shrToken=shr.Token, bindPort=bindPort)
logger.info(f"Access proxy at: {', '.join(shr.FrontendEndpoints)}")
run_proxy()