Skip to content

Unexpected handling of "localhost" in proxyBaseUrl #279

@matthijskooijman

Description

@matthijskooijman

I have a local testing setup with a number of related dockers. One of them runs OSH, one of them runs nginx as a reverse proxy in front of OSH. The reverse proxy exposes OSH on "http://localhost:8080/sensorhub", which is forwarded to "http://osh:8080/sensorhub" (where osh is the docker hostname of the osh container).

To make sure that OSH generates the right URLs, I configured "proxyBaseUrl": "http://localhost:8080/" in the HTTP server in config.json. However, this does not work: Clickable links and resources (JS, CSS) use "http://osh:8080/..." URLs, which are not valid from my browser.

It turns out there is some special handling in consys.RestApiServlet that prevents this from working:

public String getApiRootURL(HttpServletRequest req)
{
if (rootUrl.contains("localhost") && req != null)
return rootUrl.replace("localhost", req.getServerName());
return rootUrl;
}

AFAIU, the proxyBaseUrl setting is processed by the HTTP server. Then the RestApiServlet constructor sets its rootUrl property to the result of ConSysApiService.getPublicEndpointUrl(), which calls HttpServer.getPublicEndpointUrl('/consys'), which returns the proxyBaseUrl, plus servletsRootUrl.

If proxyBaseUrl is not set, HTTP server defaults to localhost:

public String getServerBaseUrl()
{
String baseUrl = "";
if (!Strings.isNullOrEmpty(config.proxyBaseUrl))
baseUrl = config.proxyBaseUrl;
else if (config.httpPort > 0)
baseUrl = "http://localhost" + (config.httpPort != 80 ? ":" + config.httpPort : "");
else if (config.httpsPort > 0)
baseUrl = "https://localhost" + (config.httpsPort != 443 ? ":" + config.httpsPort : "");
return baseUrl;
}

So I suspect the intention of the above code in RestApiServlet is to autodetect the proxy url if it is not specified explicitly: Use the hostname from the request's Host header. However, the check for "not specified" is currently rootUrl.contains("localhost") which is inaccurate (since the explicitly specified URL might have localhost as the hostname, but also because this checks too broad, it would also trigger on e.g. "http://example.org/localhost/sensorhub").

It also seems weird that the code for achieving this is in the consys service at all. This sounds like something that is more generic and should live in HttpServer. It is not possible to move this into HttpServer.getServerBaseUrl() directly, as that does not have access to a current request (and might be called during initialization when there is not even a current request).

But I can imagine that instead of figuring out things itself (using a previously stored rootUrl), RestApiServlet.getApiRootURL() just calls into HttpServer (via RestApiService if needed), passing the current HttpServletRequest along so this logic can live in HttpServer instead.

Alternatively, the base/root url could be calculated by HttpServer at the start of every request, and then be passed down to the request handling code (either as a parameter, or as an extra attribute on the HttpServerletRequest` object.

It seems the same problematic logic exists in sensorhub-service-swe/src/main/java/org/sensorhub/impl/service/swe/SWEServlet.java and sensorhub-service-sweapi/src/main/java/org/sensorhub/impl/service/sweapi/RestApiServlet.java.

Of course my usecase is a bit contrived - typically you would not run a reverse proxy on localhost. However, since I'm working towards a production stack and want to test that locally, I think it is not a super weird usecase. Of course, this is easy to work around by just adding a different hostname to /etc/hosts and putting that in proxyBaseUrl (tested this, works), but it is only easy after I spent some time going through code to debug this, which I'd rather spare others. And of course, the current code is just smelly ;-)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions