docs/HTTP Resolver

HTTP Resolver (Server Data)

TCP server on port 80 returning server connection data to clients

Overview

The HTTP resolver is a separate TCP server that runs on port 80. It responds to client requests with server connection information (IP, port, type) in the format expected by the Growtopia client (server_data.php format).

Thread: Detached std::thread (commented out in main, but can be enabled)

Implementation

static void resolver_loop(int game_port) {
    WSADATA wsadata;
    if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) return;
    
    SOCKET srv = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (srv == INVALID_SOCKET) return;
    
    int yes = 1;
    setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes));
    
    sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(80);
    
    if (::bind(srv, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
        cout << "[Resolver] Failed to bind port 80 (needs Administrator)" << endl;
        closesocket(srv);
        return;
    }
    
    listen(srv, SOMAXCONN);
    
    // Response format (pipe-delimited)
    string response = "server|127.0.0.1\n"
                    + "port|" + to_string(game_port) + "\n"
                    + "type|1\n"
                    + "type2|1\n"
                    + "beta_server|127.0.0.1\n"
                    + "beta_port|" + to_string(game_port) + "\n"
                    + "beta_type|1\n"
                    + "meta|localhost\n"
                    + "RTENDMARKERBS1001";
    
    cout << "[Resolver] Listening on port 80 (server_data.php -> 127.0.0.1:" << game_port << ")" << endl;
    
    while (true) {
        SOCKET c = accept(srv, nullptr, nullptr);
        if (c == INVALID_SOCKET) continue;
        
        char buf[1024] = { 0 };
        recv(c, buf, sizeof(buf), 0);
        
        string http = "HTTP/1.1 200 OK\r\n"
                    + "Content-Type: text/plain\r\n"
                    + "Content-Length: " + to_string(response.size()) + "\r\n"
                    + "Connection: close\r\n\r\n"
                    + response;
        send(c, http.c_str(), (int)http.size(), 0);
        closesocket(c);
    }
}

Response Format

FieldExample ValueDescription
server127.0.0.1Main game server IP
port53181Game UDP port
type1Server type (1=main)
type21Secondary type
beta_server127.0.0.1Beta server IP
beta_port53181Beta server port
beta_type1Beta server type
metalocalhostMetadata identifier
RTENDMARKERBS1001(fixed)End-of-response marker

Client Request Flow

  1. Game client starts, resolves server domain
  2. Client sends HTTP GET to http://server_domain/ (port 80)
  3. Resolver returns pipe-delimited response above
  4. Client parses response, extracts server and port
  5. Client initiates ENet connection to game server UDP port

Requirements & Caveats

Administrator Required

Binding to port 80 requires elevated privileges on Windows. Run server as Administrator.

HTTPS Not Supported

Resolver uses plain HTTP. Modern clients may require HTTPS or certificate pinning.

Thread Safety

Resolver runs in detached thread. Uses read-only access to server_port.

Enabling the Resolver

The resolver thread is currently commented out in main():

// std::thread resolver(resolver_loop, server_port);
// resolver.detach();

Uncomment and ensure the server is run as Administrator to enable.

HTTP Client Utility

The codebase includes a full HTTP client implementation used for outbound requests (e.g., crypto price fetching). Features:

  • Sync HTTP/1.1 client with chunked encoding support
  • Timeout handling, redirect following
  • Cross-platform (Windows/Linux/macOS)
  • Used in crypto price updates