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/CompletionHandler.h>
37#include <wtf/MainThread.h>
38#include <wtf/NeverDestroyed.h>
39#include <wtf/text/AtomicStringHash.h>
40#include <wtf/text/CString.h>
41
42namespace WebCore {
43
44static bool shouldForceContentSniffing;
45
46typedef HashMap<AtomicString, ResourceHandle::BuiltinConstructor> BuiltinResourceHandleConstructorMap;
47static BuiltinResourceHandleConstructorMap& builtinResourceHandleConstructorMap()
48{
49#if PLATFORM(IOS_FAMILY)
50 ASSERT(WebThreadIsLockedOrDisabled());
51#else
52 ASSERT(isMainThread());
53#endif
54 static NeverDestroyed<BuiltinResourceHandleConstructorMap> map;
55 return map;
56}
57
58void ResourceHandle::registerBuiltinConstructor(const AtomicString& protocol, ResourceHandle::BuiltinConstructor constructor)
59{
60 builtinResourceHandleConstructorMap().add(protocol, constructor);
61}
62
63typedef HashMap<AtomicString, ResourceHandle::BuiltinSynchronousLoader> BuiltinResourceHandleSynchronousLoaderMap;
64static BuiltinResourceHandleSynchronousLoaderMap& builtinResourceHandleSynchronousLoaderMap()
65{
66 ASSERT(isMainThread());
67 static NeverDestroyed<BuiltinResourceHandleSynchronousLoaderMap> map;
68 return map;
69}
70
71void ResourceHandle::registerBuiltinSynchronousLoader(const AtomicString& protocol, ResourceHandle::BuiltinSynchronousLoader loader)
72{
73 builtinResourceHandleSynchronousLoaderMap().add(protocol, loader);
74}
75
76ResourceHandle::ResourceHandle(NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff, bool shouldContentEncodingSniff)
77 : d(std::make_unique<ResourceHandleInternal>(this, context, request, client, defersLoading, shouldContentSniff && shouldContentSniffURL(request.url()), shouldContentEncodingSniff))
78{
79 if (!request.url().isValid()) {
80 scheduleFailure(InvalidURLFailure);
81 return;
82 }
83
84 if (!portAllowed(request.url())) {
85 scheduleFailure(BlockedFailure);
86 return;
87 }
88}
89
90RefPtr<ResourceHandle> ResourceHandle::create(NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff, bool shouldContentEncodingSniff)
91{
92 if (auto constructor = builtinResourceHandleConstructorMap().get(request.url().protocol().toStringWithoutCopying()))
93 return constructor(request, client);
94
95 auto newHandle = adoptRef(*new ResourceHandle(context, request, client, defersLoading, shouldContentSniff, shouldContentEncodingSniff));
96
97 if (newHandle->d->m_scheduledFailureType != NoFailure)
98 return newHandle;
99
100 if (newHandle->start())
101 return newHandle;
102
103 return nullptr;
104}
105
106void ResourceHandle::scheduleFailure(FailureType type)
107{
108 d->m_scheduledFailureType = type;
109 d->m_failureTimer.startOneShot(0_s);
110}
111
112void ResourceHandle::failureTimerFired()
113{
114 if (!client())
115 return;
116
117 switch (d->m_scheduledFailureType) {
118 case NoFailure:
119 ASSERT_NOT_REACHED();
120 return;
121 case BlockedFailure:
122 d->m_scheduledFailureType = NoFailure;
123 client()->wasBlocked(this);
124 return;
125 case InvalidURLFailure:
126 d->m_scheduledFailureType = NoFailure;
127 client()->cannotShowURL(this);
128 return;
129 }
130
131 ASSERT_NOT_REACHED();
132}
133
134void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentialsPolicy storedCredentialsPolicy, ResourceError& error, ResourceResponse& response, Vector<char>& data)
135{
136 if (auto constructor = builtinResourceHandleSynchronousLoaderMap().get(request.url().protocol().toStringWithoutCopying())) {
137 constructor(context, request, storedCredentialsPolicy, error, response, data);
138 return;
139 }
140
141 platformLoadResourceSynchronously(context, request, storedCredentialsPolicy, error, response, data);
142}
143
144ResourceHandleClient* ResourceHandle::client() const
145{
146 return d->m_client;
147}
148
149void ResourceHandle::clearClient()
150{
151 d->m_client = nullptr;
152}
153
154void ResourceHandle::didReceiveResponse(ResourceResponse&& response, CompletionHandler<void()>&& completionHandler)
155{
156 if (response.isHTTP09()) {
157 auto url = response.url();
158 Optional<uint16_t> port = url.port();
159 if (port && !WTF::isDefaultPortForProtocol(port.value(), url.protocol())) {
160 cancel();
161 String message = "Cancelled load from '" + url.stringCenterEllipsizedToLength() + "' because it is using HTTP/0.9.";
162 d->m_client->didFail(this, { String(), 0, url, message });
163 completionHandler();
164 return;
165 }
166 }
167 client()->didReceiveResponseAsync(this, WTFMove(response), WTFMove(completionHandler));
168}
169
170#if !USE(SOUP) && !USE(CURL)
171void ResourceHandle::platformContinueSynchronousDidReceiveResponse()
172{
173 // Do nothing.
174}
175#endif
176
177ResourceRequest& ResourceHandle::firstRequest()
178{
179 return d->m_firstRequest;
180}
181
182NetworkingContext* ResourceHandle::context() const
183{
184 return d->m_context.get();
185}
186
187const String& ResourceHandle::lastHTTPMethod() const
188{
189 return d->m_lastHTTPMethod;
190}
191
192bool ResourceHandle::hasAuthenticationChallenge() const
193{
194 return !d->m_currentWebChallenge.isNull();
195}
196
197void ResourceHandle::clearAuthentication()
198{
199#if PLATFORM(COCOA)
200 d->m_currentMacChallenge = nil;
201#endif
202 d->m_currentWebChallenge.nullify();
203}
204
205bool ResourceHandle::shouldContentSniff() const
206{
207 return d->m_shouldContentSniff;
208}
209
210bool ResourceHandle::shouldContentEncodingSniff() const
211{
212 return d->m_shouldContentEncodingSniff;
213}
214
215bool ResourceHandle::shouldContentSniffURL(const URL& url)
216{
217#if PLATFORM(COCOA)
218 if (shouldForceContentSniffing)
219 return true;
220#endif
221 // We shouldn't content sniff file URLs as their MIME type should be established via their extension.
222 return !url.protocolIs("file");
223}
224
225void ResourceHandle::forceContentSniffing()
226{
227 shouldForceContentSniffing = true;
228}
229
230void ResourceHandle::setDefersLoading(bool defers)
231{
232 LOG(Network, "Handle %p setDefersLoading(%s)", this, defers ? "true" : "false");
233
234 ASSERT(d->m_defersLoading != defers); // Deferring is not counted, so calling setDefersLoading() repeatedly is likely to be in error.
235 d->m_defersLoading = defers;
236
237 if (defers) {
238 ASSERT(d->m_failureTimer.isActive() == (d->m_scheduledFailureType != NoFailure));
239 if (d->m_failureTimer.isActive())
240 d->m_failureTimer.stop();
241 } else if (d->m_scheduledFailureType != NoFailure) {
242 ASSERT(!d->m_failureTimer.isActive());
243 d->m_failureTimer.startOneShot(0_s);
244 }
245
246 platformSetDefersLoading(defers);
247}
248
249} // namespace WebCore
250