Skip to content

Async bodySequence never completes #139

@auramagi

Description

@auramagi

While trying to implement a web server in an iOS app that handles remote file uploads, I've run into an issue where receiving a request with a body over a certain size will always time out.

Iterating over request.bodySequence produces the correct amount of chunks, but indefinitely suspends after reaching the total content size.

I've reproduced the issue in sample code below. When the request body size is small everything is fine, but you can see this problem after increasing it to a certain point (seems to be bodySize = 2049*1024).

Sample code
struct ContentView: View {
    let bodySize = 4096*1024

    @State var server = HTTPServer(port: 80)

    @State var state = "Idle"

    @State var response: String?

    @State var received: Int?

    @State var task: Task<Void, Never>?

    var body: some View {
        ScrollView {
            VStack {
                GroupBox("State") {
                    Text(state)
                }

                if let received {
                    GroupBox("Received") {
                        Text(
                            Measurement(value: Double(received), unit: UnitInformationStorage.bytes),
                            format: .byteCount(style: .binary)
                        )
                    }
                }

                if let response {
                    GroupBox("Response") {
                        Text(response)
                    }
                }
            }
        }
        .safeAreaInset(edge: .bottom) {
            Button("Send") {
                task = Task {
                    defer { task = nil }
                    do {
                        let data = Data(repeating: 0, count: bodySize)
                        var request = URLRequest(url: URL(string: "http://localhost/upload")!)
                        request.httpMethod = "PUT"
                        let (_, response) = try await URLSession.shared.upload(for: request, from: data)
                        self.response = "Status Code: \((response as! HTTPURLResponse).statusCode)"
                    } catch {
                        response = error.localizedDescription
                    }
                }
            }
            .disabled(task != nil)
        }
        .task {
            await server.appendRoute("PUT /upload") { request in
                await setState("Receiving")
                await setReceived(0)
                var total = 0
                do {
                    for try await chunk in request.bodySequence {
                        total += chunk.count
                        await setReceived(total)
                    }
                } catch {
                    await setState("Error: \(error.localizedDescription)")
                    throw error
                }
                await setState("Idle")
                return HTTPResponse(statusCode: .ok)
            }

            try? await server.run()
        }
    }

    func setState(_ state: String) {
        self.state = state
    }

    func setReceived(_ received: Int?) {
        self.received = received
    }
}

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