MasterPoppy Amat による投稿

SecondLifeでオープンソース化され、一部で話題になっていたAVsitterですが、OpenSIM対応版がリリースされたそうです。

https://github.com/AVsitter/AVsitter/releases/tag/2.2-02

OpenSim version 0.8.2.1以降対応とのこと。 よかったら誰か試してみて下さい。

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側でも簡単に対応できそうな案件なので方針はおまかせします。

以上

 

http-inのURL(ttp://andromeda03.jogrid.net:9000/lslhttp/<hash>/形式)をプリムに貼り付けた共有メディアでは接続が遅くて、[内蔵ブラウザで開く]だとすぐに表示されるのでビューワ側の問題っぽいですね。もう少し調べてみます。

結局,HTMLを表示しているのは(たぶん)ビューア内蔵ブラウザのコンポーネントを使った、共有メディア(Media on Prim, Shared Media)です。

esekiさん

早速の御対応ありがとうございます。
おかげさまで目的のアプリが動くようになりました。
まだOSSL用にコードを整理しないといけないのですが、現状で一応実用レベルになったので明日の勉強会あたりでお披露目しようかとおもいます。

lsl-markdown for JOG

 

問題があるとすれば、今回対応頂いたUser-Agentチェック?を完全にはずしている状態だと、
滅多とないとは思いますがhttp-inのURLが第3者に漏れた場合に、JOG外からJOG内のhttp-inアプリを操作できるためセキュリティホールとなる可能性があります。

将来的にはSecondLife Grid同様にUserAgentによるチェックをかけて内蔵ブラウザでしかHTML表示しないようにすべきかと思います。
ご検討ください。

 

あとはFirestormでShared Mediaの起動がすごく遅いのどうかならないかなぁ

あっちこっちでテストしてるうちに普通にtext/htmlで返ってくるSIMがあるのを見つけました。
dejima, sandbox, sandbox2, sandbox3など(OpenSim 0.9.0.0 Dev 76a2d90 (Unix/Mono))

むしろ外部ブラウザで叩いてもHTMLになります。もしかして試しにいじっていただいたんでしょうか...?

▽ llSetContentType(id, CONTENT_TYPE_HTML);

llSetContentType(id, CONTENT_TYPE_HTML);

▽ osSetContentType(id, "text/html");

osSetContentType(id, "text/html");


▽ テストコード
https://www.jogrid.net/wi/mod/forum/discuss.php?d=996#p3738