1/*
2 * Copyright (C) 2012 Intel Corporation. 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#include "config.h"
27#include "ProxyResolverSoup.h"
28
29#include <libsoup/soup.h>
30#include <string.h>
31#include <wtf/Vector.h>
32#include <wtf/text/CString.h>
33#include <wtf/text/WTFString.h>
34
35static const char defaultNoProxyValue[] = "localhost,127.0.0.1";
36
37typedef struct {
38 SoupURI* proxyURI;
39 CString noProxy;
40 Vector<String> proxyExceptions;
41} SoupProxyResolverWkPrivate;
42
43#define SOUP_PROXY_RESOLVER_WK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), SOUP_TYPE_PROXY_RESOLVER_WK, SoupProxyResolverWkPrivate))
44
45static void soup_proxy_resolver_wk_interface_init(SoupProxyURIResolverInterface* proxyResolverInterface);
46
47G_DEFINE_TYPE_EXTENDED(SoupProxyResolverWk, soup_proxy_resolver_wk, G_TYPE_OBJECT, 0,
48 G_IMPLEMENT_INTERFACE(SOUP_TYPE_SESSION_FEATURE, 0)
49 G_IMPLEMENT_INTERFACE(SOUP_TYPE_PROXY_URI_RESOLVER, soup_proxy_resolver_wk_interface_init))
50
51enum {
52 PROP_0,
53 PROP_PROXY_URI,
54 PROP_NO_PROXY,
55 LAST_PROP
56};
57
58static void soup_proxy_resolver_wk_init(SoupProxyResolverWk* resolverWk)
59{
60}
61
62static void soupProxyResolverWkFinalize(GObject* object)
63{
64 SoupProxyResolverWkPrivate* priv = SOUP_PROXY_RESOLVER_WK_GET_PRIVATE(object);
65
66 g_clear_pointer(&priv->proxyURI, soup_uri_free);
67
68 G_OBJECT_CLASS(soup_proxy_resolver_wk_parent_class)->finalize(object);
69}
70
71static void soupProxyResolverWkSetProperty(GObject* object, uint propID, const GValue* value, GParamSpec* pspec)
72{
73 SoupProxyResolverWkPrivate* priv = SOUP_PROXY_RESOLVER_WK_GET_PRIVATE(object);
74
75 switch (propID) {
76 case PROP_PROXY_URI:
77 SoupURI* uri = static_cast<SoupURI*>(g_value_get_boxed(value));
78 if (priv->proxyURI)
79 soup_uri_free(priv->proxyURI);
80
81 priv->proxyURI = uri ? soup_uri_copy(uri) : 0;
82 break;
83 case PROP_NO_PROXY:
84 priv->noProxy = g_value_get_string(value);
85 priv->proxyExceptions.clear();
86 String::fromUTF8(priv->noProxy.data()).replace(' ', "").split(',', priv->proxyExceptions);
87 break;
88 default:
89 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
90 break;
91 }
92}
93
94static void soupProxyResolverWkGetProperty(GObject* object, uint propID, GValue* value, GParamSpec* pspec)
95{
96 SoupProxyResolverWkPrivate* priv = SOUP_PROXY_RESOLVER_WK_GET_PRIVATE(object);
97
98 switch (propID) {
99 case PROP_PROXY_URI:
100 g_value_set_boxed(value, priv->proxyURI);
101 break;
102 case PROP_NO_PROXY:
103 g_value_set_string(value, priv->noProxy.data());
104 break;
105 default:
106 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
107 break;
108 }
109}
110
111static bool shouldBypassProxy(SoupProxyResolverWkPrivate* priv, SoupURI* uri)
112{
113 const size_t exceptionCount = priv->proxyExceptions.size();
114 for (size_t i = 0; i < exceptionCount; ++i) {
115 if (String::fromUTF8(uri->host).endsWith(priv->proxyExceptions[i], false))
116 return true;
117 }
118
119 return false;
120}
121
122typedef struct {
123 SoupProxyURIResolver* proxyResolver;
124 SoupURI* uri;
125 SoupProxyURIResolverCallback callback;
126 void* userData;
127} SoupWkAsyncData;
128
129static gboolean idle_return_proxy_uri(void* data)
130{
131 SoupWkAsyncData* ssad = static_cast<SoupWkAsyncData*>(data);
132 SoupProxyResolverWkPrivate* priv = SOUP_PROXY_RESOLVER_WK_GET_PRIVATE(ssad->proxyResolver);
133
134 SoupURI* proxyURI = 0;
135 if (!shouldBypassProxy(priv, ssad->uri))
136 proxyURI = priv->proxyURI;
137
138 ssad->callback(ssad->proxyResolver, SOUP_STATUS_OK, proxyURI, ssad->userData);
139 g_object_unref(ssad->proxyResolver);
140 soup_uri_free(ssad->uri);
141 g_slice_free(SoupWkAsyncData, ssad);
142
143 return false;
144}
145
146static void soupProxyResolverWkGetProxyURIAsync(SoupProxyURIResolver* proxyResolver, SoupURI* uri, GMainContext* asyncContext, GCancellable* cancellable, SoupProxyURIResolverCallback callback, void* userData)
147{
148 SoupWkAsyncData* ssad;
149
150 ssad = g_slice_new0(SoupWkAsyncData);
151 ssad->proxyResolver = SOUP_PROXY_URI_RESOLVER(g_object_ref(proxyResolver));
152 ssad->uri = soup_uri_copy(uri);
153 ssad->callback = callback;
154 ssad->userData = userData;
155 soup_add_completion(asyncContext, idle_return_proxy_uri, ssad);
156}
157
158static uint soupProxyResolverWkGetProxyURISync(SoupProxyURIResolver* proxyResolver, SoupURI* uri, GCancellable* cancellable, SoupURI** proxyURI)
159{
160 SoupProxyResolverWkPrivate* priv = SOUP_PROXY_RESOLVER_WK_GET_PRIVATE(proxyResolver);
161
162 if (!shouldBypassProxy(priv, uri))
163 *proxyURI = soup_uri_copy(priv->proxyURI);
164
165 return SOUP_STATUS_OK;
166}
167
168static void soup_proxy_resolver_wk_class_init(SoupProxyResolverWkClass* wkClass)
169{
170 GObjectClass* object_class = G_OBJECT_CLASS(wkClass);
171
172 g_type_class_add_private(wkClass, sizeof(SoupProxyResolverWkPrivate));
173
174 object_class->set_property = soupProxyResolverWkSetProperty;
175 object_class->get_property = soupProxyResolverWkGetProperty;
176 object_class->finalize = soupProxyResolverWkFinalize;
177
178 g_object_class_install_property(object_class, PROP_PROXY_URI,
179 g_param_spec_boxed(SOUP_PROXY_RESOLVER_WK_PROXY_URI,
180 "Proxy URI",
181 "The HTTP Proxy to use",
182 SOUP_TYPE_URI,
183 static_cast<GParamFlags>(G_PARAM_READWRITE)));
184
185 g_object_class_install_property(object_class, PROP_NO_PROXY,
186 g_param_spec_string(SOUP_PROXY_RESOLVER_WK_NO_PROXY,
187 "Proxy exceptions",
188 "Comma-separated proxy exceptions",
189 defaultNoProxyValue,
190 static_cast<GParamFlags>(G_PARAM_READWRITE)));
191}
192
193static void soup_proxy_resolver_wk_interface_init(SoupProxyURIResolverInterface* proxy_uri_resolver_interface)
194{
195 proxy_uri_resolver_interface->get_proxy_uri_async = soupProxyResolverWkGetProxyURIAsync;
196 proxy_uri_resolver_interface->get_proxy_uri_sync = soupProxyResolverWkGetProxyURISync;
197}
198
199SoupProxyURIResolver* soupProxyResolverWkNew(const char* httpProxy, const char* noProxy)
200{
201 SoupURI* proxyURI = soup_uri_new(httpProxy);
202 SoupProxyURIResolver* resolver = SOUP_PROXY_URI_RESOLVER(g_object_new(SOUP_TYPE_PROXY_RESOLVER_WK,
203 SOUP_PROXY_RESOLVER_WK_PROXY_URI, proxyURI,
204 SOUP_PROXY_RESOLVER_WK_NO_PROXY, noProxy ? noProxy : defaultNoProxyValue,
205 0));
206 soup_uri_free(proxyURI);
207
208 return resolver;
209}