mkopylec / charon-spring-boot-starter

Reverse proxy implementation in form of a Spring Boot starter.
Apache License 2.0
240 stars 54 forks source link

Q: Correcting destinations from configuration #59

Closed torsten-liermann closed 6 years ago

torsten-liermann commented 6 years ago

Hi,

Thanks for this handy reverse proxy.

Is it possible to manipulate subsequent mappings from the configuration? I need a way to put the port later.

Thanks, Torstem

mkopylec commented 6 years ago

I'm not sure what you want to do but maybe a custom mappings provider is what you need: https://github.com/mkopylec/charon-spring-boot-starter#mappings If not please provide more details.

torsten-liermann commented 6 years ago

More details: I like the configuration of the mapping in the application.yml. But in my case, the port @docindex.api.port@ is determined at runtime. So it would be convenient to subsequently correct the destination URLs.

charon:
  mappings:
    - name: forward to local external api
      path: /docindex/api
      destinations: http://127.0.0.1:@docindex.api.port@/docindex/api

Background why dynamically ports: My spring-boot app starts an external process (via de.flapdoodle.embed) which picks a random free port for this service.

mkopylec commented 6 years ago

Ok, and how you would now what is the value of @docindex.api.port@? If your backend app wil restart you need to update the @docindex.api.port@ value, how will you handle that?

mkopylec commented 6 years ago

If you have a solution how you can update the yml file when your backend is restarted than you could do something like:

some.port: @docindex.api.port@

charon:
  mappings:
    - name: forward to local external api
      path: /docindex/api
      destinations: http://127.0.0.1:${some.port}/docindex/api
torsten-liermann commented 6 years ago

Do you know the embedded mongodb construct in spring-boot? If not: spring-boot starts an embedded mongo DB with a calculated random port und put the value into the enviroment. The code looks like:

private void setPortProperty(ApplicationContext currentContext, final int port) {
        if (currentContext instanceof ConfigurableApplicationContext) {
            MutablePropertySources sources = ((ConfigurableApplicationContext) currentContext)
                    .getEnvironment().getPropertySources();
            if (sources.get("local.restheart.port") == null) {
                PropertySource<String> propertySource = new PropertySource<String>("docindex.api.port") {
                    @Override
                    public Object getProperty(String name) {
                        return "docindex.api.port".equals(name) ? port : null;
                    }
                };
                sources.addFirst(propertySource);
            }
        }
        if (currentContext.getParent() != null) {
            setPortProperty(currentContext.getParent(), port);
        }
    }

All consumers who need this information, for example the Mongo Client, get this value from the environment -- Sorry, I can not express myself in English.

My first attempt at a solution looked like this:

@Bean
    public CustomMappingsProvider customMappingsProvider(
            ServerProperties serverProperties,
            CharonProperties charonProperties,
            MappingsCorrector mappingsCorrector,
            HttpClientProvider httpClientProvider,
            Environment environment) {
        String docindexApiPort = environment.getProperty("docindex.api.port");
        if (docindexApiPort != null) {
            for (MappingProperties mappings : charonProperties.getMappings()) {
                for (int i = 0; i < mappings.getDestinations().size(); i++) {
                    if (mappings.getDestinations().get(i).contains("@docindex.api.port@")) {
                        String correctedUrl = mappings.getDestinations().get(i).replace("@docindex.api.port@", docindexApiPort);
                        mappings.getDestinations().set(i, correctedUrl);
                    }
                }
            }
        }
....
}

Unfortunately, this correction takes place too late.

torsten-liermann commented 6 years ago

Thanks for listening: this is a solution:

@Bean

    public MappingsProvider charonMappingsProvider(CharonProperties charon, ServerProperties server, MappingsCorrector mappingsCorrector, HttpClientProvider httpClientProvider, Environment environment) {
        String localApiPort = environment.getProperty("local.api.port");
        if (localApiPort != null) {
            correctProxyDestinations(charon.getMappings(), localApiPort );
        }
        return new ConfigurationMappingsProvider(server, charon, mappingsCorrector, httpClientProvider);
    }
mkopylec commented 6 years ago

Great, can we close the issue then?

torsten-liermann commented 6 years ago

Yes. Thanks for Your support.