Introdução ao JBoss Undertow
1. Visão geral
Undertow is an extremely lightweight and high-performance web server from JBoss. Suporta APIs bloqueadoras e não bloqueadoras comNIO.
Uma vez que foi escrito em Java, ele pode ser usado em qualquer aplicativo baseado em JVM no modo incorporado, até mesmo o servidor JBossWilfFly usaUndertow internamente para melhorar o desempenho do servidor.
Neste tutorial, mostraremos os recursos do Undertow e como usá-lo.
2. Por que Undertow?
-
Leve:Undertow é extremamente leve, com menos de 1 MB. No modo incorporado, ele usa apenas 4 MB de espaço de heap em tempo de execução
-
Servlet 3.1: Suporta totalmenteServlet 3.1
-
Web Socket: suporta a funcionalidade Web Socket (incluindoJSR-356)
-
Conexão persistente: Por padrão,Undertow inclui conexões persistentes HTTP adicionando o cabeçalho de respostakeep-alive. Ajuda os clientes que suportam conexões persistentes a otimizar o desempenho reutilizando detalhes da conexão
3. Usando Undertow
Vamos começar a usarUndertow criando um servidor web simples.
3.1. Dependência do Maven
Para usarUndertow, precisamos adicionar a seguinte dependência ao nossopom.xml:
io.undertow
undertow-servlet
1.4.18.Final
Para construir um jar executável, também precisamos adicionarmaven-shade-plugin. É por isso que também precisamos adicionar a configuração abaixo também:
org.apache.maven.plugins
maven-shade-plugin
package
shade
A versão mais recente deUndertow está disponível emCentral Maven Repository.
3.2. Servidor Simples
Com o snippet de código abaixo, podemos criar um servidor web simples usando a APIBuilder da Undertow:
public class SimpleServer {
public static void main(String[] args) {
Undertow server = Undertow.builder().addHttpListener(8080,
"localhost").setHandler(exchange -> {
exchange.getResponseHeaders()
.put(Headers.CONTENT_TYPE, "text/plain");
exchange.getResponseSender().send("Hello example");
}).build();
server.start();
}
}
Aqui, usamos a APIBuilder para vincular a porta8080 a este servidor. Além disso, observe que usamos uma expressão lambda para usar o manipulador.
Também podemos usar o snippet de código abaixo para fazer a mesma coisa sem usar expressões lambda:
Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
.setHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange)
throws Exception {
exchange.getResponseHeaders().put(
Headers.CONTENT_TYPE, "text/plain");
exchange.getResponseSender().send("Hello example");
}
}).build();
O importante a ser observado aqui é o uso da APIHttpHandler. It’s the most important plugin to customize an Undertow application based on our needs.
Nesse caso, adicionamos um manipulador personalizado que adicionaria o cabeçalho de respostaContent-Type: text/plain com cada solicitação.
De maneira semelhante, se queremos retornar algum texto padrão a cada resposta, podemos usar o trecho de código abaixo:
exchange.getResponseSender()
.send("Hello example");
3.3. Acesso Seguro
Na maioria dos casos, não permitimos que todos os usuários acessem nosso servidor. Normalmente, os usuários com credenciais válidas podem obter acesso. Podemos implementar o mesmo mecanismo comUndertow.
Para implementá-lo, precisamos criar um gerenciador de identidade que verificará a autenticidade do usuário para cada solicitação.
Podemos usarIdentityManager de Undertow para isso:
public class CustomIdentityManager implements IdentityManager {
private Map users;
// standard constructors
@Override
public Account verify(Account account) {
return account;
}
@Override
public Account verify(Credential credential) {
return null;
}
@Override
public Account verify(String id, Credential credential) {
Account account = getAccount(id);
if (account != null && verifyCredential(account, credential)) {
return account;
}
return null;
}
}
Depois que o gerenciador de identidades é criado, precisamos criar uma região que retenha as credenciais do usuário:
private static HttpHandler addSecurity(
HttpHandler toWrap,
IdentityManager identityManager) {
HttpHandler handler = toWrap;
handler = new AuthenticationCallHandler(handler);
handler = new AuthenticationConstraintHandler(handler);
List mechanisms = Collections.singletonList(
new BasicAuthenticationMechanism("example_Realm"));
handler = new AuthenticationMechanismsHandler(handler, mechanisms);
handler = new SecurityInitialHandler(
AuthenticationMode.PRO_ACTIVE, identityManager, handler);
return handler;
}
Aqui, usamosAuthenticationMode comoPRO_ACTIVE, o que significa que cada solicitação que chega a este servidor será passada para os mecanismos de autenticação definidos para realizar a autenticação avidamente.
Se definirmosAuthenticationMode comoCONSTRAINT_DRIVEN, então apenas essas solicitações passarão pelos mecanismos de autenticação definidos, onde a (s) restrição (ões) que exige a autenticação é acionada.
Agora, precisamos apenas mapear essa região e o gerenciador de identidades com o servidor antes de iniciar:
public static void main(String[] args) {
Map users = new HashMap<>(2);
users.put("root", "password".toCharArray());
users.put("admin", "password".toCharArray());
IdentityManager idm = new CustomIdentityManager(users);
Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
.setHandler(addSecurity(e -> setExchange(e), idm)).build();
server.start();
}
private static void setExchange(HttpServerExchange exchange) {
SecurityContext context = exchange.getSecurityContext();
exchange.getResponseSender().send("Hello " +
context.getAuthenticatedAccount().getPrincipal().getName(),
IoCallback.END_EXCHANGE);
}
Aqui, criamos duas instâncias de usuário com credenciais. Quando o servidor estiver ativo, para acessá-lo, precisamos usar qualquer uma dessas duas credenciais.
3.4. Soquete da Web
É simples criar um canal de troca de soquete da web comUnderTow’s WebSocketHttpExchange API.
Por exemplo, podemos abrir um canal de comunicação de soquete no caminhoexampleApp com o snippet de código abaixo:
public static void main(String[] args) {
Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
.setHandler(path().addPrefixPath("/exampleApp", websocket(
(exchange, channel) -> {
channel.getReceiveSetter().set(getListener());
channel.resumeReceives();
})).addPrefixPath("/", resource(new ClassPathResourceManager(
SocketServer.class.getClassLoader(),
SocketServer.class.getPackage())).addWelcomeFiles("index.html")))
.build();
server.start();
}
private static AbstractReceiveListener getListener() {
return new AbstractReceiveListener() {
@Override
protected void onFullTextMessage(WebSocketChannel channel,
BufferedTextMessage message) {
String messageData = message.getData();
for (WebSocketChannel session : channel.getPeerConnections()) {
WebSockets.sendText(messageData, session, null);
}
}
};
}
Podemos criar uma página HTML chamadaindex.html e usar a APIWebSocket do JavaScript para nos conectar a este canal.
3.5. Servidor de arquivos
ComUndertow, também podemos criar um servidor de arquivos que pode exibir o conteúdo do diretório e fornecer arquivos diretamente do diretório:
public static void main( String[] args ) {
Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
.setHandler(resource(new PathResourceManager(
Paths.get(System.getProperty("user.home")), 100 ))
.setDirectoryListingEnabled( true ))
.build();
server.start();
}
Não precisamos criar nenhum conteúdo de IU para exibir o conteúdo do diretório. Out-of-the-boxUndertow fornece uma página para esta funcionalidade de exibição.
4. Plug-in Spring Boot
Além deTomcateJetty,,Spring Boot suportaUnderTow como o contêiner de servlet embutido. Para usarUndertow, precisamos adicionar a seguinte dependência nopom.xml:
org.springframework.boot
spring-boot-starter-undertow
1.5.6.RELEASE
A versão mais recente deSpring Boot Undertow plugin está disponível emCentral Maven Repository.
5. Conclusão
Neste artigo, aprendemos sobreUndertow e como podemos criar diferentes tipos de servidores com ele.
Como sempre, o código-fonte completo está disponívelover on GitHub.