Merge pull request #1045 from norio-nomura/change-rsync-option-on-Install

diff --git a/Foundation/Data.swift b/Foundation/Data.swift
index ccfd16f..b4d22c1 100644
--- a/Foundation/Data.swift
+++ b/Foundation/Data.swift
@@ -11,7 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #if DEPLOYMENT_RUNTIME_SWIFT
-
+    
 #if os(OSX) || os(iOS)
 import Darwin
 #elseif os(Linux)
@@ -19,24 +19,24 @@
 #endif
     
 import CoreFoundation
-
+    
 internal func __NSDataInvokeDeallocatorUnmap(_ mem: UnsafeMutableRawPointer, _ length: Int) {
     munmap(mem, length)
 }
-
+    
 internal func __NSDataInvokeDeallocatorFree(_ mem: UnsafeMutableRawPointer, _ length: Int) {
     free(mem)
 }
-
+    
 #else
-
+    
 @_exported import Foundation // Clang module
 import _SwiftFoundationOverlayShims
 import _SwiftCoreFoundationOverlayShims
-
+    
 @_silgen_name("__NSDataWriteToURL")
 internal func __NSDataWriteToURL(_ data: NSData, _ url: NSURL, _ options: UInt, _ error: NSErrorPointer) -> Bool
-
+    
 #endif
 
 public final class _DataStorage {
@@ -189,7 +189,7 @@
             d.enumerateBytes { (ptr, range, stop) in
                 var stopv = false
                 let bytePtr = ptr.bindMemory(to: UInt8.self, capacity: range.length)
-                block(UnsafeBufferPointer(start: bytePtr, count: range.length), range.length, &stopv)
+                block(UnsafeBufferPointer(start: bytePtr, count: range.length), range.location, &stopv)
                 if stopv {
                     stop.pointee = true
                 }
@@ -198,7 +198,7 @@
             d.enumerateBytes { (ptr, range, stop) in
                 var stopv = false
                 let bytePtr = ptr.bindMemory(to: UInt8.self, capacity: range.length)
-                block(UnsafeBufferPointer(start: bytePtr, count: range.length), range.length, &stopv)
+                block(UnsafeBufferPointer(start: bytePtr, count: range.length), range.location, &stopv)
                 if stopv {
                     stop.pointee = true
                 }
@@ -341,7 +341,7 @@
         case .customReference(let d):
             let data = d.mutableCopy() as! NSMutableData
             data.append(bytes, length: length)
-            _backing = .customReference(data)
+            _backing = .customMutableReference(data)
         case .customMutableReference(let d):
             d.append(bytes, length: length)
         }
@@ -421,7 +421,7 @@
             if _length < range.location + range.length {
                 let newLength = range.location + range.length
                 if _capacity < newLength {
-
+                    
                     _grow(newLength, false)
                 }
                 _length = newLength
@@ -471,7 +471,7 @@
                     memset(mutableBytes! + start, 0, replacementLength)
                 }
             }
-
+            
             if resultingLength < currentLength {
                 setLength(resultingLength)
             }
@@ -703,50 +703,64 @@
     }
     
     public func withInteriorPointerReference<T>(_ range: Range<Int>, _ work: (NSData) throws -> T) rethrows -> T {
+        if range.count == 0 {
+            return try work(NSData()) // zero length data can be optimized as a singleton
+        }
+        
         switch _backing {
         case .swift:
-            let d = _bytes == nil ? NSData() : NSData(bytesNoCopy: _bytes!.advanced(by: range.lowerBound), length: range.count, freeWhenDone: false)
-            return try work(d)
+            return try work(NSData(bytesNoCopy: _bytes!.advanced(by: range.lowerBound), length: range.count, freeWhenDone: false))
         case .immutable(let d):
-            if range.lowerBound == 0 && range.upperBound == _length {
-                return try work(d)
-            } else {
-                return try work(d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC())
+            guard range.lowerBound == 0 && range.upperBound == _length else {
+                return try work(NSData(bytesNoCopy: _bytes!.advanced(by: range.lowerBound), length: range.count, freeWhenDone: false))
             }
+            return try work(d)
         case .mutable(let d):
-            if range.lowerBound == 0 && range.upperBound == _length {
-                return try work(d)
-            } else {
-                return try work(d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC())
+            guard range.lowerBound == 0 && range.upperBound == _length else {
+                return try work(NSData(bytesNoCopy: _bytes!.advanced(by: range.lowerBound), length: range.count, freeWhenDone: false))
             }
+            return try work(d)
         case .customReference(let d):
-            if range.lowerBound == 0 && range.upperBound == _length {
-                return try work(d)
-            } else {
-                return try work(d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC())
+            guard range.lowerBound == 0 && range.upperBound == _length else {
+                
+                return try work(NSData(bytesNoCopy: UnsafeMutableRawPointer(mutating: d.bytes.advanced(by: range.lowerBound)), length: range.count, freeWhenDone: false))
             }
+            return try work(d)
         case .customMutableReference(let d):
-            if range.lowerBound == 0 && range.upperBound == _length {
-                return try work(d)
-            } else {
-                return try work(d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC())
+            guard range.lowerBound == 0 && range.upperBound == _length else {
+                return try work(NSData(bytesNoCopy: UnsafeMutableRawPointer(mutating: d.bytes.advanced(by: range.lowerBound)), length: range.count, freeWhenDone: false))
             }
+            return try work(d)
         }
     }
     
-    public func bridgedReference() -> NSData {
+    public func bridgedReference(_ range: Range<Int>) -> NSData {
+        if range.count == 0 {
+            return NSData() // zero length data can be optimized as a singleton
+        }
+        
         switch _backing {
         case .swift:
-            return _NSSwiftData(backing: self)
+            return _NSSwiftData(backing: self, range: range)
         case .immutable(let d):
+            guard range.lowerBound == 0 && range.upperBound == _length else {
+                return _NSSwiftData(backing: self, range: range)
+            }
             return d
         case .mutable(let d):
+            guard range.lowerBound == 0 && range.upperBound == _length else {
+                return _NSSwiftData(backing: self, range: range)
+            }
             return d
         case .customReference(let d):
+            guard range.lowerBound == 0 && range.upperBound == d.length else {
+                return _NSSwiftData(backing: self, range: range)
+            }
             return d
         case .customMutableReference(let d):
-            // Because this is returning an object that may be mutated in the future it needs to create a copy to prevent
-            // any further mutations out from under the receiver
+            guard range.lowerBound == 0 && range.upperBound == d.length else {
+                return d.subdata(with: NSRange(location: range.lowerBound, length: range.count))._bridgeToObjectiveC()
+            }
             return d.copy() as! NSData
         }
     }
@@ -777,14 +791,15 @@
 
 internal class _NSSwiftData : NSData {
     var _backing: _DataStorage!
+    var _range: Range<Data.Index>!
     
-    convenience init(backing: _DataStorage) {
+    convenience init(backing: _DataStorage, range: Range<Data.Index>) {
         self.init()
         _backing = backing
+        _range = range
     }
-    
     override var length: Int {
-        return _backing.length
+        return _range.count
     }
     
     override var bytes: UnsafeRawPointer {
@@ -795,20 +810,28 @@
         // return value here is not needed to be an allocated value. This is specifically
         // needed to live like this to be source compatible with Swift3. Beyond that point
         // this API may be subject to correction.
-        return _backing.bytes ?? UnsafeRawPointer(bitPattern: 0xBAD0)!
+        guard let bytes = _backing.bytes else {
+            return UnsafeRawPointer(bitPattern: 0xBAD0)!
+        }
+        
+        return bytes.advanced(by: _range.lowerBound)
     }
     
     override func copy(with zone: NSZone? = nil) -> Any {
-        return NSData(bytes: _backing.bytes, length: _backing.length)
+        return self
     }
-
+    
+    override func mutableCopy(with zone: NSZone? = nil) -> Any {
+        return NSMutableData(bytes: bytes, length: length)
+    }
+    
 #if !DEPLOYMENT_RUNTIME_SWIFT
     @objc
     func _isCompact() -> Bool {
         return true
     }
 #endif
-
+    
 #if DEPLOYMENT_RUNTIME_SWIFT
     override func _providesConcreteBacking() -> Bool {
         return true
@@ -942,7 +965,7 @@
         }
         _sliceRange = 0..<count
     }
-
+    
     /// Initialize a `Data` with a repeating byte pattern
     ///
     /// - parameter repeatedValue: A byte to initialize the pattern
@@ -1055,7 +1078,7 @@
             _backing = _DataStorage(customReference: reference.copy() as! NSData)
             _sliceRange = 0..<reference.length
         }
-
+        
     }
     
     @_versioned
@@ -1174,7 +1197,7 @@
 #if !DEPLOYMENT_RUNTIME_SWIFT
     @inline(__always)
     private func _shouldUseNonAtomicWriteReimplementation(options: Data.WritingOptions = []) -> Bool {
-        
+    
         // Avoid a crash that happens on OS X 10.11.x and iOS 9.x or before when writing a bridged Data non-atomically with Foundation's standard write() implementation.
         if !options.contains(.atomic) {
             #if os(OSX)
@@ -1336,6 +1359,21 @@
         _sliceRange = _sliceRange.lowerBound..<(_sliceRange.lowerBound + resultingLength)
     }
     
+    @inline(__always)
+    public mutating func replaceSubrange(_ subrange: CountableRange<Index>, with data: Data) {
+        let nsRange = NSMakeRange(subrange.lowerBound, subrange.upperBound - subrange.lowerBound)
+        let cnt = data.count
+        if !isKnownUniquelyReferenced(&_backing) {
+            _backing = _backing.mutableCopy(_sliceRange)
+        }
+        let resultingLength = data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Int in
+            let currentLength = _backing.length
+            _backing.replaceBytes(in: nsRange, with: bytes, length: cnt)
+            return currentLength - nsRange.length + cnt
+        }
+        _sliceRange = _sliceRange.lowerBound..<(_sliceRange.lowerBound + resultingLength)
+    }
+    
     /// Replace a region of bytes in the data with new bytes from a buffer.
     ///
     /// This will resize the data if required, to fit the entire contents of `buffer`.
@@ -1387,7 +1425,7 @@
                 // In the future, if we keep the malloced pointer and count inside this struct/ref instead of deferring to NSData, we may be able to do this more efficiently.
                 self.count = resultCount
             }
-
+            
             let shift = resultCount - currentCount
             let start = subrange.lowerBound
             
@@ -1417,7 +1455,7 @@
         let resultingLength = currentLength - nsRange.length + cnt
         _sliceRange = _sliceRange.lowerBound..<(_sliceRange.lowerBound + resultingLength)
     }
-
+    
     /// Return a new copy of the data in a specified range.
     ///
     /// - parameter range: The range to copy.
@@ -1480,7 +1518,7 @@
     public subscript(index: Index) -> UInt8 {
         @inline(__always)
         get {
-            return _backing.bytes!.advanced(by: _sliceRange.lowerBound + index).assumingMemoryBound(to: UInt8.self).pointee
+            return _backing.bytes!.advanced(by: index).assumingMemoryBound(to: UInt8.self).pointee
         }
         @inline(__always)
         set {
@@ -1502,6 +1540,24 @@
         }
     }
     
+    public subscript<R: RangeExpression>(_ rangeExpression: R) -> Data
+        where R.Bound: FixedWidthInteger, R.Bound.Stride : SignedInteger {
+        @inline(__always)
+        get {
+            let range = rangeExpression.relative(to: 0..<R.Bound.max)
+            let start: Int = numericCast(range.lowerBound)
+            let end: Int = numericCast(range.upperBound)
+            return Data(backing: _backing, range: start..<end)
+        }
+        @inline(__always)
+        set {
+            let range = rangeExpression.relative(to: 0..<R.Bound.max)
+            let start: Int = numericCast(range.lowerBound)
+            let end: Int = numericCast(range.upperBound)
+            replaceSubrange(start..<end, with: newValue)
+        }
+        
+    }
     
     /// The start `Index` in the data.
     public var startIndex: Index {
@@ -1598,14 +1654,18 @@
         let backing1 = d1._backing
         let backing2 = d2._backing
         if backing1 === backing2 {
-            return true
+            if d1._sliceRange == d2._sliceRange {
+                return true
+            }
         }
-        let length1 = backing1.length
-        if length1 != backing2.length {
+        let length1 = d1.count
+        if length1 != d2.count {
             return false
         }
         if backing1.bytes == backing2.bytes {
-            return true
+            if d1._sliceRange == d2._sliceRange {
+                return true
+            }
         }
         if length1 > 0 {
             return d1.withUnsafeBytes { (b1) in
@@ -1668,7 +1728,7 @@
 extension Data : DataBridgeType {
     @_semantics("convertToObjectiveC")
     public func _bridgeToObjectiveC() -> NSData {
-        return _backing.bridgedReference()
+        return _backing.bridgedReference(_sliceRange)
     }
     
     public static func _forceBridgeFromObjectiveC(_ input: NSData, result: inout Data?) {
@@ -1683,9 +1743,8 @@
     }
     
     public static func _unconditionallyBridgeFromObjectiveC(_ source: NSData?) -> Data {
-        var result: Data?
-        _forceBridgeFromObjectiveC(source!, result: &result)
-        return result!
+        guard let src = source else { return Data() }
+        return Data(referencing: src)
     }
 }
 
@@ -1696,3 +1755,48 @@
         return AnyHashable(Data._unconditionallyBridgeFromObjectiveC(self))
     }
 }
+
+extension Data : Codable {
+    public init(from decoder: Decoder) throws {
+        var container = try decoder.unkeyedContainer()
+        
+        // It's more efficient to pre-allocate the buffer if we can.
+        if let count = container.count {
+            self.init(count: count)
+            
+            // Loop only until count, not while !container.isAtEnd, in case count is underestimated (this is misbehavior) and we haven't allocated enough space.
+            // We don't want to write past the end of what we allocated.
+            for i in 0 ..< count {
+                let byte = try container.decode(UInt8.self)
+                self[i] = byte
+            }
+        } else {
+            self.init()
+        }
+        
+        while !container.isAtEnd {
+            var byte = try container.decode(UInt8.self)
+            self.append(&byte, count: 1)
+        }
+    }
+    
+    public func encode(to encoder: Encoder) throws {
+        var container = encoder.unkeyedContainer()
+        
+        // Since enumerateBytes does not rethrow, we need to catch the error, stow it away, and rethrow if we stopped.
+        var caughtError: Error? = nil
+        self.enumerateBytes { (buffer: UnsafeBufferPointer<UInt8>, byteIndex: Data.Index, stop: inout Bool) in
+            do {
+                try container.encode(contentsOf: buffer)
+            } catch {
+                caughtError = error
+                stop = true
+            }
+        }
+        
+        if let error = caughtError {
+            throw error
+        }
+    }
+}
+
diff --git a/Foundation/NSCache.swift b/Foundation/NSCache.swift
index cce3797..0128f02 100644
--- a/Foundation/NSCache.swift
+++ b/Foundation/NSCache.swift
@@ -33,7 +33,7 @@
         switch self.value {
         case let nsObject as NSObject:
             return nsObject.hashValue
-        case let hashable as Hashable:
+        case let hashable as AnyHashable:
             return hashable.hashValue
         default: return 0
         }
diff --git a/Foundation/NSURLSession/http/HTTPURLProtocol.swift b/Foundation/NSURLSession/http/HTTPURLProtocol.swift
index 081b464..017eaae 100644
--- a/Foundation/NSURLSession/http/HTTPURLProtocol.swift
+++ b/Foundation/NSURLSession/http/HTTPURLProtocol.swift
@@ -776,10 +776,15 @@
             // For now, we'll notify the delegate, but won't pause the transfer,
             // and we'll disregard the completion handler:
             guard let s = session as? URLSession else { fatalError() }
-            s.delegateQueue.addOperation {
-                delegate.urlSession(s, dataTask: dt, didReceive: response, completionHandler: { _ in
-                    URLSession.printDebug("warning: Ignoring disposition from completion handler.")
-                })
+            switch response.statusCode {
+            case 301, 302, 303, 307:
+                break
+            default:
+                s.delegateQueue.addOperation {
+                    delegate.urlSession(s, dataTask: dt, didReceive: response, completionHandler: { _ in
+                        URLSession.printDebug("warning: Ignoring disposition from completion handler.")
+                    })
+                }
             }
         case .taskDelegate:
             break
@@ -859,7 +864,7 @@
         //TODO: Do we ever want to redirect for HEAD requests?
         func methodAndURL() -> (String, URL)? {
             guard
-                let location = response.value(forHeaderField: .location),
+                let location = response.value(forHeaderField: .location, response: response),
                 let targetURL = URL(string: location)
                 else {
                     // Can't redirect when there's no location to redirect to.
@@ -882,8 +887,24 @@
         guard let (method, targetURL) = methodAndURL() else { return nil }
         var request = fromRequest
         request.httpMethod = method
-        request.url = targetURL
-        return request
+ 
+        // If targetURL has only relative path of url, create a new valid url with relative path
+        // Otherwise, return request with targetURL ie.url from location field
+        guard targetURL.scheme == nil || targetURL.host == nil else {
+            request.url = targetURL
+            return request
+        }
+    
+        let scheme = request.url?.scheme
+        let host = request.url?.host
+
+        var components = URLComponents()
+        components.scheme = scheme
+        components.host = host
+        components.path = targetURL.relativeString
+        guard let urlString = components.string else { fatalError("Invalid URL") }
+        request.url = URL(string: urlString)
+	return request
     }
 }
 
@@ -894,7 +915,12 @@
         /// - SeeAlso: RFC 2616 section 14.30 <https://tools.ietf.org/html/rfc2616#section-14.30>
         case location = "Location"
     }
-    func value(forHeaderField field: _Field) -> String? {
-        return field.rawValue
+    func value(forHeaderField field: _Field, response: HTTPURLResponse?) -> String? {
+        let value = field.rawValue
+	guard let response = response else { fatalError("Response is nil") }
+        if let location = response.allHeaderFields[value] as? String {
+            return location
+        }
+        return nil
     }
 }
diff --git a/TestFoundation/HTTPServer.swift b/TestFoundation/HTTPServer.swift
index 8eb1239..bec7079 100644
--- a/TestFoundation/HTTPServer.swift
+++ b/TestFoundation/HTTPServer.swift
@@ -207,6 +207,7 @@
 struct _HTTPResponse {
     enum Response : Int {
         case OK = 200
+        case REDIRECT = 302
     }
     private let responseCode: Response
     private let headers: String
@@ -229,6 +230,7 @@
                                      "Peru":"Lima",
                                      "Italy":"Rome",
                                      "USA":"Washington, D.C.",
+				     "UnitedStates": "USA",
                                      "country.txt": "A country is a region that is identified as a distinct national entity in political geography"]
     let httpServer: _HTTPServer
     let startDelay: TimeInterval?
@@ -269,6 +271,18 @@
             let text = request.getCommaSeparatedHeaders()
             return _HTTPResponse(response: .OK, headers: "Content-Length: \(text.characters.count)", body: text)
         }
+
+	if uri == "/UnitedStates" {
+            let value = capitals[String(uri.characters.dropFirst())]!
+            let text = request.getCommaSeparatedHeaders()
+            let host = request.headers[1].components(separatedBy: " ")[1]
+            let ip = host.components(separatedBy: ":")[0]
+            let port = host.components(separatedBy: ":")[1]
+            let newPort = Int(port)! + 1
+            let newHost = ip + ":" + String(newPort)
+            let httpResponse = _HTTPResponse(response: .REDIRECT, headers: "Location: http://\(newHost + "/" + value)", body: text)
+            return httpResponse 
+        }
         return _HTTPResponse(response: .OK, body: capitals[String(uri.characters.dropFirst())]!) 
     }
 
diff --git a/TestFoundation/TestNSCache.swift b/TestFoundation/TestNSCache.swift
index 1dde6d5..9ab9ed8 100644
--- a/TestFoundation/TestNSCache.swift
+++ b/TestFoundation/TestNSCache.swift
@@ -23,6 +23,8 @@
             ("test_setWithMutableKeys", test_setWithMutableKeys),
             ("test_costLimit", test_costLimit),
             ("test_countLimit", test_countLimit),
+            ("test_hashableKey", test_hashableKey),
+            ("test_nonHashableKey", test_nonHashableKey)
         ]
     }
     
