Introdução ao JBoss Undertow

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.