代理服务器的简单实现 | Java、NodeJS、Python

237 阅读1分钟

Java 实现

package com.onemsg.proxy;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpClientResponse;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.http.RequestOptions;
import io.vertx.ext.web.Router;

/**
 * 代理服务器
 */
public class ProxyVerticle  extends AbstractVerticle{
    
    static final String LOCATION = "https://github.com";
    static final String HOST = "github.com";

    private HttpClient client;

    @Override
    public void start(Promise<Void> startPromise) throws Exception {

        client = vertx.createHttpClient();
        HttpServer server = vertx.createHttpServer();
    
        Router router = Router.router(vertx);

        router.route("/*").handler(ctx -> {

            String url = LOCATION + ctx.request().uri();
            RequestOptions requestOptions = new RequestOptions()
                .setMethod(ctx.request().method())
                .setAbsoluteURI(url)
                .setHeaders(ctx.request().headers().set("Host", HOST) );
            
            forward(requestOptions, ctx.getBody())
                .onSuccess(clientResponse -> {
                    HttpServerResponse serverResponse = ctx.response().setStatusCode(clientResponse.statusCode());
                    serverResponse.headers().addAll(clientResponse.headers());
                    clientResponse.body().onSuccess(serverResponse::end);
                });
        });

        server.requestHandler(router)
            .listen(9070)
            .onComplete(http -> {
                startPromise.complete();
                System.out.println("HTTP server started on port 9070");
            }).onFailure(startPromise::fail);
    }

    Future<HttpClientResponse> forward(RequestOptions options, Buffer body){
        return client.request(options)
            .onSuccess(clientRequest -> {
                if(body != null){
                    clientRequest.write(body);
                }
                clientRequest.end();
            }).flatMap(HttpClientRequest::response);
    }


    public static void main(String[] args) {
        
        Vertx.vertx().deployVerticle(ProxyVerticle.class, new DeploymentOptions());
    }
}

image.png

NodeJS 实现

const https = require('https');
const http = require('http');

const LOCALTION = 'developer.mozilla.org';

const server = http.createServer((req, res) => {

    const options = {
        hostname: LOCALTION,
        port: 443,
        path: req.url,
        method: req.method,
        headers: req.headers
    };

    options.headers['host'] = LOCALTION

    const proxyReq = https.request(options, proxyRes => {
        const body = []
        
        proxyRes.on("data", data => {
            body.push(data)
        })
        proxyRes.on("end", () => {
            const data = Buffer.concat(body)

            res.statusCode = proxyRes.statusCode
            for (const headerName in proxyRes.headers) {
                const headerValue = proxyRes.headers[headerName];
                if(headerValue == null ){
                    continue
                }
                res.setHeader(headerName, headerValue)
            }

            res.setHeader("server", "PROXY SERVER")

            res.end(data)
        })
    })

    const body = []

    req.on("error", error => {
        console.log(error)
    })

    req.on("data", data => {
        body.push(data)
    })
    req.on("end", () => {
        const data = Buffer.concat(body)
        proxyReq.end(data)
    })
});


const hostname = '127.0.0.1';
const port = 3000;

server.listen(port, hostname, () => {
	console.log(`Proxy Server running at http://${hostname}:${port}/`);
});

server.on("error", error => {
    console.log(error)
})

Python 实现

"""
API Gateway for frontend

@autor: onemsg
@since: 2022-03
"""

from email import header
from typing import Tuple
from aiohttp import ClientResponse, web
import aiohttp
import logging

log = logging.getLogger("API-Gateway-Server")

SERVICE_STORE = {
    "app-1": ("127.0.0.1", 9001),
    "app-2": ("127.0.0.1", 9002),
    "app-3": ("127.0.0.1", 9003),
}

DEFAULT_SERVICE_NAME_HEADER = "X-POLARIS-NAME"

def find_service(service_name: str) -> Tuple[str, int]:
    """
    服务发现
    """
    return SERVICE_STORE.get(service_name, (None, None))


async def proxy_handler(request: web.Request) -> web.Response:
    """
    服务代理
    """
    if DEFAULT_SERVICE_NAME_HEADER not in request.headers:
        return web.Response(status=400, text="没有指定具体服务")

    service_name = request.headers.getone(DEFAULT_SERVICE_NAME_HEADER)
    host, port = find_service(service_name)
    if host == None:
        return web.Response(status=404, text="没有找到具体服务 [{}]".format(service_name))

    return await forward(request, host, port)


async def forward(request: web.Request, host: str, port: int) -> web.Response:
    """
    请求转发
    """
    target_url = f"http://{host}:{port}" + request.rel_url.path
    async with aiohttp.ClientSession() as session:
        async with session.request(request.method, target_url, headers=request.headers, data=await request.read()) as origin_response:
            return web.Response(
                status=origin_response.status, 
                headers=origin_response.headers, 
                body=await origin_response.read() )


if __name__ == "__main__":
    
    app = web.Application()

    app.add_routes([
        web.route("*", "/{path:.*?}", proxy_handler)
    ])

    web.run_app(app, port=9870)