Skip to content

Commit 933ef9b

Browse files
committed
Fixed a truncated uploaded file
1 parent 3f59178 commit 933ef9b

File tree

2 files changed

+132
-62
lines changed

2 files changed

+132
-62
lines changed

Sources/FPSStreamTask.swift

+18-7
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,9 @@ public class FileProviderStreamTask: URLSessionTask, StreamDelegate {
522522
}
523523

524524
guard outputStream != nil else {
525+
if self.state == .canceling || self.state == .completed {
526+
completionHandler(URLError(.cancelled))
527+
}
525528
return
526529
}
527530
if data.count > 4096 {
@@ -613,8 +616,10 @@ public class FileProviderStreamTask: URLSessionTask, StreamDelegate {
613616
return
614617
}
615618
dispatch_queue.async {
616-
while inputStream.streamStatus != .atEnd {
617-
Thread.sleep(forTimeInterval: 0.1)
619+
if !self.isUploadTask {
620+
while inputStream.streamStatus != .atEnd {
621+
Thread.sleep(forTimeInterval: 0.1)
622+
}
618623
}
619624
inputStream.close()
620625
self.streamDelegate?.urlSession?(self._underlyingSession, readClosedFor: self)
@@ -679,6 +684,16 @@ public class FileProviderStreamTask: URLSessionTask, StreamDelegate {
679684

680685
inputStream.setProperty(StreamSocketSecurityLevel.none.rawValue, forKey: .socketSecurityLevelKey)
681686
outputStream.setProperty(StreamSocketSecurityLevel.none.rawValue, forKey: .socketSecurityLevelKey)
687+
688+
if let sslContext = inputStream.property(forKey: kCFStreamPropertySSLContext as Stream.PropertyKey) {
689+
while SSLClose(sslContext as! SSLContext) == errSSLWouldBlock {
690+
guard inputStream.streamError == nil || inputStream.streamError?._code == Int(errSSLClosedGraceful),
691+
outputStream.streamError == nil || outputStream.streamError?._code == Int(errSSLClosedGraceful) else {
692+
break
693+
}
694+
Thread.sleep(forTimeInterval: 0.1)
695+
}
696+
}
682697
}
683698
}
684699
}
@@ -757,6 +772,7 @@ extension FileProviderStreamTask {
757772

758773
if close {
759774
DispatchQueue.main.sync {
775+
outputStream.close()
760776
shouldCloseWrite = true
761777
}
762778
self.streamDelegate?.urlSession?(self._underlyingSession, writeClosedFor: self)
@@ -772,11 +788,6 @@ extension FileProviderStreamTask {
772788
inputStream?.remove(from: RunLoop.main, forMode: .init("kCFRunLoopDefaultMode"))
773789
inputStream = nil
774790

775-
// TOFIX: This sleep is a workaround for truncated file uploading
776-
if isUploadTask {
777-
Thread.sleep(forTimeInterval: _underlyingSession.configuration.timeoutIntervalForRequest * 2)
778-
}
779-
780791
outputStream?.close()
781792
outputStream?.remove(from: RunLoop.main, forMode: .init("kCFRunLoopDefaultMode"))
782793
outputStream = nil

Sources/FTPHelper.swift

+114-55
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,35 @@ internal extension FTPFileProvider {
1919
completionHandler(nil, error)
2020
return
2121
}
22-
afterSend?(error)
2322

24-
if task.state == .suspended {
25-
task.resume()
23+
self.readData(on: task, minLength: minLength, maxLength: 4096, timeout: timeout, afterSend: afterSend, completionHandler: completionHandler)
24+
}
25+
}
26+
27+
func readData(on task: FileProviderStreamTask,
28+
minLength: Int = 4, maxLength: Int = 4096, timeout: TimeInterval,
29+
afterSend: ((_ error: Error?) -> Void)? = nil,
30+
completionHandler: @escaping (_ response: String?, _ error: Error?) -> Void) {
31+
task.readData(ofMinLength: minLength, maxLength: maxLength, timeout: timeout) { (data, eof, error) in
32+
if let error = error {
33+
completionHandler(nil, error)
34+
return
2635
}
2736

28-
task.readData(ofMinLength: minLength, maxLength: 4096, timeout: timeout) { (data, eof, error) in
29-
if let error = error {
30-
completionHandler(nil, error)
37+
if let data = data, let response = String(data: data, encoding: .utf8) {
38+
let lines = response.components(separatedBy: "\n").compactMap { $0.isEmpty ? nil : $0.trimmingCharacters(in: .whitespacesAndNewlines) }
39+
if let last = lines.last, last.hasPrefix("1") {
40+
// 1XX: Need to wait for some other response
41+
let timeout = self.session.configuration.timeoutIntervalForResource
42+
self.readData(on: task, minLength: minLength, maxLength: maxLength, timeout: timeout, afterSend: afterSend, completionHandler: completionHandler)
43+
44+
// Call afterSend
45+
afterSend?(error)
3146
return
3247
}
33-
34-
if let data = data, let response = String(data: data, encoding: .utf8) {
35-
completionHandler(response.trimmingCharacters(in: .whitespacesAndNewlines), nil)
36-
} else {
37-
completionHandler(nil, URLError(.cannotParseResponse, url: self.url(of: "")))
38-
}
48+
completionHandler(response.trimmingCharacters(in: .whitespacesAndNewlines), nil)
49+
} else {
50+
completionHandler(nil, URLError(.cannotParseResponse, url: self.url(of: "")))
3951
}
4052
}
4153
}
@@ -323,14 +335,28 @@ internal extension FTPFileProvider {
323335
let success_lock = NSLock()
324336
var success = false
325337
let command = useMLST ? "MLSD \(path)" : "LIST \(path)"
326-
self.execute(command: command, on: task, afterSend: { error in
327-
let timeout = self.session.configuration.timeoutIntervalForRequest
328-
var finalData = Data()
329-
var eof = false
330-
let error_lock = NSLock()
331-
var error: Error?
332-
338+
self.execute(command: command, on: task) { (response, error) in
333339
do {
340+
if let error = error {
341+
throw error
342+
}
343+
344+
guard let response = response else {
345+
throw URLError(.cannotParseResponse, url: self.url(of: path))
346+
}
347+
348+
if response.hasPrefix("500") && useMLST {
349+
dataTask.cancel()
350+
self.supportsRFC3659 = false
351+
throw URLError(.unsupportedURL, url: self.url(of: path))
352+
}
353+
354+
let timeout = self.session.configuration.timeoutIntervalForRequest
355+
var finalData = Data()
356+
var eof = false
357+
let error_lock = NSLock()
358+
var error: Error?
359+
334360
while !eof {
335361
let group = DispatchGroup()
336362
group.enter()
@@ -361,34 +387,16 @@ internal extension FTPFileProvider {
361387
}
362388
}
363389

364-
guard let response = String(data: finalData, encoding: .utf8) else {
390+
guard let dataResponse = String(data: finalData, encoding: .utf8) else {
365391
throw URLError(.badServerResponse, url: self.url(of: path))
366392
}
367393

368-
let contents: [String] = response.components(separatedBy: "\n")
394+
let contents: [String] = dataResponse.components(separatedBy: "\n")
369395
.compactMap({ $0.trimmingCharacters(in: .whitespacesAndNewlines) })
370396
success_lock.try()
371397
success = true
372398
success_lock.unlock()
373399
completionHandler(contents, nil)
374-
} catch {
375-
completionHandler([], error)
376-
}
377-
}) { (response, error) in
378-
do {
379-
if let error = error {
380-
throw error
381-
}
382-
383-
guard let response = response else {
384-
throw URLError(.cannotParseResponse, url: self.url(of: path))
385-
}
386-
387-
if response.hasPrefix("500") && useMLST {
388-
dataTask.cancel()
389-
self.supportsRFC3659 = false
390-
throw URLError(.unsupportedURL, url: self.url(of: path))
391-
}
392400

393401
success_lock.try()
394402
if !success && !(response.hasPrefix("25") || response.hasPrefix("15")) {
@@ -480,7 +488,7 @@ internal extension FTPFileProvider {
480488
}
481489

482490
// Send retreive command
483-
self.execute(command: "TYPE I" + "\r\n" + "REST \(position)" + "\r\n" + "RETR \(filePath)", on: task, afterSend: { error in
491+
self.execute(command: "TYPE I" + "\r\n" + "REST \(position)" + "\r\n" + "RETR \(filePath)", on: task) { (response, error) in
484492
// starting passive task
485493
onTask?(dataTask)
486494

@@ -549,7 +557,7 @@ internal extension FTPFileProvider {
549557
}
550558

551559
completionHandler?(nil)
552-
}) { (response, error) in
560+
553561
do {
554562
if let error = error {
555563
throw error
@@ -695,6 +703,20 @@ internal extension FTPFileProvider {
695703
}
696704
let success_lock = NSLock()
697705
var success = false
706+
707+
let completed_lock = NSLock()
708+
var completed = false
709+
func completionOnce(completion: () -> ()) {
710+
completed_lock.lock()
711+
guard !completed else {
712+
completed_lock.unlock()
713+
return
714+
}
715+
completion()
716+
completed = true
717+
completed_lock.unlock()
718+
}
719+
698720
self.execute(command: "STOR \(filePath)", on: task, afterSend: { error in
699721
onTask?(dataTask)
700722

@@ -708,14 +730,20 @@ internal extension FTPFileProvider {
708730
stream.open()
709731

710732
repeat {
733+
guard !completed else {
734+
return
735+
}
736+
711737
lock.lock()
712738
var subdata = Data.init(count: chunkSize)
713739
let count = subdata.withUnsafeMutableBytes { buffer in
714740
stream.read(buffer, maxLength: chunkSize)
715741
}
716742
if count < 0 {
717743
lock.unlock()
718-
completionHandler(stream.streamError ?? URLError(.requestBodyStreamExhausted, url: self.url(of: filePath)))
744+
completionOnce {
745+
completionHandler(stream.streamError ?? URLError(.requestBodyStreamExhausted, url: self.url(of: filePath)))
746+
}
719747
return
720748
}
721749
subdata.count = count
@@ -741,15 +769,20 @@ internal extension FTPFileProvider {
741769
let waitResult = group.wait(timeout: .now() + timeout)
742770

743771
lock.lock()
772+
744773
if let error = error {
745774
lock.unlock()
746-
completionHandler(error)
775+
completionOnce {
776+
completionHandler(error)
777+
}
747778
return
748779
}
749780

750781
if waitResult == .timedOut {
751782
lock.unlock()
752-
completionHandler(URLError(.timedOut, url: self.url(of: filePath)))
783+
completionOnce {
784+
completionHandler(URLError(.timedOut, url: self.url(of: filePath)))
785+
}
753786
return
754787
}
755788
lock.unlock()
@@ -758,15 +791,14 @@ internal extension FTPFileProvider {
758791
success_lock.lock()
759792
success = true
760793
success_lock.unlock()
761-
completionHandler(nil)
762-
}) { (response, error) in
763-
success_lock.lock()
764-
guard success else {
765-
success_lock.unlock()
766-
return
767-
}
768-
success_lock.unlock()
769794

795+
if self.securedDataConnection {
796+
dataTask.stopSecureConnection()
797+
}
798+
// TOFIX: Close read/write stream for receive a FTP response from the server
799+
dataTask.closeRead()
800+
dataTask.closeWrite()
801+
}) { (response, error) in
770802
do {
771803
if let error = error {
772804
throw error
@@ -776,11 +808,38 @@ internal extension FTPFileProvider {
776808
throw URLError(.cannotParseResponse, url: self.url(of: filePath))
777809
}
778810

779-
if !(response.hasPrefix("1") || response.hasPrefix("2")) {
780-
throw FileProviderFTPError(message: response)
811+
let lines = response.components(separatedBy: "\n").compactMap { $0.isEmpty ? nil : $0.trimmingCharacters(in: .whitespacesAndNewlines) }
812+
if lines.count > 0 {
813+
for line in lines {
814+
if !(line.hasPrefix("1") || line.hasPrefix("2")) {
815+
// FTP Error Response
816+
throw FileProviderFTPError(message: response)
817+
}
818+
}
819+
}
820+
821+
success_lock.lock()
822+
if success, let last = lines.last, last.hasPrefix("2") {
823+
success_lock.unlock()
824+
// File successfully transferred.
825+
completionOnce {
826+
completionHandler(nil)
827+
}
828+
return
829+
} else {
830+
success_lock.unlock()
831+
throw URLError(.cannotCreateFile, url: self.url(of: filePath))
781832
}
782833
} catch {
783-
completionHandler(error)
834+
success_lock.lock()
835+
if !success {
836+
dataTask.cancel()
837+
}
838+
success_lock.unlock()
839+
840+
completionOnce {
841+
completionHandler(error)
842+
}
784843
}
785844
}
786845
}

0 commit comments

Comments
 (0)