Skip to content

Commit 02cd292

Browse files
committed
Fixes #14208 - Document ProxyHandler.
Added documentation for `ConnectHandler` and `ProxyHandler`. Signed-off-by: Simone Bordet <[email protected]>
1 parent 0c6aa1c commit 02cd292

File tree

3 files changed

+252
-0
lines changed

3 files changed

+252
-0
lines changed

documentation/jetty/modules/code/examples/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@
6262
<groupId>org.eclipse.jetty</groupId>
6363
<artifactId>jetty-openid</artifactId>
6464
</dependency>
65+
<dependency>
66+
<groupId>org.eclipse.jetty</groupId>
67+
<artifactId>jetty-proxy</artifactId>
68+
</dependency>
6569
<dependency>
6670
<groupId>org.eclipse.jetty</groupId>
6771
<artifactId>jetty-rewrite</artifactId>

documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Set;
2424
import java.util.TimeZone;
2525
import java.util.concurrent.CompletableFuture;
26+
import java.util.concurrent.atomic.AtomicLong;
2627
import javax.net.ssl.SSLEngine;
2728
import javax.net.ssl.SSLException;
2829

@@ -36,6 +37,8 @@
3637
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
3738
import org.eclipse.jetty.client.ContentResponse;
3839
import org.eclipse.jetty.client.HttpClient;
40+
import org.eclipse.jetty.client.transport.HttpClientConnectionFactory;
41+
import org.eclipse.jetty.client.transport.HttpClientTransportDynamic;
3942
import org.eclipse.jetty.compression.server.CompressionConfig;
4043
import org.eclipse.jetty.compression.server.CompressionHandler;
4144
import org.eclipse.jetty.ee11.servlet.DefaultServlet;
@@ -54,12 +57,17 @@
5457
import org.eclipse.jetty.http.MultiPartConfig;
5558
import org.eclipse.jetty.http.MultiPartFormData;
5659
import org.eclipse.jetty.http.pathmap.PathSpec;
60+
import org.eclipse.jetty.http2.client.HTTP2Client;
61+
import org.eclipse.jetty.http2.client.transport.ClientConnectionFactoryOverHTTP2;
5762
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
5863
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
5964
import org.eclipse.jetty.http3.server.HTTP3ServerConnectionFactory;
6065
import org.eclipse.jetty.http3.server.HTTP3ServerQuicConfiguration;
66+
import org.eclipse.jetty.io.ClientConnectionFactory;
67+
import org.eclipse.jetty.io.ClientConnector;
6168
import org.eclipse.jetty.io.Content;
6269
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
70+
import org.eclipse.jetty.proxy.ProxyHandler;
6371
import org.eclipse.jetty.quic.quiche.server.QuicheServerConnector;
6472
import org.eclipse.jetty.quic.quiche.server.QuicheServerQuicConfiguration;
6573
import org.eclipse.jetty.rewrite.handler.CompactPathRule;
@@ -2052,4 +2060,106 @@ public boolean handle(Request request, Response response, Callback callback)
20522060
server.start();
20532061
// end::dosHandler[]
20542062
}
2063+
2064+
public void proxyNewHttpClient() throws Exception
2065+
{
2066+
// tag::proxyNewHttpClient[]
2067+
Server server = new Server();
2068+
ServerConnector connector = new ServerConnector(server);
2069+
server.addConnector(connector);
2070+
2071+
// Customize the forward proxy's HttpClient transports.
2072+
ProxyHandler.Forward forwardProxy = new ProxyHandler.Forward()
2073+
{
2074+
@Override
2075+
protected HttpClient newHttpClient()
2076+
{
2077+
ClientConnector clientConnector = new ClientConnector();
2078+
ClientConnectionFactory.Info http11 = HttpClientConnectionFactory.HTTP11;
2079+
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector));
2080+
return new HttpClient(new HttpClientTransportDynamic(clientConnector, http11, http2));
2081+
}
2082+
};
2083+
2084+
server.setHandler(forwardProxy);
2085+
server.start();
2086+
// end::proxyNewHttpClient[]
2087+
}
2088+
2089+
public void proxyForwardForbidden() throws Exception
2090+
{
2091+
// tag::proxyForwardForbidden[]
2092+
Server server = new Server();
2093+
ServerConnector connector = new ServerConnector(server);
2094+
server.addConnector(connector);
2095+
2096+
// Customize the forward proxy's HttpClient transports.
2097+
ProxyHandler.Forward forwardProxy = new ProxyHandler.Forward()
2098+
{
2099+
@Override
2100+
public boolean handle(Request clientToProxyRequest, Response proxyToClientResponse, Callback proxyToClientCallback)
2101+
{
2102+
String domain = Request.getServerName(clientToProxyRequest);
2103+
2104+
// Allow requests to non-bad domains.
2105+
if (!domain.contains(".bad.com"))
2106+
return super.handle(clientToProxyRequest, proxyToClientResponse, proxyToClientCallback);
2107+
2108+
// Deny requests to bad domains.
2109+
Response.writeError(clientToProxyRequest, proxyToClientResponse, proxyToClientCallback, HttpStatus.FORBIDDEN_403);
2110+
return true;
2111+
}
2112+
};
2113+
2114+
server.setHandler(forwardProxy);
2115+
server.start();
2116+
// end::proxyForwardForbidden[]
2117+
}
2118+
2119+
public void proxyReverse() throws Exception
2120+
{
2121+
// tag::proxyReverse[]
2122+
Server server = new Server();
2123+
ServerConnector connector = new ServerConnector(server);
2124+
server.addConnector(connector);
2125+
2126+
// Rewrite the client URI to the backend server.
2127+
ProxyHandler.Reverse reverseProxy = new ProxyHandler.Reverse(
2128+
clientToProxyRequest -> HttpURI.build("http://backend1/proxy/" + clientToProxyRequest.getHttpURI().getPathQuery())
2129+
);
2130+
2131+
server.setHandler(reverseProxy);
2132+
server.start();
2133+
// end::proxyReverse[]
2134+
}
2135+
2136+
public void proxyReverseLoadBalancer() throws Exception
2137+
{
2138+
// tag::proxyReverseLoadBalancer[]
2139+
Server server = new Server();
2140+
ServerConnector connector = new ServerConnector(server);
2141+
server.addConnector(connector);
2142+
2143+
// Rewrite the client URI to the backend server.
2144+
AtomicLong counter = new AtomicLong();
2145+
ProxyHandler.Reverse reverseProxy = new ProxyHandler.Reverse(
2146+
clientToProxyRequest ->
2147+
{
2148+
// Even goes to backend1, odd goes to backend2.
2149+
long index = counter.getAndIncrement();
2150+
int turn = (index & 1) == 0 ? 1 : 2;
2151+
String backendHost = "backend" + turn;
2152+
2153+
// Rewrite the URI to the chosen backend.
2154+
return HttpURI.build()
2155+
.scheme("http")
2156+
.host(backendHost)
2157+
.path("/proxy/" + clientToProxyRequest.getHttpURI().getPathQuery());
2158+
}
2159+
);
2160+
2161+
server.setHandler(reverseProxy);
2162+
server.start();
2163+
// end::proxyReverseLoadBalancer[]
2164+
}
20552165
}

