Skip to content

Feature/capabilities replacements expansion#16

Open
OLamelas wants to merge 5 commits intositmun:devfrom
OLamelas:feature/capabilitiesReplacementsExpansion
Open

Feature/capabilities replacements expansion#16
OLamelas wants to merge 5 commits intositmun:devfrom
OLamelas:feature/capabilitiesReplacementsExpansion

Conversation

@OLamelas
Copy link
Copy Markdown
Contributor

Solves issues on proxied services that return unexpected URLs in GetCapabilities response by providing replacement customization via application.yml or environment variables.

Using the existing decorator, base behaviour stays the same, replacing service URL as is first. Then, based on configured OGC service paths and extra sources that may be encountered in capabilities response, those are replaced as well.

For example:

Given configuration:

  wms:
    capabilities:
      # OGC service path suffixes recognized when rewriting URLs in GetCapabilities responses.
      service-paths:
        - wms
        - wfs
        - wcs
        - ows
      # Optional extra source URL prefixes to replace with the proxy URL (empty list by default).
      extra-sources:
       - http://localhost:3000/
       - http://internal-geoserver:8080/geoserver

Every quoted (to avoid false positives) URL that matches either of the 2 extra sources until OGC service path (not included) or matches the exact stored service URL +optionally any of the OGC service paths is replaced. So, in this case, if received capabilities from (proxied) https://geoserver.example.com/geoserver looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<WMS_Capabilities version="1.3.0"
	xmlns:xlink="http://www.w3.org/1999/xlink">
	<Service>
		<Name>WMS</Name>
		<Title>My GeoServer</Title>
		<OnlineResource xlink:href="https://geoserver.example.com/geoserver?SERVICE=WMS"/>
	</Service>
	<Capability>
		<Request>
			<GetCapabilities>
				<DCPType>
					<HTTP>
						<Get>
							<OnlineResource  xlink:href="https://geoserver.example.com/geoserver/wms?SERVICE=WMS&REQUEST=GetCapabilities"/>
						</Get>
						<Post>
							<OnlineResource xlink:href="https://geoserver.example.com/geoserver/wms?SERVICE=WMS&amp;REQUEST=GetCapabilities"/>
						</Post>
					</HTTP>
				</DCPType>
			</GetCapabilities>
			<GetMap>
				<DCPType>
					<HTTP>
						<Get>
							<OnlineResource xlink:href="https://geoserver.example.com/geoserver/wms?SERVICE=WMS&amp;REQUEST=GetMap"/>
						</Get>
					</HTTP>
				</DCPType>
			</GetMap>
			<GetFeatureInfo>
				<DCPType>
					<HTTP>
						<Get>
							<OnlineResource xlink:href="https://geoserver.example.com/geoserver/wms?SERVICE=WMS&amp;REQUEST=GetFeatureInfo"/>
						</Get>
					</HTTP>
				</DCPType>
			</GetFeatureInfo>
		</Request>
		<Layer>
			<Abstract>Direct access at https://geoserver.example.com/geoserver/wms (internal only)</Abstract>
		</Layer>
	</Capability>
</WMS_Capabilities>

The final capabilities would look like this:

<?xml version="1.0" encoding="UTF-8"?>
<WMS_Capabilities version="1.3.0"
	xmlns:xlink="http://www.w3.org/1999/xlink">
	<Service>
		<Name>WMS</Name>
		<Title>My GeoServer</Title>
		<OnlineResource xlink:href="https://proxy.sitmun.org/middleware/proxy/3/66/WMS/4?SERVICE=WMS"/>
	</Service>
	<Capability>
		<Request>
			<GetCapabilities>
				<DCPType>
					<HTTP>
						<Get>
							<OnlineResource  xlink:href="https://proxy.sitmun.org/middleware/proxy/3/66/WMS/4?SERVICE=WMS&amp;REQUEST=GetCapabilities"/>
						</Get>
						<Post>
							<OnlineResource xlink:href="https://proxy.sitmun.org/middleware/proxy/3/66/WMS/4?SERVICE=WMS&amp;REQUEST=GetCapabilities"/>
						</Post>
					</HTTP>
				</DCPType>
			</GetCapabilities>
			<GetMap>
				<DCPType>
					<HTTP>
						<Get>
							<OnlineResource xlink:href="https://proxy.sitmun.org/middleware/proxy/3/66/WMS/4?SERVICE=WMS&amp;REQUEST=GetMap"/>
						</Get>
					</HTTP>
				</DCPType>
			</GetMap>
			<GetFeatureInfo>
				<DCPType>
					<HTTP>
						<Get>
							<OnlineResource xlink:href="https://proxy.sitmun.org/middleware/proxy/3/66/WMS/4?SERVICE=WMS&amp;REQUEST=GetFeatureInfo"/>
						</Get>
					</HTTP>
				</DCPType>
			</GetFeatureInfo>
		</Request>
		<Layer>
			<Abstract>Direct access at https://geoserver.example.com/geoserver/wms (internal only)</Abstract>
		</Layer>
	</Capability>