@@ -106,4 +108,66 @@
         XCTAssertNil(cache.object(forKey: key1), "should be nil")
         
     }
+
+
+    class TestHashableCacheKey: Hashable {
+        let string: String
+        var hashValue: Int { return string.hashValue }
+
+        init(string: String) {
+            self.string = string
+        }
+
+        static func ==(lhs: TestHashableCacheKey,
+            rhs:TestHashableCacheKey) -> Bool {
+            return lhs.string == rhs.string
+        }
+    }
+
+    // Test when NSCacheKey.value is AnyHashable
+    func test_hashableKey() {
+        let cache = NSCache<TestHashableCacheKey, NSString>()
+        cache.countLimit = 2
+
+        let key1 = TestHashableCacheKey(string: "key1")
+        let key2 = TestHashableCacheKey(string: "key2")
+        let key3 = TestHashableCacheKey(string: "key3")
+        let value = NSString(string: "value")
+
+        cache.setObject(value, forKey: key1)
+        cache.setObject(value, forKey: key2)
+        cache.setObject(value, forKey: key3)
+
+        XCTAssertEqual(cache.object(forKey: key2), value, "should be equal to \(value)")
+        XCTAssertEqual(cache.object(forKey: key3), value, "should be equal to \(value)")
+        XCTAssertNil(cache.object(forKey: key1), "should be nil")
+    }
+
+
+    class TestCacheKey {
+        let string: String
+
+        init(string: String) {
+            self.string = string
+        }
+    }
+
+    // Test when NSCacheKey.value is neither NSObject or AnyHashable
+    func test_nonHashableKey() {
+        let cache = NSCache<TestCacheKey, NSString>()
+        cache.countLimit = 2
+
+        let key1 = TestCacheKey(string: "key1")
+        let key2 = TestCacheKey(string: "key2")
+        let key3 = TestCacheKey(string: "key3")
+        let value = NSString(string: "value")
+
+        cache.setObject(value, forKey: key1)
+        cache.setObject(value, forKey: key2)
+        cache.setObject(value, forKey: key3)
+
+        XCTAssertEqual(cache.object(forKey: key2), value, "should be equal to \(value)")
+        XCTAssertEqual(cache.object(forKey: key3), value, "should be equal to \(value)")
+        XCTAssertNil(cache.object(forKey: key1), "should be nil")
+    }
 }
