如何编写反向HTTP代理?

问题描述 投票:0回答:1

所以我正在搞套接字编程,我写了一个转发代理,其工作原理如下:

#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <iostream>
#include <thread>

#pragma comment(lib, "ws2_32.lib")

#define BUFFER_SIZE 8192
#define DEFAULT_PORT "87"

// Global variables
SOCKET g_listenSocket = INVALID_SOCKET;
bool g_running = false;

bool initWinsock() {
    WSADATA wsaData;
    return WSAStartup(MAKEWORD(2, 2), &wsaData) == 0;
}

bool initServer() {
    struct addrinfo* result = nullptr, hints;
    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    if (getaddrinfo(NULL, DEFAULT_PORT, &hints, &result) != 0) {
        return false;
    }

    g_listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (g_listenSocket == INVALID_SOCKET) {
        freeaddrinfo(result);
        return false;
    }

    if (bind(g_listenSocket, result->ai_addr, (int)result->ai_addrlen) == SOCKET_ERROR) {
        freeaddrinfo(result);
        closesocket(g_listenSocket);
        return false;
    }

    freeaddrinfo(result);

    if (listen(g_listenSocket, SOMAXCONN) == SOCKET_ERROR) {
        closesocket(g_listenSocket);
        return false;
    }

    return true;
}

bool isConnectMethod(const char* request) {
    return strncmp(request, "CONNECT", 7) == 0;
}

void parseHostAndPort(const char* request, char* host, char* port) {
    char* line = new char[strlen(request) + 1];
    strcpy(line, request);

    if (isConnectMethod(line)) {
        // Handle CONNECT method (HTTPS)
        char* start = line + 8; // Skip "CONNECT "
        char* end = strchr(start, ' ');
        if (end) {
            *end = '\0';
            char* colon = strchr(start, ':');
            if (colon) {
                *colon = '\0';
                strcpy(host, start);
                strcpy(port, colon + 1);   
            }
            else {
                strcpy(host, start);
                strcpy(port, "443");
            }
        }
    }
    else {
        // Handle HTTP requests
        bool foundHost = false;
        char* current = line;
        char* eol;

        // Look for Host header
        while ((eol = strstr(current, "\r\n")) != nullptr) {
            *eol = '\0';
            if (strncmp(current, "Host: ", 6) == 0) {
                char* hostLine = current + 6;
                char* colon = strchr(hostLine, ':');
                if (colon) {
                    *colon = '\0';
                    strcpy(host, hostLine);
                    strcpy(port, colon + 1);
                }
                else {
                    strcpy(host, hostLine);
                    strcpy(port, "80");
                }
                foundHost = true;
                break;
            }
            current = eol + 2;
        }

        // If no Host header found, try to parse from the request line
        if (!foundHost) {
            if (strncmp(line, "GET http://", 11) == 0 ||
                strncmp(line, "POST http://", 12) == 0) {
                char* start = strchr(line, '/') + 2;
                char* end = strchr(start, '/');
                if (end) {
                    *end = '\0';
                    char* colon = strchr(start, ':');
                    if (colon) {
                        *colon = '\0';
                        strcpy(host, start);
                        strcpy(port, colon + 1);
                    }
                    else {
                        strcpy(host, start);
                        strcpy(port, "80");
                    }
                }
            }
        }
    }

    delete[] line;
}

SOCKET connectToHost(const char* host, const char* port) {
    struct addrinfo hints = {}, * result = nullptr;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if (getaddrinfo(host, port, &hints, &result) != 0) {
        return INVALID_SOCKET;
    }

    SOCKET serverSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (serverSocket == INVALID_SOCKET) {
        freeaddrinfo(result);
        return INVALID_SOCKET;
    }

    if (connect(serverSocket, result->ai_addr, (int)result->ai_addrlen) == SOCKET_ERROR) {
        closesocket(serverSocket);
        freeaddrinfo(result);
        return INVALID_SOCKET;
    }

    freeaddrinfo(result);
    return serverSocket;
}

void handleBidirectionalTraffic(SOCKET client, SOCKET server) {
    fd_set readSet;
    char buffer[BUFFER_SIZE];

    while (true) {
        FD_ZERO(&readSet);
        FD_SET(client, &readSet);
        FD_SET(server, &readSet);

        // Wait for data on either socket
        if (select(0, &readSet, NULL, NULL, NULL) == SOCKET_ERROR) {
            break;
        }

        // Check client -> server
        if (FD_ISSET(client, &readSet)) {
            int bytes = recv(client, buffer, sizeof(buffer), 0);
            if (bytes <= 0) break;
            if (send(server, buffer, bytes, 0) <= 0) break;
        }

        // Check server -> client
        if (FD_ISSET(server, &readSet)) {
            int bytes = recv(server, buffer, sizeof(buffer), 0);
            if (bytes <= 0) break;
            if (send(client, buffer, bytes, 0) <= 0) break;
        }
    }

    closesocket(client);
    closesocket(server);
}