</WMS_Capabilities>

@OLamelas OLamelas marked this pull request as ready for review March 27, 2026 12:59
@fjlopez
Copy link
Copy Markdown
Contributor

fjlopez commented Mar 31, 2026

@OLamelas @rbejar @RicardCots @codinachssm

This change improves rewriting of GetCapabilities responses, but it does not fully solve the issue observed when testing against a corpus of real SITMUN service definitions.

The problem is not only that capabilities documents may expose unexpected upstream URLs. The deeper issue is that many URLs stored in SITMUN 2 or SITMUN 3 by an administrator may be discovery URLs that route to the service, rather than the service’s actual operational URL. Examples from the corpus:

The original version of the decorator implicitly assumes that the registered service URL is the operational URL for WMS operations. In that scenario, replacing the registered service URL with the proxy URL is a valid solution. Later, when the client sends GetMap / GetFeatureInfo requests to the proxy using the SITMUN service ID, the proxy resolves that service ID back to the operational URL.

However, when the operational URL advertised in the capabilities is different, this PR only fixes what clients see in the GetCapabilities document. It does not fix the URL stored by SITMUN. So the real issue is not only proxy routing: the stored service definition itself may be wrong for any later operation that depends on that URL. Clients may therefore use the proxy URLs correctly and still fail, because the proxy may forward the request to the wrong upstream endpoint. Capabilities rewriting is not sufficient when the service ID is bound to a URL that does not correspond to the actual operational URL.

In practice, the problem with a service (proxied or not) will not surface as long as the upstream infrastructure continues to preserve routing equivalence between the URL and the actual operational URL. For example, if upstream proxies or services keep redirects, rewrites, or compatibility routes in place, proxied requests may still reach the correct backend. Is in these scenarios where this PR might apparently work for a while.

However, that is a weak position for SITMUN, because correctness then depends on external routing compatibility being preserved over time. If those routing rules are changed or removed upstream (for example, by IGN, ICGC, etc.), SITMUN requests, whether proxied or not, may start failing even though SITMUN’s own stored configuration has not changed.

Because of that, I think the solution should be implemented in the admin web client rather than in the proxy:

  • treat the user-entered URL always as a discovery URL
  • fetch always capabilities from it
  • detect the effective operational upstream base from the capabilities
  • show both values to the user, and then, either:
    a) replace the value to be saved with the detected operationalURL, or at least warn and ask for confirmation
    b) store both URLs, using the operational URL for layer discovery, clients and the proxy

Option a) may be clearer for users.

We also need a way to revalidate this configuration later, because the operational URL may change over time. Revalidation from the admin UI?

@rbejar
Copy link
Copy Markdown
Contributor

rbejar commented Mar 31, 2026

If I understand this issue (not sure about that), b) would seem a safer bet. The users of the web admin must understand this difference, they are SITMUN administrators not end users. And this would make it a bit simpler to address the case where the owners of the service change the operational URL (the client would just need to "refresh" from the discovery URL). Of course, if the discovery URL changes it should need to be changed too, but this is the same as with a). The main drawback is that two URLs would have to be stored for every service. I would also argue, and that would be my main point, that b) would be an application of the Single Responsibility Principle: a discovery URL and an operational URL are different thilngs, have different purposes and need to be treated differently.

I don't think, after considering it for the best part of 5 minutes 😉, that this can be definitely solved in any other way: the SITMUN data model and the web admin app need to adjust for this, or this problem will arise from time to time.

@OLamelas
Copy link
Copy Markdown
Contributor Author

In my opinion, even though it does not completely fix the issue, it partially does, allowing current installations to work better than they did before. I'd like it to be merged to avoid the issues that prompted this PR. I do agree and understand that further development should be considered in the future. Would you consider merging it now, even if it is a partial solution?

@fjlopez
Copy link
Copy Markdown
Contributor

fjlopez commented Apr 12, 2026

Could you please confirm whether you have tested that the map client can access the map layers smoothly after this change? If so, I’d be happy to move forward with merging the PR.

@OLamelas
Copy link
Copy Markdown
Contributor Author

The affected and tested installation seems to work with no problem after checking out this branch and redeploying, both for proxied and non-proxied services.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants