// Call this when you have tried to load a url via XMLHttpRequest or // something along those lines, and the attempt has failed. We will mark the // domain as needing proxying, and will return a new proxy-enabled URL to try. // The exception is for flagship website URLs, which we know that the proxy // won't help with. For those, null is returned. public string activateProxy(string url) { // Get the domain. XXX copy/pastey from the above. string lc = url.ToLowerCase(); string url_no_protocol; if (lc.StartsWith("http://")) { url_no_protocol = url.Substring(7); } else if (lc.StartsWith("https://")) { url_no_protocol = url.Substring(8); } else if (lc.StartsWith("//")) { url_no_protocol = url.Substring(2); } else { url_no_protocol = url; } string lcdomain; int slash_index = url_no_protocol.IndexOf('/'); if (slash_index < 0) { lcdomain = url_no_protocol; } else { lcdomain = url_no_protocol.Substring(0, slash_index).ToLowerCase(); } // Is this a flagship or never-proxy URL? If so, don't bother proxying. if (!this.domain_handling.ContainsKey(lcdomain)) { if (lcdomain.StartsWith("localhost:") || lcdomain.StartsWith("127.0.0.1:")) { this.domain_handling[lcdomain] = DomainHandling.Localhost; } else { this.domain_handling[lcdomain] = DomainHandling.TryNoProxy; } } DomainHandling mode = this.domain_handling[lcdomain]; if (mode == DomainHandling.WWTFlagship || mode == DomainHandling.NeverProxy || mode == DomainHandling.Localhost) { return(null); } // OK, we should try proxying. So: this.domain_handling[lcdomain] = DomainHandling.Proxy; return(this.rewrite(url, URLRewriteMode.AsIfAbsolute)); }
public String rewrite(String url, URLRewriteMode rwmode) { // Sadly, we can't take advantage of JS/browser URL parsing // because this function might be passed template URLs like // "http://r{S:2}.ortho.tiles.virtualearth.net/..." that won't // parse. So we have to split up the URL manually. string lc = url.ToLowerCase(); string lcproto; string url_no_protocol; if (lc.StartsWith("http://")) { lcproto = "http:"; url_no_protocol = url.Substring(7); } else if (lc.StartsWith("https://")) { lcproto = "https:"; url_no_protocol = url.Substring(8); } else if (lc.StartsWith("//")) { lcproto = ""; url_no_protocol = url.Substring(2); } else if (lc.StartsWith("blob:")) { // The web client uses URL.createObjectURL() to ingest local // disk files into the web app. That function creates blob // URLs, and it turns out that we definitely don't want to // rewrite them! return(url); } else { switch (rwmode) { case URLRewriteMode.AsIfAbsolute: default: // Treat `foo/bar` as a domain name of `foo` and a // path of `/bar`. Really we should demand that the // caller always pass us an absolute URL, but URLs // will be coming from random data sources and we're // not currently rigorous enough to guarantee that // this function will get validated inputs -- and in // such cases, throwing exceptions won't help. lcproto = ""; url_no_protocol = url; break; case URLRewriteMode.OriginRelative: // Treat `foo/bar` as a URL relative to the window // origin. Since it looks relative, any weird // templating stuff in the URL text *ought* not cause // problems for the browser URL parsing ... url = (String)Script.Literal("(new URL({0}, window.location.href)).toString()", url); return(this.rewrite(url, URLRewriteMode.AsIfAbsolute)); } } string domain; string rest; // potentially "/foo/CASE/bar?q=1&b=1#fragment" int slash_index = url_no_protocol.IndexOf('/'); if (slash_index < 0) { domain = url_no_protocol; rest = "/"; } else { domain = url_no_protocol.Substring(0, slash_index); rest = url_no_protocol.Substring(slash_index); // starts with "/" } string lcdomain = domain.ToLowerCase(); string lcpath = rest.ToLowerCase().Split('?')[0]; if (!this.domain_handling.ContainsKey(lcdomain)) { // Domains include nonstandard port specifications, so it's // possible that we could get here with a discernably // localhost-y domain. if (lcdomain.StartsWith("localhost:") || lcdomain.StartsWith("127.0.0.1:")) { this.domain_handling[lcdomain] = DomainHandling.Localhost; } else { this.domain_handling[lcdomain] = DomainHandling.TryNoProxy; } } DomainHandling mode = this.domain_handling[lcdomain]; switch (mode) { case DomainHandling.Localhost: return(url); // can't proxy, so we'll just have to hope it works case DomainHandling.NeverProxy: case DomainHandling.TryNoProxy: default: if (this.force_https && lcproto != "https:") { // Force HTTPS and we'll see what happens. If // downloading fails, we'll set a flag and use our // proxy to launder the security. // // NOTE: it is important that we use `domain` and not // `lcdomain`, even though domain names are // case-insensitive, because we might be processing a // template URL containing text like `{S}`, and WWT's // replacements *are* case-sensitive. Yes, I did learn // this the hard way. return("https://" + domain + rest); } return(url); case DomainHandling.Proxy: if (lcproto == "") { // Make sure that we give the proxy a real absolute // URL. Guess http, and if the proxy is forced to // upgrade, so be it. url = "http://" + url; } // We need to encode the URL as a query-string parameter // to pass to the proxy. However, the encoding will turn // "{}" into "%7B%7D", so that *if* this URL is then going // to be fed into the templating system, // search-and-replace for e.g. "{0}" will break. So we // un-encode those particular characters, since it ought // to be safe to do so anyway. url = url.EncodeUriComponent().Replace("%7B", "{").Replace("%7D", "}"); return(this.core_dynamic_baseurl + "/webserviceproxy.aspx?targeturl=" + url); case DomainHandling.WWTFlagship: // Rewrite "flagship"/core URLs to go through whatever our // core bases are. Assume that URLs are dynamic (=> are // not loaded through the CDN) unless proven otherwise. bool is_static = false; if (lcpath.StartsWith("/data/")) { is_static = true; } else if (this.flagship_static_lcpaths.ContainsKey(lcpath)) { is_static = true; } else if (lcpath.StartsWith("/content/")) { is_static = true; } else if (lcpath.StartsWith("/engine/assets/")) { is_static = true; } if (is_static) { return(this.core_static_baseurl + rest); } return(this.core_dynamic_baseurl + rest); } }