void handleClient(SOCKET clientSocket) {
    char buffer[BUFFER_SIZE];
    int bytesReceived = recv(clientSocket, buffer, BUFFER_SIZE - 1, 0);
    if (bytesReceived <= 0) {
        closesocket(clientSocket);
        return;
    }

    buffer[bytesReceived] = '\0';

    char host[256] = { 0 };
    char port[16] = { 0 };
    parseHostAndPort(buffer, host, port);

    if (strlen(host) == 0) {
        const char* response = "HTTP/1.1 400 Bad Request\r\n\r\n";
        send(clientSocket, response, strlen(response), 0);
        closesocket(clientSocket);
        return;
    }

    SOCKET serverSocket = connectToHost(host, port);
    if (serverSocket == INVALID_SOCKET) {
        const char* response = "HTTP/1.1 502 Bad Gateway\r\n\r\n";
        send(clientSocket, response, strlen(response), 0);
        closesocket(clientSocket);
        return;
    }

    if (isConnectMethod(buffer)) {
        // HTTPS: Send connection established
        const char* response = "HTTP/1.1 200 Connection established\r\n\r\n";
        if (send(clientSocket, response, strlen(response), 0) <= 0) {
            closesocket(clientSocket);
            closesocket(serverSocket);
            return;
        }
    }
    else {
        // HTTP: Forward the original request
        if (send(serverSocket, buffer, bytesReceived, 0) <= 0) {
            closesocket(clientSocket);
            closesocket(serverSocket);
            return;
        }
    }

    // Handle bidirectional traffic for both HTTP and HTTPS
    handleBidirectionalTraffic(clientSocket, serverSocket);
}

void cleanup() {
    g_running = false;
    if (g_listenSocket != INVALID_SOCKET) {
        closesocket(g_listenSocket);
        g_listenSocket = INVALID_SOCKET;
    }
    WSACleanup();
}

int main() {
    if (!initWinsock()) {
        std::cerr << "Failed to initialize Winsock\n";
        return 1;
    }

    if (!initServer()) {
        std::cerr << "Failed to initialize server\n";
        cleanup();
        return 1;
    }

    g_running = true;
    std::cout << "Proxy server listening on localhost:87...\n";

    while (g_running) {
        SOCKET clientSocket = accept(g_listenSocket, NULL, NULL);
        if (clientSocket == INVALID_SOCKET) {
            continue;
        }

        std::cout << "New client connected\n";
        // Create a new thread to handle the client
        std::thread(handleClient, clientSocket).detach();
    }

    cleanup();
    return 0;
}

程序在循环中使用accept(),为每个连接的客户端创建一个线程,浏览器为每个http请求打开一个tcp连接。现在我想将此代码转换为一个反向代理,它将像这样工作,server.cpp,客户端.cpp。服务器将监听浏览器的连接,接收请求并将其传递给 client.cpp ,其中 client.cpp 将连接到目标主机,然后将响应传递给 server.cpp 然后将响应传递给 browser.client.cpp 将通过以下方式连接简单的 tcp 流到 server.cpp。

我的问题是:由于client.cpp/server.cpp将通过一个tcp流进行通信,我如何才能传递所有请求处理它们并将它们发送回server.cpp?,其中server.cpp针对从浏览器有一个线程吗? 我的第一个想法是在 client.cpp 中的循环内有一个 recv() ,并为从 server.cpp 发送的每个请求创建一个线程,但问题是我不知道如何将每个响应发送回其server.cpp 中的特定线程。

提前致谢。

c++ sockets proxy
1个回答
0
投票

如果您要通过单个连接发送多个数据流,则必须封装每个流中的数据。为此,您可能需要构建数据。

也就是说,在代理中读取一些数据,然后在发送到服务器时,添加一点标头,表示后面的 N 字节数据来自流 M。然后在服务器端,你需要有一点一段代码,用于解压数据、单独处理每个流中的数据,并将结果与类似的标头打包在一起,表示以下 N 个字节的回复用于流 M。

为了提供一点额外的保证(以及在出现问题时恢复同步的能力),您可能还需要在每帧的开头和结尾添加相当独特的模式。

© www.soinside.com 2019 - 2024. All rights reserved.