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