HTTPトンネリングの件に関連して、FCS関連でプロキシサーバを経由した場合の接続方法についての下記の記事も簡単に和訳しました。
Macromedia – Developer Center : Tunneling Macromedia Flash Communications through Firewalls and Proxy Servers. Page 3
例によって文書自体が古い上に意訳・適当な訳文ですが、HTTPトンネリングと同じく何かの参考までに。
※注:HTTPトンネリングの記事同様、こちらもリンク先が無くなってしまいました。アドビサイトにどこかに原文残ってないのかな・・・。
プロキシサーバに関する問題
内部のネットワークとインターネットとの中継役としてプロキシサーバを利用することで、組織のネットワーク環境をより安全にすることができる。WEBプロキシサーバというのは通常、ネットワークの通信料を削減するためのキャッシングサーバとして利用される。とはいえ、プロキシサーバの使い道は他にもある。
ネットワークレイヤーのファイヤーウォールとプロキシサーバを組み合わせることで、WEBへのアクセス自体は確保した上で、組織のネットワーク内にあるワークステーションから外部のネットワークに対して直接アクセスする必要がなくなる。ブラウザはリモートのサーバに対してWEBページのデータを直接要求するのではなく、プロキシサーバに対して要求しなければならない。ページのデータがキャッシュされていなかった場合には、プロキシサーバがリモートのサーバに対してリクエストを行ない、返ってきたデータをブラウザに戻すことになる。プロキシサーバが中継役として存在する場合、ファイヤーウォールは基本的にネットワーク内部から外部に対して直接送出されるリクエストは全てブロックするように設定されていることが多い―――たとえそれが80番ポートに対するものであったとしても。プロキシサーバだけが80番ポートを通じて外側の世界との接続を許されているのだ(図8を参照)。
HTTPトンネリングを使わない場合、プロキシサーバの内側にいて、インターネットへの直接接続が許可されていない環境にあるユーザーはコミュニケーションサーバへの接続ができない。HTTPトンネリングを使えばできるようになることがしばしばある。プロキシサーバからしてみれば、RTMPTを使った接続要求というのは通常のHTTPリクエストと同様に捉えられる。コミュニケーションサーバからのレスポンスもHTTPレスポンスと同様に判断される。このため、プロキシサーバはRTMPTリクエストの送出もコミュニケーションサーバからのデータ受信についても対応できるはずである。ところが、HTTPトンネリングがうまくいくかどうかは保証されていないのだ。プロキシサーバにはWEBページをキャッシュする中継役以上の機能がある。それは、外部のネットワークリソースに対するアクセスの制御ということだ。例えば、プロキシの管理者はアクセス禁止サイトのリストを作成し、プロキシサーバ内の全ユーザーに対して、それらのサイトへのアクセスを拒否することができる。プロキシサーバの中には、さらに詳細にデータの調査やフィルタリングが可能なものもある。例えば、特定のコンテンツや、text/thmlのようなMIMEタイプのみを許可することもできる。WEBサーバがWEBページのデータをブラウザに対して返す際、HTTPヘッダにはContentTypeというデータが含まれる。WEBサーバがWEBページのデータを送出する場合のHTTPヘッダの書式は以下のようになる:
HTTP/1.1 200 OK
Server: Netscape-Enterprise/6.0
Date: Sat, 24 May 2003 01:09:43 GMT
Content-type: text/html
Etag: "c96066c3-1-0-1e8"
Last-modified: Tue, 25 Jun 1996 19:11:18 GMT
Content-length: 488
Accept-ranges: bytes
また、画像データを送出する場合のHTTPヘッダの書式は以下のようになる:
HTTP/1.1 200 OK
Server: Netscape-Enterprise/6.0
Date: Sat, 24 May 2003 01:51:17 GMT
Content-type: image/gif
Etag: "782311ca-126-0-f1ae"
Last-modified: Mon, 06 May 2002 21:10:38 GMT
Content-length: 61870
Accept-ranges: bytes
Macromedia Flash Communication Serverが返すデータはテキストデータではないが、HTTPヘッダにはContent-typeの記述が含まれない。そして、Content-typeが記述されていない場合にはtext/htmlと判断されてしまう。このため、RTMPTのトラフィックであっても、全てのプロキシサーバを通過できるという保証は無いのである。プロキシサーバとアプリケーションレイヤのファイヤーウォールというものが、システムアドミニストレータの設定した幅広いバリエーションのルールに従ってコンテンツのフィルタリングを行うように作られているということを考慮すれば、これはおかしなことではない。ITセキュリティの部署が正規のHTML文書といくつかの画像フォーマット(image/GIFなど)のみを許可すると決定したならば、それを実現するための精巧なツールが開発されるということだ。もしRTMPT接続さえも通さないプロキシサーバの場合は、組織のファイヤーウォール担当の管理者に連絡を取り、プロキシサーバの例外設定が可能かどうか確認するといい。
図8.プロキシサーバとファイヤーウォール
注:各端末(Workstation)はインターネットに直接接続することはできず、必ずプロキシサーバを通してリソース要求を行うことになる。プロキシサーバはインターネットからリソースを取得し、それを各端末に転送する。各端末がファイヤーウォールを越えて外部のシステムにアクセスすることは決してない。
一度に複数のポートを利用する
Macromediaは先日、Flash Communication Server バージョン1.5のリリースとともに、Flash Communication Serverコンポーネントのアップデート版をリリースした。このアップデートの目玉のひとつが、Simple Connectコンポーネント内部に新規に追加されたコードである。このアップデートによって、デフォルトのRTMPポート(1935)経由で到達できないサーバへの接続時間を短縮するための機能が追加された。Simple Connectコンポーネントのアプリケーションディレクトリのパラメーターに「rtmp」が指定されていて、かつポート番号が指定されていない場合、Simple Connectコンポーネントは次のように動作する:
- NetConnectionオブジェクトを生成し、指定されたアドレスに対して、1935番ポートを使って通常の接続を試みる。
- 2つめのNetConnectionオブジェクトを生成し、250ミリ秒待機した後、サーバに対して80番ポートを使ったRTMPT接続を試みる。
- 先に接続が成功した方を受け入れ、もう片方のNetConnectionオブジェクトは接続を閉じてから破棄する。
厳密に言えばこのようなやり方は必要ない。FlashPlayerはデフォルトでまず1935番ポートを使ったRTMP接続を試み、その後443番ポート、80番ポートを試みる。その後、80番ポートを使ったRTMPT接続を試みるからだ。ただしこれらの試みは時間がかかる。はじめのRTMP接続が失敗した場合、250ミリ秒後に80番ポートを使ったRMTPT接続を試みることで、接続プロセスにかかる時間は劇的に短縮されるのだ。このコンポーネントの作りを理解するため、FCSimpleConnectClassのactualConnect()メソッドを見てみよう。すべての人がSimple Connectコンポーネントを使うわけではないので、以下に示すコードは本来のコードを少々編集したものになっている。ただし行っている処理はほぼ同じだ。筆者はこのコードをメインのタイムラインに記述したが、よりオブジェクト指向的なアプローチで書き換えることも可能である。
//接続に成功するとonConnectファンクションが実行される
function onConnect(nc) {
_global.main_nc = nc;// グローバル変数に接続成功済みのNetConnectionオブジェクトを記録
// 確認のためURIをtraceし、使用中のプロトコルを確認
trace("onConnect> "+nc.uri);
// 接続に成功したNetConnectionオブジェクトのためのonStatusハンドラを生成
main_nc.onStatus = function(info) {
//ここに各種のイベントをハンドルするためのコードを記述
//以下のコードはinfoオブジェクトの内容を出力するもの
trace("----main_nc.onStatus----");
for (var p in info) {
trace(p+": "+info[p]);
}
};
//あとは他のコンポーネントに接続したりプログラムを初期化するなど・・・
}
//接続に失敗するとonConnectFailedファンクションが実行される
function onConnectFailed(info) {
//infoオブジェクトを検証し、接続失敗の原因についてレポートする
//以下のコードは単にinfoオブジェクトの内容をtraceしているだけなので、
//各自でエラーハンドリングコードを書き換えること
trace("----onConnectFailed----");
//infoオブジェクトの全プロパティの分ループ処理
for (var p in info) {
trace(p+": "+info[p]);
// If the application rejects the connection and passes back
// and application object loop through all its properties too.
if (p == "application") {
var appObj = info.application;
for (var q in appObj) {
trace(" "+q+": "+appObj[q]);
}
}
}
}
//RTMP接続用のNetConnectionオブジェクトを生成
rtmp_nc = new NetConnection();
//こちらの接続が先に成功した場合に、rtmpt_ncオブジェクトを破棄するための
//onStatusハンドラを生成
rtmp_nc.onStatus = function(info) {
this.pending = false;
if (info.code == "NetConnection.Connect.Success") {
if (rtmpt_nc.pending) {
rtmpt_nc.onStatus = null;
rtmpt_nc.close();
rtmpt_nc = null;
clearInterval(connectionID);
}
onConnect(this);
} else if (!rtmpt_nc.pending) {
onConnectFailed(info);
}
};
//RTMPT接続用のNetConnectionオブジェクトを生成
rtmpt_nc = new NetConnection();
//こちらの接続が先に成功した場合に、rtmp_ncオブジェクトを破棄するための
//onStatusハンドラを生成
rtmpt_nc.onStatus = function(info) {
this.pending = false;
if (info.code == "NetConnection.Connect.Success") {
if (rtmp_nc.pending) {
rtmp_nc.onStatus = null;
rtmp_nc.close();
rtmp_nc = null;
}
onConnect(this);
} else if (!rtmp_nc.pending) {
onConnectFailed(info);
}
};
//どちらのNetConnectionオブジェクトについてもペンディングフラグを立てておく
rtmp_nc.pending = true;
rtmpt_nc.pending = true;
//rtmp接続を試みる
rtmp_nc.connect("rtmp://host.domain.com/myApp/myInstance",userName,password);
//rtmpt接続を400ミリ秒後に行うように設定
connectionID = setInterval(connectRTMPT, 400);
//このファンクションが実行されたらインターバルを破棄してRTMPT接続を試みる
function connectRTMPT() {
clearInterval(connectionID);
rtmpt_nc.connect("rtmpt://host.domain.com/myApp/myInstance",userName,password);
}
//80番ポートが使えず、8080番ポートを使う場合などのconnectRTMPTメソッドの記述例は以下のとおり
function connectRTMPT() {
clearInterval(connectionID);
rtmpt_nc.connect("rtmpt://myHost.myDomain.com:8080/myApp/myInstance",userName,password);
}