Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get corresponding https port number for http port in Tomcat

I'm trying to minimize the configuration for my webapp, and one of the things I want to remove is the port number mapping:

  • http: 80 -> https: 443
  • http: 9080 -> https: 9443

Since the configuration already exists in conf/server.xml, I don't want to duplicate these settings in my app. It's a pain to have to change (and remember to change) multiple config files. Also, the app should not care what environment/container it was deployed into, as long as it supports http and https.

I have the following configured in my web.xml, but I need to be able to build https URLs in addition to redirecting:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>secure</web-resource-name>
        <url-pattern>/https/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

The above block tells the servlet container to redirect http requests to its https counterpart. Obviously, Tomcat knows what connector the request came from, looks up the "redirectPort" attribute in server.xml for that connector and builds the https URL using that.

According to these previous/related questions, there doesn't seem to be a standard or tomcat-specific way of getting the corresponding https port for a request:

  • Tomcat: how to get the secure port number in Java?
  • get server name and port number of JSP page for HTTPS

TL;DR: My question is, what ideas do you have for getting the corresponding https port number for an insecure request without explicitly configuring them in your webapp?

One idea I have is to make a http call from the webapp to itself to a path behind the security constraint using the same port number as the current request and parse the redirect location from the response. The responses should be cached, of course. Is there a more straightforward way?

EDIT: Added an answer with the code for the idea.

like image 527
andy Avatar asked Dec 31 '25 06:12

andy


1 Answers

In case anyone wanted to know how to implement the idea I offered in the question, here's some sample code I put together to test it. I don't think I'm going with this solution though.

This works with the security-constraint block from the question body.

private static final String HTTPS_CONSTRAINED_PATH = "/https";

private static ConcurrentHashMap<String, Integer> resolvedHttpsPortMap = new ConcurrentHashMap<String, Integer>();
private static final ReentrantLock portMapLock = new ReentrantLock();

public static int resolveHttpsPort(HttpServletRequest request) {
    if (request.isSecure()) return request.getServerPort();
    String key = request.getServerName() + ":" + request.getServerPort();
    Integer port = resolvedHttpsPortMap.get(key);
    if (port == null) {
        portMapLock.lock();
        try {
            port = resolvedHttpsPortMap.get(key);
            if (port == null) {
                URL url = new URL(request.getScheme(), request.getServerName(), request.getServerPort(), request.getContextPath() + HTTPS_CONSTRAINED_PATH);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                if (conn.getResponseCode() != 301 && conn.getResponseCode() != 302
                        && conn.getResponseCode() != 303 && conn.getResponseCode() != 307) {
                    throw new IllegalStateException("Got unexpected response code " + conn.getResponseCode() + " from URL: " + url);
                }
                String location = conn.getHeaderField("Location");
                if (location == null) {
                    throw new IllegalStateException("Did not get a Location header in the response from URL: " + url);
                }
                URL locationUrl = new URL(location);
                port = locationUrl.getPort();
            }
        } catch (Exception e) {
            logger.warn("Could not determine corresponding HTTPS port for '" + key + "'", e);
        } finally {
            if (port == null) port = -1;
            resolvedHttpsPortMap.put(key, port);
            portMapLock.unlock();
        }
    }
    return port;
}
like image 132
andy Avatar answered Jan 01 '26 18:01

andy