第10章 Undertow
10.1. Undertow ハンドラーについて
Undertow は、ブロックタスクと非ブロックタスクの両方に使用するよう設計された Web サーバーです。JBoss EAP 7 では JBoss Web は Undertow に置き換わります。主な機能の一部は以下のとおりです。
- ハイパフォーマンス
- 組み込み可能
- Servlet 4.0
- 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 サーバーの設定 」を参照してください。
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. カスタムハンドラーの作成
カスタムハンドラーを定義する方法は 2 つあります。
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 つのものを作成する必要があります。
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; } }; } }
ファイルのエントリー。
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 メカニズムの使用
カスタムモジュールを追加します。
/subsystem=elytron/service-loader-http-server-mechanism-factory=custom-factory:add(module=org.wildfly.security.examples.custom-http)
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}])
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
からのメカニズムが使用されます。よって、デプロイメント自体の変更を必要としなくても、デプロイメント内で設定をオーバーライドしてカスタムメカニズムを利用することが可能です。サーバーをリロードします。
reload