Skip to content

Commit 5b1b508

Browse files
author
Pushkar N Kulkarni
authored
Merge pull request #968 from nethraravindran/urlprotocol-urlsession-branch
Implementation of URLProtocol and refactoring URLSession
2 parents 124680b + ec515ac commit 5b1b508

13 files changed

+1260
-934
lines changed

Foundation.xcodeproj/project.pbxproj

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
5B1FD9DE1D6D16580080E83C /* TaskRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1FD9D21D6D16580080E83C /* TaskRegistry.swift */; };
7979
5B1FD9DF1D6D16580080E83C /* TransferState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1FD9D31D6D16580080E83C /* TransferState.swift */; };
8080
5B1FD9E11D6D178E0080E83C /* libcurl.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B1FD9E01D6D178E0080E83C /* libcurl.3.dylib */; };
81+
E429ED451E9638DA0031BC20 /* HTTPURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E429ED441E9638DA0031BC20 /* HTTPURLProtocol.swift */; };
8182
5B1FD9E31D6D17B80080E83C /* TestNSURLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1FD9E21D6D17B80080E83C /* TestNSURLSession.swift */; };
8283
5B23AB871CE62D17000DB898 /* Boxing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B23AB861CE62D17000DB898 /* Boxing.swift */; };
8384
5B23AB891CE62D4D000DB898 /* ReferenceConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B23AB881CE62D4D000DB898 /* ReferenceConvertible.swift */; };
@@ -500,6 +501,7 @@
500501
5B1FD9D21D6D16580080E83C /* TaskRegistry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskRegistry.swift; sourceTree = "<group>"; };
501502
5B1FD9D31D6D16580080E83C /* TransferState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransferState.swift; sourceTree = "<group>"; };
502503
5B1FD9E01D6D178E0080E83C /* libcurl.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcurl.3.dylib; path = usr/lib/libcurl.3.dylib; sourceTree = SDKROOT; };
504+
E429ED441E9638DA0031BC20 /* HTTPURLProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HTTPURLProtocol.swift path = HTTPURLProtocol.swift; sourceTree = "<group>"; };
503505
5B1FD9E21D6D17B80080E83C /* TestNSURLSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSURLSession.swift; sourceTree = "<group>"; };
504506
5B23AB861CE62D17000DB898 /* Boxing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Boxing.swift; sourceTree = "<group>"; };
505507
5B23AB881CE62D4D000DB898 /* ReferenceConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReferenceConvertible.swift; sourceTree = "<group>"; };
@@ -938,23 +940,32 @@
938940
5B1FD9C71D6D162D0080E83C /* Session */ = {
939941
isa = PBXGroup;
940942
children = (
943+
E4F889331E9CF04D008A70EB /* http */,
941944
5B1FD9C81D6D16580080E83C /* Configuration.swift */,
942-
5B1FD9C91D6D16580080E83C /* EasyHandle.swift */,
943-
5B1FD9CA1D6D16580080E83C /* HTTPBodySource.swift */,
944-
5B1FD9CB1D6D16580080E83C /* HTTPMessage.swift */,
945-
5B1FD9CC1D6D16580080E83C /* libcurlHelpers.swift */,
946-
5B1FD9CD1D6D16580080E83C /* MultiHandle.swift */,
947945
5B1FD9CE1D6D16580080E83C /* NSURLSession.swift */,
948946
5B1FD9CF1D6D16580080E83C /* NSURLSessionConfiguration.swift */,
949947
5B1FD9D01D6D16580080E83C /* NSURLSessionDelegate.swift */,
950948
5B1FD9D11D6D16580080E83C /* NSURLSessionTask.swift */,
951949
5B1FD9D21D6D16580080E83C /* TaskRegistry.swift */,
952-
5B1FD9D31D6D16580080E83C /* TransferState.swift */,
953950
);
954951
name = Session;
955952
path = NSURLSession;
956953
sourceTree = "<group>";
957954
};
955+
E4F889331E9CF04D008A70EB /* http */ = {
956+
isa = PBXGroup;
957+
children = (
958+
E429ED441E9638DA0031BC20 /* HTTPURLProtocol.swift */,
959+
5B1FD9C91D6D16580080E83C /* EasyHandle.swift */,
960+
5B1FD9CA1D6D16580080E83C /* HTTPBodySource.swift */,
961+
5B1FD9CB1D6D16580080E83C /* HTTPMessage.swift */,
962+
5B1FD9CC1D6D16580080E83C /* libcurlHelpers.swift */,
963+
5B1FD9CD1D6D16580080E83C /* MultiHandle.swift */,
964+
5B1FD9D31D6D16580080E83C /* TransferState.swift */,
965+
);
966+
name = http;
967+
sourceTree = "<group>";
968+
};
958969
5B5D88531BBC938800234F36 = {
959970
isa = PBXGroup;
960971
children = (
@@ -2057,6 +2068,7 @@
20572068
5B23AB871CE62D17000DB898 /* Boxing.swift in Sources */,
20582069
5BF7AEA41BCD51F9008F214A /* Bundle.swift in Sources */,
20592070
5B23AB891CE62D4D000DB898 /* ReferenceConvertible.swift in Sources */,
2071+
E429ED451E9638DA0031BC20 /* HTTPURLProtocol.swift in Sources */,
20602072
D3E8D6D11C367AB600295652 /* NSSpecialValue.swift in Sources */,
20612073
5B1FD9D51D6D16580080E83C /* EasyHandle.swift in Sources */,
20622074
EAB57B721BD1C7A5004AC5C5 /* NSPortMessage.swift in Sources */,

Foundation/NSURLProtocol.swift

Lines changed: 177 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
88
//
99

10+
import CoreFoundation
11+
import Dispatch
12+
1013
/*!
1114
@header NSURLProtocol.h
1215

@@ -142,6 +145,96 @@ public protocol URLProtocolClient : NSObjectProtocol {
142145
func urlProtocol(_ protocol: URLProtocol, didCancel challenge: URLAuthenticationChallenge)
143146
}
144147

148+
internal class _ProtocolClient : NSObject, URLProtocolClient {
149+
150+
func urlProtocol(_ protocol: URLProtocol, didReceive response: URLResponse, cacheStoragePolicy policy: URLCache.StoragePolicy) {
151+
`protocol`.task?.response = response
152+
}
153+
154+
func urlProtocolDidFinishLoading(_ protocol: URLProtocol) {
155+
guard let task = `protocol`.task else { fatalError() }
156+
guard let session = task.session as? URLSession else { fatalError() }
157+
switch session.behaviour(for: task) {
158+
case .taskDelegate(let delegate):
159+
guard let s = session as? URLSession else { fatalError() }
160+
s.delegateQueue.addOperation {
161+
delegate.urlSession(s, task: task, didCompleteWithError: nil)
162+
task.state = .completed
163+
}
164+
case .noDelegate:
165+
task.state = .completed
166+
case .dataCompletionHandler(let completion):
167+
let data = Data()
168+
guard let client = `protocol`.client else { fatalError() }
169+
client.urlProtocol(`protocol`, didLoad: data)
170+
return
171+
case .downloadCompletionHandler(let completion):
172+
guard let s = session as? URLSession else { fatalError() }
173+
s.delegateQueue.addOperation {
174+
completion(task.currentRequest?.url, task.response, nil)
175+
task.state = .completed
176+
}
177+
}
178+
}
179+
180+
func urlProtocol(_ protocol: URLProtocol, didCancel challenge: URLAuthenticationChallenge) {
181+
NSUnimplemented()
182+
}
183+
184+
func urlProtocol(_ protocol: URLProtocol, didReceive challenge: URLAuthenticationChallenge) {
185+
NSUnimplemented()
186+
}
187+
188+
func urlProtocol(_ protocol: URLProtocol, didLoad data: Data) {
189+
guard let task = `protocol`.task else { fatalError() }
190+
guard let session = task.session as? URLSession else { fatalError() }
191+
switch session.behaviour(for: task) {
192+
case .dataCompletionHandler(let completion):
193+
guard let s = task.session as? URLSession else { fatalError() }
194+
s.delegateQueue.addOperation {
195+
completion(data, task.response, nil)
196+
task.state = .completed
197+
}
198+
default: return
199+
}
200+
}
201+
202+
func urlProtocol(_ protocol: URLProtocol, didFailWithError error: Error) {
203+
guard let task = `protocol`.task else { fatalError() }
204+
guard let session = task.session as? URLSession else { fatalError() }
205+
switch session.behaviour(for: task) {
206+
case .taskDelegate(let delegate):
207+
guard let s = session as? URLSession else { fatalError() }
208+
s.delegateQueue.addOperation {
209+
delegate.urlSession(s, task: task, didCompleteWithError: error as Error)
210+
task.state = .completed
211+
}
212+
case .noDelegate:
213+
task.state = .completed
214+
case .dataCompletionHandler(let completion):
215+
guard let s = session as? URLSession else { fatalError() }
216+
s.delegateQueue.addOperation {
217+
completion(nil, nil, error)
218+
task.state = .completed
219+
}
220+
case .downloadCompletionHandler(let completion):
221+
guard let s = session as? URLSession else { fatalError() }
222+
s.delegateQueue.addOperation {
223+
completion(nil, nil, error)
224+
task.state = .completed
225+
}
226+
}
227+
}
228+
229+
func urlProtocol(_ protocol: URLProtocol, cachedResponseIsValid cachedResponse: CachedURLResponse) {
230+
NSUnimplemented()
231+
}
232+
233+
func urlProtocol(_ protocol: URLProtocol, wasRedirectedTo request: URLRequest, redirectResponse: URLResponse) {
234+
NSUnimplemented()
235+
}
236+
}
237+
145238
/*!
146239
@class NSURLProtocol
147240

@@ -151,7 +244,9 @@ public protocol URLProtocolClient : NSObjectProtocol {
151244
or more protocols or URL schemes.
152245
*/
153246
open class URLProtocol : NSObject {
154-
247+
248+
private static var _registeredProtocolClasses = [AnyClass]()
249+
private static var _classesLock = NSLock()
155250
/*!
156251
@method initWithRequest:cachedResponse:client:
157252
@abstract Initializes an NSURLProtocol given request,
@@ -165,28 +260,43 @@ open class URLProtocol : NSObject {
165260
interface the protocol implementation can use to report results back
166261
to the URL loading system.
167262
*/
168-
public init(request: URLRequest, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) { NSUnimplemented() }
169-
263+
public required init(request: URLRequest, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) {
264+
self._request = request
265+
self._cachedResponse = cachedResponse
266+
self._client = client ?? _ProtocolClient()
267+
}
268+
269+
private var _request : URLRequest
270+
private var _cachedResponse : CachedURLResponse?
271+
private var _client : URLProtocolClient?
272+
170273
/*!
171274
@method client
172275
@abstract Returns the NSURLProtocolClient of the receiver.
173276
@result The NSURLProtocolClient of the receiver.
174277
*/
175-
open var client: URLProtocolClient? { NSUnimplemented() }
278+
open var client: URLProtocolClient? {
279+
set { self._client = newValue }
280+
get { return self._client }
281+
}
176282

177283
/*!
178284
@method request
179285
@abstract Returns the NSURLRequest of the receiver.
180286
@result The NSURLRequest of the receiver.
181287
*/
182-
/*@NSCopying*/ open var request: URLRequest { NSUnimplemented() }
288+
/*@NSCopying*/ open var request: URLRequest {
289+
return _request
290+
}
183291

184292
/*!
185293
@method cachedResponse
186294
@abstract Returns the NSCachedURLResponse of the receiver.
187295
@result The NSCachedURLResponse of the receiver.
188296
*/
189-
/*@NSCopying*/ open var cachedResponse: CachedURLResponse? { NSUnimplemented() }
297+
/*@NSCopying*/ open var cachedResponse: CachedURLResponse? {
298+
return _cachedResponse
299+
}
190300

191301
/*======================================================================
192302
Begin responsibilities for protocol implementors
@@ -207,7 +317,9 @@ open class URLProtocol : NSObject {
207317
@param request A request to inspect.
208318
@result YES if the protocol can handle the given request, NO if not.
209319
*/
210-
open class func canInit(with request: URLRequest) -> Bool { NSUnimplemented() }
320+
open class func canInit(with request: URLRequest) -> Bool {
321+
NSRequiresConcreteImplementation()
322+
}
211323

212324
/*!
213325
@method canonicalRequestForRequest:
@@ -246,7 +358,9 @@ open class URLProtocol : NSObject {
246358
@discussion When this method is called, the protocol implementation
247359
should start loading a request.
248360
*/
249-
open func startLoading() { NSUnimplemented() }
361+
open func startLoading() {
362+
NSRequiresConcreteImplementation()
363+
}
250364

251365
/*!
252366
@method stopLoading
@@ -256,7 +370,9 @@ open class URLProtocol : NSObject {
256370
to a cancel operation, so protocol implementations must be able to
257371
handle this call while a load is in progress.
258372
*/
259-
open func stopLoading() { NSUnimplemented() }
373+
open func stopLoading() {
374+
NSRequiresConcreteImplementation()
375+
}
260376

261377
/*======================================================================
262378
End responsibilities for protocol implementors
@@ -323,19 +439,65 @@ open class URLProtocol : NSObject {
323439
The only way that failure can occur is if the given class is not a
324440
subclass of NSURLProtocol.
325441
*/
326-
open class func registerClass(_ protocolClass: AnyClass) -> Bool { NSUnimplemented() }
327-
442+
open class func registerClass(_ protocolClass: AnyClass) -> Bool {
443+
if protocolClass is URLProtocol.Type {
444+
_classesLock.lock()
445+
guard !_registeredProtocolClasses.contains(where: { $0 === protocolClass }) else {
446+
_classesLock.unlock()
447+
return true
448+
}
449+
_registeredProtocolClasses.append(protocolClass)
450+
_classesLock.unlock()
451+
return true
452+
}
453+
return false
454+
}
455+
456+
internal class func getProtocolClass(protocols: [AnyClass], request: URLRequest) -> AnyClass? {
457+
// Registered protocols are consulted in reverse order.
458+
// This behaviour makes the latest registered protocol to be consulted first
459+
_classesLock.lock()
460+
let protocolClasses = protocols
461+
for protocolClass in protocolClasses {
462+
let urlProtocolClass: AnyClass = protocolClass
463+
guard let urlProtocol = urlProtocolClass as? URLProtocol.Type else { fatalError() }
464+
if urlProtocol.canInit(with: request) {
465+
_classesLock.unlock()
466+
return urlProtocol
467+
}
468+
}
469+
_classesLock.unlock()
470+
return nil
471+
}
472+
473+
internal class func getProtocols() -> [AnyClass]? {
474+
return _registeredProtocolClasses
475+
}
328476
/*!
329477
@method unregisterClass:
330478
@abstract This method unregisters a protocol.
331479
@discussion After unregistration, a protocol class is no longer
332480
consulted in calls to NSURLProtocol class methods.
333481
@param protocolClass The class to unregister.
334482
*/
335-
open class func unregisterClass(_ protocolClass: AnyClass) { NSUnimplemented() }
483+
open class func unregisterClass(_ protocolClass: AnyClass) {
484+
_classesLock.lock()
485+
if let idx = _registeredProtocolClasses.index(where: { $0 === protocolClass }) {
486+
_registeredProtocolClasses.remove(at: idx)
487+
}
488+
_classesLock.unlock()
489+
}
336490

337491
open class func canInit(with task: URLSessionTask) -> Bool { NSUnimplemented() }
338-
public convenience init(task: URLSessionTask, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) { NSUnimplemented() }
339-
/*@NSCopying*/ open var task: URLSessionTask? { NSUnimplemented() }
340-
}
492+
public required convenience init(task: URLSessionTask, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) {
493+
let urlRequest = task.originalRequest
494+
self.init(request: urlRequest!, cachedResponse: cachedResponse, client: client)
495+
self.task = task
496+
}
497+
/*@NSCopying*/ open var task: URLSessionTask? {
498+
set { self._task = newValue }
499+
get { return self._task }
500+
}
341501

502+
private var _task : URLSessionTask? = nil
503+
}

Foundation/NSURLSession/NSURLSession.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ open class URLSession : NSObject {
220220
let c = URLSession._Configuration(URLSessionConfiguration: configuration)
221221
self._configuration = c
222222
self.multiHandle = _MultiHandle(configuration: c, workQueue: workQueue)
223+
// registering all the protocol classes with URLProtocol
224+
let _ = URLProtocol.registerClass(_HTTPURLProtocol.self)
223225
}
224226

225227
/*
@@ -245,6 +247,8 @@ open class URLSession : NSObject {
245247
let c = URLSession._Configuration(URLSessionConfiguration: configuration)
246248
self._configuration = c
247249
self.multiHandle = _MultiHandle(configuration: c, workQueue: workQueue)
250+
// registering all the protocol classes with URLProtocol
251+
let _ = URLProtocol.registerClass(_HTTPURLProtocol.self)
248252
}
249253

250254
open let delegateQueue: OperationQueue

0 commit comments

Comments
 (0)