Skip to content

Commit 0d9a6ed

Browse files
Merge pull request #223 from nextcloud/header
parameter-transfers
2 parents bc3d109 + 86ff508 commit 0d9a6ed

5 files changed

Lines changed: 60 additions & 79 deletions

File tree

Sources/NextcloudKit/Extensions/String+Extension.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ extension String {
2929
return String(NSString(string: self).pathExtension)
3030
}
3131

32-
func parsedDate(using format: String) -> Date? {
32+
public func parsedDate(using format: String) -> Date? {
3333
NKLogFileManager.shared.convertDate(self, format: format)
3434
}
3535
}

Sources/NextcloudKit/NKCommon.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ public struct NKCommon: Sendable {
367367
return serverUrl.asUrl
368368
}
369369

370-
func findHeader(_ header: String, allHeaderFields: [AnyHashable: Any]?) -> String? {
370+
public func findHeader(_ header: String, allHeaderFields: [AnyHashable: Any]?) -> String? {
371371
guard let allHeaderFields = allHeaderFields else { return nil }
372372
let keyValues = allHeaderFields.map { (String(describing: $0.key).lowercased(), String(describing: $0.value)) }
373373

@@ -377,6 +377,17 @@ public struct NKCommon: Sendable {
377377
return nil
378378
}
379379

380+
/// Normalizes an HTTP ETag value by removing wrapping quotes when present.
381+
/// - Parameter value: The raw ETag header value returned by the HTTP response.
382+
/// - Returns: The normalized ETag value without surrounding double quotes.
383+
public func normalizedETag(_ value: String?) -> String? {
384+
guard let value else {
385+
return nil
386+
}
387+
388+
return value.trimmingCharacters(in: CharacterSet(charactersIn: "\""))
389+
}
390+
380391
func getHostName(urlString: String) -> String? {
381392
if let url = URL(string: urlString) {
382393
guard let hostName = url.host else { return nil }

Sources/NextcloudKit/NextcloudKit+Download.swift

Lines changed: 7 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public extension NextcloudKit {
2626
requestHandler: @escaping (_ request: DownloadRequest) -> Void = { _ in },
2727
taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in },
2828
progressHandler: @escaping (_ progress: Progress) -> Void = { _ in },
29-
completionHandler: @escaping (_ account: String, _ etag: String?, _ date: Date?, _ lenght: Int64, _ headers: [AnyHashable: any Sendable]?, _ afError: AFError?, _ nKError: NKError) -> Void) {
29+
completionHandler: @escaping (_ account: String, _ response: AFDownloadResponse<URL?>?, _ nKError: NKError) -> Void) {
3030
var convertible: URLConvertible?
3131
if serverUrlFileName is URL {
3232
convertible = serverUrlFileName as? URLConvertible
@@ -36,7 +36,7 @@ public extension NextcloudKit {
3636
guard let url = convertible,
3737
let nkSession = nkCommonInstance.nksessions.session(forAccount: account),
3838
let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else {
39-
return options.queue.async { completionHandler(account, nil, nil, 0, nil, nil, .urlError) }
39+
return options.queue.async { completionHandler(account, nil, .urlError) }
4040
}
4141
var destination: Alamofire.DownloadRequest.Destination?
4242
let fileNamePathLocalDestinationURL = NSURL.fileURL(withPath: fileNameLocalPath)
@@ -51,30 +51,8 @@ public extension NextcloudKit {
5151
} .downloadProgress { progress in
5252
options.queue.async { progressHandler(progress) }
5353
} .response(queue: self.nkCommonInstance.backgroundQueue) { response in
54-
if let error = response.error {
55-
let resultError = NKError(error: error, afResponse: response, responseData: nil)
56-
options.queue.async { completionHandler(account, nil, nil, 0, response.response?.allHeaderFields, error, resultError) }
57-
} else {
58-
var date: Date?
59-
var etag: String?
60-
var length: Int64 = 0
61-
62-
if let result = response.response?.allHeaderFields["Content-Length"] as? String {
63-
length = Int64(result) ?? 0
64-
}
65-
if self.nkCommonInstance.findHeader("oc-etag", allHeaderFields: response.response?.allHeaderFields) != nil {
66-
etag = self.nkCommonInstance.findHeader("oc-etag", allHeaderFields: response.response?.allHeaderFields)
67-
} else if self.nkCommonInstance.findHeader("etag", allHeaderFields: response.response?.allHeaderFields) != nil {
68-
etag = self.nkCommonInstance.findHeader("etag", allHeaderFields: response.response?.allHeaderFields)
69-
}
70-
if etag != nil {
71-
etag = etag?.replacingOccurrences(of: "\"", with: "")
72-
}
73-
if let dateRaw = self.nkCommonInstance.findHeader("Date", allHeaderFields: response.response?.allHeaderFields) {
74-
date = dateRaw.parsedDate(using: "yyyy-MM-dd HH:mm:ss")
75-
}
76-
77-
options.queue.async { completionHandler(account, etag, date, length, response.response?.allHeaderFields, nil, .success) }
54+
options.queue.async {
55+
completionHandler(account, response, self.evaluateDownloadResponse(response))
7856
}
7957
}
8058

@@ -100,11 +78,7 @@ public extension NextcloudKit {
10078
progressHandler: @escaping (_ progress: Progress) -> Void = { _ in }
10179
) async -> (
10280
account: String,
103-
etag: String?,
104-
date: Date?,
105-
length: Int64,
106-
headers: [AnyHashable: any Sendable]?,
107-
afError: AFError?,
81+
response: AFDownloadResponse<URL?>?,
10882
nkError: NKError
10983
) {
11084
await withCheckedContinuation { continuation in
@@ -114,14 +88,10 @@ public extension NextcloudKit {
11488
options: options,
11589
requestHandler: requestHandler,
11690
taskHandler: taskHandler,
117-
progressHandler: progressHandler) { account, etag, date, length, headers, afError, nkError in
91+
progressHandler: progressHandler) { account, response, nkError in
11892
continuation.resume(returning: (
11993
account: account,
120-
etag: etag,
121-
date: date,
122-
length: length,
123-
headers: headers,
124-
afError: afError,
94+
response: response,
12595
nkError: nkError
12696
))
12797
}

Sources/NextcloudKit/NextcloudKit+Upload.swift

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,8 @@ public extension NextcloudKit {
4242
requestHandler: @escaping (_ request: UploadRequest) -> Void = { _ in },
4343
taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in },
4444
progressHandler: @escaping (_ progress: Progress) -> Void = { _ in },
45-
completionHandler: @escaping (_ account: String, _ ocId: String?, _ etag: String?, _ date: Date?, _ size: Int64, _ ownerId: String?, _ permissions: String?, _ response: AFDataResponse<Data>?, _ nkError: NKError) -> Void) {
45+
completionHandler: @escaping (_ account: String, _ response: AFDataResponse<Data>?, _ nkError: NKError) -> Void) {
4646
var convertible: URLConvertible?
47-
var uploadedSize: Int64 = 0
4847

4948
if serverUrlFileName is URL {
5049
convertible = serverUrlFileName as? URLConvertible
@@ -54,7 +53,7 @@ public extension NextcloudKit {
5453
guard let url = convertible,
5554
let nkSession = nkCommonInstance.nksessions.session(forAccount: account),
5655
var headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else {
57-
return options.queue.async { completionHandler(account, nil, nil, nil, 0, nil, nil, nil, .urlError) }
56+
return options.queue.async { completionHandler(account, nil, .urlError) }
5857
}
5958
let fileNameLocalPathUrl = URL(fileURLWithPath: fileNameLocalPath)
6059
// Epoch of linux do not permitted negativ value
@@ -79,33 +78,10 @@ public extension NextcloudKit {
7978
task.taskDescription = options.taskDescription
8079
options.queue.async { taskHandler(task) }
8180
}) .uploadProgress { progress in
82-
uploadedSize = progress.totalUnitCount
8381
options.queue.async { progressHandler(progress) }
8482
} .responseData(queue: self.nkCommonInstance.backgroundQueue) { response in
85-
var ocId: String?, etag: String?, date: Date?, ownerId: String?, permissions: String?
86-
let allHeaderFields = response.response?.allHeaderFields
87-
88-
ownerId = self.nkCommonInstance.findHeader("x-nc-ownerid", allHeaderFields: allHeaderFields)
89-
permissions = self.nkCommonInstance.findHeader("x-nc-permissions", allHeaderFields: allHeaderFields)
90-
if self.nkCommonInstance.findHeader("oc-fileid", allHeaderFields: allHeaderFields) != nil {
91-
ocId = self.nkCommonInstance.findHeader("oc-fileid", allHeaderFields: allHeaderFields)
92-
} else if self.nkCommonInstance.findHeader("fileid", allHeaderFields: allHeaderFields) != nil {
93-
ocId = self.nkCommonInstance.findHeader("fileid", allHeaderFields: allHeaderFields)
94-
}
95-
if self.nkCommonInstance.findHeader("oc-etag", allHeaderFields: allHeaderFields) != nil {
96-
etag = self.nkCommonInstance.findHeader("oc-etag", allHeaderFields: allHeaderFields)
97-
} else if self.nkCommonInstance.findHeader("etag", allHeaderFields: allHeaderFields) != nil {
98-
etag = self.nkCommonInstance.findHeader("etag", allHeaderFields: allHeaderFields)
99-
}
100-
if etag != nil {
101-
etag = etag?.replacingOccurrences(of: "\"", with: "")
102-
}
103-
if let dateRaw = self.nkCommonInstance.findHeader("date", allHeaderFields: allHeaderFields) {
104-
date = dateRaw.parsedDate(using: "EEE, dd MMM y HH:mm:ss zzz")
105-
}
106-
10783
options.queue.async {
108-
completionHandler(account, ocId, etag, date, uploadedSize, ownerId, permissions, response, self.evaluateResponse(response))
84+
completionHandler(account, response, self.evaluateResponse(response))
10985
}
11086
}
11187

@@ -141,12 +117,6 @@ public extension NextcloudKit {
141117
progressHandler: @escaping (_ progress: Progress) -> Void = { _ in }
142118
) async -> (
143119
account: String,
144-
ocId: String?,
145-
etag: String?,
146-
date: Date?,
147-
size: Int64,
148-
ownerId: String?,
149-
permissions: String?,
150120
response: AFDataResponse<Data>?,
151121
error: NKError
152122
) {
@@ -161,15 +131,9 @@ public extension NextcloudKit {
161131
options: options,
162132
requestHandler: requestHandler,
163133
taskHandler: taskHandler,
164-
progressHandler: progressHandler) { account, ocId, etag, date, size, ownerId, permissions, response, error in
134+
progressHandler: progressHandler) { account, response, error in
165135
continuation.resume(returning: (
166136
account: account,
167-
ocId: ocId,
168-
etag: etag,
169-
date: date,
170-
size: size,
171-
ownerId: ownerId,
172-
permissions: permissions,
173137
response: response,
174138
error: error
175139
))

Sources/NextcloudKit/NextcloudKit.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,40 @@ open class NextcloudKit: @unchecked Sendable {
220220
return NKError(error: error, afResponse: response, responseData: response.data)
221221
}
222222
}
223+
224+
/// Evaluates a generic Alamofire download response into NKError with simple HTTP-aware rules.
225+
/// - Note:
226+
/// - Explicit cancellations return `.cancelled`.
227+
/// - Any HTTP 2xx is considered success, regardless of downloaded file presence.
228+
/// - If no HTTP status is available, fall back to Alamofire's `Result`.
229+
func evaluateDownloadResponse<Data>(_ response: AFDownloadResponse<Data>) -> NKError {
230+
// 1) Cancellations take precedence
231+
if let afError = response.error?.asAFError,
232+
afError.isExplicitlyCancelledError {
233+
return .cancelled
234+
}
235+
236+
// 2) Prefer HTTP status code when available
237+
if let code = response.response?.statusCode {
238+
if (200...299).contains(code) {
239+
return .success
240+
}
241+
// Non-2xx: let the error flow below, even if serializer said "success".
242+
}
243+
244+
// 3) Fall back to Alamofire's result.
245+
// This covers transport errors, missing status code, file move errors,
246+
// serializer errors, and other download-specific failures.
247+
switch response.result {
248+
case .success:
249+
return .success
250+
251+
case .failure(let error):
252+
return NKError(
253+
error: error,
254+
afResponse: response,
255+
responseData: nil
256+
)
257+
}
258+
}
223259
}

0 commit comments

Comments
 (0)