883722b15566f91630de554386da53f441dfffd7
[WebKit-https.git] / Source / WebKit2 / NetworkProcess / cocoa / NetworkSessionCocoa.mm
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #import "config.h"
27 #import "NetworkSession.h"
28
29 #if USE(NETWORK_SESSION)
30
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/ResourceRequest.h>
41 #import <WebCore/ResourceResponse.h>
42 #import <WebCore/SharedBuffer.h>
43 #import <wtf/MainThread.h>
44 #import <wtf/NeverDestroyed.h>
45
46 static NSURLSessionResponseDisposition toNSURLSessionResponseDisposition(WebCore::PolicyAction disposition)
47 {
48     switch (disposition) {
49     case WebCore::PolicyAction::PolicyIgnore:
50         return NSURLSessionResponseCancel;
51     case WebCore::PolicyAction::PolicyUse:
52         return NSURLSessionResponseAllow;
53     case WebCore::PolicyAction::PolicyDownload:
54         return NSURLSessionResponseBecomeDownload;
55     }
56 }
57
58 static NSURLSessionAuthChallengeDisposition toNSURLSessionAuthChallengeDisposition(WebKit::AuthenticationChallengeDisposition disposition)
59 {
60     switch (disposition) {
61     case WebKit::AuthenticationChallengeDisposition::UseCredential:
62         return NSURLSessionAuthChallengeUseCredential;
63     case WebKit::AuthenticationChallengeDisposition::PerformDefaultHandling:
64         return NSURLSessionAuthChallengePerformDefaultHandling;
65     case WebKit::AuthenticationChallengeDisposition::Cancel:
66         return NSURLSessionAuthChallengeCancelAuthenticationChallenge;
67     case WebKit::AuthenticationChallengeDisposition::RejectProtectionSpace:
68         return NSURLSessionAuthChallengeRejectProtectionSpace;
69     }
70 }
71
72 @interface NetworkSessionDelegate : NSObject <NSURLSessionDataDelegate> {
73     WebKit::NetworkSession* _session;
74 }
75
76 - (id)initWithNetworkSession:(WebKit::NetworkSession&)session;
77
78 @end
79
80 @implementation NetworkSessionDelegate
81
82 - (id)initWithNetworkSession:(WebKit::NetworkSession&)session
83 {
84     self = [super init];
85     if (!self)
86         return nil;
87
88     _session = &session;
89
90     return self;
91 }
92
93 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler
94 {
95     UNUSED_PARAM(session);
96
97     if (auto* networkingTask = _session->dataTaskForIdentifier(task.taskIdentifier)) {
98         if (auto* client = networkingTask->client()) {
99             auto completionHandlerCopy = Block_copy(completionHandler);
100             client->willPerformHTTPRedirection(response, request, [completionHandlerCopy](const WebCore::ResourceRequest& request)
101                 {
102                     completionHandlerCopy(request.nsURLRequest(WebCore::UpdateHTTPBody));
103                     Block_release(completionHandlerCopy);
104                 }
105             );
106         }
107     }
108 }
109
110 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
111 {
112     UNUSED_PARAM(session);
113
114     if (auto* networkingTask = _session->dataTaskForIdentifier(task.taskIdentifier)) {
115         if (auto* client = networkingTask->client()) {
116             auto completionHandlerCopy = Block_copy(completionHandler);
117             client->didReceiveChallenge(challenge, [completionHandlerCopy](WebKit::AuthenticationChallengeDisposition disposition, const WebCore::Credential& credential)
118                 {
119                     completionHandlerCopy(toNSURLSessionAuthChallengeDisposition(disposition), credential.nsCredential());
120                     Block_release(completionHandlerCopy);
121                 }
122             );
123         }
124     }
125 }
126
127 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
128 {
129     UNUSED_PARAM(session);
130
131     if (auto* networkingTask = _session->dataTaskForIdentifier(task.taskIdentifier)) {
132         if (auto* client = networkingTask->client())
133             client->didCompleteWithError(error);
134     }
135 }
136
137 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
138 {
139     UNUSED_PARAM(session);
140
141     if (auto* networkingTask = _session->dataTaskForIdentifier(dataTask.taskIdentifier)) {
142         if (auto* client = networkingTask->client()) {
143             ASSERT(isMainThread());
144             WebCore::ResourceResponse resourceResponse(response);
145             auto completionHandlerCopy = Block_copy(completionHandler);
146             client->didReceiveResponse(resourceResponse, [completionHandlerCopy](WebCore::PolicyAction policyAction)
147                 {
148                     completionHandlerCopy(toNSURLSessionResponseDisposition(policyAction));
149                     Block_release(completionHandlerCopy);
150                 }
151             );
152         }
153     }
154 }
155
156 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
157 {
158     UNUSED_PARAM(session);
159
160     if (auto* networkingTask = _session->dataTaskForIdentifier(dataTask.taskIdentifier)) {
161         if (auto* client = networkingTask->client())
162             client->didReceiveData(WebCore::SharedBuffer::wrapNSData(data));
163     }
164 }
165
166 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
167 {
168     notImplemented();
169 }
170
171 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
172 {
173     ASSERT_WITH_MESSAGE(!_session->dataTaskForIdentifier([downloadTask taskIdentifier]), "The NetworkDataTask should be destroyed immediately after didBecomeDownloadTask returns");
174     notImplemented();
175 }
176
177 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
178 {
179     notImplemented();
180 }
181
182 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
183 {
184     auto* networkDataTask = _session->dataTaskForIdentifier([dataTask taskIdentifier]);
185     ASSERT(networkDataTask);
186     if (auto* client = networkDataTask->client())
187         client->didBecomeDownload();
188 }
189
190 @end
191
192 namespace WebKit {
193     
194 static NSURLSessionConfiguration *configurationForType(NetworkSession::Type type)
195 {
196     switch (type) {
197     case NetworkSession::Type::Normal:
198         return [NSURLSessionConfiguration defaultSessionConfiguration];
199     case NetworkSession::Type::Ephemeral:
200         return [NSURLSessionConfiguration ephemeralSessionConfiguration];
201     }
202 }
203
204 NetworkSession& NetworkSession::defaultSession()
205 {
206     ASSERT(isMainThread());
207     static NeverDestroyed<NetworkSession> session(NetworkSession::Type::Normal, WebCore::SessionID::defaultSessionID());
208     return session;
209 }
210
211 NetworkSession::NetworkSession(Type type, WebCore::SessionID sessionID)
212     : m_sessionID(sessionID)
213 {
214     m_sessionDelegate = adoptNS([[NetworkSessionDelegate alloc] initWithNetworkSession:*this]);
215
216     NSURLSessionConfiguration *configuration = configurationForType(type);
217     if (auto* storageSession = SessionTracker::storageSession(sessionID)) {
218         if (CFHTTPCookieStorageRef storage = storageSession->cookieStorage().get())
219             configuration.HTTPCookieStorage = [[[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:storage] autorelease];
220     }
221     m_session = [NSURLSession sessionWithConfiguration:configuration delegate:static_cast<id>(m_sessionDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
222 }
223
224 NetworkSession::~NetworkSession()
225 {
226     [m_session invalidateAndCancel];
227 }
228
229 Ref<NetworkDataTask> NetworkSession::createDataTaskWithRequest(const WebCore::ResourceRequest& request, NetworkSessionTaskClient& client)
230 {
231     return adoptRef(*new NetworkDataTask(*this, client, [m_session dataTaskWithRequest:request.nsURLRequest(WebCore::UpdateHTTPBody)]));
232 }
233
234 NetworkDataTask* NetworkSession::dataTaskForIdentifier(uint64_t taskIdentifier)
235 {
236     ASSERT(isMainThread());
237     return m_dataTaskMap.get(taskIdentifier);
238 }
239
240 NetworkDataTask::NetworkDataTask(NetworkSession& session, NetworkSessionTaskClient& client, RetainPtr<NSURLSessionDataTask>&& task)
241     : m_session(session)
242     , m_client(&client)
243     , m_task(WTF::move(task))
244 {
245     ASSERT(!m_session.m_dataTaskMap.contains(taskIdentifier()));
246     ASSERT(isMainThread());
247     m_session.m_dataTaskMap.add(taskIdentifier(), this);
248 }
249
250 NetworkDataTask::~NetworkDataTask()
251 {
252     ASSERT(m_session.m_dataTaskMap.contains(taskIdentifier()));
253     ASSERT(m_session.m_dataTaskMap.get(taskIdentifier()) == this);
254     ASSERT(isMainThread());
255     m_session.m_dataTaskMap.remove(taskIdentifier());
256 }
257
258 void NetworkDataTask::cancel()
259 {
260     [m_task cancel];
261 }
262
263 void NetworkDataTask::resume()
264 {
265     [m_task resume];
266 }
267
268 uint64_t NetworkDataTask::taskIdentifier()
269 {
270     return [m_task taskIdentifier];
271 }
272
273 }
274
275 #endif