diff --git a/TestFoundation/TestNSURLSession.swift b/TestFoundation/TestNSURLSession.swift
index c89f5ae..3bb16f6 100644
--- a/TestFoundation/TestNSURLSession.swift
+++ b/TestFoundation/TestNSURLSession.swift
@@ -39,6 +39,7 @@
             ("test_verifyHttpAdditionalHeaders", test_verifyHttpAdditionalHeaders),
             ("test_timeoutInterval", test_timeoutInterval),
 	    ("test_customProtocol", test_customProtocol),
+	    ("test_httpRedirection", test_httpRedirection),
         ]
     }
 
@@ -467,6 +468,24 @@
         task.resume()
         waitForExpectations(timeout: 12)
     }
+
+    func test_httpRedirection() {
+        let serverReady = ServerSemaphore()
+        globalDispatchQueue.async {
+            do {
+                try self.runServer(with: serverReady)
+            } catch {
+                XCTAssertTrue(true)
+                return
+            }
+        }
+        serverReady.wait()
+        let urlString = "http://127.0.0.1:\(serverPort)/UnitedStates"
+        let url = URL(string: urlString)!
+        let d = HTTPRedirectionDataTask(with: expectation(description: "data task"))
+        d.run(with: url)
+        waitForExpectations(timeout: 12)
+    }
 }
 
 class SessionDelegate: NSObject, URLSessionDelegate {
@@ -516,14 +535,13 @@
 extension DataTask : URLSessionDataDelegate {
     public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
         capital = String(data: data, encoding: String.Encoding.utf8)!
-        dataTaskExpectation.fulfill()
     }
 }
 
 extension DataTask : URLSessionTaskDelegate {
     public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
-         guard (error as? URLError) != nil else { return }
          dataTaskExpectation.fulfill()
+	 guard (error as? URLError) != nil else { return }
          if let cancellation = cancelExpectation {
              cancellation.fulfill()
          }
@@ -608,3 +626,61 @@
         return
     }
 }
