When upgrading from Jetty 11 to Jetty 12 I decided to do away with servlets and try the core Jetty libraries. Migration is still in progress but I'm finding that my new REST API server is automatically sending 301 redirects when it receives POST requests over http if the path does not end in a slash /
. Strangely the same behavior is not true for GET requests. My question is: is there a way to control 301 redirect behavior in Jetty 12? I looked inside HttConfig and the only option related to redirect seemed to be is/setRelativeRedirectAllowed
QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setName("web-server");
// Create a Server instance.
Server server = new Server(threadPool);
// The number of acceptor threads. Used for opening *new* connections. One is probably enough!
int acceptors = 1;
// The number of selectors. Used to juggle active/inactive sockets. For more activity, more selectors may be needed?
int selectors = 1;
// Create a ServerConnector to accept connections from clients.
HttpConnectionFactory httpConfig = new HttpConnectionFactory();
log.debug("Relative redirect allowed: " + httpConfig.getHttpConfiguration().isRelativeRedirectAllowed());
ServerConnector connector = new ServerConnector(server, acceptors, selectors, httpConfig);
connector.setHost("localhost");
connector.setPort(5000);
// The TCP accept queue size. How many new un-connected sockets should we keep around? Prevent DDoS?
connector.setAcceptQueueSize(64);
// Add the Connector to the Server
server.addConnector(connector);
// Initialize handlers
Handler.Sequence topLevelList = new Handler.Sequence();
ContextHandlerCollection apiContext = new ContextHandlerCollection();
topLevelList.addHandler(apiContext);
server.setHandler(topLevelList);
apiContext.addHandler(new ContextHandler(new Handler.Abstract() {
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception {
System.out.println("The request method is: " + request.getMethod());
callback.succeeded();
return true;
}
}, "/api/user/authenticate"));
server.start();
server.join();
And the associated curl
command to test:
curl -D - -d '{"key1":"value1", "key2":"value2"}' -H "Content-Type: application/json" -X POST http://localhost:5000/api/user/authenticate
which gives me:
HTTP/1.1 301 Moved Permanently
Server: Jetty(12.0.5)
Date: Thu, 28 Dec 2023 17:14:15 GMT
Location: /api/user/authenticate/
Content-Length: 0
My development team has decided this is not intuitive default behavior! This was a surprise in our migration.
The ContextHandler
you have is setup at the "Context Path" of /api/user/authenticate
A "Context Path" is a root for all behaviors within that specific Context.
So Jetty will automatically append a trailing /
to the "Context Path" to make it sane.
I would recommend setting up a ContextPath
at /api
.
Then using the PathMappingsHandler
within that ContextPath
to specify specific endpoints.
Something along the lines of ...
import org.eclipse.jetty.server.handler.PathMappingsHandler;
ContextHandler contextHandler = new ContextHandler();
contextHandler.setContextPath("/api");
PathMappingsHandler pathMappingsHandler = new PathMappingsHandler();
pathMappingsHandler.addMapping(
new ServletPathSpec("/user/authenticate"),
new ExampleHandler());
contextHandler.setHandler(pathMappingsHandler);
You might be interested in using one of the other PathSpec
implementations too (RegexPathSpec
or UriTemplatePathSpec
).
You can have as many PathSpec
implementations you want, even mix and match.
You can even setup a unit test with PathMappings
to ensure that the mix of behaviors is operating the way you want.