Implement Web Timing when using NETWORK_SESSION
[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/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>
46
47 static NSURLSessionResponseDisposition toNSURLSessionResponseDisposition(WebCore::PolicyAction disposition)
48 {
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;
56     }
57 }
58
59 static NSURLSessionAuthChallengeDisposition toNSURLSessionAuthChallengeDisposition(WebKit::AuthenticationChallengeDisposition disposition)
60 {
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;
70     }
71 }
72
73 @interface NetworkSessionDelegate : NSObject <NSURLSessionDataDelegate> {
74     WebKit::NetworkSession* _session;
75 }
76
77 - (id)initWithNetworkSession:(WebKit::NetworkSession&)session;
78
79 @end
80
81 @implementation NetworkSessionDelegate
82
83 - (id)initWithNetworkSession:(WebKit::NetworkSession&)session
84 {
85     self = [super init];
86     if (!self)
87         return nil;
88
89     _session = &session;
90
91     return self;
92 }
93
94 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler
95 {
96     UNUSED_PARAM(session);
97
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)
102                 {
103                     completionHandlerCopy(request.nsURLRequest(WebCore::UpdateHTTPBody));
104                     Block_release(completionHandlerCopy);
105                 }
106             );
107         }
108     }
109 }
110
111 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
112 {
113     UNUSED_PARAM(session);
114
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)
119                 {
120                     completionHandlerCopy(toNSURLSessionAuthChallengeDisposition(disposition), credential.nsCredential());
121                     Block_release(completionHandlerCopy);
122                 }
123             );
124         }
125     }
126 }
127
128 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
129 {
130     UNUSED_PARAM(session);
131
132     if (auto* networkingTask = _session->dataTaskForIdentifier(task.taskIdentifier)) {
133         if (auto* client = networkingTask->client())
134             client->didCompleteWithError(error);
135     }
136 }
137
138 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
139 {
140     UNUSED_PARAM(session);
141
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)
149                 {
150                     completionHandlerCopy(toNSURLSessionResponseDisposition(policyAction));
151                     Block_release(completionHandlerCopy);
152                 }
153             );
154         }
155     }
156 }
157
158 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
159 {
160     UNUSED_PARAM(session);
161
162     if (auto* networkingTask = _session->dataTaskForIdentifier(dataTask.taskIdentifier)) {
163         if (auto* client = networkingTask->client())
164             client->didReceiveData(WebCore::SharedBuffer::wrapNSData(data));
165     }
166 }
167
168 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
169 {
170     notImplemented();
171 }
172
173 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
174 {
175     ASSERT_WITH_MESSAGE(!_session->dataTaskForIdentifier([downloadTask taskIdentifier]), "The NetworkDataTask should be destroyed immediately after didBecomeDownloadTask returns");
176     notImplemented();
177 }
178
179 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
180 {
181     notImplemented();
182 }
183
184 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
185 {
186     auto* networkDataTask = _session->dataTaskForIdentifier([dataTask taskIdentifier]);
187     ASSERT(networkDataTask);
188     if (auto* client = networkDataTask->client())
189         client->didBecomeDownload();
190 }
191
192 @end
193
194 namespace WebKit {
195     
196 static NSURLSessionConfiguration *configurationForType(NetworkSession::Type type)
197 {
198     switch (type) {
199     case NetworkSession::Type::Normal:
200         return [NSURLSessionConfiguration defaultSessionConfiguration];
201     case NetworkSession::Type::Ephemeral:
202         return [NSURLSessionConfiguration ephemeralSessionConfiguration];
203     }
204 }
205
206 NetworkSession& NetworkSession::defaultSession()
207 {
208     ASSERT(isMainThread());
209     static NeverDestroyed<NetworkSession> session(NetworkSession::Type::Normal, WebCore::SessionID::defaultSessionID());
210     return session;
211 }
212
213 NetworkSession::NetworkSession(Type type, WebCore::SessionID sessionID)
214     : m_sessionID(sessionID)
215 {
216     m_sessionDelegate = adoptNS([[NetworkSessionDelegate alloc] initWithNetworkSession:*this]);
217
218     NSURLSessionConfiguration *configuration = configurationForType(type);
219
220 #if HAVE(TIMINGDATAOPTIONS)
221     configuration._timingDataOptions = _TimingDataOptionsEnableW3CNavigationTiming;
222 #else
223     setCollectsTimingData();
224 #endif
225
226     if (auto* storageSession = SessionTracker::storageSession(sessionID)) {
227         if (CFHTTPCookieStorageRef storage = storageSession->cookieStorage().get())
228             configuration.HTTPCookieStorage = [[[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:storage] autorelease];
229     }
230     m_session = [NSURLSession sessionWithConfiguration:configuration delegate:static_cast<id>(m_sessionDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
231 }
232
233 NetworkSession::~NetworkSession()
234 {
235     [m_session invalidateAndCancel];
236 }
237
238 Ref<NetworkDataTask> NetworkSession::createDataTaskWithRequest(const WebCore::ResourceRequest& request, NetworkSessionTaskClient& client)
239 {
240     return adoptRef(*new NetworkDataTask(*this, client, [m_session dataTaskWithRequest:request.nsURLRequest(WebCore::UpdateHTTPBody)]));
241 }
242
243 NetworkDataTask* NetworkSession::dataTaskForIdentifier(uint64_t taskIdentifier)
244 {
245     ASSERT(isMainThread());
246     return m_dataTaskMap.get(taskIdentifier);
247 }
248
249 NetworkDataTask::NetworkDataTask(NetworkSession& session, NetworkSessionTaskClient& client, RetainPtr<NSURLSessionDataTask>&& task)
250     : m_session(session)
251     , m_client(&client)
252     , m_task(WTF::move(task))
253 {
254     ASSERT(!m_session.m_dataTaskMap.contains(taskIdentifier()));
255     ASSERT(isMainThread());
256     m_session.m_dataTaskMap.add(taskIdentifier(), this);
257 }
258
259 NetworkDataTask::~NetworkDataTask()
260 {
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());
265 }
266
267 void NetworkDataTask::cancel()
268 {
269     [m_task cancel];
270 }
271
272 void NetworkDataTask::resume()
273 {
274     [m_task resume];
275 }
276
277 uint64_t NetworkDataTask::taskIdentifier()
278 {
279     return [m_task taskIdentifier];
280 }
281
282 }
283
284 #endif