提交 7a6017de 编写于 作者: C Christian Noon

Added Timeline struct to capture timings through the lifecycle of a Request.

上级 8b618c76
......@@ -32,6 +32,10 @@
4C341BBB1B1A865A00C1B34D /* CacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C341BB91B1A865A00C1B34D /* CacheTests.swift */; };
4C4CBE7B1BAF700C0024D659 /* String+AlamofireTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4CBE7A1BAF700C0024D659 /* String+AlamofireTests.swift */; };
4C4CBE7C1BAF700C0024D659 /* String+AlamofireTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4CBE7A1BAF700C0024D659 /* String+AlamofireTests.swift */; };
4C574E6A1C67D207000B3128 /* Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C574E691C67D207000B3128 /* Timeline.swift */; };
4C574E6B1C67D207000B3128 /* Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C574E691C67D207000B3128 /* Timeline.swift */; };
4C574E6C1C67D207000B3128 /* Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C574E691C67D207000B3128 /* Timeline.swift */; };
4C574E6D1C67D207000B3128 /* Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C574E691C67D207000B3128 /* Timeline.swift */; };
4C743CF61C22772D00BCB23E /* certDER.cer in Resources */ = {isa = PBXBuildFile; fileRef = B39E2F831C1A72F8002DA1A9 /* certDER.cer */; };
4C743CF71C22772D00BCB23E /* certDER.crt in Resources */ = {isa = PBXBuildFile; fileRef = B39E2F841C1A72F8002DA1A9 /* certDER.crt */; };
4C743CF81C22772D00BCB23E /* certDER.der in Resources */ = {isa = PBXBuildFile; fileRef = B39E2F851C1A72F8002DA1A9 /* certDER.der */; };
......@@ -240,6 +244,7 @@
4C33A1421B52089C00873DFF /* ServerTrustPolicyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerTrustPolicyTests.swift; sourceTree = "<group>"; };
4C341BB91B1A865A00C1B34D /* CacheTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacheTests.swift; sourceTree = "<group>"; };
4C4CBE7A1BAF700C0024D659 /* String+AlamofireTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+AlamofireTests.swift"; sourceTree = "<group>"; };
4C574E691C67D207000B3128 /* Timeline.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Timeline.swift; sourceTree = "<group>"; };
4C7C8D211B9D0D9000948136 /* NSURLSessionConfiguration+AlamofireTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSURLSessionConfiguration+AlamofireTests.swift"; sourceTree = "<group>"; };
4C811F8C1B51856D00E0F59A /* ServerTrustPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerTrustPolicy.swift; sourceTree = "<group>"; };
4C812C3A1B535F220017E0BF /* alamofire-root-ca.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = "alamofire-root-ca.cer"; path = "alamofire.org/alamofire-root-ca.cer"; sourceTree = "<group>"; };
......@@ -513,6 +518,7 @@
4CDE2C451AF89FF300BABAE5 /* ResponseSerialization.swift */,
4C811F8C1B51856D00E0F59A /* ServerTrustPolicy.swift */,
4C83F41A1B749E0E00203445 /* Stream.swift */,
4C574E691C67D207000B3128 /* Timeline.swift */,
4CDE2C3F1AF89E0700BABAE5 /* Upload.swift */,
4CDE2C421AF89F0900BABAE5 /* Validation.swift */,
);
......@@ -945,6 +951,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4C574E6C1C67D207000B3128 /* Timeline.swift in Sources */,
4CF627121BA7CBF60011A099 /* Upload.swift in Sources */,
4CF627111BA7CBF60011A099 /* Stream.swift in Sources */,
4CF6270C1BA7CBF60011A099 /* Result.swift in Sources */,
......@@ -992,6 +999,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4C574E6B1C67D207000B3128 /* Timeline.swift in Sources */,
4CDE2C411AF89E0700BABAE5 /* Upload.swift in Sources */,
4CE272501AF88FB500F1D59A /* ParameterEncoding.swift in Sources */,
4CDE2C3B1AF899EC00BABAE5 /* Request.swift in Sources */,
......@@ -1014,6 +1022,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4C574E6D1C67D207000B3128 /* Timeline.swift in Sources */,
E4202FCF1B667AA100C997FB /* Upload.swift in Sources */,
E4202FD01B667AA100C997FB /* ParameterEncoding.swift in Sources */,
E4202FD11B667AA100C997FB /* Request.swift in Sources */,
......@@ -1036,6 +1045,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4C574E6A1C67D207000B3128 /* Timeline.swift in Sources */,
4CDE2C401AF89E0700BABAE5 /* Upload.swift in Sources */,
4CE2724F1AF88FB500F1D59A /* ParameterEncoding.swift in Sources */,
4CDE2C3A1AF899EC00BABAE5 /* Request.swift in Sources */,
......
......@@ -211,6 +211,8 @@ extension Request {
totalBytesWritten: Int64,
totalBytesExpectedToWrite: Int64)
{
if initialResponseTime == nil { initialResponseTime = NSDate() }
if let downloadTaskDidWriteData = downloadTaskDidWriteData {
downloadTaskDidWriteData(
session,
......
......@@ -48,6 +48,9 @@ public class Request {
/// The progress of the request lifecycle.
public var progress: NSProgress { return delegate.progress }
let startTime: NSDate
var endTime: NSDate?
// MARK: - Lifecycle
init(session: NSURLSession, task: NSURLSessionTask) {
......@@ -55,14 +58,18 @@ public class Request {
switch task {
case is NSURLSessionUploadTask:
self.delegate = UploadTaskDelegate(task: task)
delegate = UploadTaskDelegate(task: task)
case is NSURLSessionDataTask:
self.delegate = DataTaskDelegate(task: task)
delegate = DataTaskDelegate(task: task)
case is NSURLSessionDownloadTask:
self.delegate = DownloadTaskDelegate(task: task)
delegate = DownloadTaskDelegate(task: task)
default:
self.delegate = TaskDelegate(task: task)
delegate = TaskDelegate(task: task)
}
startTime = NSDate()
delegate.queue.addOperationWithBlock { self.endTime = NSDate() }
}
// MARK: - Authentication
......@@ -199,6 +206,7 @@ public class Request {
var data: NSData? { return nil }
var error: NSError?
var initialResponseTime: NSDate?
var credential: NSURLCredential?
init(task: NSURLSessionTask) {
......@@ -385,6 +393,8 @@ public class Request {
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
if initialResponseTime == nil { initialResponseTime = NSDate() }
if let dataTaskDidReceiveData = dataTaskDidReceiveData {
dataTaskDidReceiveData(session, dataTask, data)
} else {
......
......@@ -36,6 +36,9 @@ public struct Response<Value, Error: ErrorType> {
/// The result of response serialization.
public let result: Result<Value, Error>
/// The timeline of the complete lifecycle of the `Request`.
public let timeline: Timeline
/**
Initializes the `Response` instance with the specified URL request, URL response, server data and response
serialization result.
......@@ -44,14 +47,22 @@ public struct Response<Value, Error: ErrorType> {
- parameter response: The server's response to the URL request.
- parameter data: The data returned by the server.
- parameter result: The result of response serialization.
- parameter timeline: The timeline of the complete lifecycle of the `Request`.
- returns: the new `Response` instance.
*/
public init(request: NSURLRequest?, response: NSHTTPURLResponse?, data: NSData?, result: Result<Value, Error>) {
public init(
request: NSURLRequest?,
response: NSHTTPURLResponse?,
data: NSData?,
result: Result<Value, Error>,
timeline: Timeline)
{
self.request = request
self.response = response
self.data = data
self.result = result
self.timeline = timeline
}
}
......@@ -77,6 +88,7 @@ extension Response: CustomDebugStringConvertible {
output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil")
output.append("[Data]: \(data?.length ?? 0) bytes")
output.append("[Result]: \(result.debugDescription)")
output.append("[Timeline]: \(timeline.debugDescription)")
return output.joinWithSeparator("\n")
}
......
......@@ -119,16 +119,25 @@ extension Request {
self.delegate.error
)
dispatch_async(queue ?? dispatch_get_main_queue()) {
let response = Response<T.SerializedObject, T.ErrorObject>(
request: self.request,
response: self.response,
data: self.delegate.data,
result: result
)
let requestCompletedTime = self.endTime ?? NSDate()
let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime
let timeline = Timeline(
requestStartTime: self.startTime,
initialResponseTime: initialResponseTime,
requestCompletedTime: requestCompletedTime,
serializationCompletedTime: NSDate()
)
completionHandler(response)
}
let response = Response<T.SerializedObject, T.ErrorObject>(
request: self.request,
response: self.response,
data: self.delegate.data,
result: result,
timeline: timeline
)
dispatch_async(queue ?? dispatch_get_main_queue()) { completionHandler(response) }
}
return self
......
// Timeline.swift
//
// Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
/// Responsible for computing the timing metrics for the complete lifecycle of a `Request`.
public struct Timeline {
/// The time the request was initialized.
public let requestStartTime: NSDate
/// The time the first bytes were received from or sent to the server.
public let initialResponseTime: NSDate
/// The time when the request was completed.
public let requestCompletedTime: NSDate
/// The time when the response serialization was completed.
public let serializationCompletedTime: NSDate
/// The time interval in seconds from the time the request started to the initial response from the server.
public let latency: NSTimeInterval
/// The time interval in seconds from the time the request started to the time the request completed.
public let requestDuration: NSTimeInterval
/// The time interval in seconds from the time the request started to the time response serialization completed.
public let totalDuration: NSTimeInterval
init(
requestStartTime: NSDate,
initialResponseTime: NSDate,
requestCompletedTime: NSDate,
serializationCompletedTime: NSDate)
{
self.requestStartTime = requestStartTime
self.initialResponseTime = initialResponseTime
self.requestCompletedTime = requestCompletedTime
self.serializationCompletedTime = serializationCompletedTime
self.latency = initialResponseTime.timeIntervalSinceDate(requestStartTime)
self.requestDuration = requestCompletedTime.timeIntervalSinceDate(requestStartTime)
self.totalDuration = serializationCompletedTime.timeIntervalSinceDate(requestStartTime)
}
}
// MARK: - CustomStringConvertible
extension Timeline: CustomStringConvertible {
/// The textual representation used when written to an output stream, which includes the latency, the request
/// duration and the total duration.
public var description: String {
let latency = String(format: "%.3f", self.latency)
let requestDuration = String(format: "%.3f", self.requestDuration)
let totalDuration = String(format: "%.3f", self.totalDuration)
let timings = [
"\"Latency\": \(latency) secs",
"\"Request Duration\": \(requestDuration) secs",
"\"Total Duration\": \(totalDuration) secs"
]
return "Timeline: { \(timings.joinWithSeparator(", ")) }"
}
}
// MARK: - CustomDebugStringConvertible
extension Timeline: CustomDebugStringConvertible {
/// The textual representation used when written to an output stream, which includes the request start time, the
/// initial response time, the request completed time, the serialization completed time, the latency, the request
/// duration and the total duration.
public var debugDescription: String {
let timings = [
"\"Request Start Time\": \(requestStartTime.timeIntervalSince1970)",
"\"Initial Response Time\": \(initialResponseTime.timeIntervalSince1970)",
"\"Request Completed Time\": \(requestCompletedTime.timeIntervalSince1970)",
"\"Serialization Completed Time\": \(serializationCompletedTime.timeIntervalSince1970)",
"\"Latency\": \(latency) secs",
"\"Request Duration\": \(requestDuration) secs",
"\"Total Duration\": \(totalDuration) secs"
]
return "Timeline: { \(timings.joinWithSeparator(", ")) }"
}
}
......@@ -359,6 +359,8 @@ extension Request {
totalBytesSent: Int64,
totalBytesExpectedToSend: Int64)
{
if initialResponseTime == nil { initialResponseTime = NSDate() }
if let taskDidSendBodyData = taskDidSendBodyData {
taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
} else {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册