Red Hat Training

A Red Hat training course is available for Red Hat JBoss Enterprise Application Platform

第10章 Undertow

10.1. Undertow ハンドラーについて

Undertow は、ブロックタスクと非ブロックタスクの両方に使用するよう設計された Web サーバーです。JBoss EAP 7 では JBoss Web は Undertow に置き換わります。主な機能の一部は以下のとおりです。

  • ハイパフォーマンス
  • 組み込み可能
  • Servlet 3.1
  • Web ソケット
  • リバースプロキシー

リクエストライフサイクル

クライアントがサーバーに接続するときに、Undertow によって io.undertow.server.HttpServerConnection が作成されます。クライアントがリクエストを送信するときに、リクエストは Undertow パーサーによって解析され、生成される io.undertow.server.HttpServerExchange はルートハンドラーに渡されます。ルートハンドラーが完了すると、以下の 4 つのいずれかのことが起こります。

  • 交換が完了します。

    リクエストチャネルと応答チャネルが完全に読み取られたり、書き込まれた場合に、交換が完了したと見なされます。リクエスト側は、GET や HEAD などのコンテンツがないリクエストの場合に、自動的に完全に読み取られたと見なされます。読み取り側は、ハンドラーが完全な応答を書き込み、応答チャネルを閉じ、応答チャネルを完全にフラッシュしたときに、完了したと見なされます。交換がすでに完了した場合は、どんなアクションも行われません。

  • 交換を完了せずにルートハンドラーが通常どおり返されます。

    この場合、交換は HttpServerExchange.endExchange() を呼び出して完了します。

  • ルートハンドラーが例外で返されます。

    この場合、500 の応答コードが設定され、HttpServerExchange.endExchange() を使用して交換が終了します。

  • ルートハンドラーは、HttpServerExchange.dispatch() が呼び出された後、または非同期 IO が開始された後に返すことができます。

    この場合、ディスパッチされたタスクはディスパッチエグゼキューターに送信されます。また、非同期 IO がリクエストチャネルまたは応答チャネルのいずれかで開始された場合は、このタスクが開始されます。両方の場合で交換は完了しません。非同期タスクによって、処理が完了したときに交換が完了します。

HttpServerExchange.dispatch() の最も一般的な使用方法は、実行をブロッキングが許可されない IO スレッドから、ブロッキング操作を許可するワーカースレッドに移動することです。

例: ワーカースレッドへのディスパッチ

public void handleRequest(final HttpServerExchange exchange) throws Exception {
    if (exchange.isInIoThread()) {
      exchange.dispatch(this);
      return;
    }
    //handler code
}

交換は呼び出しスタックが返されるまで実際にはディスパッチされないため、交換で一度に複数のスレッドがアクティブにならないようにすることができます。交換はスレッドセーフではありません。ただし、交換は、両方のスレッドが 1 度に変更しようとしない限り、複数のスレッド間で渡すことができます。

交換の終了

交換を終了するには、リクエストチャネルを読み取り、応答チャネルで shutdownWrites() を呼び出し、フラッシュする方法と HttpServerExchange.endExchange() を呼び出す方法の 2 つがあります。endExchange() が呼び出された場合、Undertow はコンテンツが生成されたかどうかを確認します。生成された場合、Undertow はリクエストチャネルを単にドレインし、応答チャネルを閉じ、フラッシュします。生成されず、交換で登録されたデフォルトの応答リスナーがある場合は、Undertow によってそれらの各応答リスナーがデフォルトの応答を生成できるようになります。このメカニズムにより、デフォルトのエラーページが生成されます。

Undertow の設定に関する詳細は、JBoss EAP『設定ガイド』の「Web サーバーの設定 (Undertow)」を参照してください。

10.2. デプロイメントでの既存の Undertow ハンドラーの使用

Undertow は、JBoss EAP にデプロイされたすべてのアプリケーションと使用できる、デフォルトのハンドラーセットを提供します。

デプロイメントでハンドラーを使用するには、WEB-INF/undertow-handlers.conf ファイルを追加する必要があります。

例: WEB-INF/undertow-handlers.conf ファイル

allowed-methods(methods='GET')

また、特定のケースで指定のハンドラーを提供するために、すべてのハンドラーは任意の述語を取ることもできます。

例: 任意の述語がある WEB-INF/undertow-handlers.conf ファイル

path('/my-path') -> allowed-methods(methods='GET')

上記の例では、allowed-methods ハンドラーのみがパス /my-path に適用されます。

Undertow ハンドラーのデフォルトパラメーター

