Mantis Bug Tracker

View Issue Details Jump to Notes ] Issue History ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0005993opensim[REGION] OpenSim Corepublic2012-04-26 13:262012-05-14 18:27
Reportertomhainescciu 
Assigned Tojustincc 
PrioritynormalSeverityminorReproducibilityalways
StatusresolvedResolutionfixed 
PlatformLinuxOSUbuntuOS Version11.10
Product Versionmaster (dev code) 
Target VersionFixed in Version 
Summary0005993: In attachment, llRequestURL() immediately after changing regions results in broken HTTP-in URL being returned
DescriptionWhen an attachment requests a new HTTP-in URL in the changed event, the new URL doesn't function properly.


$ curl -v http://x.x.x.x:9000/lslhttp/f46bdfa1-885f-48f2-aa58-b802ae001790/ [^]
* About to connect() to x.x.x.x port 9000 (#0)
* Trying x.x.x.x... connected
* Connected to x.x.x.x (x.x.x.x) port 9000 (#0)
> GET /lslhttp/f46bdfa1-885f-48f2-aa58-b802ae001790/ HTTP/1.1
> User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5
> Host: x.x.x.x:9000
> Accept: */*
>

The request just hangs here.
Steps To Reproduceadd this script to an attachment, and then teleport to a new region. when attempting to connect to the URL granted in the changed event, the HTTP connection hangs after connecting

 
 
string _httpin_url_request_key;

default
{
    changed(integer mask) {
        if ( mask & CHANGED_REGION ) {
             _httpin_url_request_key = llRequestURL();
        }
    }
    http_request(key request_id, string http_method, string body) {
        integer response_status_code = 200;
        // check if this is a call back from llRequestURL()
        llOwnerSay("process_http_request_event() - http_method: " + http_method);
        if ( request_id == _httpin_url_request_key) {
            _httpin_url_request_key = "";
            if (http_method == URL_REQUEST_GRANTED){
                // set the new HTTP-In URL
                llOwnerSay(body);
            } else if (http_method == URL_REQUEST_DENIED) {
                //
            }
        }
        llHTTPResponse(request_id, 200, "GREAT SUCCESS");
    }
    state_entry()
    {
       _httpin_url_request_key = llRequestURL();
    }
}
TagsNo tags attached.
Git Revision or version numberf36fe45fa7468dd6e785d523c5df38666140135f
Run Mode Grid (Multiple Regions per Sim)
Physics EngineODE
EnvironmentMono / Linux64
Mono Version2.10
ViewerSecond Life Viewer 2
Attached Files

- Relationships

-  Notes
(0021309)
tomhainescciu (reporter)
2012-04-26 13:26

I should note that in my script I'm releasing the URL before generating a new one.
(0021366)
justincc (administrator)
2012-05-04 21:15

I wasn't able to reproduce this on current git master da4819a1 with this script in a box on my head whilst teleporting between two regions separated by a one region space - I was able to wget the given url output after teleport.

I have done some changes to teleport timings very recently. In theory, this shouldn't impact url requests but it's worth checking against really bleeding edge. In da4819a1 I also added some debug log lines which say when an url is requested and released.

It might be worth including the url releases in the test script since that may well have an impact on this. Also, your current script has a small weakness where llHTTPResponse(request_id, 200, "GREAT SUCCESS"); is still called even if the script is just being notified about a granted/denied URL.
(0021368)
justincc (administrator)
2012-05-04 22:08

Also, are the teleport source and target on the same simulator or different simulators?
(0021372)
Michelle Argus (reporter)
2012-05-05 11:33

Does this only happen were the URL generated uses the hostname instead of the IP?

 When the hostname is used instead of the IP there is often a connection problem which can be solved by replacing the incorrect hostname with the IP, adjusting the server settings or ajusting the ExternalHostNameForLSL in the opensim.ini . Unfortunatly many hosters have incorrect hostnames set by default, eg. all common german server providers.

http://opensimulator.org/mantis/view.php?id=5615 [^]

Otherwise, I also could not repruduce the problem.

Tip: if the a adress is not reachable, replace the hostname with the IP. My experiance sofar is, that it always works. You then dont need to contact each regionowner to have them change their settings ;)
(0021385)
tomhainescciu (reporter)
2012-05-07 16:35
edited on: 2012-05-07 16:39

Justin,

That script was thrown together for the sake of a quick test. I realize now that I'm not seeing the problem in this script, but am still seeing it in the original. I wanted to break it down to the smallest component that still manages to cause the problem. I will attach in a second note the full text of the original script. The HUD in question contains about 15 prims, each running one script. The script that uses llRequestURL passes linked messages to the other scripts. None of the child scripts use LLRequestURL.

I will be upgrading to Git master shortly, and will see if that fixes the issue.

Michelle, I am using IP addresses everywhere and not Hostnames, and if the script is reset after landing in a new region, llRequestURL returns a working URL

(0021386)
tomhainescciu (reporter)
2012-05-07 16:37
edited on: 2012-05-07 17:51

// the host name of the server
string server_hostname = "host.name.of.game.server";
integer server_use_ssl = FALSE;

// whether we ask for a new httpin url on region changes by llRequestURL() or llResetScript();
// valid values are 'request' or 'reset'
// see http://opensimulator.org/mantis/view.php?id=5993 [^]
string _new_region_httpin_request_method = "request";

/****
 * NO MORE USER CUSTOMIZABLE SETTINGS
 *****/
float version = 0.5;

// the various control groups we can communicate with
integer CONTROL_GROUP_COMMUNICATOR = 1;
integer CONTROL_GROUP_PAGE_FULL = 2;
integer CONTROL_GROUP_PAGE_SMALL = 4;
integer CONTROL_GROUP_INVENTORY = 8;
integer CONTROL_GROUP_MUSIC = 16;
integer CONTROL_GROUP_ALL = 2147483647;

// various child methods that can be called
integer CHILD_METHOD_SHOW = 1;
integer CHILD_METHOD_HIDE = 2;
integer CHILD_METHOD_RESET = 4;
integer CHILD_METHOD_LOAD_URL = 8;
integer CHILD_METHOD_CLEAR_URL = 16;
integer CHILD_METHOD_DISPLAY_CHARACTER = 32;
integer CHILD_METHOD_DISPLAY_DIALOGUE = 64;
integer CHILD_METHOD_SET_INVENTORY_STATE = 128;
integer CHILD_METHOD_SHOW_INVENTORY_ITEM = 256;
integer CHILD_METHOD_HIDE_INVENTORY_ITEM = 512;
integer CHILD_METHOD_CONTROL_MUSIC = 1024;

// various inventory items
integer INVENTORY_ITEM_CHART = 1;
integer INVENTORY_ITEM_HEALTHY_BLUEPRINT = 2;
integer INVENTORY_ITEM_HELICASE_ENZYME = 4;
integer INVENTORY_ITEM_HEALTHY_DNA = 8;
integer INVENTORY_ITEM_POLYMERASE = 16;
integer INVENTORY_ITEM_RNA_POLYMERASE = 32;
integer INVENTORY_ITEM_MRNA = 64;
integer INVENTORY_ITEM_ALL = 2147483647;

string _current_httpin_url = "";
// the private var used to identify llRequestURL() and llHTTPRequest() calls
key _httpin_url_request_key;
key _http_event_trip_key;

// the endpoint on the server for event tripping
string _server_event_trip_endpoint = "/triggers";

// debug functions
integer DEBUG = FALSE;
dm(string message) { if ( DEBUG ) llOwnerSay(message); }

// boot the HUD
_initialize_hud() {

    
    // tell the server we've booted
    trip_server_event("hud_boot", []);
    // request an httpin url
    _request_new_httpin_url();
}
    

// this just restarts this script
_restart_supervisor() {
    llResetScript();
}


// this function takes the name of an argument and a strided list of args and values (as returned by parse_arguments),
// and returns the value
string get_argument(string arg_name, list args) {
    integer index = llListFindList(args, [arg_name]);
    if ( index == -1 ) {
        return "";
    } else {
        return ( llList2String(args, index+1));
    }
}


// ###############################################
// Routine to parse a string sent through the
// http server via post.
// parsePostData(theMessage)
// Returns a strided list with stride length 2.
// Each set has the key and then its value.
list parse_arguments(string message) {
    list postData = []; // The list with the data that was passed in.
    list parsedMessage = llParseString2List(message,["&"],[]); // The key/value pairs parsed into one list.
    integer len = ~llGetListLength(parsedMessage);
    
    while(++len) {
        string currentField = llList2String(parsedMessage, len); // Current key/value pair as a string.
 
        integer split = llSubStringIndex(currentField,"="); // Find the "=" sign
        if(split == -1) { // There is only one field in this part of the message.
            postData += [llUnescapeURL(currentField),""];
        } else {
            postData += [llUnescapeURL(llDeleteSubString(currentField,split,-1)), llUnescapeURL(llDeleteSubString(currentField,0,split))];
        }
    }
    // Return the strided list.
    return postData;
}

// adds a param and value to a strided list and returns the value
list append_arguments(string param, string value, list args) {
    args = args + [param, value];
    return args;
}

// this turns a param, value strided list into ad URL safe param string
// e.g. ['firstname','ringo','lastname','starr'] becomes 'firstname=ringo&lastname=starr'
// not sure if this handles empty args list properly
string build_parameter_string(list args) {
    integer i;
    string paramString = "";
    integer length = llGetListLength(args);
    
    for (i = 0; i < length; i+=2) {
        // add the param
        if (i != 0) { paramString += "&";}
        paramString += llEscapeURL(llList2String(args,i));
        paramString += "=" + llEscapeURL(llList2String(args,i+1));
        
    }
    return paramString;
}

/* process any state change events that are triggered
 * Should respond to: CHANGED_REGION, CHANGED_TELEPORT, CHANGED_REGION_START
 */
process_changed_event(integer change) {
    // if we moved to a new region, request a new HTTPin url.
    // also let the server know we've changed to a new region.
    if ( change & CHANGED_REGION ) {
        dm("process_changed_event() change: " + change);
        dm("change & CHANGED_REGION");
        
        
/********
    We'd like to just get a new httpin_url here, but it looks like an OpenSim bug causes the httpin_url to not function. The only way around it seems to be to restart this script here. A bit kludgy.
**********/
        if (_new_region_httpin_request_method == "request" ) {
            // request a new httpin url.
            _request_new_httpin_url();
            // tell the server we've changed regions after we request a new url
            trip_server_event("region_changed", []);
        } else {
            // tell the server we've changed regions before we reset the script
            trip_server_event("region_changed", []);
            _restart_supervisor();
        }
    }
}
    
process_on_rez_event(integer param) {
    trip_server_event("hud_rez", []);
}

process_attach_event(key id) {
        dm("process_attach_event: id: " + id);
    if( id ) {

// llResetScript();
    } else {
// state inactive;
    }
}
// request a new http in url
_request_new_httpin_url() {
    _httpin_url_request_key = llRequestURL();
}

/* handle any incoming http_request events. implemented here to ensure
 * uniform HTTP request handling across lsl states
 */
process_http_request_event(key request_id, string http_method, string body) {
    integer response_status_code = 200;
    // check if this is a call back from llRequestURL()
    dm("process_http_request_event(): http_method: " + http_method);
    dm("process_http_request_event(): body: " + body);
    if ( request_id == _httpin_url_request_key) {
        _httpin_url_request_key = "";
        if (http_method == URL_REQUEST_GRANTED){
            // set the new HTTP-In URL
            update_httpin_url(body);
            trip_server_event("httpin_url_changed", ["httpin_url", body]);
        } else if (http_method == URL_REQUEST_DENIED) {
            state fatal_error;
        }
    // otherwise treat it as a method call from the server
    } else {
        // respond affirmatively
        llHTTPResponse(request_id, response_status_code, "");
        list args = parse_arguments(body);
        string method = get_argument("method", args);
        if ( method != "" ) {
            
            if ( method == "loadPageFull") {_loadPageFull(get_argument("url", args));}
            if ( method == "hidePageFull") {_hidePageFull();}
            if ( method == "loadPageSmall") {_loadPageSmall(get_argument("url",args));}
            if ( method == "hidePageSmall") {_hidePageSmall();}
            if ( method == "teleportPlayer") {_teleportPlayer(get_argument("region", args), (vector) "<" + get_argument("teleport_x", args) + "," + get_argument("teleport_y", args) + "," +get_argument("teleport_z", args) + ">", (vector) "<" + get_argument("facing_x", args) + "," + get_argument("facing_y", args) + "," +get_argument("facing_z", args) + ">" );}
            if ( method == "displayDialogue") {_displayDialogue( (key) get_argument("character_texture_id", args), get_argument("dialogue_url", args));}
            if ( method == "hideDialogue") {_hideDialogue();}
            if ( method == "setInventoryItemState") {_setInventoryItemState((integer) get_argument("inventory_item_id", args), (integer) get_argument("inventory_item_state", args));}
            if ( method == "showInventoryItem") {_showInventoryItem((integer) get_argument("inventory_item_id", args));}
            if ( method == "hideInventoryItem") {_hideInventoryItem((integer) get_argument("inventory_item_id", args));}
            if ( method == "controlMusic") {_controlMusic( get_argument("music_command", args));}
            if ( method == "restartHUD") {_restartHUD();}
        }
    }
}

/* handle any incoming http_response event. implemented here to ensure
 * uniform HTTP request handling across lsl states
 */
process_http_response_event( key request_id, integer status, list metadata, string body ) {
    // if this is a call back from an event trip
    if ( request_id = _http_event_trip_key ) {
        
    }
}

/* This function accepts a new _httpin_url as returned from a call to
 * llRequestURLand gives passes it to the server
 * it also must free the old URL in _current_httpin_url */
update_httpin_url(string new_url) {
    if ( _current_httpin_url != "") { llReleaseURL( new_url );}
    dm ( new_url );
    _current_httpin_url = new_url;
}

/* This function trips an event on the server.
 * It is up to the server to decide what to do with the event.
 * The following parameters are relayed in the headers, so they do not need to be passed as paramaters here:
 * avatar_id, region_name */
trip_server_event(string event_name, list args) {
    string protocol;
    if ( server_use_ssl == TRUE ) { protocol = "https://";} [^] else { protocol = "http://";} [^]
    string url = protocol + server_hostname + _server_event_trip_endpoint + "/" + event_name;
    list http_params = [ HTTP_METHOD, "POST", HTTP_MIMETYPE, "application/x-www-form-urlencoded" ];
    // add mandatory arguments to the list
    args += ["version", version, "avatar_key", llGetOwner()];
    string post_params = build_parameter_string(args);
    dm("trip_server_event: post_params: " + post_params);
    _http_event_trip_key = llHTTPRequest(url, http_params, post_params);
}

/* This function calls a method on one of the children prims
 * by sending a linked message to the entire set. child prims
 * have a component_group property. The method specifies a
 * particular control_group or ALL. Prims will respond if they
 * are in the control_group or ALL is specified. */
call_child_method(integer method, integer control_group, list parameters) {
    string parameter_string = build_parameter_string(parameters);
    llMessageLinked(LINK_SET, control_group, (string) method, (key) parameter_string);
}

// load a URL in the full screen browser
_loadPageFull (string url) {
    call_child_method(CHILD_METHOD_SHOW, CONTROL_GROUP_PAGE_FULL, []);
    call_child_method(CHILD_METHOD_LOAD_URL, CONTROL_GROUP_PAGE_FULL, ["url",url]);
}
_hidePageFull () {
    call_child_method(CHILD_METHOD_HIDE, CONTROL_GROUP_PAGE_FULL, []);
    call_child_method(CHILD_METHOD_CLEAR_URL, CONTROL_GROUP_PAGE_FULL, []);
}
_loadPageSmall (string url) {
    call_child_method(CHILD_METHOD_SHOW, CONTROL_GROUP_PAGE_SMALL, []);
    call_child_method(CHILD_METHOD_LOAD_URL, CONTROL_GROUP_PAGE_SMALL, ["url",url]);
}
_hidePageSmall () {
    call_child_method(CHILD_METHOD_HIDE, CONTROL_GROUP_PAGE_SMALL, []);
    call_child_method(CHILD_METHOD_CLEAR_URL, CONTROL_GROUP_PAGE_SMALL, []);

}
_teleportPlayer (string region, vector location, vector facing) {
    // no idea
}

_displayDialogue (key character_texture_id, string dialogue_url) {
    // call the child methods to display the character and dialogue before the show command
    call_child_method(CHILD_METHOD_DISPLAY_CHARACTER, CONTROL_GROUP_COMMUNICATOR, ["character_texture_id", character_texture_id]);
    call_child_method(CHILD_METHOD_DISPLAY_DIALOGUE, CONTROL_GROUP_COMMUNICATOR, ["dialogue_url", dialogue_url]);
    call_child_method(CHILD_METHOD_SHOW, CONTROL_GROUP_COMMUNICATOR, []);
 }
_hideDialogue () {
    call_child_method(CHILD_METHOD_HIDE, CONTROL_GROUP_COMMUNICATOR, []);
}
_setInventoryItemState(integer inventory_item_id, integer inventory_item_state) {
    call_child_method(CHILD_METHOD_SET_INVENTORY_STATE, CONTROL_GROUP_INVENTORY, ["inventory_item_id", inventory_item_id, "inventory_item_state", inventory_item_state]);
}
_showInventoryItem(integer inventory_item_id) {
    call_child_method(CHILD_METHOD_SHOW_INVENTORY_ITEM, CONTROL_GROUP_INVENTORY, ["inventory_item_id", inventory_item_id]);
}
_hideInventoryItem(integer inventory_item_id) {
    call_child_method(CHILD_METHOD_HIDE_INVENTORY_ITEM, CONTROL_GROUP_INVENTORY, ["inventory_item_id", inventory_item_id]);
}
_controlMusic(string music_command) {
    call_child_method(CHILD_METHOD_CONTROL_MUSIC, CONTROL_GROUP_MUSIC, ["music_command", music_command]);
}
_restartHUD () {
    call_child_method(CHILD_METHOD_RESET, CONTROL_GROUP_ALL, []);
    _restart_supervisor();
}
_hideAll () {
    call_child_method(CHILD_METHOD_HIDE, CONTROL_GROUP_ALL, []);
}

default {
    state_entry() {
       _initialize_hud();
    }
    attach(key id) {process_attach_event(id);}
    changed(integer mask) {process_changed_event(mask);}
    on_rez(integer param) {process_on_rez_event(param);}
    http_response( key request_id, integer status, list metadata, string body ) { process_http_response_event( request_id, status, metadata, body );}
    http_request(key request_id, string http_method, string body) { process_http_request_event(request_id, http_method, body);}
}

state active_gameplay {
    state_entry() {
        
    }
     attach(key id) {process_attach_event(id);}
    changed(integer mask) {process_changed_event(mask);}
    on_rez(integer param) {process_on_rez_event(param);}
    http_response( key request_id, integer status, list metadata, string body ) { process_http_response_event( request_id, status, metadata, body );}
    http_request(key request_id, string http_method, string body) { process_http_request_event(request_id, http_method, body);}
    
}

state inactive {
    state_entry() {
        
    }
    attach(key id) {process_attach_event(id);}
    changed(integer mask) {process_changed_event(mask);}
    on_rez(integer param) {process_on_rez_event(param);}
    http_response( key request_id, integer status, list metadata, string body ) { process_http_response_event( request_id, status, metadata, body );}
    http_request(key request_id, string http_method, string body) { process_http_request_event(request_id, http_method, body);}
}

state incorrect_version {
    state_entry() {
        
    }
    attach(key id) {process_attach_event(id);}
    changed(integer mask) {process_changed_event(mask);}
    on_rez(integer param) {process_on_rez_event(param);}
    http_response( key request_id, integer status, list metadata, string body ) { process_http_response_event( request_id, status, metadata, body );}
    http_request(key request_id, string http_method, string body) { process_http_request_event(request_id, http_method, body);}
}

state fatal_error {
    state_entry() {
        // TODO: Process error
        llSay(0, "Aww snap!");
    }
    attach(key id) {process_attach_event(id);}
    changed(integer mask) {process_changed_event(mask);}
    on_rez(integer param) {process_on_rez_event(param);}
    http_response( key request_id, integer status, list metadata, string body ) { process_http_response_event( request_id, status, metadata, body );}
    http_request(key request_id, string http_method, string body) { process_http_request_event(request_id, http_method, body);}
}

(0021405)
justincc (administrator)
2012-05-11 02:31

Is it possible to cut down this second script to the part that actually causes the failure?

Also, it's crucial to know whether source and destination regions are on the same simulator.
(0021420)
justincc (administrator)
2012-05-12 02:09

In fact, even the posted script will not compile - it has copy/paste errors on line 3 of trip_server_event()
(0021422)
BlueWall (administrator)
2012-05-12 02:17

Try adding another state to the small test script and switching out of default to it before jumping. See if that will trigger the behavior.
(0021447)
tomhainescciu (reporter)
2012-05-12 14:13
edited on: 2012-05-12 14:19

@justincc, The script doesn't compile because mantis tries to turn http:// in trip_server_event into a link and inserts characters that throw syntax errors. See above.

I will strip it down to smalest breaking part today. And both region and destination are on the same simulator. I will also test today if problem occurs when they are not on the same simulator.

@BlueWall, I will try this today.

(0021448)
tomhainescciu (reporter)
2012-05-12 16:30

In stripping out unnecessary functionality, I found the source of the problem. I have a housekeeping function that fires llReleaseURL on the old url after a new one is obtained, and of course I was executing it upon the newly retrieved url. Sorry for the wasted time. Everything is now working as it should.
(0021458)
justincc (administrator)
2012-05-14 18:27

No problem, good to hear that you got it working!

- Issue History
Date Modified Username Field Change
2012-04-26 13:26 tomhainescciu New Issue
2012-04-26 13:26 tomhainescciu Note Added: 0021309
2012-04-26 13:30 tomhainescciu Steps to Reproduce Updated View Revisions
2012-04-26 13:30 tomhainescciu Steps to Reproduce Updated View Revisions
2012-05-04 21:15 justincc Note Added: 0021366
2012-05-04 21:15 justincc Assigned To => justincc
2012-05-04 21:15 justincc Status new => feedback
2012-05-04 22:08 justincc Note Added: 0021368
2012-05-05 11:33 Michelle Argus Note Added: 0021372
2012-05-07 16:35 tomhainescciu Note Added: 0021385
2012-05-07 16:35 tomhainescciu Status feedback => assigned
2012-05-07 16:37 tomhainescciu Note Added: 0021386
2012-05-07 16:39 tomhainescciu Note Edited: 0021385 View Revisions
2012-05-07 16:48 tomhainescciu Note Edited: 0021386 View Revisions
2012-05-07 16:49 tomhainescciu Note Edited: 0021386 View Revisions
2012-05-07 17:51 tomhainescciu Note Edited: 0021386 View Revisions
2012-05-11 02:31 justincc Note Added: 0021405
2012-05-12 02:09 justincc Note Added: 0021420
2012-05-12 02:17 BlueWall Note Added: 0021422
2012-05-12 14:13 tomhainescciu Note Added: 0021447
2012-05-12 14:19 tomhainescciu Note Edited: 0021447 View Revisions
2012-05-12 16:30 tomhainescciu Note Added: 0021448
2012-05-14 18:27 justincc Note Added: 0021458
2012-05-14 18:27 justincc Status assigned => resolved
2012-05-14 18:27 justincc Resolution open => fixed


Copyright © 2000 - 2012 MantisBT Group
Powered by Mantis Bugtracker