mirror of
https://github.com/apple/swift-nio-extras.git
synced 2025-05-14 08:52:42 +08:00
HTTPResumableUpload contains the core logic. It uses an event loop to synchronize its state internally. Some methods are safe to call from off of that event loop and have been moved to a new sendable view. The HTTPResumableUpload type is marked as explicitly not sendable. As such, most other types now hold on to the sendable view and use that as the interface to HTTPResumableUpload. HTTPResumableUploadChannel must be sendable (it's a Channel) and now uses safe abstractions (where possible).
75 lines
2.5 KiB
Swift
75 lines
2.5 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the SwiftNIO open source project
|
|
//
|
|
// Copyright (c) 2023-2024 Apple Inc. and the SwiftNIO project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import NIOConcurrencyHelpers
|
|
import NIOCore
|
|
|
|
/// `HTTPResumableUploadContext` manages ongoing uploads.
|
|
public final class HTTPResumableUploadContext: Sendable {
|
|
let origin: String
|
|
let path: String
|
|
let timeout: TimeAmount
|
|
private let uploads: NIOLockedValueBox<[String: HTTPResumableUpload.SendableView]> = .init([:])
|
|
|
|
/// Create an `HTTPResumableUploadContext` for use with `HTTPResumableUploadHandler`.
|
|
/// - Parameters:
|
|
/// - origin: Scheme and authority of the upload server. For example, "https://www.example.com".
|
|
/// - path: Request path for resumption URLs. `HTTPResumableUploadHandler` intercepts all requests to this path.
|
|
/// - timeout: Time to wait before failure if the client didn't attempt an upload resumption.
|
|
public init(origin: String, path: String = "/resumable_upload/", timeout: TimeAmount = .hours(1)) {
|
|
self.origin = origin
|
|
self.path = path
|
|
self.timeout = timeout
|
|
}
|
|
|
|
func isResumption(path: String) -> Bool {
|
|
path.hasPrefix(self.path)
|
|
}
|
|
|
|
private func path(fromToken token: String) -> String {
|
|
"\(self.path)\(token)"
|
|
}
|
|
|
|
private func token(fromPath path: String) -> String {
|
|
assert(self.isResumption(path: path))
|
|
return String(path.dropFirst(self.path.count))
|
|
}
|
|
|
|
func startUpload(_ upload: HTTPResumableUpload) -> String {
|
|
var random = SystemRandomNumberGenerator()
|
|
let token = "\(random.next())-\(random.next())"
|
|
self.uploads.withLockedValue {
|
|
assert($0[token] == nil)
|
|
$0[token] = upload.sendableView
|
|
}
|
|
return self.path(fromToken: token)
|
|
}
|
|
|
|
func stopUpload(_ upload: HTTPResumableUpload) {
|
|
if let path = upload.resumePath {
|
|
let token = token(fromPath: path)
|
|
self.uploads.withLockedValue {
|
|
$0[token] = nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func findUpload(path: String) -> HTTPResumableUpload.SendableView? {
|
|
let token = token(fromPath: path)
|
|
return self.uploads.withLockedValue {
|
|
$0[token]
|
|
}
|
|
}
|
|
}
|