一部のハンドラーにはデフォルトのパラメーターがあり、名前を使用せずにハンドラー定義でそのパラメーターの値を指定できます。

例: デフォルトのパラメーターを使用する WEB-INF/undertow-handlers.conf ファイル

path('/a') -> redirect('/b')

また、WEB-INF/jboss-web.xml ファイルを更新して 1 つまたは複数のハンドラーの定義を含めることもできますが、WEB-INF/undertow-handlers.conf を使用することが推奨されます。

例: WEB-INF/jboss-web.xml ファイル

<jboss-web>
    <http-handler>
        <class-name>io.undertow.server.handlers.AllowedMethodsHandler</class-name>
        <param>
            <param-name>methods</param-name>
            <param-value>GET</param-value>
        </param>
    </http-handler>
</jboss-web>

提供される Undertow ハンドラーの完全リストは「提供される Undertow ハンドラー」を参照してください。

10.3. カスタムハンドラーの作成

There are two ways to define custom handlers:

WEB-INF/jboss-web.xml ファイルを使用したカスタムハンドラーの定義

カスタムハンドラーは WEB-INF/jboss-web.xml ファイルで定義できます。

例: WEB-INF/jboss-web.xml でのカスタマーハンドラーの定義

<jboss-web>
    <http-handler>
        <class-name>org.jboss.example.MyHttpHandler</class-name>
    </http-handler>
</jboss-web>

例: HttpHandler クラス

package org.jboss.example;

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;

public class MyHttpHandler implements HttpHandler {
    private HttpHandler next;

    public MyHttpHandler(HttpHandler next) {
        this.next = next;
    }

    public void handleRequest(HttpServerExchange exchange) throws Exception {
        // do something
        next.handleRequest(exchange);
    }
}

WEB-INF/jboss-web.xml ファイルを使用して、カスタムハンドラーにパラメーターを設定することもできます。

例: WEB-INF/jboss-web.xml でのパラメーターの定義

<jboss-web>
    <http-handler>
        <class-name>org.jboss.example.MyHttpHandler</class-name>
        <param>
            <param-name>myParam</param-name>
            <param-value>foobar</param-value>
        </param>
    </http-handler>
</jboss-web>

これらのパラメーターが機能するには、ハンドラークラスに対応するセッターが必要です。

例: ハンドラーでのセッターメソッドの定義

package org.jboss.example;

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;

public class MyHttpHandler implements HttpHandler {
    private HttpHandler next;
    private String myParam;

    public MyHttpHandler(HttpHandler next) {
        this.next = next;
    }

    public void setMyParam(String myParam) {
        this.myParam = myParam;
    }

    public void handleRequest(HttpServerExchange exchange) throws Exception {
        // do something, use myParam
        next.handleRequest(exchange);
    }
}

WEB-INF/undertow-handlers.conf ファイルでのカスタムハンドラーの定義

ハンドラーの定義に WEB-INF/jboss-web.xml を使用する代わりに、ハンドラーは WEB-INF/undertow-handlers.confファイルで定義することもできます。

myHttpHandler(myParam='foobar')

WEB-INF/undertow-handlers.conf で定義されたハンドラーが機能するには、以下の 2 つのものを作成する必要があります。

  1. HandlerWrapper にラップされた HandlerBuilder (undertow-handlers.conf 向けの対応する構文を定義し、HttpHandler を作成します)。

    例: HandlerBuilder クラス

    package org.jboss.example;
    
    import io.undertow.server.HandlerWrapper;
    import io.undertow.server.HttpHandler;
    import io.undertow.server.handlers.builder.HandlerBuilder;
    
    import java.util.Collections;
    import java.util.Map;
    import java.util.Set;
    
    public class MyHandlerBuilder implements HandlerBuilder {
        public String name() {
            return "myHttpHandler";
        }
    
        public Map<String, Class<?>> parameters() {
            return Collections.<String, Class<?>>singletonMap("myParam", String.class);
        }
    
        public Set<String> requiredParameters() {
            return Collections.emptySet();
    
        }
    
        public String defaultParameter() {
            return null;
    
        }
    
        public HandlerWrapper build(final Map<String, Object> config) {
            return new HandlerWrapper() {
                public HttpHandler wrap(HttpHandler handler) {
                    MyHttpHandler result = new MyHttpHandler(handler);
                    result.setMyParam((String) config.get("myParam"));
                    return result;
                }
            };
        }
    }

  2. ファイル META-INF/services/io.undertow.server.handlers.builder.HandlerBuilder のエントリー。このファイルはクラスパスにある必要があります(例: WEB-INF/classes)。

    org.jboss.example.MyHandlerBuilder

