JOG Markdownとどちらに書こうか迷ったのですがlsl-markdownに限らないネタなのでこちらに覚書を。
先週末にlsl-markdownのアルファ版を配布したときに、共有メディアからhttp-inにアクセスしたときに、なんか開くまで5分くらいかかるとか言っていた件、
いろいろ検証しているうちに原因判明しましたので共有します。LSL-Webアプリを開発される方は参考になさってください。
検証用LSLコード
key url_request;
string HTML_BODY =
"<!DOCTYPE html>
<html>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
</body>
</html>";
default
{
state_entry()
{
llClearLinkMedia(LINK_THIS,4);
url_request = llRequestURL();
}
http_request(key id, string method, string body)
{
if (url_request == id)
{
url_request = "";
if (method == URL_REQUEST_GRANTED){
llOwnerSay("計測開始");
llResetTime();
integer status = llSetLinkMedia(LINK_THIS, 4, [
PRIM_MEDIA_HOME_URL, body,
PRIM_MEDIA_CURRENT_URL, body,
PRIM_MEDIA_AUTO_ZOOM, TRUE,
PRIM_MEDIA_AUTO_PLAY, TRUE,
PRIM_MEDIA_AUTO_SCALE, FALSE,
PRIM_MEDIA_WIDTH_PIXELS, 1024,
PRIM_MEDIA_HEIGHT_PIXELS, 1024,
PRIM_MEDIA_PERMS_INTERACT, PRIM_MEDIA_PERM_NONE,
PRIM_MEDIA_PERMS_CONTROL, PRIM_MEDIA_PERM_NONE
]);
}
else if (method == URL_REQUEST_DENIED){
llOwnerSay("Something went wrong, no url:\n" + body);
}
}
else{
llOwnerSay("http_request " + (string)method + " method / " + (string)llGetTime() + " sec");
if(method == "GET" || method == "HEAD"){
llSetContentType(id, CONTENT_TYPE_HTML);
//osSetContentType(id, "text/html");
llHTTPResponse(id, 200, HTML_BODY);
llOwnerSay("llHTTPResponse / " + (string)llGetTime() + " sec");
}
}
}
}
実行結果 (チャットウインドウのコピー)
各タイムスタンプは通信環境とSIMの状態によってゆらぎがあります。
Japan Open Gird (OpenSim 0.9.0.0 Dev, 76a2d90 (Unix/Mono)) の場合
HTTP HEADがきてHTTP GETが来る (場合が多い)
計測開始 http_request HEAD method / 3.118848 sec llHTTPResponse / 3.118993 sec http_request GET method / 3.365243 sec llHTTPResponse / 3.365354 sec
SecondLife Grid の場合
なんか 2回 HTTP GET が上がってくる (1回だけの場合もある)
計測開始 http_request GET method / 2.645186 sec llHTTPResponse / 2.689124 sec http_request GET method / 11.070300 sec llHTTPResponse / 11.090170 sec
試しに先程の検証用コードでHEAD methodに対してはresponseを返さないようにしてやると、ブラウザからは25秒起きにretryがあり、
およそ250秒たったところで諦めたようにGET methodでリクエストしてくるような挙動になります。 (タイムアウトか?)
JOGでの結果
計測開始
http_request HEAD method / 3.108885 sec
http_request HEAD method / 28.422782 sec
http_request HEAD method / 53.984875 sec
http_request HEAD method / 80.104905 sec
http_request HEAD method / 107.220953 sec
http_request HEAD method / 137.343455 sec
http_request HEAD method / 167.469765 sec
http_request HEAD method / 197.593544 sec
http_request HEAD method / 227.713249 sec
http_request GET method / 252.940812 sec
llHTTPResponse / 252.940964 sec
整理としては内蔵ブラウザからはHTTP GET methodする前にHTTP HEAD methodを先に飛ばしており、
SL serverの場合はHEADをGETにすげ替えてhttp_request()を発火させていて、GETが2回飛んでいるように見えるものと思われます。
はじめhttp_requestからGETでHTMLとjavascriptをもらって、
あとからajaxでデータをもらってごにょごにょする感じのLSL-Webアプリだと、SLでは以下のようなコードになります。
http_request(key id, string method, string body)
{
if (url_request == id)
{
//共有メディアを設定する処理は省略
}
else{
if(method == "GET"){
llSetContentType(id, CONTENT_TYPE_HTML);
llHTTPResponse(id, 200, HTML_BODY);
}
else if(method == "POST"){
llSetContentType(id, CONTENT_TYPE_JSON);
llHTTPResponse(id, 200, hogehoge_data);
}
}
}
このコードをJOGで実行すると、はじめのHEADを拾っておらず、約25秒おきにリクエストを繰り返したあとにGETが飛んでくるので
「すぐに画面に表示されない、5分位したら表示される」という挙動になります。
で結局どうすればいいか? SLにもJOGにも対応するコードとしては以下のようになります。
http_request(key id, string method, string body)
{
if (url_request == id)
{
//共有メディアを設定する処理は省略
}
else{
if(method == "GET" || method == "HEAD"){ //この1行が今回の成果物!
llSetContentType(id, CONTENT_TYPE_HTML);
llHTTPResponse(id, 200, HTML_BODY);
}
else if(method == "POST"){
llSetContentType(id, CONTENT_TYPE_JSON);
llHTTPResponse(id, 200, hogehoge_data);
}
}
}
HEADとGETを厳格に場合分けしてもOKですが、HEADだからってなにか違うことをするわけでもないのでいっそ一緒でいいかということでこのようになりました。
SLと全く同じ挙動にするには、http-in宛にHTTP HEADが飛んできたときはGETにすげ替えてイベント発火するようにOpenSIMのコードを直せばいいですが、
上述のようにLSL側でも簡単に対応できそうな案件なので方針はおまかせします。
以上