13

I've got a Tomcat app that is being served up from multiple domains. Previous developers built a method to return the application URL (see below). In the method they request the server name (request.getServerName()) which, appropriately, returns the ServerName from the httpd.conf file.

However, I don't want that. What I want is the host name that the browser sent the request to, i.e. whichever domain the browser is accessing the application from.

I tried getHeader("Host"), but that is still returning the ServerName set in the httpd.conf file.

Instead of request.getServerName(), what should I use to get the server name that the browser sent the request to?

For example:

  • ServerName in httpd.conf: www.myserver.net
  • User accesses Tomcat app on www.yourserver.net

I need to return www.yourserver.net NOT www.myserver.net. The request.getServerName() call only seems to return www.myserver.net

/**
 * Convenience method to get the application's URL based on request
 * variables.
 * 
 * @param request the current request
 * @return URL to application
 */
public static String getAppURL(HttpServletRequest request) {
    StringBuffer url = new StringBuffer();
    int port = request.getServerPort();
    if (port < 0) {
        port = 80; // Work around java.net.URL bug
    }
    String scheme = request.getScheme();
    url.append(scheme);
    url.append("://");
    url.append(request.getServerName());
    if (("http".equals(scheme) && (port != 80)) || ("https".equals(scheme) && (port != 443))) {
        url.append(':');
        url.append(port);
    }
    url.append(request.getContextPath());
    return url.toString();
}

Thanks in advance!

f_puras
  • 2,521
  • 4
  • 33
  • 38
Mark Hall
  • 475
  • 1
  • 8
  • 14
  • 1
    Tomcat can't even see the `httpd.conf` file, let alone return a value from it. Tomcat gets the server host from the request. – user207421 Sep 06 '16 at 23:47

4 Answers4

17

You need to ensure that httpd passes the Host header provided by the client to Tomcat. The easiest way (assuming you are using mod_proxy_http - you didn't say) is with the following:

ProxyPreserveHost On
Kevin Panko
  • 8,356
  • 19
  • 50
  • 61
Mark Thomas
  • 16,339
  • 1
  • 39
  • 60
6

How about using something like I did in this demo JSP ?

<%
  String requestURL = request.getRequestURL().toString();
  String servletPath = request.getServletPath();
  String appURL = requestURL.substring(0, requestURL.indexOf(servletPath));
%>
appURL is <%=appURL%>
rickz
  • 4,324
  • 2
  • 19
  • 30
  • Rick, this worked great - though I ended up using Mark's suggestion above, as it didn't require code change. – Mark Hall May 03 '12 at 15:59
  • @rickz How does one unit test something like this? The key problem is how to create an instance of HttpServletRequest for unit testing? Google search brings up suggestions to mock the class but if you mock its methods, you are not really testing anything at that point. I also came across httpunit library but that also does not help. – morpheus Sep 06 '16 at 02:44
  • I have the same requirement to get the Servername mentioned inside VirtualHost as below. DocumentRoot /usr/local/apache/htdocs/EMPTY ServerName xxx.yyy.com RewriteEngine On # Redirect all requests to the local Apache server to port 8000 RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L] It seems "ProxyPreserveHost On" not applied for this configuration as it is not using mod_proxy configuration. Please advice me – Prabha Mar 01 '19 at 12:54
  • I don't have solution. You should ask new question. – rickz Mar 01 '19 at 15:49
2

Maybe not related to this question. If you are using tomcat, you can specify any Host string in the request header, even javascript like <script>alert(document.cookie);</script>

Then it could be shown on the page.:

<p> host name is : <%= request.getServerName() %> </p>

So you need to verify it before using it.

maoyang
  • 1,067
  • 1
  • 11
  • 11
-1

This is indeed very problematic because sometimes you don't even know where the host that you expect to be a fully qualified domain has been removed. @rickz provided a great solution, but here's another one that I consider to be more complete and covers many different urls:

Basically, you strip the protocol (http://, https://, ftp://,...) then the port (should it exist) and then the whole URI. That gives you the complete list of top level domain and subdomains.

String requestURL = request.getRequestURL().toString();
String withoutProtocol = requestURL.replaceAll("(.*\\/{2})", "")
String withoutPort = withoutProtocol.replaceAll("(:\\d*)", "") 
String domain = withoutPort.replaceAll("(\\/.*)", "")

I did this in scala using inline method definitions, but the code above is more verbose because I found it better to post the solution in pure java. So if you create methods for this you could chain them to do something like this:

removeURI(removePort(removeProtocol(requestURL)))
David Pelaez
  • 1,374
  • 1
  • 13
  • 16