[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / WebCore / platform / network / soup / DNSResolveQueueSoup.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc.  All rights reserved.
3  * Copyright (C) 2009, 2012 Igalia S.L.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "DNSResolveQueueSoup.h"
29
30 #if USE(SOUP)
31
32 #include <libsoup/soup.h>
33 #include <wtf/CompletionHandler.h>
34 #include <wtf/Function.h>
35 #include <wtf/MainThread.h>
36 #include <wtf/NeverDestroyed.h>
37 #include <wtf/glib/GUniquePtr.h>
38 #include <wtf/text/CString.h>
39
40 namespace WebCore {
41
42 // Initially true to ensure prefetch stays disabled until we have proxy settings.
43 static bool isUsingHttpProxy = true;
44 static bool isUsingHttpsProxy = true;
45
46 static bool didResolveProxy(char** uris)
47 {
48     // We have a list of possible proxies to use for the URI. If the first item in the list is
49     // direct:// (the usual case), then the user prefers not to use a proxy. This is similar to
50     // resolving hostnames: there could be many possibilities returned in order of preference, and
51     // if we're trying to connect we should attempt each one in order, but here we are not trying
52     // to connect, merely to decide whether a proxy "should" be used.
53     return uris && *uris && strcmp(*uris, "direct://");
54 }
55
56 static void didResolveProxy(GProxyResolver* resolver, GAsyncResult* result, bool* isUsingProxyType, bool* isUsingProxy)
57 {
58     GUniqueOutPtr<GError> error;
59     GUniquePtr<char*> uris(g_proxy_resolver_lookup_finish(resolver, result, &error.outPtr()));
60     if (error) {
61         WTFLogAlways("Error determining system proxy settings: %s", error->message);
62         return;
63     }
64
65     *isUsingProxyType = didResolveProxy(uris.get());
66     *isUsingProxy = isUsingHttpProxy || isUsingHttpsProxy;
67 }
68
69 static void proxyResolvedForHttpUriCallback(GObject* source, GAsyncResult* result, void* userData)
70 {
71     didResolveProxy(G_PROXY_RESOLVER(source), result, &isUsingHttpProxy, static_cast<bool*>(userData));
72 }
73
74 static void proxyResolvedForHttpsUriCallback(GObject* source, GAsyncResult* result, void* userData)
75 {
76     didResolveProxy(G_PROXY_RESOLVER(source), result, &isUsingHttpsProxy, static_cast<bool*>(userData));
77 }
78
79 Function<SoupSession*()>& globalDefaultSoupSessionAccessor()
80 {
81     static NeverDestroyed<Function<SoupSession*()>> accessor;
82     return accessor.get();
83 }
84
85 void DNSResolveQueueSoup::setGlobalDefaultSoupSessionAccessor(Function<SoupSession*()>&& accessor)
86 {
87     globalDefaultSoupSessionAccessor() = WTFMove(accessor);
88 }
89
90 void DNSResolveQueueSoup::updateIsUsingProxy()
91 {
92     GRefPtr<GProxyResolver> resolver;
93     g_object_get(globalDefaultSoupSessionAccessor()(), "proxy-resolver", &resolver.outPtr(), nullptr);
94     ASSERT(resolver);
95
96     g_proxy_resolver_lookup_async(resolver.get(), "http://example.com/", nullptr, proxyResolvedForHttpUriCallback, &m_isUsingProxy);
97     g_proxy_resolver_lookup_async(resolver.get(), "https://example.com/", nullptr, proxyResolvedForHttpsUriCallback, &m_isUsingProxy);
98 }
99
100 static void resolvedCallback(SoupAddress*, guint, void*)
101 {
102     DNSResolveQueue::singleton().decrementRequestCount();
103 }
104
105 static void resolvedWithObserverCallback(SoupAddress* address, guint status, void* data)
106 {
107     ASSERT(data);
108     auto* resolveQueue = static_cast<DNSResolveQueueSoup*>(data);
109
110     uint64_t identifier = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(address), "identifier"));
111
112     auto completionAndCancelHandlers = resolveQueue->takeCompletionAndCancelHandlers(identifier);
113
114     if (!completionAndCancelHandlers)
115         return;
116
117     auto completionHandler = WTFMove(completionAndCancelHandlers.get()->first);
118
119     if (status != SOUP_STATUS_OK) {
120         DNSError error = DNSError::Unknown;
121
122         switch (status) {
123         case SOUP_STATUS_CANT_RESOLVE:
124             error = DNSError::CannotResolve;
125             break;
126         case SOUP_STATUS_CANCELLED:
127             error = DNSError::Cancelled;
128             break;
129         case SOUP_STATUS_OK:
130         default:
131             ASSERT_NOT_REACHED();
132         };
133
134         completionHandler(makeUnexpected(error));
135         return;
136     }
137
138     if (!soup_address_is_resolved(address)) {
139         completionHandler(makeUnexpected(DNSError::Unknown));
140         return;
141     }
142
143     Vector<WebCore::IPAddress> addresses;
144     addresses.reserveInitialCapacity(1);
145     int len;
146     auto* ipAddress = reinterpret_cast<const struct sockaddr_in*>(soup_address_get_sockaddr(address, &len));
147     for (unsigned i = 0; i < sizeof(*ipAddress) / len; i++)
148         addresses.uncheckedAppend(WebCore::IPAddress(ipAddress[i]));
149
150     completionHandler(addresses);
151 }
152
153 std::unique_ptr<DNSResolveQueueSoup::CompletionAndCancelHandlers> DNSResolveQueueSoup::takeCompletionAndCancelHandlers(uint64_t identifier)
154 {
155     ASSERT(isMainThread());
156
157     auto completionAndCancelHandlers = m_completionAndCancelHandlers.take(identifier);
158
159     if (!completionAndCancelHandlers)
160         return nullptr;
161
162     return completionAndCancelHandlers;
163 }
164
165 void DNSResolveQueueSoup::removeCancelAndCompletionHandler(uint64_t identifier)
166 {
167     ASSERT(isMainThread());
168
169     m_completionAndCancelHandlers.remove(identifier);
170 }
171
172 void DNSResolveQueueSoup::platformResolve(const String& hostname)
173 {
174     ASSERT(isMainThread());
175
176     soup_session_prefetch_dns(globalDefaultSoupSessionAccessor()(), hostname.utf8().data(), nullptr, resolvedCallback, nullptr);
177 }
178
179 void DNSResolveQueueSoup::resolve(const String& hostname, uint64_t identifier, DNSCompletionHandler&& completionHandler)
180 {
181     ASSERT(isMainThread());
182
183     auto address = adoptGRef(soup_address_new(hostname.utf8().data(), 0));
184     auto cancellable = adoptGRef(g_cancellable_new());
185     soup_address_resolve_async(address.get(), soup_session_get_async_context(globalDefaultSoupSessionAccessor()()), cancellable.get(), resolvedWithObserverCallback, this);
186
187     g_object_set_data(G_OBJECT(address.get()), "identifier", GUINT_TO_POINTER(identifier));
188
189     m_completionAndCancelHandlers.add(identifier, makeUniqueWithoutFastMallocCheck<DNSResolveQueueSoup::CompletionAndCancelHandlers>(WTFMove(completionHandler), WTFMove(cancellable)));
190 }
191
192 void DNSResolveQueueSoup::stopResolve(uint64_t identifier)
193 {
194     ASSERT(isMainThread());
195
196     if (auto completionAndCancelHandler = m_completionAndCancelHandlers.take(identifier)) {
197         g_cancellable_cancel(completionAndCancelHandler.get()->second.get());
198         completionAndCancelHandler.get()->first(makeUnexpected(DNSError::Cancelled));
199     }
200 }
201
202 }
203
204 #endif