[Sync] Finish transition to new AndroidSyncSettings interface.
[chromium-blink-merge.git] / net / proxy / proxy_resolver_mac.cc
blob521bd6a7a4a7c34054cb5611ff91256caa2e0fd1
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/proxy/proxy_resolver_mac.h"
7 #include <CoreFoundation/CoreFoundation.h>
9 #include "base/logging.h"
10 #include "base/mac/foundation_util.h"
11 #include "base/mac/scoped_cftyperef.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "net/base/net_errors.h"
15 #include "net/proxy/proxy_info.h"
16 #include "net/proxy/proxy_resolver.h"
17 #include "net/proxy/proxy_server.h"
19 #if defined(OS_IOS)
20 #include <CFNetwork/CFProxySupport.h>
21 #else
22 #include <CoreServices/CoreServices.h>
23 #endif
25 namespace net {
27 namespace {
29 // Utility function to map a CFProxyType to a ProxyServer::Scheme.
30 // If the type is unknown, returns ProxyServer::SCHEME_INVALID.
31 ProxyServer::Scheme GetProxyServerScheme(CFStringRef proxy_type) {
32 if (CFEqual(proxy_type, kCFProxyTypeNone))
33 return ProxyServer::SCHEME_DIRECT;
34 if (CFEqual(proxy_type, kCFProxyTypeHTTP))
35 return ProxyServer::SCHEME_HTTP;
36 if (CFEqual(proxy_type, kCFProxyTypeHTTPS)) {
37 // The "HTTPS" on the Mac side here means "proxy applies to https://" URLs;
38 // the proxy itself is still expected to be an HTTP proxy.
39 return ProxyServer::SCHEME_HTTP;
41 if (CFEqual(proxy_type, kCFProxyTypeSOCKS)) {
42 // We can't tell whether this was v4 or v5. We will assume it is
43 // v5 since that is the only version OS X supports.
44 return ProxyServer::SCHEME_SOCKS5;
46 return ProxyServer::SCHEME_INVALID;
49 // Callback for CFNetworkExecuteProxyAutoConfigurationURL. |client| is a pointer
50 // to a CFTypeRef. This stashes either |error| or |proxies| in that location.
51 void ResultCallback(void* client, CFArrayRef proxies, CFErrorRef error) {
52 DCHECK((proxies != NULL) == (error == NULL));
54 CFTypeRef* result_ptr = reinterpret_cast<CFTypeRef*>(client);
55 DCHECK(result_ptr != NULL);
56 DCHECK(*result_ptr == NULL);
58 if (error != NULL) {
59 *result_ptr = CFRetain(error);
60 } else {
61 *result_ptr = CFRetain(proxies);
63 CFRunLoopStop(CFRunLoopGetCurrent());
66 class ProxyResolverMac : public ProxyResolver {
67 public:
68 explicit ProxyResolverMac(
69 const scoped_refptr<ProxyResolverScriptData>& script_data);
70 ~ProxyResolverMac() override;
72 // ProxyResolver methods:
73 int GetProxyForURL(const GURL& url,
74 ProxyInfo* results,
75 const CompletionCallback& callback,
76 RequestHandle* request,
77 const BoundNetLog& net_log) override;
79 void CancelRequest(RequestHandle request) override;
81 LoadState GetLoadState(RequestHandle request) const override;
83 void CancelSetPacScript() override;
85 int SetPacScript(const scoped_refptr<ProxyResolverScriptData>& script_data,
86 const CompletionCallback& /*callback*/) override;
88 private:
89 const scoped_refptr<ProxyResolverScriptData> script_data_;
92 ProxyResolverMac::ProxyResolverMac(
93 const scoped_refptr<ProxyResolverScriptData>& script_data)
94 : ProxyResolver(false /*expects_pac_bytes*/), script_data_(script_data) {
97 ProxyResolverMac::~ProxyResolverMac() {}
99 // Gets the proxy information for a query URL from a PAC. Implementation
100 // inspired by http://developer.apple.com/samplecode/CFProxySupportTool/
101 int ProxyResolverMac::GetProxyForURL(const GURL& query_url,
102 ProxyInfo* results,
103 const CompletionCallback& /*callback*/,
104 RequestHandle* /*request*/,
105 const BoundNetLog& net_log) {
106 base::ScopedCFTypeRef<CFStringRef> query_ref(
107 base::SysUTF8ToCFStringRef(query_url.spec()));
108 base::ScopedCFTypeRef<CFURLRef> query_url_ref(
109 CFURLCreateWithString(kCFAllocatorDefault, query_ref.get(), NULL));
110 if (!query_url_ref.get())
111 return ERR_FAILED;
112 base::ScopedCFTypeRef<CFStringRef> pac_ref(base::SysUTF8ToCFStringRef(
113 script_data_->type() == ProxyResolverScriptData::TYPE_AUTO_DETECT
114 ? std::string()
115 : script_data_->url().spec()));
116 base::ScopedCFTypeRef<CFURLRef> pac_url_ref(
117 CFURLCreateWithString(kCFAllocatorDefault, pac_ref.get(), NULL));
118 if (!pac_url_ref.get())
119 return ERR_FAILED;
121 // Work around <rdar://problem/5530166>. This dummy call to
122 // CFNetworkCopyProxiesForURL initializes some state within CFNetwork that is
123 // required by CFNetworkExecuteProxyAutoConfigurationURL.
125 CFArrayRef dummy_result = CFNetworkCopyProxiesForURL(query_url_ref.get(),
126 NULL);
127 if (dummy_result)
128 CFRelease(dummy_result);
130 // We cheat here. We need to act as if we were synchronous, so we pump the
131 // runloop ourselves. Our caller moved us to a new thread anyway, so this is
132 // OK to do. (BTW, CFNetworkExecuteProxyAutoConfigurationURL returns a
133 // runloop source we need to release despite its name.)
135 CFTypeRef result = NULL;
136 CFStreamClientContext context = { 0, &result, NULL, NULL, NULL };
137 base::ScopedCFTypeRef<CFRunLoopSourceRef> runloop_source(
138 CFNetworkExecuteProxyAutoConfigurationURL(
139 pac_url_ref.get(), query_url_ref.get(), ResultCallback, &context));
140 if (!runloop_source)
141 return ERR_FAILED;
143 const CFStringRef private_runloop_mode =
144 CFSTR("org.chromium.ProxyResolverMac");
146 CFRunLoopAddSource(CFRunLoopGetCurrent(), runloop_source.get(),
147 private_runloop_mode);
148 CFRunLoopRunInMode(private_runloop_mode, DBL_MAX, false);
149 CFRunLoopSourceInvalidate(runloop_source.get());
150 DCHECK(result != NULL);
152 if (CFGetTypeID(result) == CFErrorGetTypeID()) {
153 // TODO(avi): do something better than this
154 CFRelease(result);
155 return ERR_FAILED;
157 base::ScopedCFTypeRef<CFArrayRef> proxy_array_ref(
158 base::mac::CFCastStrict<CFArrayRef>(result));
159 DCHECK(proxy_array_ref != NULL);
161 // This string will be an ordered list of <proxy-uri> entries, separated by
162 // semi-colons. It is the format that ProxyInfo::UseNamedProxy() expects.
163 // proxy-uri = [<proxy-scheme>"://"]<proxy-host>":"<proxy-port>
164 // (This also includes entries for direct connection, as "direct://").
165 std::string proxy_uri_list;
167 CFIndex proxy_array_count = CFArrayGetCount(proxy_array_ref.get());
168 for (CFIndex i = 0; i < proxy_array_count; ++i) {
169 CFDictionaryRef proxy_dictionary = base::mac::CFCastStrict<CFDictionaryRef>(
170 CFArrayGetValueAtIndex(proxy_array_ref.get(), i));
171 DCHECK(proxy_dictionary != NULL);
173 // The dictionary may have the following keys:
174 // - kCFProxyTypeKey : The type of the proxy
175 // - kCFProxyHostNameKey
176 // - kCFProxyPortNumberKey : The meat we're after.
177 // - kCFProxyUsernameKey
178 // - kCFProxyPasswordKey : Despite the existence of these keys in the
179 // documentation, they're never populated. Even if a
180 // username/password were to be set in the network
181 // proxy system preferences, we'd need to fetch it
182 // from the Keychain ourselves. CFProxy is such a
183 // tease.
184 // - kCFProxyAutoConfigurationURLKey : If the PAC file specifies another
185 // PAC file, I'm going home.
187 CFStringRef proxy_type = base::mac::GetValueFromDictionary<CFStringRef>(
188 proxy_dictionary, kCFProxyTypeKey);
189 ProxyServer proxy_server = ProxyServer::FromDictionary(
190 GetProxyServerScheme(proxy_type),
191 proxy_dictionary,
192 kCFProxyHostNameKey,
193 kCFProxyPortNumberKey);
194 if (!proxy_server.is_valid())
195 continue;
197 if (!proxy_uri_list.empty())
198 proxy_uri_list += ";";
199 proxy_uri_list += proxy_server.ToURI();
202 if (!proxy_uri_list.empty())
203 results->UseNamedProxy(proxy_uri_list);
204 // Else do nothing (results is already guaranteed to be in the default state).
206 return OK;
209 void ProxyResolverMac::CancelRequest(RequestHandle request) {
210 NOTREACHED();
213 LoadState ProxyResolverMac::GetLoadState(RequestHandle request) const {
214 NOTREACHED();
215 return LOAD_STATE_IDLE;
218 void ProxyResolverMac::CancelSetPacScript() {
219 NOTREACHED();
222 int ProxyResolverMac::SetPacScript(
223 const scoped_refptr<ProxyResolverScriptData>& script_data,
224 const CompletionCallback& /*callback*/) {
225 NOTREACHED();
226 return ERR_NOT_IMPLEMENTED;
229 } // namespace
231 ProxyResolverFactoryMac::ProxyResolverFactoryMac()
232 : ProxyResolverFactory(false /*expects_pac_bytes*/) {
235 int ProxyResolverFactoryMac::CreateProxyResolver(
236 const scoped_refptr<ProxyResolverScriptData>& pac_script,
237 scoped_ptr<ProxyResolver>* resolver,
238 const CompletionCallback& callback,
239 scoped_ptr<Request>* request) {
240 resolver->reset(new ProxyResolverMac(pac_script));
241 return OK;
244 } // namespace net