Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions documentation/jetty/modules/code/examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-openid</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-proxy</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-rewrite</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;

Expand All @@ -36,6 +37,8 @@
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.transport.HttpClientConnectionFactory;
import org.eclipse.jetty.client.transport.HttpClientTransportDynamic;
import org.eclipse.jetty.compression.server.CompressionConfig;
import org.eclipse.jetty.compression.server.CompressionHandler;
import org.eclipse.jetty.ee11.servlet.DefaultServlet;
Expand All @@ -54,12 +57,17 @@
import org.eclipse.jetty.http.MultiPartConfig;
import org.eclipse.jetty.http.MultiPartFormData;
import org.eclipse.jetty.http.pathmap.PathSpec;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.transport.ClientConnectionFactoryOverHTTP2;
import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.http3.server.HTTP3ServerConnectionFactory;
import org.eclipse.jetty.http3.server.HTTP3ServerQuicConfiguration;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
import org.eclipse.jetty.proxy.ProxyHandler;
import org.eclipse.jetty.quic.quiche.server.QuicheServerConnector;
import org.eclipse.jetty.quic.quiche.server.QuicheServerQuicConfiguration;
import org.eclipse.jetty.rewrite.handler.CompactPathRule;
Expand Down Expand Up @@ -2052,4 +2060,106 @@ public boolean handle(Request request, Response response, Callback callback)
server.start();
// end::dosHandler[]
}

public void proxyNewHttpClient() throws Exception
{
// tag::proxyNewHttpClient[]
Server server = new Server();
ServerConnector connector = new ServerConnector(server);
server.addConnector(connector);

// Customize the forward proxy's HttpClient transports.
ProxyHandler.Forward forwardProxy = new ProxyHandler.Forward()
{
@Override
protected HttpClient newHttpClient()
{
ClientConnector clientConnector = new ClientConnector();
ClientConnectionFactory.Info http11 = HttpClientConnectionFactory.HTTP11;
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector));
return new HttpClient(new HttpClientTransportDynamic(clientConnector, http11, http2));
}
};

server.setHandler(forwardProxy);
server.start();
// end::proxyNewHttpClient[]
}

public void proxyForwardForbidden() throws Exception
{
// tag::proxyForwardForbidden[]
Server server = new Server();
ServerConnector connector = new ServerConnector(server);
server.addConnector(connector);

// Customize the forward proxy's HttpClient transports.
ProxyHandler.Forward forwardProxy = new ProxyHandler.Forward()
{
@Override
public boolean handle(Request clientToProxyRequest, Response proxyToClientResponse, Callback proxyToClientCallback)
{
String domain = Request.getServerName(clientToProxyRequest);

// Allow requests to non-bad domains.
if (!domain.contains(".bad.com"))
return super.handle(clientToProxyRequest, proxyToClientResponse, proxyToClientCallback);

// Deny requests to bad domains.
Response.writeError(clientToProxyRequest, proxyToClientResponse, proxyToClientCallback, HttpStatus.FORBIDDEN_403);
return true;
}
};

server.setHandler(forwardProxy);
server.start();
// end::proxyForwardForbidden[]
}

public void proxyReverse() throws Exception
{
// tag::proxyReverse[]
Server server = new Server();
ServerConnector connector = new ServerConnector(server);
server.addConnector(connector);

// Rewrite the client URI to the backend server.
ProxyHandler.Reverse reverseProxy = new ProxyHandler.Reverse(
clientToProxyRequest -> HttpURI.build("http://backend1/proxy/" + clientToProxyRequest.getHttpURI().getPathQuery())
);

server.setHandler(reverseProxy);
server.start();
// end::proxyReverse[]
}

public void proxyReverseLoadBalancer() throws Exception
{
// tag::proxyReverseLoadBalancer[]
Server server = new Server();
ServerConnector connector = new ServerConnector(server);
server.addConnector(connector);

// Rewrite the client URI to the backend server.
AtomicLong counter = new AtomicLong();
ProxyHandler.Reverse reverseProxy = new ProxyHandler.Reverse(
clientToProxyRequest ->
{
// Even goes to backend1, odd goes to backend2.
long index = counter.getAndIncrement();
int turn = (index & 1) == 0 ? 1 : 2;
String backendHost = "backend" + turn;

// Rewrite the URI to the chosen backend.
return HttpURI.build()
.scheme("http")
.host(backendHost)
.path("/proxy/" + clientToProxyRequest.getHttpURI().getPathQuery());
}
);

server.setHandler(reverseProxy);
server.start();
// end::proxyReverseLoadBalancer[]
}
}
138 changes: 138 additions & 0 deletions documentation/jetty/modules/programming-guide/pages/server/http.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1591,6 +1591,144 @@ For example, a `LoginHandler` that extends `ConditionalHandler` may be configure

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

