/// <summary> /// Extracts information out of the path parts for a fake URL that has had its substitution markers replaced with /// real values by Leaflet. /// </summary> /// <param name="pathParts">A collection of strings that represent the folders in a URL's path. The filename /// and scheme are missing. An empty collection indicates that the request was for root.</param> /// <param name="fileName"></param> /// <returns></returns> public FakeUrlEncodedValues ExtractEncodedValuesFromUrlParts(IEnumerable <string> pathParts, string fileName) { FakeUrlEncodedValues result = null; if (pathParts != null && String.Equals(pathParts.FirstOrDefault(), PageName, StringComparison.OrdinalIgnoreCase)) { result = new FakeUrlEncodedValues(); foreach (var pathPart in pathParts) { var hyphenIdx = pathPart.IndexOf('-'); if (hyphenIdx > 0) { var name = pathPart.Substring(0, hyphenIdx); var value = pathPart.Substring(hyphenIdx + 1); if (name.StartsWith(VariablePrefix)) { var substitutionMarker = name.Substring(VariablePrefix.Length); switch (substitutionMarker) { case "r": result.Retina = value; break; case "x": result.X = value; break; case "y": result.Y = value; break; case "z": result.Zoom = value; break; default: result.OtherValues.Add(substitutionMarker, value); break; } } else { switch (name) { case MapProviderName: if (int.TryParse(value, out var mp) && Enum.IsDefined(typeof(MapProvider), mp)) { result.MapProvider = (MapProvider)mp; } break; case TileServerName: result.Name = value.Trim(); break; } } } } result.TileImageExtension = String.IsNullOrEmpty(fileName) ? ".png" : Path.GetExtension(fileName); } return(result); }
internal void CopyUrlValues(FakeUrlEncodedValues urlValues) { if (urlValues != null) { TileServerName = urlValues.Name; Zoom = urlValues.Zoom; X = urlValues.X; Y = urlValues.Y; Retina = urlValues.Retina; } }
/// <summary> /// Returns the cached tile image or null if no such image exists. /// </summary> /// <param name="urlValues"></param> /// <returns></returns> public static CachedTile GetCachedTile(FakeUrlEncodedValues urlValues) { return(GetCachedTile( urlValues.MapProvider, urlValues.Name, urlValues.Zoom, urlValues.X, urlValues.Y, urlValues.Retina, urlValues.TileImageExtension )); }
/// <summary> /// Saves the image bytes. /// </summary> /// <param name="urlValues"></param> /// <param name="content"></param> public static void SaveTile(FakeUrlEncodedValues urlValues, byte[] content) { SaveTile( urlValues.MapProvider, urlValues.Name, urlValues.Zoom, urlValues.X, urlValues.Y, urlValues.Retina, urlValues.TileImageExtension, content ); }
/// <summary> /// Subtitutes the values from a fake encoded URL into a real tile server URL. /// </summary> /// <param name="urlWithMarkers"></param> /// <param name="values"></param> /// <param name="subdomains">Leaflet-style list of subdomains to substitute into the {s} parameter (if present). Defaults to abc if null / empty.</param> /// <returns></returns> public string ExpandUrlParameters(string urlWithMarkers, FakeUrlEncodedValues values, string subdomains) { var result = new StringBuilder(urlWithMarkers ?? ""); if (String.IsNullOrEmpty(subdomains)) { subdomains = "abc"; } foreach (var match in _MatchSubstitutionMarker.Matches(result.ToString()).OfType <Match>().OrderByDescending(r => r.Index)) { var value = ""; var start = match.Groups[1].Index; var length = match.Groups[1].Length; var marker = match.Groups[2].Value; switch (marker) { case "r": value = values.Retina; break; case "x": value = values.X; break; case "y": value = values.Y; break; case "z": value = values.Zoom; break; case "s": var random = new Random(); var idx = random.Next(subdomains.Length); value = new String(subdomains[idx], 1); break; default: if (values.OtherValues.TryGetValue(marker, out var otherValue)) { value = otherValue; } break; } result.Remove(start, length); result.Insert(start, value ?? ""); } return(result.ToString()); }
/// <summary> /// Downloads the tile image using the URL values passed across. /// </summary> /// <param name="options"></param> /// <param name="urlValues"></param> /// <param name="clientEndpoint"></param> /// <param name="headers"></param> /// <param name="outcome"></param> /// <param name="displayOutcome"></param> /// <returns></returns> private void FetchTileServerImage(Options options, FakeUrlEncodedValues urlValues, IPAddress clientEndpoint, HeadersDictionary headers, WebRequestOutcome outcome, RequestOutcome displayOutcome) { var tileServerSettings = Plugin.TileServerSettingsManagerWrapper.GetRealTileServerSettings( urlValues.MapProvider, urlValues.Name ); if (tileServerSettings != null) { var tileImageUrl = _TileServerUrlTranslator.ExpandUrlParameters( tileServerSettings.Url, urlValues, tileServerSettings.Subdomains ); var request = (HttpWebRequest)HttpWebRequest.Create(tileImageUrl); request.Method = "GET"; request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip; foreach (var headerKey in headers.Keys) { var value = headers[headerKey]; switch (headerKey.ToLower()) { case "accept": request.Accept = value; break; case "accept-encoding": break; case "connection": break; case "content-length": break; case "content-type": request.ContentType = value; break; case "date": break; case "expect": request.Expect = value; break; case "host": break; case "if-modified-since": break; case "proxy-connection": break; case "range": break; case "referer": request.Referer = value; break; case "transfer-encoding": request.TransferEncoding = value; break; case "user-agent": request.UserAgent = value; break; default: request.Headers[headerKey] = value; break; } } request.AuthenticationLevel = AuthenticationLevel.None; request.Credentials = null; request.UseDefaultCredentials = false; request.KeepAlive = true; request.Timeout = 1000 * options.TileServerTimeoutSeconds; lock (_SyncLock) { if (!_TileServerNameToCookieCollection.TryGetValue(urlValues.Name, out var cookies)) { cookies = new CookieCollection(); _TileServerNameToCookieCollection.Add(urlValues.Name, cookies); } foreach (Cookie cookie in cookies) { request.CookieContainer.Add(cookie); } } var forwarded = request.Headers["Forwarded"] ?? ""; if (forwarded.Length > 0) { forwarded = $"{forwarded}, "; } forwarded = $"{forwarded}for={clientEndpoint}"; request.Headers["Forwarded"] = forwarded; try { using (var response = (HttpWebResponse)request.GetResponse()) { using (var memoryStream = new MemoryStream()) { using (var responseStream = response.GetResponseStream()) { var buffer = new byte[1024]; var bytesRead = 0; do { bytesRead = responseStream.Read(buffer, 0, buffer.Length); if (bytesRead > 0) { memoryStream.Write(buffer, 0, bytesRead); } } while(bytesRead > 0); } outcome.ImageBytes = memoryStream.ToArray(); displayOutcome.FetchedFromTileServer = true; displayOutcome.TileServerResponseStatusCode = (int)response.StatusCode; } var cookies = new CookieCollection(); foreach (Cookie cookie in response.Cookies) { cookies.Add(cookie); } lock (_SyncLock) { _TileServerNameToCookieCollection[urlValues.Name] = cookies; } } } catch (WebException ex) { if (ex.Status == WebExceptionStatus.Timeout) { displayOutcome.TimedOut = true; } else { displayOutcome.WebExceptionErrorMessage = ex.Message; outcome.StatusCode = HttpStatusCode.InternalServerError; if (ex.Response is HttpWebResponse httpResponse) { if (httpResponse.StatusCode != HttpStatusCode.OK) { outcome.StatusCode = httpResponse.StatusCode; displayOutcome.TileServerResponseStatusCode = (int)httpResponse.StatusCode; } } } } } }