+
+class HTTPRedirectionDataTask : NSObject {
+    let dataTaskExpectation: XCTestExpectation!
+    var session: URLSession! = nil
+    var task: URLSessionDataTask! = nil
+    var cancelExpectation: XCTestExpectation?
+
+    public var error = false
+
+    init(with expectation: XCTestExpectation) {
+        dataTaskExpectation = expectation
+    }
+
+    func run(with request: URLRequest) {
+        let config = URLSessionConfiguration.default
+        config.timeoutIntervalForRequest = 8
+        session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
+        task = session.dataTask(with: request)
+        task.resume()
+    }
+
+    func run(with url: URL) {
+        let config = URLSessionConfiguration.default
+        config.timeoutIntervalForRequest = 8
+        session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
+        task = session.dataTask(with: url)
+        task.resume()
+    }
+
+    func cancel() {
+        task.cancel()
+    }
+}
+
+extension HTTPRedirectionDataTask : URLSessionDataDelegate {
+    public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
+        guard let httpresponse = response as? HTTPURLResponse else { fatalError() }
+	XCTAssertNotNil(response)
+        XCTAssertEqual(200, httpresponse.statusCode, "HTTP response code is not 200")
+    }
+}
+
+extension HTTPRedirectionDataTask : URLSessionTaskDelegate {
+    public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
+        dataTaskExpectation.fulfill()
+        guard (error as? URLError) != nil else { return }
+        if let cancellation = cancelExpectation {
+            cancellation.fulfill()
+        }
+        self.error = true
+    }
+
+    public func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) {
+        XCTAssertNotNil(response)
+        XCTAssertEqual(302, response.statusCode, "HTTP response code is not 302")
+        completionHandler(request)
+    }
+}