import Foundation import NIOCore import NIOPosix import NIOHTTP1 public final class ProxyServer: Sendable { private let host: String private let port: Int private let group: EventLoopGroup private let trafficRepo: TrafficRepository nonisolated(unsafe) private var channel: Channel? public init( host: String = ProxyConstants.proxyHost, port: Int = ProxyConstants.proxyPort, trafficRepo: TrafficRepository = TrafficRepository() ) { self.host = host self.port = port // Use only 1 thread to conserve memory in the extension (50MB budget) self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1) self.trafficRepo = trafficRepo } public func start() async throws { let trafficRepo = self.trafficRepo let bootstrap = ServerBootstrap(group: group) .serverChannelOption(.backlog, value: 256) .serverChannelOption(.socketOption(.so_reuseaddr), value: 1) .childChannelInitializer { channel in channel.pipeline.addHandler( ByteToMessageHandler(HTTPRequestDecoder(leftOverBytesStrategy: .forwardBytes)) ).flatMap { channel.pipeline.addHandler(HTTPResponseEncoder()) }.flatMap { channel.pipeline.addHandler(ConnectHandler(trafficRepo: trafficRepo)) } } .childChannelOption(.socketOption(.so_reuseaddr), value: 1) .childChannelOption(.maxMessagesPerRead, value: 16) channel = try await bootstrap.bind(host: host, port: port).get() print("[ProxyServer] Listening on \(host):\(port)") } public func stop() async { do { try await channel?.close() try await group.shutdownGracefully() } catch { print("[ProxyServer] Shutdown error: \(error)") } } }