[[handler-use-connect]]
==== ConnectHandler

`ConnectHandler` provides support for the HTTP `CONNECT` method, and it is used in forward proxies, as detailed in <<handler-use-proxy-forward,this section>>.

[[handler-use-proxy]]
==== ProxyHandler

`ProxyHandler` provides proxying capabilities for two use cases:

* _Forward_ proxying, implemented by <<handler-use-proxy-forward,`ProxyHandler.Forward`>>.
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.
Some forward proxies apply transformations to HTTP messages or their content as they are forwarded (for example, rewriting of URIs in HTML documents).
* _Reverse_ proxying, implemented by <<handler-use-proxy-reverse,`ProxyHandler.Reverse`>>.
A reverse proxy, or _gateway_, is an intermediary that acts as an origin server.
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.
Reverse proxies receive requests and forward them to another server, and are typically used for TLS offloading, caching, and load balancing.

`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].

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):

* `newHttpClient()` to customize the `HttpClient` transport.
* `configureHttpClient(\...)` to customize the `HttpClient` configuration.
* `handle(\...)` to customize the proxy-to-server request and proxy-to-client response, for example to deny certain requests.
* `newProxyToServerRequest(\...)` to customize the proxy-to-server request.
* `newProxyToServerRequestContent(\...)` to customize the proxy-to-server request content.
* `newServerToProxyResponseListener(\...)` to customize the server-to-proxy response.

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`):

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=proxyNewHttpClient]
----

[[handler-use-proxy-forward]]
===== ProxyHandler.Forward

HTTP clients, such as browsers or `HttpClient`, interact with forward proxies in two ways:

* When the request is plain-text, using the `http` scheme, the request is sent to the forward proxy with little modifications.
The forward proxy typically just forwards the request to the origin server.
* 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.
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.

The Jetty component that manages HTTP `CONNECT` requests and establishes tunnels to origin server is `ConnectHandler`.

A forward proxy that only supports plain-text requests has the following simple `Handler` tree structure:

[,screen]
----
Server
└── ProxyHandler.Forward
----

You can override `ProxyHandler.Forward.handle(\...)` to deny requests to certain URIs, or to certain domains:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=proxyForwardForbidden]
----

On the other hand, a forward proxy that supports both plain-text and secure requests has the following `Handler` tree structure:

[,screen]
----
Server
└── ConnectHandler
└── ProxyHandler.Forward
----

If your forward proxy only accepts secure requests (see also <<handler-use-secured,`SecuredRedirectHandler`>>), you can also configure it with just `ConnectHandler`:

[,screen]
----
Server
└── ConnectHandler
----

You can configure `ConnectHandler` with a white list of origin servers the forward proxy is allowed to connect to.
Similarly, you can configure `ConnectHandler` with a black list of origin servers the forward proxy is forbidden to connect to.

You may need to add authentication to the forward proxy.

For simpler setups, authentication can be implemented by overriding `ConnectionHandler.handleAuthentication(\...)` for `CONNECT` requests, and by overriding `ProxyHandler.Forward.handle(\...)` for plain-text requests.

Full-blown authentication support can be achieved using <<handler-use-security,`SecurityHandler`>>:

[,screen]
----
Server
└── SecurityHandler
└── ConnectHandler
└── ProxyHandler.Forward
----

Forward proxies are configured in `HttpClient` as described in xref:client/http.adoc#proxy[this section].

[[handler-use-proxy-reverse]]
===== ProxyHandler.Reverse

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.

A simple reverse proxy server has the following `Handler` tree structure:

[,screen]
----
Server
└── ProxyHandler.Reverse
----

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+`:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=proxyReverse]
----

You can implement a very simple stateless load balancer in this way:

[,java,indent=0]
----
include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=proxyReverseLoadBalancer]
----

[NOTE]
====
Reverse proxies do not typically need to be set up with a `ConnectHandler`.

Clients typically only send HTTP `CONNECT` requests to forward proxies, not to reverse proxies.
====

Reverse proxies can be set up to communicate with the backend using a protocol different from the one used on the frontend.

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.
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.

[[handler-use-servlet]]
=== Servlet API Handlers

Expand Down