73371b1b8f8b16fc975b6f7e8edd57d38628e64a
[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/ResourceError.h>
37 #import <WebCore/ResourceRequest.h>
38 #import <WebCore/ResourceResponse.h>
39 #import <WebCore/SharedBuffer.h>
40 #import <wtf/MainThread.h>
41 #import <wtf/NeverDestroyed.h>
42
43 static NSURLSessionResponseDisposition toNSURLSessionResponseDisposition(WebKit::ResponseDisposition disposition)
44 {
45     switch (disposition) {
46     case WebKit::ResponseDisposition::Cancel:
47         return NSURLSessionResponseCancel;
48     case WebKit::ResponseDisposition::Allow:
49         return NSURLSessionResponseAllow;
50     case WebKit::ResponseDisposition::BecomeDownload:
51         return NSURLSessionResponseBecomeDownload;
52     }
53 }
54
55 static NSURLSessionAuthChallengeDisposition toNSURLSessionAuthChallengeDisposition(WebKit::AuthenticationChallengeDisposition disposition)
56 {
57     switch (disposition) {
58     case WebKit::AuthenticationChallengeDisposition::UseCredential:
59         return NSURLSessionAuthChallengeUseCredential;
60     case WebKit::AuthenticationChallengeDisposition::PerformDefaultHandling:
61         return NSURLSessionAuthChallengePerformDefaultHandling;
62     case WebKit::AuthenticationChallengeDisposition::Cancel:
63         return NSURLSessionAuthChallengeCancelAuthenticationChallenge;
64     case WebKit::AuthenticationChallengeDisposition::RejectProtectionSpace:
65         return NSURLSessionAuthChallengeRejectProtectionSpace;
66     }
67 }
68
69 @interface NetworkSessionDelegate : NSObject <NSURLSessionDataDelegate> {
70     WebKit::NetworkSession* _session;
71 }
72
73 - (id)initWithNetworkSession:(WebKit::NetworkSession&)session;
74
75 @end
76
77 @implementation NetworkSessionDelegate
78
79 - (id)initWithNetworkSession:(WebKit::NetworkSession&)session
80 {
81     self = [super init];
82     if (!self)
83         return nil;
84
85     _session = &session;
86
87     return self;
88 }
89
90 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler
91 {
92     UNUSED_PARAM(session);
93
94     if (auto* networkingTask = _session->dataTaskForIdentifier(task.taskIdentifier)) {
95         if (auto* client = networkingTask->client()) {
96             client->willPerformHTTPRedirection(response, request, ^(const WebCore::ResourceRequest& request)
97                 {
98                     completionHandler(request.nsURLRequest(WebCore::UpdateHTTPBody));
99                 }
100             );
101         }
102     }
103 }
104
105 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
106 {
107     UNUSED_PARAM(session);
108
109     if (auto* networkingTask = _session->dataTaskForIdentifier(task.taskIdentifier)) {
110         if (auto* client = networkingTask->client()) {
111             client->didReceiveChallenge(challenge, ^(WebKit::AuthenticationChallengeDisposition disposition, const WebCore::Credential& credential)
112                 {
113                     completionHandler(toNSURLSessionAuthChallengeDisposition(disposition), credential.nsCredential());
114                 }
115             );
116         }
117     }
118 }
119
120 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
121 {
122     UNUSED_PARAM(session);
123
124     if (auto* networkingTask = _session->dataTaskForIdentifier(task.taskIdentifier)) {
125         if (auto* client = networkingTask->client())
126             client->didCompleteWithError(error);
127     }
128 }
129
130 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
131 {
132     UNUSED_PARAM(session);
133
134     if (auto* networkingTask = _session->dataTaskForIdentifier(dataTask.taskIdentifier)) {
135         if (auto* client = networkingTask->client()) {
136             ASSERT(isMainThread());
137             WebCore::ResourceResponse resourceResponse(response);
138             client->didReceiveResponse(resourceResponse, ^(WebKit::ResponseDisposition responseDisposition)
139                 {
140                     completionHandler(toNSURLSessionResponseDisposition(responseDisposition));
141                 }
142             );
143         }
144     }
145 }
146
147 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
148 {
149     UNUSED_PARAM(session);
150
151     if (auto* networkingTask = _session->dataTaskForIdentifier(dataTask.taskIdentifier)) {
152         if (auto* client = networkingTask->client())
153             client->didReceiveData(WebCore::SharedBuffer::wrapNSData(data));
154     }
155 }
156
157 @end
158
159 namespace WebKit {
160     
161 Ref<NetworkSession> NetworkSession::create(Type type)
162 {
163     return adoptRef(*new NetworkSession(type));
164 }
165
166 Ref<NetworkSession> NetworkSession::singleton()
167 {
168     static NeverDestroyed<Ref<NetworkSession>> sharedInstance(NetworkSession::create(Type::Normal));
169     return sharedInstance.get().copyRef();
170 }
171     
172 static NSURLSessionConfiguration *configurationForType(NetworkSession::Type type)
173 {
174     switch (type) {
175     case NetworkSession::Type::Normal:
176         return [NSURLSessionConfiguration defaultSessionConfiguration];
177     case NetworkSession::Type::Ephemeral:
178         return [NSURLSessionConfiguration ephemeralSessionConfiguration];
179     }
180 }
181
182 NetworkSession::NetworkSession(Type type)
183 {
184     m_sessionDelegate = adoptNS([[NetworkSessionDelegate alloc] initWithNetworkSession:*this]);
185
186     NSURLSessionConfiguration *configuration = configurationForType(type);
187     // FIXME: Use SessionTracker to make sure the correct cookie storage is used once there is more than one NetworkSession.
188     if (auto* session = SessionTracker::session(WebCore::SessionID::defaultSessionID())) {
189         if (CFHTTPCookieStorageRef storage = session->cookieStorage().get())
190             configuration.HTTPCookieStorage = [[[NSHTTPCookieStorage alloc] _initWithCFHTTPCookieStorage:storage] autorelease];
191     }
192     m_session = [NSURLSession sessionWithConfiguration:configuration delegate:static_cast<id>(m_sessionDelegate.get()) delegateQueue:[NSOperationQueue mainQueue]];
193 }
194
195 Ref<NetworkingDataTask> NetworkSession::createDataTaskWithRequest(const WebCore::ResourceRequest& request, NetworkSessionTaskClient& client)
196 {
197     return adoptRef(*new NetworkingDataTask(*this, client, [m_session dataTaskWithRequest:request.nsURLRequest(WebCore::UpdateHTTPBody)]));
198 }
199
200 NetworkingDataTask* NetworkSession::dataTaskForIdentifier(uint64_t taskIdentifier)
201 {
202     ASSERT(isMainThread());
203     return m_dataTaskMap.get(taskIdentifier);
204 }
205
206 NetworkingDataTask::NetworkingDataTask(NetworkSession& session, NetworkSessionTaskClient& client, RetainPtr<NSURLSessionDataTask> task)
207     : m_session(session)
208     , m_task(task)
209     , m_client(&client)
210 {
211     ASSERT(!m_session.m_dataTaskMap.contains(taskIdentifier()));
212     ASSERT(isMainThread());
213     m_session.m_dataTaskMap.add(taskIdentifier(), this);
214 }
215
216 NetworkingDataTask::~NetworkingDataTask()
217 {
218     ASSERT(m_session.m_dataTaskMap.contains(taskIdentifier()));
219     ASSERT(m_session.m_dataTaskMap.get(taskIdentifier()) == this);
220     ASSERT(isMainThread());
221     m_session.m_dataTaskMap.remove(taskIdentifier());
222 }
223
224 void NetworkingDataTask::suspend()
225 {
226     [m_task suspend];
227 }
228
229 void NetworkingDataTask::resume()
230 {
231     [m_task resume];
232 }
233
234 uint64_t NetworkingDataTask::taskIdentifier()
235 {
236     return [m_task taskIdentifier];
237 }
238
239 }
240
241 #endif