public void BuildEndpoints_JustHostWithWildcard_Works() { // Arrange var builder = Create <RuntimeRouteBuilder>(); var parsedRoute = new ParsedRoute { RouteId = "route1", Host = "*.example.com", Priority = 12, }; var backend = new BackendInfo("backend1", new DestinationManager(), new Mock <IProxyHttpClientFactory>().Object); var routeInfo = new RouteInfo("route1"); // Act var config = builder.Build(parsedRoute, backend, routeInfo); // Assert Assert.Same(backend, config.BackendOrNull); Assert.Equal(12, config.Priority); Assert.Equal(parsedRoute.GetConfigHash(), config.ConfigHash); Assert.Single(config.Endpoints); var routeEndpoint = config.Endpoints[0] as AspNetCore.Routing.RouteEndpoint; Assert.Equal("route1", routeEndpoint.DisplayName); Assert.Same(config, routeEndpoint.Metadata.GetMetadata <RouteConfig>()); Assert.Equal("/{**catchall}", routeEndpoint.RoutePattern.RawText); var hostMetadata = routeEndpoint.Metadata.GetMetadata <AspNetCore.Routing.HostAttribute>(); Assert.NotNull(hostMetadata); Assert.Single(hostMetadata.Hosts); Assert.Equal("*.example.com", hostMetadata.Hosts[0]); }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { var connection = context.Features.Get <IHttpConnectionFeature>(); var backendInfo = new BackendInfo() { IP = connection.LocalIpAddress.ToString(), Hostname = Dns.GetHostName(), }; context.Response.ContentType = "application/json; charset=utf-8"; await JsonSerializer.SerializeAsync(context.Response.Body, backendInfo); }); endpoints.MapGet("/crash", context => { System.Environment.Exit(1); return(Task.CompletedTask); }); endpoints.MapHealthChecks("/healthz"); }); }
public void BuildEndpoints_NullMatchers_Works() { // Arrange var builder = Create <RuntimeRouteBuilder>(); var parsedRoute = new ParsedRoute { RouteId = "route1", Priority = 12, }; var backend = new BackendInfo("backend1", new EndpointManager(), new Mock <IProxyHttpClientFactory>().Object); var routeInfo = new RouteInfo("route1"); // Act var config = builder.Build(parsedRoute, backend, routeInfo); // Assert Assert.Same(backend, config.BackendOrNull); Assert.Equal(12, config.Priority); Assert.Empty(config.MatcherSummary); Assert.Single(config.AspNetCoreEndpoints); var routeEndpoint = config.AspNetCoreEndpoints[0] as AspNetCore.Routing.RouteEndpoint; Assert.Equal("route1", routeEndpoint.DisplayName); Assert.Same(config, routeEndpoint.Metadata.GetMetadata <RouteConfig>()); Assert.Equal("/{**catchall}", routeEndpoint.RoutePattern.RawText); var hostMetadata = routeEndpoint.Metadata.GetMetadata <AspNetCore.Routing.HostAttribute>(); Assert.Null(hostMetadata); }
public void BuildEndpoints_JustHostWithWildcard_Works() { // Arrange var builder = Create <RuntimeRouteBuilder>(); var parsedRoute = new ParsedRoute { RouteId = "route1", Host = "*.example.com", Priority = 12, }; var backend = new BackendInfo("backend1", new EndpointManager(), new Mock <IProxyHttpClientFactory>().Object); var routeInfo = new RouteInfo("route1"); // Act var config = builder.Build(parsedRoute, backend, routeInfo); // Assert config.BackendOrNull.Should().BeSameAs(backend); config.Priority.Should().Be(12); config.MatcherSummary.Should().Be(parsedRoute.GetMatcherSummary()); config.AspNetCoreEndpoints.Should().HaveCount(1); var routeEndpoint = config.AspNetCoreEndpoints[0] as AspNetCore.Routing.RouteEndpoint; routeEndpoint.DisplayName.Should().Be("route1"); routeEndpoint.Metadata.GetMetadata <RouteConfig>().Should().BeSameAs(config); routeEndpoint.RoutePattern.RawText.Should().Be("/{**catchall}"); var hostMetadata = routeEndpoint.Metadata.GetMetadata <AspNetCore.Routing.HostAttribute>(); hostMetadata.Should().NotBeNull(); hostMetadata.Hosts.Should().BeEquivalentTo("*.example.com"); }
public async Task Invoke_Works() { var proxyHttpClientFactoryMock = new Mock <IProxyHttpClientFactory>(); var backend1 = new BackendInfo( backendId: "backend1", destinationManager: new DestinationManager(), proxyHttpClientFactory: proxyHttpClientFactoryMock.Object); backend1.Config.Value = new BackendConfig(default, new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.RoundRobin), default);
/// <inheritdoc/> public RouteConfig Build(ParsedRoute source, BackendInfo backendOrNull, RouteInfo runtimeRoute) { Contracts.CheckValue(source, nameof(source)); Contracts.CheckValue(runtimeRoute, nameof(runtimeRoute)); var transforms = _transformBuilder.Build(source.Transforms); // NOTE: `new RouteConfig(...)` needs a reference to the list of ASP .NET Core endpoints, // but the ASP .NET Core endpoints cannot be created without a `RouteConfig` metadata item. // We solve this chicken-egg problem by creating an (empty) list first // and passing a read-only wrapper of it to `RouteConfig.ctor`. // Recall that `List<T>.AsReadOnly()` creates a wrapper over the original list, // and changes to the underlying list *are* reflected on the read-only view. var aspNetCoreEndpoints = new List <Endpoint>(1); var newRouteConfig = new RouteConfig( runtimeRoute, source.GetConfigHash(), source.Priority, backendOrNull, aspNetCoreEndpoints.AsReadOnly(), transforms); // TODO: Handle arbitrary AST's properly // Catch-all pattern when no path was specified var pathPattern = string.IsNullOrEmpty(source.Path) ? "/{**catchall}" : source.Path; // TODO: Propagate route priority var endpointBuilder = new AspNetCore.Routing.RouteEndpointBuilder( requestDelegate: _pipeline ?? Invoke, routePattern: AspNetCore.Routing.Patterns.RoutePatternFactory.Parse(pathPattern), order: 0); endpointBuilder.DisplayName = source.RouteId; endpointBuilder.Metadata.Add(newRouteConfig); if (!string.IsNullOrEmpty(source.Host)) { endpointBuilder.Metadata.Add(new AspNetCore.Routing.HostAttribute(source.Host)); } if (source.Methods != null && source.Methods.Count > 0) { endpointBuilder.Metadata.Add(new AspNetCore.Routing.HttpMethodMetadata(source.Methods)); } var endpoint = endpointBuilder.Build(); aspNetCoreEndpoints.Add(endpoint); return(newRouteConfig); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req, ILogger log) { log.LogInformation("C# HTTP trigger function processed a request."); var backendInfo = new BackendInfo() { IP = req.HttpContext.Connection.LocalIpAddress.ToString(), Hostname = System.Net.Dns.GetHostName(), }; return(new OkObjectResult(backendInfo)); }
public async Task Invoke_NoHealthyEndpoints_503() { var proxyHttpClientFactoryMock = new Mock <IProxyHttpClientFactory>(); var backend1 = new BackendInfo( backendId: "backend1", destinationManager: new DestinationManager(), proxyHttpClientFactory: proxyHttpClientFactoryMock.Object); backend1.Config.Value = new BackendConfig( new BackendConfig.BackendHealthCheckOptions(enabled: true, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan, 0, ""), new BackendConfig.BackendLoadBalancingOptions(), new BackendConfig.BackendSessionAffinityOptions()); var destination1 = backend1.DestinationManager.GetOrCreateItem( "destination1", destination => { destination.Config.Value = new DestinationConfig("https://localhost:123/a/b/"); destination.DynamicState.Value = new DestinationDynamicState(DestinationHealth.Unhealthy); }); var aspNetCoreEndpoints = new List <Endpoint>(); var routeConfig = new RouteConfig( route: new RouteInfo("route1"), configHash: 0, priority: null, backendOrNull: backend1, aspNetCoreEndpoints: aspNetCoreEndpoints.AsReadOnly(), transforms: null); var aspNetCoreEndpoint = CreateAspNetCoreEndpoint(routeConfig); aspNetCoreEndpoints.Add(aspNetCoreEndpoint); var httpContext = new DefaultHttpContext(); httpContext.SetEndpoint(aspNetCoreEndpoint); var sut = Create <DestinationInitializerMiddleware>(); await sut.Invoke(httpContext); var feature = httpContext.Features.Get <IAvailableDestinationsFeature>(); Assert.Null(feature); var backend = httpContext.Features.Get <BackendInfo>(); Assert.Null(backend); Assert.Equal(503, httpContext.Response.StatusCode); }
public async Task Invoke_SetsFeatures() { var proxyHttpClientFactoryMock = new Mock <IProxyHttpClientFactory>(); var backend1 = new BackendInfo( backendId: "backend1", destinationManager: new DestinationManager(), proxyHttpClientFactory: proxyHttpClientFactoryMock.Object); var destination1 = backend1.DestinationManager.GetOrCreateItem( "destination1", destination => { destination.Config.Value = new DestinationConfig("https://localhost:123/a/b/"); destination.DynamicState.Value = new DestinationDynamicState(DestinationHealth.Healthy); }); var aspNetCoreEndpoints = new List <Endpoint>(); var routeConfig = new RouteConfig( new RouteInfo("route1"), configHash: 0, priority: null, backend1, aspNetCoreEndpoints.AsReadOnly(), transforms: null); var aspNetCoreEndpoint = CreateAspNetCoreEndpoint(routeConfig); aspNetCoreEndpoints.Add(aspNetCoreEndpoint); var httpContext = new DefaultHttpContext(); httpContext.SetEndpoint(aspNetCoreEndpoint); var sut = Create <DestinationInitializerMiddleware>(); await sut.Invoke(httpContext); var feature = httpContext.Features.Get <IAvailableDestinationsFeature>(); Assert.NotNull(feature); Assert.NotNull(feature.Destinations); Assert.Equal(1, feature.Destinations.Count); Assert.Same(destination1, feature.Destinations[0]); var backend = httpContext.Features.Get <BackendInfo>(); Assert.Same(backend1, backend); Assert.Equal(200, httpContext.Response.StatusCode); }
public async Task Invoke_SetsFeatures() { var proxyHttpClientFactoryMock = new Mock <IProxyHttpClientFactory>(); var backend1 = new BackendInfo( backendId: "backend1", endpointManager: new EndpointManager(), proxyHttpClientFactory: proxyHttpClientFactoryMock.Object); var endpoint1 = backend1.EndpointManager.GetOrCreateItem( "endpoint1", endpoint => { endpoint.Config.Value = new EndpointConfig("https://localhost:123/a/b/"); endpoint.DynamicState.Value = new EndpointDynamicState(EndpointHealth.Healthy); }); var aspNetCoreEndpoints = new List <Endpoint>(); var routeConfig = new RouteConfig( route: new RouteInfo("route1"), matcherSummary: null, priority: null, backendOrNull: backend1, aspNetCoreEndpoints: aspNetCoreEndpoints.AsReadOnly()); var aspNetCoreEndpoint = CreateAspNetCoreEndpoint(routeConfig); aspNetCoreEndpoints.Add(aspNetCoreEndpoint); var httpContext = new DefaultHttpContext(); httpContext.SetEndpoint(aspNetCoreEndpoint); var sut = Create <EndpointInitializerMiddleware>(); await sut.Invoke(httpContext); var feature = httpContext.Features.Get <IAvailableBackendEndpointsFeature>(); Assert.NotNull(feature); Assert.NotNull(feature.Endpoints); Assert.Equal(1, feature.Endpoints.Count); Assert.Same(endpoint1, feature.Endpoints[0]); var backend = httpContext.Features.Get <BackendInfo>(); Assert.Same(backend1, backend); Assert.Equal(200, httpContext.Response.StatusCode); }
public void BuildEndpoints_InvalidPath_BubblesOutException() { // Arrange var builder = Create <RuntimeRouteBuilder>(); var parsedRoute = new ParsedRoute { RouteId = "route1", Path = "/{invalid", Priority = 12, }; var backend = new BackendInfo("backend1", new DestinationManager(), new Mock <IProxyHttpClientFactory>().Object); var routeInfo = new RouteInfo("route1"); // Act Action action = () => builder.Build(parsedRoute, backend, routeInfo); // Assert Assert.Throws <AspNetCore.Routing.Patterns.RoutePatternException>(action); }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { var backendInfo = new BackendInfo() { IP = context.Connection.LocalIpAddress.ToString(), Hostname = Dns.GetHostName(), }; context.Response.ContentType = "application/json; charset=utf-8"; await JsonSerializer.SerializeAsync(context.Response.Body, backendInfo); }); }); }
public Game(string path, EventHandler <string> statusCallback) { List <string> files = Directory.GetFiles(path).ToList(); List <string> dirs = Directory.GetDirectories(path).ToList(); string gameName = files.Where(f => Path.GetExtension(f) == ".exe" && dirs.Select(d => Path.GetFileName(d)).Contains($"{Path.GetFileNameWithoutExtension(f)}_Data")).FirstOrDefault(); if (string.IsNullOrEmpty(gameName)) { throw new ArgumentException("Could not find game executable and Data folder pair."); } Name = Path.GetFileNameWithoutExtension(gameName); var appInfo = File.ReadLines($@"{path}\{Name}_Data\app.info").ToList(); if (appInfo.Count != 2) { throw new ArgumentException("Malformed app.info file found in Data folder."); } Developer = appInfo[0].Length > 14 ? appInfo[0].Substring(0, 11) + "..." : appInfo[0]; VisualName = appInfo[1].Length > 14 ? appInfo[1].Substring(0, 11) + "..." : appInfo[1]; Version = Helpers.FromAssetFile($@"{path}\{Name}_Data\globalgamemanagers.assets").ToString(); ScriptingBackend = BackendInfo.FromPath(path, Name, statusCallback); var binarypath = ""; if (ScriptingBackend.Def == BackendDef.Il2Cpp) { binarypath = files.First(f => Path.GetFileName(f) == "GameAssembly.dll"); } else { binarypath = @$ "{path}\{Name}_Data\Managed\Assembly-CSharp.dll"; }
public async Task Invoke_Works() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.Request.Method = "GET"; httpContext.Request.Scheme = "https"; httpContext.Request.Host = new HostString("example.com"); httpContext.Request.Path = "/api/test"; httpContext.Request.QueryString = new QueryString("?a=b&c=d"); var proxyHttpClientFactoryMock = new Mock <IProxyHttpClientFactory>(); var backend1 = new BackendInfo( backendId: "backend1", destinationManager: new DestinationManager(), proxyHttpClientFactory: proxyHttpClientFactoryMock.Object); var destination1 = backend1.DestinationManager.GetOrCreateItem( "destination1", destination => { destination.Config.Value = new DestinationConfig("https://localhost:123/a/b/"); destination.DynamicState.Value = new DestinationDynamicState(DestinationHealth.Healthy); }); httpContext.Features.Set <IAvailableDestinationsFeature>( new AvailableDestinationsFeature() { Destinations = new List <DestinationInfo>() { destination1 }.AsReadOnly() }); httpContext.Features.Set(backend1); var aspNetCoreEndpoints = new List <Endpoint>(); var routeConfig = new RouteConfig( route: new RouteInfo("route1"), matcherSummary: null, priority: null, backendOrNull: backend1, aspNetCoreEndpoints: aspNetCoreEndpoints.AsReadOnly()); var aspNetCoreEndpoint = CreateAspNetCoreEndpoint(routeConfig); aspNetCoreEndpoints.Add(aspNetCoreEndpoint); httpContext.SetEndpoint(aspNetCoreEndpoint); var tcs1 = new TaskCompletionSource <bool>(); var tcs2 = new TaskCompletionSource <bool>(); Mock <IHttpProxy>() .Setup(h => h.ProxyAsync( httpContext, It.Is <Uri>(uri => uri == new Uri("https://localhost:123/a/b/api/test?a=b&c=d")), proxyHttpClientFactoryMock.Object, It.Is <ProxyTelemetryContext>(ctx => ctx.BackendId == "backend1" && ctx.RouteId == "route1" && ctx.DestinationId == "destination1"), It.IsAny <CancellationToken>(), It.IsAny <CancellationToken>())) .Returns( async() => { tcs1.TrySetResult(true); await tcs2.Task; }) .Verifiable(); var sut = Create <ProxyInvokerMiddleware>(); // Act Assert.Equal(0, backend1.ConcurrencyCounter.Value); Assert.Equal(0, destination1.ConcurrencyCounter.Value); var task = sut.Invoke(httpContext); if (task.IsFaulted) { // Something went wrong, don't hang the test. await task; } await tcs1.Task; // Wait until we get to the proxying step. Assert.Equal(1, backend1.ConcurrencyCounter.Value); Assert.Equal(1, destination1.ConcurrencyCounter.Value); tcs2.TrySetResult(true); await task; Assert.Equal(0, backend1.ConcurrencyCounter.Value); Assert.Equal(0, destination1.ConcurrencyCounter.Value); // Assert Mock <IHttpProxy>().Verify(); }
private async Task InvokeInternal(HttpContext context, BackendConfig.BackendSessionAffinityOptions options, BackendInfo backend) { var destinationsFeature = context.GetRequiredDestinationFeature(); var destinations = destinationsFeature.Destinations; var affinityResult = _operationLogger.Execute( "ReverseProxy.FindAffinitizedDestinations", () => { var currentProvider = _sessionAffinityProviders.GetRequiredServiceById(options.Mode); return(currentProvider.FindAffinitizedDestinations(context, destinations, backend.BackendId, options)); }); switch (affinityResult.Status) { case AffinityStatus.OK: destinationsFeature.Destinations = affinityResult.Destinations; break; case AffinityStatus.AffinityKeyNotSet: // Nothing to do so just continue processing break; case AffinityStatus.AffinityKeyExtractionFailed: case AffinityStatus.DestinationNotFound: var keepProcessing = await _operationLogger.ExecuteAsync("ReverseProxy.HandleAffinityFailure", () => { var failurePolicy = _affinityFailurePolicies.GetRequiredServiceById(options.AffinityFailurePolicy); return(failurePolicy.Handle(context, options, affinityResult.Status)); }); if (!keepProcessing) { // Policy reported the failure is unrecoverable and took the full responsibility for its handling, // so we simply stop processing. Log.AffinityResolutionFailedForBackend(_logger, backend.BackendId); return; } Log.AffinityResolutionFailureWasHandledProcessingWillBeContinued(_logger, backend.BackendId, options.AffinityFailurePolicy); break; default: throw new NotSupportedException($"Affinity status '{affinityResult.Status}' is not supported."); } await _next(context); }
public async Task InvokeAsync_Works() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.Request.Method = "GET"; httpContext.Request.Scheme = "https"; httpContext.Request.Host = new HostString("example.com"); httpContext.Request.Path = "/api/test"; httpContext.Request.QueryString = new QueryString("?a=b&c=d"); var proxyHttpClientFactoryMock = new Mock <IProxyHttpClientFactory>(); var backend1 = new BackendInfo( backendId: "backend1", endpointManager: new EndpointManager(), proxyHttpClientFactory: proxyHttpClientFactoryMock.Object); var endpoint1 = backend1.EndpointManager.GetOrCreateItem( "endpoint1", endpoint => { endpoint.Config.Value = new EndpointConfig("https://localhost:123/a/b/"); endpoint.DynamicState.Value = new EndpointDynamicState(EndpointHealth.Healthy); }); var aspNetCoreEndpoints = new List <Endpoint>(); var routeConfig = new RouteConfig( route: new RouteInfo("route1"), matcherSummary: null, priority: null, backendOrNull: backend1, aspNetCoreEndpoints: aspNetCoreEndpoints.AsReadOnly()); var aspNetCoreEndpoint = CreateAspNetCoreEndpoint(routeConfig); aspNetCoreEndpoints.Add(aspNetCoreEndpoint); httpContext.SetEndpoint(aspNetCoreEndpoint); Mock <ILoadBalancer>() .Setup(l => l.PickEndpoint(It.IsAny <IReadOnlyList <EndpointInfo> >(), It.IsAny <IReadOnlyList <EndpointInfo> >(), It.IsAny <BackendConfig.BackendLoadBalancingOptions>())) .Returns(endpoint1); var tcs1 = new TaskCompletionSource <bool>(); var tcs2 = new TaskCompletionSource <bool>(); Mock <IHttpProxy>() .Setup(h => h.ProxyAsync( httpContext, It.Is <Uri>(uri => uri == new Uri("https://localhost:123/a/b/api/test?a=b&c=d")), proxyHttpClientFactoryMock.Object, It.Is <ProxyTelemetryContext>(ctx => ctx.BackendId == "backend1" && ctx.RouteId == "route1" && ctx.EndpointId == "endpoint1"), It.IsAny <CancellationToken>(), It.IsAny <CancellationToken>())) .Returns( async() => { tcs1.TrySetResult(true); await tcs2.Task; }) .Verifiable(); var sut = Create <ProxyInvoker>(); // Act backend1.ConcurrencyCounter.Value.Should().Be(0); endpoint1.ConcurrencyCounter.Value.Should().Be(0); var task = sut.InvokeAsync(httpContext); await tcs1.Task; // Wait until we get to the proxying step. backend1.ConcurrencyCounter.Value.Should().Be(1); endpoint1.ConcurrencyCounter.Value.Should().Be(1); tcs2.TrySetResult(true); await task; backend1.ConcurrencyCounter.Value.Should().Be(0); endpoint1.ConcurrencyCounter.Value.Should().Be(0); // Assert Mock <IHttpProxy>().Verify(); }
public async Task Invoke_Works() { var proxyHttpClientFactoryMock = new Mock <IProxyHttpClientFactory>(); var backend1 = new BackendInfo( backendId: "backend1", endpointManager: new EndpointManager(), proxyHttpClientFactory: proxyHttpClientFactoryMock.Object); var endpoint1 = backend1.EndpointManager.GetOrCreateItem( "endpoint1", endpoint => { endpoint.Config.Value = new EndpointConfig("https://localhost:123/a/b/"); endpoint.DynamicState.Value = new EndpointDynamicState(EndpointHealth.Healthy); }); var endpoint2 = backend1.EndpointManager.GetOrCreateItem( "endpoint2", endpoint => { endpoint.Config.Value = new EndpointConfig("https://localhost:123/a/b/"); endpoint.DynamicState.Value = new EndpointDynamicState(EndpointHealth.Healthy); }); var aspNetCoreEndpoints = new List <Endpoint>(); var routeConfig = new RouteConfig( route: new RouteInfo("route1"), matcherSummary: null, priority: null, backendOrNull: backend1, aspNetCoreEndpoints: aspNetCoreEndpoints.AsReadOnly()); var aspNetCoreEndpoint = CreateAspNetCoreEndpoint(routeConfig); aspNetCoreEndpoints.Add(aspNetCoreEndpoint); var httpContext = new DefaultHttpContext(); httpContext.SetEndpoint(aspNetCoreEndpoint); Mock <ILoadBalancer>() .Setup(l => l.PickEndpoint(It.IsAny <IReadOnlyList <EndpointInfo> >(), It.IsAny <BackendConfig.BackendLoadBalancingOptions>())) .Returns(endpoint1); httpContext.Features.Set <IAvailableBackendEndpointsFeature>( new AvailableBackendEndpointsFeature() { Endpoints = new List <EndpointInfo>() { endpoint1, endpoint2 }.AsReadOnly() }); httpContext.Features.Set(backend1); var sut = Create <LoadBalancingMiddleware>(); await sut.Invoke(httpContext); var feature = httpContext.Features.Get <IAvailableBackendEndpointsFeature>(); Assert.NotNull(feature); Assert.NotNull(feature.Endpoints); Assert.Equal(1, feature.Endpoints.Count); Assert.Same(endpoint1, feature.Endpoints[0]); Assert.Equal(200, httpContext.Response.StatusCode); }
public BackendInfoController(ILogger <BackendInfoController> logger) { _logger = logger; Info = new BackendInfo(); }