From 4b1eccfa2701847712b97562fae0b6f8a3ea10db Mon Sep 17 00:00:00 2001 From: Jon Shier Date: Sun, 27 Sep 2020 19:53:20 -0400 Subject: [PATCH 1/2] Add URLSessionTaskMetrics and associated types. --- Foundation.xcodeproj/project.pbxproj | 4 + Sources/FoundationNetworking/CMakeLists.txt | 1 + .../URLSession/URLSessionDelegate.swift | 4 + .../URLSession/URLSessionTaskMetrics.swift | 129 ++++++++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 Sources/FoundationNetworking/URLSession/URLSessionTaskMetrics.swift diff --git a/Foundation.xcodeproj/project.pbxproj b/Foundation.xcodeproj/project.pbxproj index 2e1860c400..b4b2299f71 100644 --- a/Foundation.xcodeproj/project.pbxproj +++ b/Foundation.xcodeproj/project.pbxproj @@ -81,6 +81,7 @@ 231503DB1D8AEE5D0061694D /* TestDecimal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231503DA1D8AEE5D0061694D /* TestDecimal.swift */; }; 294E3C1D1CC5E19300E4F44C /* TestNSAttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */; }; 2EBE67A51C77BF0E006583D5 /* TestDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBE67A31C77BF05006583D5 /* TestDateFormatter.swift */; }; + 316577C425214A8400492943 /* URLSessionTaskMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 316577C325214A8400492943 /* URLSessionTaskMetrics.swift */; }; 3E55A2331F52463B00082000 /* TestUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E55A2321F52463B00082000 /* TestUnit.swift */; }; 3EA9D6701EF0532D00B362D6 /* TestJSONEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA9D66F1EF0532D00B362D6 /* TestJSONEncoder.swift */; }; 3EDCE50C1EF04D8100C2EC04 /* Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EDCE5051EF04D8100C2EC04 /* Codable.swift */; }; @@ -792,6 +793,7 @@ 231503DA1D8AEE5D0061694D /* TestDecimal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDecimal.swift; sourceTree = ""; }; 294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSAttributedString.swift; sourceTree = ""; }; 2EBE67A31C77BF05006583D5 /* TestDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDateFormatter.swift; sourceTree = ""; }; + 316577C325214A8400492943 /* URLSessionTaskMetrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = URLSessionTaskMetrics.swift; path = URLSession/URLSessionTaskMetrics.swift; sourceTree = ""; }; 3E55A2321F52463B00082000 /* TestUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUnit.swift; sourceTree = ""; }; 3EA9D66F1EF0532D00B362D6 /* TestJSONEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestJSONEncoder.swift; sourceTree = ""; }; 3EDCE5051EF04D8100C2EC04 /* Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Codable.swift; sourceTree = ""; }; @@ -2193,6 +2195,7 @@ F023075823F0B7AC0023DBEC /* URLSessionConfiguration.swift */, F023075623F0B7AC0023DBEC /* URLSessionDelegate.swift */, F023075B23F0B7AD0023DBEC /* URLSessionTask.swift */, + 316577C325214A8400492943 /* URLSessionTaskMetrics.swift */, ); name = URLSession; sourceTree = ""; @@ -2796,6 +2799,7 @@ F023076823F0B8740023DBEC /* Boxing.swift in Sources */, F023073123F0B6E20023DBEC /* BodySource.swift in Sources */, F023075E23F0B7AD0023DBEC /* NetworkingSpecific.swift in Sources */, + 316577C425214A8400492943 /* URLSessionTaskMetrics.swift in Sources */, F023073423F0B6F10023DBEC /* FTPURLProtocol.swift in Sources */, F023073E23F0B7100023DBEC /* MultiHandle.swift in Sources */, 15B803E6228F376000B30FF6 /* NSURLRequest.swift in Sources */, diff --git a/Sources/FoundationNetworking/CMakeLists.txt b/Sources/FoundationNetworking/CMakeLists.txt index 2a571f4a24..164ef63b91 100644 --- a/Sources/FoundationNetworking/CMakeLists.txt +++ b/Sources/FoundationNetworking/CMakeLists.txt @@ -45,6 +45,7 @@ add_library(FoundationNetworking URLSession/URLSessionConfiguration.swift URLSession/URLSessionDelegate.swift URLSession/URLSessionTask.swift + URLSession/URLSessionTaskMetrics.swift URLSession/libcurl/EasyHandle.swift URLSession/libcurl/MultiHandle.swift URLSession/libcurl/libcurlHelpers.swift) diff --git a/Sources/FoundationNetworking/URLSession/URLSessionDelegate.swift b/Sources/FoundationNetworking/URLSession/URLSessionDelegate.swift index d1985b5a82..a03dcea3ad 100644 --- a/Sources/FoundationNetworking/URLSession/URLSessionDelegate.swift +++ b/Sources/FoundationNetworking/URLSession/URLSessionDelegate.swift @@ -128,6 +128,8 @@ public protocol URLSessionTaskDelegate : URLSessionDelegate { func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) func urlSession(_ session: URLSession, task: URLSessionTask, willBeginDelayedRequest request: URLRequest, completionHandler: @escaping (URLSession.DelayedRequestDisposition, URLRequest?) -> Void) + + func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) } extension URLSessionTaskDelegate { @@ -148,6 +150,8 @@ extension URLSessionTaskDelegate { public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { } public func urlSession(_ session: URLSession, task: URLSessionTask, willBeginDelayedRequest request: URLRequest, completionHandler: @escaping (URLSession.DelayedRequestDisposition, URLRequest?) -> Void) { } + + public func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { } } /* diff --git a/Sources/FoundationNetworking/URLSession/URLSessionTaskMetrics.swift b/Sources/FoundationNetworking/URLSession/URLSessionTaskMetrics.swift new file mode 100644 index 0000000000..035a5d802b --- /dev/null +++ b/Sources/FoundationNetworking/URLSession/URLSessionTaskMetrics.swift @@ -0,0 +1,129 @@ +// Foundation/URLSession/URLSessionTaskMetrics.swift - URLSession API +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +// ----------------------------------------------------------------------------- +/// +/// URLSession API code. +/// - SeeAlso: URLSession.swift +/// +// ----------------------------------------------------------------------------- + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +import SwiftFoundation +#else +import Foundation +#endif +@_implementationOnly import CoreFoundation + +open class URLSessionTaskMetrics : NSObject { + public internal(set) var transactionMetrics: [URLSessionTaskTransactionMetrics] = [] + public internal(set) var taskInterval: DateInterval = .init() + public internal(set) var redirectCount = 0 + + public enum ResourceFetchType: Int { + case unknown = 0 + case networkLoad = 1 + case serverPush = 2 + case localCache = 3 + } + + public enum DomainResolutionProtocol: Int { + case unknown = 0 + case udp = 1 + case tcp = 2 + case tls = 3 + case https = 4 + } +} + +open class URLSessionTaskTransactionMetrics: NSObject { + public let request: URLRequest + public internal(set) var response: URLResponse? + + public internal(set) var fetchStartDate: Date? + public internal(set) var domainLookupStartDate: Date? + public internal(set) var domainLookupEndDate: Date? + public internal(set) var connectStartDate: Date? + public internal(set) var secureConnectionStartDate: Date? + public internal(set) var secureConnectionEndDate: Date? + public internal(set) var connectEndDate: Date? + public internal(set) var requestStartDate: Date? + public internal(set) var requestEndDate: Date? + public internal(set) var responseStartDate: Date? + public internal(set) var responseEndDate: Date? + + public internal(set) var countOfRequestBodyBytesBeforeEncoding: Int64 = 0 + public internal(set) var countOfRequestBodyBytesSent: Int64 = 0 + public internal(set) var countOfRequestHeaderBytesSent: Int64 = 0 + public internal(set) var countOfResponseBodyBytesAfterDecoding: Int64 = 0 + public internal(set) var countOfResponseBodyBytesReceived: Int64 = 0 + public internal(set) var countOfResponseHeaderBytesReceived: Int64 = 0 + + public internal(set) var networkProtocolName: String? + public internal(set) var remoteAddress: String? + public internal(set) var remotePort: String? + public internal(set) var localAddress: String? + public internal(set) var localPort: String? + public internal(set) var negotiatedTLSCipherSuite: tls_ciphersuite_t? + public internal(set) var negotiatedTLSProtocolVersion: tls_protocol_version_t? + public internal(set) var isCellular: Bool = false + public internal(set) var isExpensive: Bool = false + public internal(set) var isConstrained: Bool = false + public internal(set) var isProxyConnection: Bool = false + public internal(set) var isReusedConnection: Bool = false + public internal(set) var isMultipath: Bool = false + public internal(set) var resourceFetchType: URLSessionTaskMetrics.ResourceFetchType = .unknown + public internal(set) var domainResolutionProtocol: URLSessionTaskMetrics.DomainResolutionProtocol = .unknown + + init(request: URLRequest) { + self.request = request + } +} + +public enum tls_ciphersuite_t: UInt16 { + case AES_128_GCM_SHA256 = 4865 + case AES_256_GCM_SHA384 = 4866 + + case CHACHA20_POLY1305_SHA256 = 4867 + + case ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 49160 + case ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 49161 + case ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 49187 + case ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 49195 + case ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 49162 + case ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 49188 + case ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 49196 + case ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 52393 + case ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 49170 + case ECDHE_RSA_WITH_AES_128_CBC_SHA = 49171 + case ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 49191 + case ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 49199 + case ECDHE_RSA_WITH_AES_256_CBC_SHA = 49172 + case ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 49192 + case ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 49200 + case ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 52392 + + case RSA_WITH_3DES_EDE_CBC_SHA = 10 + case RSA_WITH_AES_128_CBC_SHA = 47 + case RSA_WITH_AES_128_CBC_SHA256 = 60 + case RSA_WITH_AES_128_GCM_SHA256 = 156 + case RSA_WITH_AES_256_CBC_SHA = 53 + case RSA_WITH_AES_256_CBC_SHA256 = 61 + case RSA_WITH_AES_256_GCM_SHA384 = 157 +} + +public enum tls_protocol_version_t: UInt16 { + case TLSv10 = 769 + case TLSv11 = 770 + case TLSv12 = 771 + case TLSv13 = 772 + case DTLSv10 = 65279 + case DTLSv12 = 65277 +} From 75bcd79a15e4234f3d4437c3338ab721b9a8f792 Mon Sep 17 00:00:00 2001 From: Jon Shier Date: Mon, 28 Sep 2020 17:59:19 -0400 Subject: [PATCH 2/2] Correct method indentation. --- .../URLSession/URLSessionDelegate.swift | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Sources/FoundationNetworking/URLSession/URLSessionDelegate.swift b/Sources/FoundationNetworking/URLSession/URLSessionDelegate.swift index a03dcea3ad..4cb7d41351 100644 --- a/Sources/FoundationNetworking/URLSession/URLSessionDelegate.swift +++ b/Sources/FoundationNetworking/URLSession/URLSessionDelegate.swift @@ -71,7 +71,7 @@ public protocol URLSessionDelegate : NSObjectProtocol { * behavior will be to use the default handling, which may involve user * interaction. */ - func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) + func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) } extension URLSessionDelegate { @@ -115,19 +115,19 @@ public protocol URLSessionTaskDelegate : URLSessionDelegate { * necessary when authentication has failed for any request that * involves a body stream. */ - func urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) + func urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) /* Sent periodically to notify the delegate of upload progress. This * information is also available as properties of the task. */ - func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) + func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) /* Sent as the last message related to a specific task. Error may be * nil, which implies that no error occurred and this task is complete. */ - func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) + func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) - func urlSession(_ session: URLSession, task: URLSessionTask, willBeginDelayedRequest request: URLRequest, completionHandler: @escaping (URLSession.DelayedRequestDisposition, URLRequest?) -> Void) + func urlSession(_ session: URLSession, task: URLSessionTask, willBeginDelayedRequest request: URLRequest, completionHandler: @escaping (URLSession.DelayedRequestDisposition, URLRequest?) -> Void) func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) } @@ -208,7 +208,7 @@ public protocol URLSessionDataDelegate : URLSessionTaskDelegate { * attempted for a given resource, and you should not rely on this * message to receive the resource data. */ - func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void) + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void) } @@ -236,17 +236,17 @@ public protocol URLSessionDownloadDelegate : URLSessionTaskDelegate { * removed when the delegate message returns. URLSession:task:didCompleteWithError: will * still be called. */ - func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) + func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) /* Sent periodically to notify the delegate of download progress. */ - func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) + func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) /* Sent when a download has been resumed. If a download failed with an * error, the -userInfo dictionary of the error will contain an * URLSessionDownloadTaskResumeData key, whose value is the resume * data. */ - func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) + func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) } extension URLSessionDownloadDelegate { @@ -263,13 +263,13 @@ public protocol URLSessionStreamDelegate : URLSessionTaskDelegate { * this delegate message is received, there may still be bytes * available. You only know that no more bytes are available when you * are able to read until EOF. */ - func urlSession(_ session: URLSession, readClosedFor streamTask: URLSessionStreamTask) + func urlSession(_ session: URLSession, readClosedFor streamTask: URLSessionStreamTask) /* Indicates that the write side of a connection has been closed. * Any outstanding writes complete, but future writes will immediately * fail. */ - func urlSession(_ session: URLSession, writeClosedFor streamTask: URLSessionStreamTask) + func urlSession(_ session: URLSession, writeClosedFor streamTask: URLSessionStreamTask) /* A notification that the system has determined that a better route * to the host has been detected (eg, a wi-fi interface becoming @@ -278,7 +278,7 @@ public protocol URLSessionStreamDelegate : URLSessionTaskDelegate { * there is no guarantee that the future task will be able to connect * to the host, so callers should should be prepared for failure of * reads and writes over any new interface. */ - func urlSession(_ session: URLSession, betterRouteDiscoveredFor streamTask: URLSessionStreamTask) + func urlSession(_ session: URLSession, betterRouteDiscoveredFor streamTask: URLSessionStreamTask) /* The given task has been completed, and unopened InputStream and * OutputStream objects are created from the underlying network @@ -286,7 +286,7 @@ public protocol URLSessionStreamDelegate : URLSessionTaskDelegate { * completed (including any necessary handshakes.) The streamTask * will not receive any further delegate messages. */ - func urlSession(_ session: URLSession, streamTask: URLSessionStreamTask, didBecome inputStream: InputStream, outputStream: OutputStream) + func urlSession(_ session: URLSession, streamTask: URLSessionStreamTask, didBecome inputStream: InputStream, outputStream: OutputStream) } extension URLSessionStreamDelegate {