2 * Copyright (C) 2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #import "NetworkSession.h"
29 #if USE(NETWORK_SESSION)
31 #import "SessionTracker.h"
32 #import <Foundation/NSURLSession.h>
33 #import <WebCore/AuthenticationChallenge.h>
34 #import <WebCore/CFNetworkSPI.h>
35 #import <WebCore/Credential.h>
36 #import <WebCore/FrameLoaderTypes.h>
37 #import <WebCore/NetworkStorageSession.h>
38 #import <WebCore/NotImplemented.h>
39 #import <WebCore/ResourceError.h>
40 #import <WebCore/ResourceLoadTiming.h>
41 #import <WebCore/ResourceRequest.h>
42 #import <WebCore/ResourceResponse.h>
43 #import <WebCore/SharedBuffer.h>
44 #import <wtf/MainThread.h>
45 #import <wtf/NeverDestroyed.h>
47 static NSURLSessionResponseDisposition toNSURLSessionResponseDisposition(WebCore::PolicyAction disposition)
49 switch (disposition) {
50 case WebCore::PolicyAction::PolicyIgnore:
51 return NSURLSessionResponseCancel;
52 case WebCore::PolicyAction::PolicyUse:
53 return NSURLSessionResponseAllow;
54 case WebCore::PolicyAction::PolicyDownload:
55 return NSURLSessionResponseBecomeDownload;
59 static NSURLSessionAuthChallengeDisposition toNSURLSessionAuthChallengeDisposition(WebKit::AuthenticationChallengeDisposition disposition)
61 switch (disposition) {
62 case WebKit::AuthenticationChallengeDisposition::UseCredential:
63 return NSURLSessionAuthChallengeUseCredential;
64 case WebKit::AuthenticationChallengeDisposition::PerformDefaultHandling:
65 return NSURLSessionAuthChallengePerformDefaultHandling;
66 case WebKit::AuthenticationChallengeDisposition::Cancel:
67 return NSURLSessionAuthChallengeCancelAuthenticationChallenge;
68 case WebKit::AuthenticationChallengeDisposition::RejectProtectionSpace:
69 return NSURLSessionAuthChallengeRejectProtectionSpace;
73 @interface WKNetworkSessionDelegate : NSObject <NSURLSessionDataDelegate> {
74 WebKit::NetworkSession* _session;
77 - (id)initWithNetworkSession:(WebKit::NetworkSession&)session;
81 @implementation WKNetworkSessionDelegate
83 - (id)initWithNetworkSession:(WebKit::NetworkSession&)session
94 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler
96 UNUSED_PARAM(session);
98 if (auto* networkingTask = _session->dataTaskForIdentifier(task.taskIdentifier)) {
99 if (auto* client = networkingTask->client()) {
100 auto completionHandlerCopy = Block_copy(completionHandler);
101 client->willPerformHTTPRedirection(response, request, [completionHandlerCopy](const WebCore::ResourceRequest& request)
103 completionHandlerCopy(request.nsURLRequest(WebCore::UpdateHTTPBody));
104 Block_release(completionHandlerCopy);
111 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
113 UNUSED_PARAM(session);
115 if (auto* networkingTask = _session->dataTaskForIdentifier(task.taskIdentifier)) {
116 if (auto* client = networkingTask->client()) {
117 auto completionHandlerCopy = Block_copy(completionHandler);
118 client->didReceiveChallenge(challenge, [completionHandlerCopy](WebKit::AuthenticationChallengeDisposition disposition, const WebCore::Credential& credential)
120 completionHandlerCopy(toNSURLSessionAuthChallengeDisposition(disposition), credential.nsCredential());
121 Block_release(completionHandlerCopy);
128 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
130 UNUSED_PARAM(session);
132 if (auto* networkingTask = _session->dataTaskForIdentifier(task.taskIdentifier)) {
133 if (auto* client = networkingTask->client())
134 client->didCompleteWithError(error);
138 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
140 UNUSED_PARAM(session);
142 if (auto* networkingTask = _session->dataTaskForIdentifier(dataTask.taskIdentifier)) {
143 if (auto* client = networkingTask->client()) {
144 ASSERT(isMainThread());
145 WebCore::ResourceResponse resourceResponse(response);
146 copyTimingData([dataTask _timingData], resourceResponse.resourceLoadTiming());
147 auto completionHandlerCopy = Block_copy(completionHandler);
148 client->didReceiveResponse(resourceResponse, [completionHandlerCopy](WebCore::PolicyAction policyAction)
150 completionHandlerCopy(toNSURLSessionResponseDisposition(policyAction));
151 Block_release(completionHandlerCopy);
158 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
160 UNUSED_PARAM(session);
162 if (auto* networkingTask = _session->dataTaskForIdentifier(dataTask.taskIdentifier)) {
163 if (auto* client = networkingTask->client())
164 client->didReceiveData(WebCore::SharedBuffer::wrapNSData(data));
168 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
173 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
175 ASSERT_WITH_MESSAGE(!_session->dataTaskForIdentifier([downloadTask taskIdentifier]), "The NetworkDataTask should be destroyed immediately after didBecomeDownloadTask returns");
179 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
184 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
186 auto* networkDataTask = _session->dataTaskForIdentifier([dataTask taskIdentifier]);
187 ASSERT(networkDataTask);
188 if (auto* client = networkDataTask->client())
189 client->didBecomeDownload();
196 static NSURLSessionConfiguration *configurationForType(NetworkSession::Type type)
199 case NetworkSession::Type::Normal:
200 return [NSURLSessionConfiguration defaultSessionConfiguration];
201 case NetworkSession::Type::Ephemeral:
202 return [NSURLSessionConfiguration ephemeralSessionConfiguration];
206 NetworkSession& NetworkSession::defaultSession()
208 ASSERT(isMainThread());
209 static NeverDestroyed<NetworkSession> session(NetworkSession::Type::Normal, WebCore::SessionID::defaultSessionID());
213 NetworkSession::NetworkSession(Type type, WebCore::SessionID sessionID)
214 : m_sessionID(sessionID)
216 m_sessionDelegate = adoptNS([[WKNetworkSessionDelegate alloc] initWithNetworkSession:*this]);
218 NSURLSessionConfiguration *configuration = configurationForType(type);
220 #if HAVE(TIMINGDATAOPTIONS)
221 configuration._timingDataOptions = _TimingDataOptionsEnableW3CNavigationTiming;
223 setCollectsTimingData();
226 if (auto* storageSession = SessionTracker::storageSession(sessionID)) {
227 if (CFHTTPCookieStorageRef storage = storageSession->cookieStorage().get())
228 configuration.HTTPCookieStorage = [[[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:storage] autorelease];
230 m_session = [NSURLSession sessionWithConfiguration:configuration delegate:static_cast<id>(m_sessionDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
233 NetworkSession::~NetworkSession()
235 [m_session invalidateAndCancel];
238 Ref<NetworkDataTask> NetworkSession::createDataTaskWithRequest(const WebCore::ResourceRequest& request, NetworkSessionTaskClient& client)
240 return adoptRef(*new NetworkDataTask(*this, client, [m_session dataTaskWithRequest:request.nsURLRequest(WebCore::UpdateHTTPBody)]));
243 NetworkDataTask* NetworkSession::dataTaskForIdentifier(uint64_t taskIdentifier)
245 ASSERT(isMainThread());
246 return m_dataTaskMap.get(taskIdentifier);
249 NetworkDataTask::NetworkDataTask(NetworkSession& session, NetworkSessionTaskClient& client, RetainPtr<NSURLSessionDataTask>&& task)
252 , m_task(WTF::move(task))
254 ASSERT(!m_session.m_dataTaskMap.contains(taskIdentifier()));
255 ASSERT(isMainThread());
256 m_session.m_dataTaskMap.add(taskIdentifier(), this);
259 NetworkDataTask::~NetworkDataTask()
261 ASSERT(m_session.m_dataTaskMap.contains(taskIdentifier()));
262 ASSERT(m_session.m_dataTaskMap.get(taskIdentifier()) == this);
263 ASSERT(isMainThread());
264 m_session.m_dataTaskMap.remove(taskIdentifier());
267 void NetworkDataTask::cancel()
272 void NetworkDataTask::resume()
277 uint64_t NetworkDataTask::taskIdentifier()
279 return [m_task taskIdentifier];