Introduction à JBoss Undertow

Introduction à JBoss Undertow

1. Vue d'ensemble

Undertow is an extremely lightweight and high-performance web server from JBoss. Il prend en charge les API bloquantes et non bloquantes avecNIO.

Comme il est écrit en Java, il peut être utilisé dans toutes les applications JVM en mode intégré, même le serveurWilfFly de JBoss utilise en interneUndertow pour améliorer les performances du serveur.

Dans ce didacticiel, nous allons montrer les fonctionnalités d'Undertow et comment l'utiliser.

2. Pourquoi Undertow?

  • Léger:Undertow est extrêmement léger à moins de 1 Mo. En mode intégré, il n’utilise que 4 Mo d’espace mémoire lors de l’exécution

  • Servlet 3.1: il prend entièrement en chargeServlet 3.1

  • Web Socket: il prend en charge la fonctionnalité Web Socket (y comprisJSR-356)

  • Connexion persistante: par défaut,Undertow inclut les connexions persistantes HTTP en ajoutant l'en-tête de réponsekeep-alive. Il aide les clients prenant en charge les connexions persistantes à optimiser les performances en réutilisant les détails de la connexion.

3. Utiliser Undertow

Commençons à utiliserUndertow en créant un serveur Web simple.

3.1. Dépendance Maven

Pour utiliserUndertow, nous devons ajouter la dépendance suivante à nospom.xml:


    io.undertow
    undertow-servlet
    1.4.18.Final

Pour créer un fichier jar exécutable, nous devons également ajoutermaven-shade-plugin. C’est pourquoi nous devons également ajouter la configuration ci-dessous:


    org.apache.maven.plugins
    maven-shade-plugin
    
        
            package
            
                shade
            
        
    

La dernière version deUndertow est disponible enCentral Maven Repository.

3.2. Serveur simple

Avec l'extrait de code ci-dessous, nous pouvons créer un serveur Web simple à l'aide de l'APIBuilder d'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();
    }
}

Ici, nous avons utilisé l'APIBuilder pour lier le port de8080 à ce serveur. Notez également que nous avons utilisé une expression lambda pour utiliser le gestionnaire.

Nous pouvons également utiliser l'extrait de code ci-dessous pour faire la même chose sans utiliser les expressions 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();

La chose importante à noter ici est l'utilisation de l'APIHttpHandler. It’s the most important plugin to customize an Undertow application based on our needs.

Dans ce cas, nous avons ajouté un gestionnaire personnalisé qui ajouterait l'en-tête de réponseContent-Type: text/plain à chaque requête.

De manière similaire, si nous voulons renvoyer du texte par défaut avec chaque réponse, nous pouvons utiliser le fragment de code ci-dessous:

exchange.getResponseSender()
  .send("Hello example");

3.3. Accès sécurisé

Dans la plupart des cas, nous n'autorisons pas tous les utilisateurs à accéder à notre serveur. Habituellement, les utilisateurs avec des informations d'identification valides peuvent y accéder. Nous pouvons implémenter le même mécanisme avec lesUndertow.

Pour l'implémenter, nous devons créer un gestionnaire d'identité qui vérifiera l'authenticité de l'utilisateur pour chaque demande.

Nous pouvons utiliser lesIdentityManager d'Undertow pour cela:

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;
    }
}

Une fois le gestionnaire d’identités créé, nous devons créer un domaine qui contiendra les informations d’identité de l’utilisateur:

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;
}

Ici, nous avons utilisé lesAuthenticationMode commePRO_ACTIVE, ce qui signifie que chaque demande venant sur ce serveur sera transmise aux mécanismes d'authentification définis pour effectuer l'authentification avec empressement.

Si nous définissonsAuthenticationMode commeCONSTRAINT_DRIVEN, alors seules ces requêtes passeront par les mécanismes d'authentification définis où la contrainte / s qui impose l'authentification est déclenchée.

Maintenant, il suffit de mapper ce domaine et le gestionnaire d’identités avec le serveur avant de commencer:

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);
}

Ici, nous avons créé deux instances d'utilisateur avec des informations d'identification. Une fois le serveur installé, vous devez y accéder avec l'une de ces deux informations d'identification.

3.4. Prise Web

Il est simple de créer un canal d'échange de socket Web avec l'APIUnderTow’s WebSocketHttpExchange.

Par exemple, nous pouvons ouvrir un canal de communication de socket sur le cheminexampleApp avec l'extrait de code ci-dessous:

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);
            }
        }
    };
}

Nous pouvons créer une page HTML nomméeindex.html et utiliser l'APIWebSocket de JavaScript pour nous connecter à ce canal.

3.5. Serveur de fichiers

AvecUndertow, nous pouvons également créer un serveur de fichiers qui peut afficher le contenu du répertoire et sert directement les fichiers du répertoire:

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();
}

Nous n'avons pas besoin de créer de contenu d'interface utilisateur pour afficher le contenu du répertoire. Out-of-the-boxUndertow fournit une page pour cette fonctionnalité d'affichage.

4. Plug-in Spring Boot

En dehors deTomcat etJetty,,Spring Boot prend en chargeUnderTow en tant que conteneur de servlet intégré. Pour utiliserUndertow, nous devons ajouter la dépendance suivante dans lepom.xml:


    org.springframework.boot
    spring-boot-starter-undertow
    1.5.6.RELEASE

La dernière version deSpring Boot Undertow plugin est disponible enCentral Maven Repository.

5. Conclusion

Dans cet article, nous avons découvertUndertow et comment nous pouvons créer différents types de serveurs avec lui.

Comme toujours, le code source complet est disponibleover on GitHub.