Введение в JBoss Undertow

Введение в JBoss Undertow

1. обзор

Undertow is an extremely lightweight and high-performance web server from JBoss. Он поддерживает как блокирующие, так и неблокирующие API сNIO.

Поскольку он написан на Java, его можно использовать в любых приложениях на основе JVM во встроенном режиме, даже сервер JBossWilfFly внутренне используетUndertow для повышения производительности сервера.

В этом руководстве мы покажем возможности Undertow и способы их использования.

2. Почему Undertow?

  • Легковес:Undertow чрезвычайно легок и занимает менее 1 МБ. Во встроенном режиме он использует только 4 МБ пространства кучи во время выполнения

  • Сервлет 3.1: полностью поддерживаетServlet 3.1

  • Веб-сокет: поддерживает функциональность веб-сокета (включаяJSR-356)

  • Постоянное соединение: по умолчаниюUndertow включает постоянные HTTP-соединения, добавляя заголовок ответаkeep-alive. Это помогает клиентам, которые поддерживают постоянные соединения, оптимизировать производительность за счет повторного использования сведений о соединении.

3. Использование Undertow

Давайте начнем использоватьUndertow с создания простого веб-сервера.

3.1. Maven Dependency

Чтобы использоватьUndertow, нам нужно добавить следующую зависимость к нашемуpom.xml:


    io.undertow
    undertow-servlet
    1.4.18.Final

Чтобы создать работающий jar, нам также нужно добавитьmaven-shade-plugin. Вот почему нам также необходимо добавить следующие настройки:


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

Последняя версияUndertow доступна вCentral Maven Repository.

3.2. Простой Сервер

С помощью приведенного ниже фрагмента кода мы можем создать простой веб-сервер, используя APIBuilder от 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();
    }
}

Здесь мы использовали APIBuilder для привязки порта8080 к этому серверу. Также обратите внимание, что мы использовали лямбда-выражение для использования обработчика.

Мы также можем использовать приведенный ниже фрагмент кода, чтобы сделать то же самое без использования лямбда-выражений:

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

Здесь важно отметить использование APIHttpHandler. It’s the most important plugin to customize an Undertow application based on our needs.

В этом случае мы добавили настраиваемый обработчик, который будет добавлять заголовок ответаContent-Type: text/plain с каждым запросом.

Аналогичным образом, если мы хотим возвращать некоторый текст по умолчанию с каждым ответом, мы можем использовать приведенный ниже фрагмент кода:

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

3.3. Безопасный доступ

В большинстве случаев мы не разрешаем всем пользователям получить доступ к нашему серверу. Обычно доступ могут получить пользователи с действующими учетными данными. Мы можем реализовать тот же механизм сUndertow.

Для его реализации нам нужно создать диспетчер удостоверений, который будет проверять подлинность пользователя при каждом запросе.

Для этого мы можем использоватьIdentityManager Undertow:

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

После того, как менеджер идентичности создан, нам нужно создать область, в которой будут храниться учетные данные пользователя:

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

Здесь мы использовалиAuthenticationMode какPRO_ACTIVE, что означает, что каждый запрос, поступающий на этот сервер, будет передан определенным механизмам аутентификации для быстрого выполнения аутентификации.

Если мы определимAuthenticationMode какCONSTRAINT_DRIVEN, то только те запросы будут проходить через определенные механизмы аутентификации, где срабатывает ограничение / s, которое требует аутентификации.

Теперь нам просто нужно сопоставить эту область и диспетчер удостоверений с сервером перед его запуском:

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

Здесь мы создали два пользовательских экземпляра с учетными данными. Когда сервер запущен, для доступа к нему нам нужно использовать любые из этих двух учетных данных.

3.4. Веб-сокет

Создать канал обмена веб-сокетами с APIUnderTow’s WebSocketHttpExchange несложно.

Например, мы можем открыть канал связи сокета на путиexampleApp с помощью приведенного ниже фрагмента кода:

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

Мы можем создать HTML-страницу с именемindex.html и использовать API JavaScriptWebSocket для подключения к этому каналу.

3.5. Файловый сервер

С помощьюUndertow мы также можем создать файловый сервер, который может отображать содержимое каталога и напрямую обслуживать файлы из каталога:

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

Нам не нужно создавать какой-либо контент пользовательского интерфейса для отображения содержимого каталога. Undertow предоставляет страницу для этой функции отображения.

4. Плагин Spring Boot

ПомимоTomcat иJetty,Spring Boot поддерживаетUnderTow в качестве встроенного контейнера сервлетов. Чтобы использоватьUndertow, нам нужно добавить следующую зависимость вpom.xml:


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

Последняя версияSpring Boot Undertow plugin доступна вCentral Maven Repository.

5. Заключение

В этой статье мы узнали оUndertow и о том, как с его помощью создавать разные типы серверов.

Как всегда, доступен полный исходный кодover on GitHub.