Another Jetty 6 trick I have had to use several times is patching it to enable redirects with the correct URL scheme when proxyied via HTTP behind a webserver using SSL. Because the connection between the webserver and jetty is just plain HTTP (without SSL), jetty will send the redirect with a plain http
scheme:
Location: http://example.com/foo
But when the webserver is using SSL, what I really want is for it to send the redirect with an https
scheme:
Location: https://example.com/foo
X-Forwarded- Headers
As described in jetty's reverse proxy docs, by setting the forwarded
property in jetty's connector configuration, you can get jetty to use the server name/port and remote client IP-address from the X-Forwarded-Host
and X-Forwarded-For
headers that apache's mod_proxy includes automatically.
One header that mod_proxy does not include automatically, however, is X-Forwarded-Proto
. You have to add that manually, via the RequestHeader
directive in your apache config (wherever you included the ProxyPass
directive that forwards requests to jetty):
ProxyPass http://localhost:8080/
ProxyPassReverse http://localhost:8080/
RequestHeader set X-Forwarded-Proto "https"
Patching Jetty 6
For jetty 7/8, that would be sufficient. With the above configuration, they'll send the correct redirects. But jetty 6 doesn't use the X-Forwarded-Proto
header, so you have to create your own connector class to handle it. This is what I've done (for the nio connectors):
package com.pitchstone.lib.jetty;
import java.io.IOException;
import org.mortbay.io.EndPoint;
import org.mortbay.jetty.HttpFields;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.nio.SelectChannelConnector;
/**
* Jetty nio connector.
* Adds the ability to use the 'X-Forwarded-Proto' header
* to set the request 'scheme' property.
*/
public class NioConnector extends SelectChannelConnector {
private String _forwardedProtoHeader = "X-Forwarded-Proto";
public NioConnector() {
super();
}
// AbstractConnector
protected void checkForwardedHeaders(EndPoint endpoint, Request request)
throws IOException {
super.checkForwardedHeaders(endpoint, request);
HttpFields httpFields = request.getConnection().getRequestFields();
String forwardedProto = httpFields.getStringField(getForwardedProtoHeader());
forwardedProto = getLeftMostValue(forwardedProto);
if ("http".equals(forwardedProto) || "https".equals(forwardedProto))
request.setScheme(forwardedProto);
}
// impl
public String getForwardedProtoHeader() {
return _forwardedProtoHeader;
}
public void setForwardedProtoHeader(String x) {
_forwardedProtoHeader = x;
}
}
To use it, compile it, jar it up, and add the jar to jetty's lib/ext
directory (/usr/share/jetty/lib/ext
by default under ubuntu/debian). Then configure jetty.xml
to use it, replacing org.mortbay.jetty.nio.SelectChannelConnector
with this custom version:
<Call name="addConnector">
<Arg>
<New class="org.mortbay.jetty.nio.SelectChannelConnector">
<New class="com.pitchstone.lib.jetty.NioConnector">
<Set name="host"><SystemProperty name="jetty.host" /></Set>
...
<Set name="forwarded">true</Set>
</New>
</Arg>
</Call>
With that configuration and patch in place, jetty will now send redirects with the https
scheme. It also will map these X-Forwarded-For
headers to the ServletRequest API like so:
Header | ServletRequest Method |
---|---|
X-Forwarded-Host | getServerName() |
X-Forwarded-Host | getServerPort() |
X-Forwarded-For | getRemoteAddr() |
X-Forwarded-For | getRemoteHost() |
X-Forwarded-Proto | getScheme() |
I also encountered this problem and solved it using a custom filter that looked at X-Forwarded-Proto. Worked like a charm.
ReplyDeleteThanks, the solution worked as a charm.
ReplyDelete-Alexey