テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

spray: mapRequestはパスマッチングに影響を与えない

S3 AWS SDK: エンドポイントによるパススタイルの違い - テストステ論

S3はURIの表現として仮想ホスト方式とパススタイル方式があることを述べた. そして, 昨日行ったs3cmd mbをする実験では, 仮想ホスト方式でリクエストを投げてくることに対処する必要があることを述べた.

なぜ投げてくる方式に違いがあるのかと考察してみると, AWS SDKに渡すサーバのアドレスがホスト名である場合は仮想ホスト方式で渡してきて, 生のIPアドレスである場合はパススタイル方式で渡してくるのではないかと, 具体的なエビデンスはないが, 今は考えている. 今までは生のIP(127.0.0.1)を入れてテストを行っていたからである. これは, AWS SDKDNSルックアップに失敗してクライアントサイドで落ちることの回避であるが, たぶんくだらない設定ミスか何かだろう・・・.

本題に戻ると, 仮想ホスト方式に対応するため, 実際のルーティングに入る前にリクエストのURIを書き換えることにした. 署名計算では, カノニカルリソースパスといって, 方式によらず同一の署名が作れるようなアルゴリズムになっているため, 署名マッチの前に実装することは理がある.

ざっくりと以下のようなコードを書いた. 単に仮想ホスト方式かどうか判定して, バケット名をOptionで切り出すというものである. このコードは, Hostヘッダの書き換えはしていないものの, 正しく変更後のURIを計算出来るように見える.

  def toPathStyle: Directive0 = mapRequest { req =>
    val uri = req.uri
    val reqAddress = uri.authority.host.address
    val serverAddress = config.ip
    val bucketName: Option[String] = PathStyle.bucketName(reqAddress, serverAddress)
    if (bucketName.isDefined) {
      val newUri = uri
        .withAuthority(config.ip, config.port)
        .withPath(PathStyle.prepend(bucketName.get, uri.path))
      req.copy(uri = newUri)
    } else {
      req
    }
  }

val route = toPathStyle { mainRoute }

しかし, mainRoute内で, このURI書き換えがルーティングに影響を与えていないことが, 色々な実験の結果分かった. 原因はドキュメントに書いてあった.

http://spray.io/documentation/1.2.3/spray-routing/basic-directives/mapRequest/

The mapRequest directive is used as a building block for Custom Directives to transform a request before it is handled by the inner route. Changing the request.uri parameter has no effect on path matching in the inner route because the unmatched path is a separate field of the RequestContext value which is passed into routes. To change the unmatched path or other fields of the RequestContext use the mapRequestContext directive.

完全には理解出来ないが, ルーティングに影響のある情報はRequestContextの別の部分にあるからHttpRequestなんかいじっても無駄だよーべろべろばーというわけである. 糞だ