documentation/jetty/modules/programming-guide/pages/server/http.adoc

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,6 +1591,144 @@ For example, a `LoginHandler` that extends `ConditionalHandler` may be configure
15911591

15921592
`SecurityHandler` allows to configure authentication and authorization for your web applications, and it is discussed in details in xref:security/index.adoc[this section].
15931593

1594+
[[handler-use-connect]]
1595+
==== ConnectHandler
1596+
1597+
`ConnectHandler` provides support for the HTTP `CONNECT` method, and it is used in forward proxies, as detailed in <<handler-use-proxy-forward,this section>>.
1598+
1599+
[[handler-use-proxy]]
1600+
==== ProxyHandler
1601+
1602+
`ProxyHandler` provides proxying capabilities for two use cases:
1603+
1604+
* _Forward_ proxying, implemented by <<handler-use-proxy-forward,`ProxyHandler.Forward`>>.
1605+
A forward proxy is chosen by clients (for example, it is configured in browsers or in `HttpClient`), and it is typically used intercept an organization's HTTP requests for sake of providing security or selective access to domains or resources.
1606+
Some forward proxies apply transformations to HTTP messages or their content as they are forwarded (for example, rewriting of URIs in HTML documents).
1607+
* _Reverse_ proxying, implemented by <<handler-use-proxy-reverse,`ProxyHandler.Reverse`>>.
1608+
A reverse proxy, or _gateway_, is an intermediary that acts as an origin server.
1609+
Clients cannot tell whether they are connecting to a reverse proxy or directly to the origin server: from the client point of view, the reverse proxy _is_ the origin server.
1610+
Reverse proxies receive requests and forward them to another server, and are typically used for TLS offloading, caching, and load balancing.
1611+
1612+
`ProxyHandler.Forward` and `ProxyHandler.Reverse` are typically subclassed to implement the custom logic that you want to apply in either case, and to configure the xref:client/http.adoc[`HttpClient`] instance, for example by specifying different `HttpClient` xref:client/http.adoc#transport[transports].
1613+
1614+
The most common methods to override are the following (consult the link:{javadoc-url}/org/eclipse/jetty/proxy/ProxyHandler.html[javadocs] for the full list):
1615+
1616+
* `newHttpClient()` to customize the `HttpClient` transport.
1617+
* `configureHttpClient(\...)` to customize the `HttpClient` configuration.
1618+
* `handle(\...)` to customize the proxy-to-server request and proxy-to-client response, for example to deny certain requests.
1619+
* `newProxyToServerRequest(\...)` to customize the proxy-to-server request.
1620+
* `newProxyToServerRequestContent(\...)` to customize the proxy-to-server request content.
1621+
* `newServerToProxyResponseListener(\...)` to customize the server-to-proxy response.
1622+
1623+
You may want to override `newHttpClient()` to customize the `HttpClient` transport, which is by default only HTTP/1.1, to also support HTTP/2, as shown below for `ProxyHandler.Forward` (but the same could be done for `ProxyHandler.Reverse`):
1624+
1625+
[,java,indent=0]
1626+
----
1627+
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=proxyNewHttpClient]
1628+
----
1629+
1630+
[[handler-use-proxy-forward]]
1631+
===== ProxyHandler.Forward
1632+
1633+
HTTP clients, such as browsers or `HttpClient`, interact with forward proxies in two ways:
1634+
1635+
* When the request is plain-text, using the `http` scheme, the request is sent to the forward proxy with little modifications.
1636+
The forward proxy typically just forwards the request to the origin server.
1637+
* When the request is secure, using the `https` scheme, the client first sends an HTTP `CONNECT` request to the forward proxy to establish a tunnel to the origin server.
1638+
Then the client sends the original request through the tunnel, and the forward proxy only acts as a pass-through for the encrypted bytes and cannot decrypt them.
1639+
1640+
The Jetty component that manages HTTP `CONNECT` requests and establishes tunnels to origin server is `ConnectHandler`.
1641+
1642+
A forward proxy that only supports plain-text requests has the following simple `Handler` tree structure:
1643+
1644+
[,screen]
1645+
----
1646+
Server
1647+
└── ProxyHandler.Forward
1648+
----
1649+
1650+
You can override `ProxyHandler.Forward.handle(\...)` to deny requests to certain URIs, or to certain domains:
1651+
1652+
[,java,indent=0]
1653+
----
1654+
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=proxyForwardForbidden]
1655+
----
1656+
1657+
On the other hand, a forward proxy that supports both plain-text and secure requests has the following `Handler` tree structure:
1658+
1659+
[,screen]
1660+
----
1661+
Server
1662+
└── ConnectHandler
1663+
└── ProxyHandler.Forward
1664+
----
1665+
1666+
If your forward proxy only accepts secure requests (see also <<handler-use-secured,`SecuredRedirectHandler`>>), you can also configure it with just `ConnectHandler`:
1667+
1668+
[,screen]
1669+
----
1670+
Server
1671+
└── ConnectHandler
1672+
----
1673+
1674+
You can configure `ConnectHandler` with a white list of origin servers the forward proxy is allowed to connect to.
1675+
Similarly, you can configure `ConnectHandler` with a black list of origin servers the forward proxy is forbidden to connect to.
1676+
1677+
You may need to add authentication to the forward proxy.
1678+
1679+
For simpler setups, authentication can be implemented by overriding `ConnectionHandler.handleAuthentication(\...)` for `CONNECT` requests, and by overriding `ProxyHandler.Forward.handle(\...)` for plain-text requests.
1680+
1681+
Full-blown authentication support can be achieved using <<handler-use-security,`SecurityHandler`>>:
1682+
1683+
[,screen]
1684+
----
1685+
Server
1686+
└── SecurityHandler
1687+
└── ConnectHandler
1688+
└── ProxyHandler.Forward
1689+
----
1690+
1691+
Forward proxies are configured in `HttpClient` as described in xref:client/http.adoc#proxy[this section].
1692+
1693+
[[handler-use-proxy-reverse]]
1694+
===== ProxyHandler.Reverse
1695+
1696+
Reverse proxy acts as origin servers (from the client point of view) and process incoming requests typically by forwarding them to a backend server, although they can also directly return cached responses.
1697+
1698+
A simple reverse proxy server has the following `Handler` tree structure:
1699+
1700+
[,screen]
1701+
----
1702+
Server
1703+
└── ProxyHandler.Reverse
1704+
----
1705+
1706+
A reverse proxy that forwards requests to a backend server, for example, may be configured to accept requests at `+https://domain.com/path+` and forward them to the backend server at `+http://backend1/proxy/path+`:
1707+
1708+
[,java,indent=0]
1709+
----
1710+
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=proxyReverse]
1711+
----
1712+
1713+
You can implement a very simple stateless load balancer in this way:
1714+
1715+
[,java,indent=0]
1716+
----
1717+
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=proxyReverseLoadBalancer]
1718+
----
1719+
1720+
[NOTE]
1721+
====
1722+
Reverse proxies do not typically need to be set up with a `ConnectHandler`.
1723+
1724+
Clients typically only send HTTP `CONNECT` requests to forward proxies, not to reverse proxies.
1725+
====
1726+
1727+
Reverse proxies can be set up to communicate with the backend using a protocol different from the one used on the frontend.
1728+
1729+
For example, it is possible to set up a reverse proxy that accepts on the frontend both HTTP/1.1 and HTTP/2 over network, both plain-text and secure, and communicates with the backend using FastCGI over Unix-Domain sockets.
1730+
This allows you to use WordPress to serve your website through Jetty: see xref:operations-guide:protocols/index.adoc#fcgi[this section] for more information.
1731+
15941732
[[handler-use-servlet]]
15951733
=== Servlet API Handlers
15961734

0 commit comments

Comments
 (0)