Skip to content

Use URLSessionTask.count* properties to track upload/download progress #1138

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Foundation/NSURLSession/NSURLSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ fileprivate func nextSessionIdentifier() -> Int32 {
sessionCounter += 1
return sessionCounter
}
public let URLSessionTransferSizeUnknown: Int64 = -1
public let NSURLSessionTransferSizeUnknown: Int64 = -1

open class URLSession : NSObject {
fileprivate let _configuration: _Configuration
Expand Down
8 changes: 4 additions & 4 deletions Foundation/NSURLSession/NSURLSessionTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ open class URLSessionTask : NSObject, NSCopying {
*/

/// Number of body bytes already received
open fileprivate(set) var countOfBytesReceived: Int64 {
open internal(set) var countOfBytesReceived: Int64 {
get {
semaphore.wait()
defer {
Expand All @@ -160,7 +160,7 @@ open class URLSessionTask : NSObject, NSCopying {
fileprivate var _countOfBytesReceived: Int64 = 0

/// Number of body bytes already sent */
open fileprivate(set) var countOfBytesSent: Int64 {
open internal(set) var countOfBytesSent: Int64 {
get {
semaphore.wait()
defer {
Expand All @@ -178,10 +178,10 @@ open class URLSessionTask : NSObject, NSCopying {
fileprivate var _countOfBytesSent: Int64 = 0

/// Number of body bytes we expect to send, derived from the Content-Length of the HTTP request */
open fileprivate(set) var countOfBytesExpectedToSend: Int64 = 0
open internal(set) var countOfBytesExpectedToSend: Int64 = 0

/// Number of byte bytes we expect to receive, usually derived from the Content-Length header of an HTTP response. */
open fileprivate(set) var countOfBytesExpectedToReceive: Int64 = 0
open internal(set) var countOfBytesExpectedToReceive: Int64 = 0

/// The taskDescription property is available for the developer to
/// provide a descriptive label for the task.
Expand Down
10 changes: 4 additions & 6 deletions Foundation/NSURLSession/http/EasyHandle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ internal final class _EasyHandle {
weak var delegate: _EasyHandleDelegate?
fileprivate var headerList: _CurlStringList?
fileprivate var pauseState: _PauseState = []
internal var fileLength: Int64 = 0
internal var timeoutTimer: _TimeoutSource!
#if os(Android)
static fileprivate var _CAInfoFile: UnsafeMutablePointer<Int8>?
Expand Down Expand Up @@ -100,7 +99,7 @@ internal protocol _EasyHandleDelegate: class {
func didReceive(data: Data) -> _EasyHandle._Action
/// Handle header data read from the network.
/// - returns: the action to be taken: abort, proceed, or pause.
func didReceive(headerData data: Data) -> _EasyHandle._Action
func didReceive(headerData data: Data, contentLength: Int64) -> _EasyHandle._Action
/// Fill a buffer with data to be sent.
///
/// - parameter data: The buffer to fill
Expand Down Expand Up @@ -454,7 +453,7 @@ fileprivate extension _EasyHandle {
}
var length = Double()
try! CFURLSession_easy_getinfo_double(handle.rawHandle, CFURLSessionInfoCONTENT_LENGTH_DOWNLOAD, &length).asError()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an aside... This API returns the ContentLength as a Double, which seems crazy. There is a newer API which has a better return type. It was introduced in libcurl 7.55.0; could we switch to it instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess Ubuntu 16.04 comes with a default libcurl 7.47. I am not sure if we should be using an API that is newer than 7.47.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't realise that we rely on the system libcurl. Ubuntu 14.04 LTS ships 7.35 which is even older 😢

return handle.didReceive(headerData: data, size: size, nmemb: nmemb, fileLength: length)
return handle.didReceive(headerData: data, size: size, nmemb: nmemb, contentLength: length)
}.asError()

// socket options
Expand Down Expand Up @@ -513,11 +512,10 @@ fileprivate extension _EasyHandle {
/// data.
///
/// - SeeAlso: <https://curl.haxx.se/libcurl/c/CURLOPT_HEADERFUNCTION.html>
func didReceive(headerData data: UnsafeMutablePointer<Int8>, size: Int, nmemb: Int, fileLength: Double) -> Int {
self.fileLength = Int64(fileLength)
func didReceive(headerData data: UnsafeMutablePointer<Int8>, size: Int, nmemb: Int, contentLength: Double) -> Int {
let d: Int = {
let buffer = Data(bytes: data, count: size*nmemb)
switch delegate?.didReceive(headerData: buffer) {
switch delegate?.didReceive(headerData: buffer, contentLength: Int64(contentLength)) {
case .some(.proceed): return size * nmemb
case .some(.abort): return 0
case .some(.pause):
Expand Down
30 changes: 15 additions & 15 deletions Foundation/NSURLSession/http/HTTPURLProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ import Dispatch
internal class _HTTPURLProtocol: URLProtocol {

fileprivate var easyHandle: _EasyHandle!
fileprivate var totalDownloaded = 0
fileprivate var totalUploaded: Int64 = 0
fileprivate var requestBodyLength: Int64 = 0
fileprivate var tempFileURL: URL

public required init(task: URLSessionTask, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) {
Expand Down Expand Up @@ -129,7 +126,7 @@ fileprivate extension _HTTPURLProtocol {
set(requestBodyLength: .noBody)
case (_, .some(let length)):
set(requestBodyLength: .length(length))
requestBodyLength = Int64(length)
task!.countOfBytesExpectedToSend = Int64(length)
case (_, .none):
set(requestBodyLength: .unknown)
}
Expand Down Expand Up @@ -469,21 +466,23 @@ extension _HTTPURLProtocol: _EasyHandleDelegate {
let fileHandle = try! FileHandle(forWritingTo: self.tempFileURL)
_ = fileHandle.seekToEndOfFile()
fileHandle.write(data)
self.totalDownloaded += data.count
task.countOfBytesReceived += Int64(data.count)

s.delegateQueue.addOperation {
downloadDelegate.urlSession(s, downloadTask: task, didWriteData: Int64(data.count), totalBytesWritten: Int64(self.totalDownloaded),
totalBytesExpectedToWrite: Int64(self.easyHandle.fileLength))
downloadDelegate.urlSession(s, downloadTask: task, didWriteData: Int64(data.count), totalBytesWritten: task.countOfBytesReceived,
totalBytesExpectedToWrite: task.countOfBytesExpectedToReceive)
}
if Int(self.easyHandle.fileLength) == self.totalDownloaded {
if task.countOfBytesExpectedToReceive == task.countOfBytesReceived {
fileHandle.closeFile()
self.properties[.temporaryFileURL] = self.tempFileURL
}
}
}

func didReceive(headerData data: Data) -> _EasyHandle._Action {
guard case .transferInProgress(let ts) = internalState else { fatalError("Received body data, but no transfer in progress.") }
func didReceive(headerData data: Data, contentLength: Int64) -> _EasyHandle._Action {
guard case .transferInProgress(let ts) = internalState else { fatalError("Received header data, but no transfer in progress.") }
guard let task = task else { fatalError("Received header data but no task available.") }
task.countOfBytesExpectedToReceive = contentLength > 0 ? contentLength : NSURLSessionTransferSizeUnknown
do {
let newTS = try ts.byAppending(headerLine: data)
internalState = .transferInProgress(newTS)
Expand All @@ -499,12 +498,13 @@ extension _HTTPURLProtocol: _EasyHandleDelegate {
}

fileprivate func notifyDelegate(aboutUploadedData count: Int64) {
let session = self.task?.session as! URLSession
guard case .taskDelegate(let delegate) = session.behaviour(for: self.task!), self.task is URLSessionUploadTask else { return }
totalUploaded += count
guard let task = self.task as? URLSessionUploadTask,
let session = self.task?.session as? URLSession,
case .taskDelegate(let delegate) = session.behaviour(for: task) else { return }
task.countOfBytesSent += count
session.delegateQueue.addOperation {
delegate.urlSession(session, task: self.task!, didSendBodyData: count,
totalBytesSent: self.totalUploaded, totalBytesExpectedToSend: self.requestBodyLength)
delegate.urlSession(session, task: task, didSendBodyData: count,
totalBytesSent: task.countOfBytesSent, totalBytesExpectedToSend: task.countOfBytesExpectedToSend)
}
}

Expand Down