Search code examples
azure-service-fabric

Service listener that redirects to another listener


In my service fabric application, I currently have a Core web service that is running on HTTPS. This works fine but I wanted to make it so that if the web service was accessed via HTTP, it would redirect to the HTTPS version.

I've solved this by creating a second service listener which loads the web project on an HTTP endpoint as well, and then via middleware redirects to the HTTPS version. This works reasonably well, my only qualm is that on loading the project it needs to obviously load two versions of the Web project, and that takes a bit of time.

Ideally there would be some way to have a simple redirect at the service listener level where I could redirect to my HTTPS listener, without having to boot up a full kestrel version of a web application.

Any thoughts? This is what it looks like currently.

internal sealed class Web : StatelessService
    {
        public Web(StatelessServiceContext context)
            : base(context)
        { }

        /// <summary>
        /// Optional override to create listeners (like tcp, http) for this service instance.
        /// </summary>
        /// <returns>The collection of listeners.</returns>
        protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
        {
            return new ServiceInstanceListener[]
            {
                new ServiceInstanceListener(serviceContext =>
                    new WebListenerCommunicationListener(serviceContext, "ServiceEndpointSecure", url =>
                    {
                        ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting WebListener on {url}");

                        var context = FabricRuntime.GetActivationContext();
                        var config = context.GetConfigurationPackageObject("Config");
                        var environment = config.Settings.Sections["Environment"].Parameters["ASPNETCORE_ENVIRONMENT"].Value;

                        return new WebHostBuilder().UseWebListener()
                                    .ConfigureServices(
                                        services => services
                                            .AddSingleton<StatelessServiceContext>(serviceContext))
                                    .UseContentRoot(Directory.GetCurrentDirectory())
                                    .UseStartup<Startup>()
                                    .UseEnvironment(environment)
                                    .UseUrls(url)
                                    .Build();
                    }),"HttpsListener"),
                 new ServiceInstanceListener(serviceContext =>
                    new WebListenerCommunicationListener(serviceContext, "ServiceEndpoint", url =>
                    {
                        ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting WebListener on {url}");

                        var context = FabricRuntime.GetActivationContext();
                        var config = context.GetConfigurationPackageObject("Config");
                        var environment = config.Settings.Sections["Environment"].Parameters["ASPNETCORE_ENVIRONMENT"].Value;

                        return new WebHostBuilder().UseWebListener()
                                    .ConfigureServices(
                                        services => services
                                            .AddSingleton<StatelessServiceContext>(serviceContext))
                                    .UseContentRoot(Directory.GetCurrentDirectory())
                                    .UseStartup<Startup>()
                                    .UseEnvironment(environment)
                                    .UseUrls(url)
                                    .Build();
                    }),"HttpListener")
            };
        }
    }

And in the ServiceManifest.xml

<Resources>
    <Endpoints>         
      <Endpoint Protocol="http" Name="ServiceEndpoint" Type="Input" Port="80" />
      <Endpoint Protocol="https" Name="ServiceEndpointSecure" Type="Input" Port="443" />
    </Endpoints>
  </Resources>

Solution

  • You can simply listen to two url's from one WebListener:

    .UseUrls("http://+:80", "https://+:443")

    Add some middleware to enforce https. Something like:

     HttpRequest req = context.Request;
     if (req.IsHttps == false)
     {
         string url = "https://" + req.Host + req.Path + req.QueryString;
         context.Response.Redirect(url, permanent: true);
     }