10.4. カスタム HTTP メカニズムの開発

Elytron を使用して Web アプリケーションをセキュアにする場合、elytron サブシステムを使用して登録できるカスタム HTTP 認証メカニズムを実装することが可能です。また、このメカニズムを利用するために、デプロイメントの変更を必要とせずにデプロイメント内の設定をオーバーライドすることも可能です。

重要

すべてのカスタム HTTP メカニズムは、HttpServerAuthenticationMechanism インターフェースを実装する必要があります。

通常、HTTP メカニズムでは、HTTPServerRequest オブジェクトを渡すリクエストの処理に evaluateRequest メソッドが呼び出されます。このメカニズムがリクエストを処理し、リクエスト上で以下のコールバックメソッドの 1 つを使用して、結果を示します。

  • authenticationComplete - メカニズムによってリクエストが正常に認証されたことを示します。
  • authenticationFailed - 認証は実行され、失敗したことを示します。
  • authenticationInProgress - 認証は開始され、追加のラウンドトリップが必要であることを示します。
  • badRequest - このメカニズムの認証によってリクエストの検証に失敗したことを示します。
  • noAuthenticationInProgress - メカニズムが認証を何も実行しなかったことを示します。

HttpServerAuthenticationMechanism インターフェースを実行するカスタム HTTP メカニズムを作成したら、次にこのメカニズムのインスタンスを返すファクトリーを作成します。このファクトリーは、HttpAuthenticationFactory インターフェースを実装する必要があります。ファクトリーの実装で最も重要なのは、要求されたメカニズムの名前を二重チェックすることです。必要なメカニズムを作成できない場合は、ファクトリーが null を返すことが重要になります。メカニズムファクトリーは、要求されたメカニズムの作成が可能であるかどうかを決定するために、渡されたマップのプロパティーも考慮することができます。

メカニズムファクトリーが利用可能であるかどうかをアドバタイズするのに使用できる方法は 2 つあります。

  • 1 つ目は、サポートする各メカニズムに対して 1 度、利用可能なサービスとして登録された HttpAuthenticationFactory を用いて java.security.Provider を実装する方法です。
  • 2 つ目は、java.util.ServiceLoader を使用して代わりにファクトリーを検出する方法です。これを行うには、org.wildfly.security.http.HttpServerAuthenticationMechanismFactory という名前のファイルを META-INF/services 以下に追加する必要があります。このファイルの内容には、ファクトリー実装の完全修飾クラス名のみが必要になります。

メカニズムは使用する準備が整ったモジュールとしてアプリケーションサーバーにインストールできます。

module add --name=org.wildfly.security.examples.custom-http --resources=/path/to/custom-http-mechanism.jar --dependencies=org.wildfly.security.elytron,javax.api

カスタム HTTP メカニズムの使用

  1. カスタムモジュールを追加します。

    /subsystem=elytron/service-loader-http-server-mechanism-factory=custom-factory:add(module=org.wildfly.security.examples.custom-http)
  2. http-authentication-factory を追加して、メカニズムファクトリーを認証に使用される security-domain と結び付けます。

    /subsystem=elytron/http-authentication-factory=custom-mechanism:add(http-server-mechanism-factory=custom-factory,security-domain=ApplicationDomain,mechanism-configurations=[{mechanism-name=custom-mechanism}])
  3. application-security-domain リソースを更新し、新しい http-authentication-factory を使用するようにします。

    注記

    アプリケーションがデプロイされると、デフォルトで other セキュリティードメインを使用します。そのため、アプリケーションへのマッピングを追加して、Elytron HTTP 認証ファクトリーにマップする必要があります。

    /subsystem=undertow/application-security-domain=other:add(http-authentication-factory=application-http-authentication)

    application-security-domain リソースを更新して、新しい http-authentication-factory を使用できるようになりました。

    /subsystem=undertow/application-security-domain=other:write-attribute(name=http-authentication-factory,value=custom-mechanism)
    
    /subsystem=undertow/application-security-domain=other:write-attribute(name=override-deployment-config,value=true)

    上記のコマンドラインはデプロイメントの設定をオーバーライドすることに注意してください。そのため、デプロイメントが別のメカニズムを使用するよう設定されていても http-authentication-factory からのメカニズムが使用されます。よって、デプロイメント自体の変更を必要としなくても、デプロイメント内で設定をオーバーライドしてカスタムメカニズムを利用することが可能です。

  4. サーバーをリロードします。

    reload