Skip to content

Memory leak in FlyingFox or FlyingSocks #193

Description

@forchid

The version info

Windows 10 x64, FlyingFox 0.26.2 or the main branch.

The test

Itr#0 -------------- memory size 300M+
Itr#1 -------------- memory size 500M+
Itr#2 -------------- memory size 700M+
Itr#3 -------------- memory size 1000M+
...

The test source

import FlyingFox
import FlyingSocks
import FlyingFoxMacros

import Foundation
import FoundationNetworking

let host = "127.0.0.1"
let port: UInt16 = 3000
let addr: SocketAddress = try .inet(ip4: host, port: port)
let logger = DisabledLogger.disabled
let bodySize = 2 << 20

// Start server
let server = HTTPServer(address: addr, logger: logger, handler: MemleakHandler())
let serverTask = Task { try await server.run() }
defer { serverTask.cancel() }
        
// Start tests
for i in 0 ..< 1000 {
    await startTest(itr: i)
}
print("Bye!")

func startTest(itr: Int) async {
    var i = 0
    while i < 10000 {
        let j = i
        async let c1 = startClient(itr, j)
        async let c2 = startClient(itr, j + 1)
        async let c3 = startClient(itr, j + 2)
        async let c4 = startClient(itr, j + 3)
        //async let c5 = startClient(j + 4) // Crash?!
        
        let res = await [
            c1, 
            c2, 
            c3, 
            c4, 
            //c5
        ]
        i += res.count
    }

    i = 0
    while i < 10 {
        print("Itr#\(itr)-\(i): wait for a while")
        try? await Task.sleep(for: .seconds(1))
        i += 1
    }
}

func startClient(_ itr: Int, _ i: Int) async -> Bool {
    let url = URL(string: "http://\(host):\(port)/flyingleak")!
    var req = URLRequest(url: url)
    req.httpMethod = "POST"
    req.httpBody = Data(Array<UInt8>(repeating: 65, count: bodySize))
    do {
        let (data, res) = try await URLSession.shared.data(for: req)
        guard let res = res as? HTTPURLResponse else {
            print("Client#\(i): response type error!")
            return false
        }
        if res.statusCode == 200 && data.count == bodySize {
            if i % 1000 == 0 {
                print("Itr#\(itr)-client-\(i): ok.")
            }
            return true
        } else {
            print("Itr#\(itr)-client-\(i): failed - status \(res.statusCode).")
            return false
        }
    } catch {
        print("Itr#\(itr)-client-\(i): failed - \(error)")
        return false
    }
}

@HTTPHandler
struct MemleakHandler {

  @HTTPRoute("/flyingleak")
  func hello(_ request: HTTPRequest) async throws -> HTTPResponse {
    let data = try await request.bodyData
    if data.count != bodySize {
        return HTTPResponse(statusCode: .badRequest)
    }
    var headers = HTTPHeaders()
    headers[.contentType] = "text/plain;charset=utf-8";
    let body = Data(Array<UInt8>(repeating: 66, count: bodySize))
    return HTTPResponse(statusCode: .ok, headers: headers, body: body)
  }
  
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions