1 // Copyright (c) 2010 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. 4 5 #include "net/proxy/proxy_config.h" 6 7 #include "base/logging.h" 8 #include "base/string_tokenizer.h" 9 #include "base/string_util.h" 10 #include "base/values.h" 11 #include "net/proxy/proxy_info.h" 12 13 namespace net { 14 15 namespace { 16 17 // If |proxy| is valid, sets it in |dict| under the key |name|. 18 void AddProxyToValue(const char* name, 19 const ProxyServer& proxy, 20 DictionaryValue* dict) { 21 if (proxy.is_valid()) 22 dict->SetString(name, proxy.ToURI()); 23 } 24 25 } // namespace 26 27 ProxyConfig::ProxyRules::ProxyRules() 28 : reverse_bypass(false), 29 type(TYPE_NO_RULES) { 30 } 31 32 ProxyConfig::ProxyRules::~ProxyRules() { 33 } 34 35 void ProxyConfig::ProxyRules::Apply(const GURL& url, ProxyInfo* result) { 36 if (empty()) { 37 result->UseDirect(); 38 return; 39 } 40 41 bool bypass_proxy = bypass_rules.Matches(url); 42 if (reverse_bypass) 43 bypass_proxy = !bypass_proxy; 44 if (bypass_proxy) { 45 result->UseDirect(); 46 return; 47 } 48 49 switch (type) { 50 case ProxyRules::TYPE_SINGLE_PROXY: { 51 result->UseProxyServer(single_proxy); 52 return; 53 } 54 case ProxyRules::TYPE_PROXY_PER_SCHEME: { 55 const ProxyServer* entry = MapUrlSchemeToProxy(url.scheme()); 56 if (entry) { 57 result->UseProxyServer(*entry); 58 } else { 59 // We failed to find a matching proxy server for the current URL 60 // scheme. Default to direct. 61 result->UseDirect(); 62 } 63 return; 64 } 65 default: { 66 result->UseDirect(); 67 NOTREACHED(); 68 return; 69 } 70 } 71 } 72 73 void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) { 74 // Reset. 75 type = TYPE_NO_RULES; 76 single_proxy = ProxyServer(); 77 proxy_for_http = ProxyServer(); 78 proxy_for_https = ProxyServer(); 79 proxy_for_ftp = ProxyServer(); 80 fallback_proxy = ProxyServer(); 81 82 StringTokenizer proxy_server_list(proxy_rules, ";"); 83 while (proxy_server_list.GetNext()) { 84 StringTokenizer proxy_server_for_scheme( 85 proxy_server_list.token_begin(), proxy_server_list.token_end(), "="); 86 87 while (proxy_server_for_scheme.GetNext()) { 88 std::string url_scheme = proxy_server_for_scheme.token(); 89 90 // If we fail to get the proxy server here, it means that 91 // this is a regular proxy server configuration, i.e. proxies 92 // are not configured per protocol. 93 if (!proxy_server_for_scheme.GetNext()) { 94 if (type == TYPE_PROXY_PER_SCHEME) 95 continue; // Unexpected. 96 single_proxy = ProxyServer::FromURI(url_scheme, 97 ProxyServer::SCHEME_HTTP); 98 type = TYPE_SINGLE_PROXY; 99 return; 100 } 101 102 // Trim whitespace off the url scheme. 103 TrimWhitespaceASCII(url_scheme, TRIM_ALL, &url_scheme); 104 105 // Add it to the per-scheme mappings (if supported scheme). 106 type = TYPE_PROXY_PER_SCHEME; 107 ProxyServer* entry = MapUrlSchemeToProxyNoFallback(url_scheme); 108 ProxyServer::Scheme default_scheme = ProxyServer::SCHEME_HTTP; 109 110 // socks=XXX is inconsistent with the other formats, since "socks" 111 // is not a URL scheme. Rather this means "for everything else, send 112 // it to the SOCKS proxy server XXX". 113 if (url_scheme == "socks") { 114 DCHECK(!entry); 115 entry = &fallback_proxy; 116 default_scheme = ProxyServer::SCHEME_SOCKS4; 117 } 118 119 if (entry) { 120 *entry = ProxyServer::FromURI(proxy_server_for_scheme.token(), 121 default_scheme); 122 } 123 } 124 } 125 } 126 127 const ProxyServer* ProxyConfig::ProxyRules::MapUrlSchemeToProxy( 128 const std::string& url_scheme) const { 129 const ProxyServer* proxy_server = 130 const_cast<ProxyRules*>(this)->MapUrlSchemeToProxyNoFallback(url_scheme); 131 if (proxy_server && proxy_server->is_valid()) 132 return proxy_server; 133 if (fallback_proxy.is_valid()) 134 return &fallback_proxy; 135 return NULL; // No mapping for this scheme. Use direct. 136 } 137 138 bool ProxyConfig::ProxyRules::Equals(const ProxyRules& other) const { 139 return type == other.type && 140 single_proxy == other.single_proxy && 141 proxy_for_http == other.proxy_for_http && 142 proxy_for_https == other.proxy_for_https && 143 proxy_for_ftp == other.proxy_for_ftp && 144 fallback_proxy == other.fallback_proxy && 145 bypass_rules.Equals(other.bypass_rules) && 146 reverse_bypass == other.reverse_bypass; 147 } 148 149 ProxyServer* ProxyConfig::ProxyRules::MapUrlSchemeToProxyNoFallback( 150 const std::string& scheme) { 151 DCHECK_EQ(TYPE_PROXY_PER_SCHEME, type); 152 if (scheme == "http") 153 return &proxy_for_http; 154 if (scheme == "https") 155 return &proxy_for_https; 156 if (scheme == "ftp") 157 return &proxy_for_ftp; 158 return NULL; // No mapping for this scheme. 159 } 160 161 ProxyConfig::ProxyConfig() : auto_detect_(false), id_(INVALID_ID) { 162 } 163 164 ProxyConfig::ProxyConfig(const ProxyConfig& config) 165 : auto_detect_(config.auto_detect_), 166 pac_url_(config.pac_url_), 167 proxy_rules_(config.proxy_rules_), 168 id_(config.id_) { 169 } 170 171 ProxyConfig::~ProxyConfig() { 172 } 173 174 ProxyConfig& ProxyConfig::operator=(const ProxyConfig& config) { 175 auto_detect_ = config.auto_detect_; 176 pac_url_ = config.pac_url_; 177 proxy_rules_ = config.proxy_rules_; 178 id_ = config.id_; 179 return *this; 180 } 181 182 bool ProxyConfig::Equals(const ProxyConfig& other) const { 183 // The two configs can have different IDs. We are just interested in if they 184 // have the same settings. 185 return auto_detect_ == other.auto_detect_ && 186 pac_url_ == other.pac_url_ && 187 proxy_rules_.Equals(other.proxy_rules()); 188 } 189 190 bool ProxyConfig::HasAutomaticSettings() const { 191 return auto_detect_ || has_pac_url(); 192 } 193 194 void ProxyConfig::ClearAutomaticSettings() { 195 auto_detect_ = false; 196 pac_url_ = GURL(); 197 } 198 199 Value* ProxyConfig::ToValue() const { 200 DictionaryValue* dict = new DictionaryValue(); 201 202 // Output the automatic settings. 203 if (auto_detect_) 204 dict->SetBoolean("auto_detect", auto_detect_); 205 if (has_pac_url()) 206 dict->SetString("pac_url", pac_url_.possibly_invalid_spec()); 207 208 // Output the manual settings. 209 if (proxy_rules_.type != ProxyRules::TYPE_NO_RULES) { 210 switch (proxy_rules_.type) { 211 case ProxyRules::TYPE_SINGLE_PROXY: 212 AddProxyToValue("single_proxy", proxy_rules_.single_proxy, dict); 213 break; 214 case ProxyRules::TYPE_PROXY_PER_SCHEME: { 215 DictionaryValue* dict2 = new DictionaryValue(); 216 AddProxyToValue("http", proxy_rules_.proxy_for_http, dict2); 217 AddProxyToValue("https", proxy_rules_.proxy_for_https, dict2); 218 AddProxyToValue("ftp", proxy_rules_.proxy_for_ftp, dict2); 219 AddProxyToValue("fallback", proxy_rules_.fallback_proxy, dict2); 220 dict->Set("proxy_per_scheme", dict2); 221 break; 222 } 223 default: 224 NOTREACHED(); 225 } 226 227 // Output the bypass rules. 228 const ProxyBypassRules& bypass = proxy_rules_.bypass_rules; 229 if (!bypass.rules().empty()) { 230 if (proxy_rules_.reverse_bypass) 231 dict->SetBoolean("reverse_bypass", true); 232 233 ListValue* list = new ListValue(); 234 235 for (ProxyBypassRules::RuleList::const_iterator it = 236 bypass.rules().begin(); 237 it != bypass.rules().end(); ++it) { 238 list->Append(Value::CreateStringValue((*it)->ToString())); 239 } 240 241 dict->Set("bypass_list", list); 242 } 243 } 244 245 return dict; 246 } 247 248 } // namespace net 249 250