a92e1a603aacab5aa8639be1300ee7e8ced3a87c
[WebKit-https.git] / Source / WebCore / platform / network / ResourceHandle.cpp
1 /*
2  * Copyright (C) 2004-2017 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "ResourceHandle.h"
28 #include "ResourceHandleInternal.h"
29
30 #include "Logging.h"
31 #include "NetworkingContext.h"
32 #include "NotImplemented.h"
33 #include "ResourceHandleClient.h"
34 #include "Timer.h"
35 #include <algorithm>
36 #include <wtf/MainThread.h>
37 #include <wtf/NeverDestroyed.h>
38 #include <wtf/text/AtomicStringHash.h>
39 #include <wtf/text/CString.h>
40
41 namespace WebCore {
42
43 static bool shouldForceContentSniffing;
44
45 typedef HashMap<AtomicString, ResourceHandle::BuiltinConstructor> BuiltinResourceHandleConstructorMap;
46 static BuiltinResourceHandleConstructorMap& builtinResourceHandleConstructorMap()
47 {
48 #if PLATFORM(IOS)
49     ASSERT(WebThreadIsLockedOrDisabled());
50 #else
51     ASSERT(isMainThread());
52 #endif
53     static NeverDestroyed<BuiltinResourceHandleConstructorMap> map;
54     return map;
55 }
56
57 void ResourceHandle::registerBuiltinConstructor(const AtomicString& protocol, ResourceHandle::BuiltinConstructor constructor)
58 {
59     builtinResourceHandleConstructorMap().add(protocol, constructor);
60 }
61
62 typedef HashMap<AtomicString, ResourceHandle::BuiltinSynchronousLoader> BuiltinResourceHandleSynchronousLoaderMap;
63 static BuiltinResourceHandleSynchronousLoaderMap& builtinResourceHandleSynchronousLoaderMap()
64 {
65     ASSERT(isMainThread());
66     static NeverDestroyed<BuiltinResourceHandleSynchronousLoaderMap> map;
67     return map;
68 }
69
70 void ResourceHandle::registerBuiltinSynchronousLoader(const AtomicString& protocol, ResourceHandle::BuiltinSynchronousLoader loader)
71 {
72     builtinResourceHandleSynchronousLoaderMap().add(protocol, loader);
73 }
74
75 ResourceHandle::ResourceHandle(NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff, bool shouldContentEncodingSniff)
76     : d(std::make_unique<ResourceHandleInternal>(this, context, request, client, defersLoading, shouldContentSniff && shouldContentSniffURL(request.url()), shouldContentEncodingSniff))
77 {
78     if (!request.url().isValid()) {
79         scheduleFailure(InvalidURLFailure);
80         return;
81     }
82
83     if (!portAllowed(request.url())) {
84         scheduleFailure(BlockedFailure);
85         return;
86     }
87 }
88
89 RefPtr<ResourceHandle> ResourceHandle::create(NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff, bool shouldContentEncodingSniff)
90 {
91     if (auto constructor = builtinResourceHandleConstructorMap().get(request.url().protocol().toStringWithoutCopying()))
92         return constructor(request, client);
93
94     auto newHandle = adoptRef(*new ResourceHandle(context, request, client, defersLoading, shouldContentSniff, shouldContentEncodingSniff));
95
96     if (newHandle->d->m_scheduledFailureType != NoFailure)
97         return WTFMove(newHandle);
98
99     if (newHandle->start())
100         return WTFMove(newHandle);
101
102     return nullptr;
103 }
104
105 void ResourceHandle::scheduleFailure(FailureType type)
106 {
107     d->m_scheduledFailureType = type;
108     d->m_failureTimer.startOneShot(0_s);
109 }
110
111 void ResourceHandle::failureTimerFired()
112 {
113     if (!client())
114         return;
115
116     switch (d->m_scheduledFailureType) {
117         case NoFailure:
118             ASSERT_NOT_REACHED();
119             return;
120         case BlockedFailure:
121             d->m_scheduledFailureType = NoFailure;
122             client()->wasBlocked(this);
123             return;
124         case InvalidURLFailure:
125             d->m_scheduledFailureType = NoFailure;
126             client()->cannotShowURL(this);
127             return;
128     }
129
130     ASSERT_NOT_REACHED();
131 }
132
133 void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentialsPolicy storedCredentialsPolicy, ResourceError& error, ResourceResponse& response, Vector<char>& data)
134 {
135     if (auto constructor = builtinResourceHandleSynchronousLoaderMap().get(request.url().protocol().toStringWithoutCopying())) {
136         constructor(context, request, storedCredentialsPolicy, error, response, data);
137         return;
138     }
139
140     platformLoadResourceSynchronously(context, request, storedCredentialsPolicy, error, response, data);
141 }
142
143 ResourceHandleClient* ResourceHandle::client() const
144 {
145     return d->m_client;
146 }
147
148 void ResourceHandle::clearClient()
149 {
150     d->m_client = nullptr;
151 }
152
153 void ResourceHandle::didReceiveResponse(ResourceResponse&& response)
154 {
155     if (response.isHTTP09()) {
156         auto url = response.url();
157         std::optional<uint16_t> port = url.port();
158         if (port && !isDefaultPortForProtocol(port.value(), url.protocol())) {
159             cancel();
160             String message = "Cancelled load from '" + url.stringCenterEllipsizedToLength() + "' because it is using HTTP/0.9.";
161             d->m_client->didFail(this, { String(), 0, url, message });
162             continueDidReceiveResponse();
163             return;
164         }
165     }
166     client()->didReceiveResponseAsync(this, WTFMove(response));
167 }
168
169 #if !USE(SOUP) && !USE(CURL)
170 void ResourceHandle::platformContinueSynchronousDidReceiveResponse()
171 {
172     // Do nothing.
173 }
174 #endif
175
176 ResourceRequest& ResourceHandle::firstRequest()
177 {
178     return d->m_firstRequest;
179 }
180
181 NetworkingContext* ResourceHandle::context() const
182 {
183     return d->m_context.get();
184 }
185
186 const String& ResourceHandle::lastHTTPMethod() const
187 {
188     return d->m_lastHTTPMethod;
189 }
190
191 bool ResourceHandle::hasAuthenticationChallenge() const
192 {
193     return !d->m_currentWebChallenge.isNull();
194 }
195
196 void ResourceHandle::clearAuthentication()
197 {
198 #if PLATFORM(COCOA)
199     d->m_currentMacChallenge = nil;
200 #endif
201     d->m_currentWebChallenge.nullify();
202 }
203   
204 bool ResourceHandle::shouldContentSniff() const
205 {
206     return d->m_shouldContentSniff;
207 }
208
209 bool ResourceHandle::shouldContentEncodingSniff() const
210 {
211     return d->m_shouldContentEncodingSniff;
212 }
213
214 bool ResourceHandle::shouldContentSniffURL(const URL& url)
215 {
216 #if PLATFORM(COCOA)
217     if (shouldForceContentSniffing)
218         return true;
219 #endif
220     // We shouldn't content sniff file URLs as their MIME type should be established via their extension.
221     return !url.protocolIs("file");
222 }
223
224 void ResourceHandle::forceContentSniffing()
225 {
226     shouldForceContentSniffing = true;
227 }
228
229 void ResourceHandle::setDefersLoading(bool defers)
230 {
231     LOG(Network, "Handle %p setDefersLoading(%s)", this, defers ? "true" : "false");
232
233     ASSERT(d->m_defersLoading != defers); // Deferring is not counted, so calling setDefersLoading() repeatedly is likely to be in error.
234     d->m_defersLoading = defers;
235
236     if (defers) {
237         ASSERT(d->m_failureTimer.isActive() == (d->m_scheduledFailureType != NoFailure));
238         if (d->m_failureTimer.isActive())
239             d->m_failureTimer.stop();
240     } else if (d->m_scheduledFailureType != NoFailure) {
241         ASSERT(!d->m_failureTimer.isActive());
242         d->m_failureTimer.startOneShot(0_s);
243     }
244
245     platformSetDefersLoading(defers);
246 }
247
248 } // namespace WebCore