Difference between revisions of "Http request"

From Second Life Wiki
Jump to navigation Jump to search
m
(Add notes about LSL http pipelines.)
 
(10 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{LSL_Event|event_id
{{LSL_Event|event_id
|event=http_request|event_id=33|event_delay
|event=http_request|event_id=33|event_delay
|p1_type=key|p1_name=request_id|p1_desc=HTTP request id for response use, and function response identification.
|p1_type=key|p1_subtype=handle|p1_name=request_id|p1_desc=HTTP request id for response use, and function response identification.
|p2_type=string|p2_name=method|p2_desc=GET, POST, PUT, DELETE
|p2_type=string|p2_name=method|p2_desc=<code>{{String|GET}}</code>, <code>{{String|POST}}</code>, <code>{{String|PUT}}</code>, <code>{{String|DELETE}}</code>, [[URL_REQUEST_GRANTED]], [[URL_REQUEST_DENIED]]|p2_hover={{String|GET}}, {{String|POST}}, {{String|PUT}}, {{String|DELETE}}, URL_REQUEST_GRANTED, URL_REQUEST_DENIED
|p3_type=string|p3_name=body|p3_desc=Contents of the request.
|p3_type=string|p3_name=body|p3_desc=Contents of the request.
|event_desc=Triggered when task receives an HTTP request.
|event_desc=Triggered when task receives an HTTP request.
Line 8: Line 8:
|spec=See [[LSL_http_server]] for full specification.
|spec=See [[LSL_http_server]] for full specification.
|caveats=
|caveats=
* '''body''' is [[limit]]ed to 2048 bytes; anything longer will be truncated to 2048 bytes.
* {{LSLP|body}} is [[limit]]ed to 2048 bytes; anything longer will be truncated to 2048 bytes.
** As per the above, this function is unaffected by any values passed to [[llHTTPRequest]] via [[HTTP_BODY_MAXLENGTH]], as they reside on the [[:Category:LSL HTTP#Understanding_LSL_HTTP_Communications | Outgoing]] pipeline, and this function is on the [[:Category:LSL HTTP#Understanding_LSL_HTTP_Communications | Incoming]] pipeline.
* headers (accessed with [[llGetHTTPHeader]]) are limited to 255 bytes.
* headers (accessed with [[llGetHTTPHeader]]) are limited to 255 bytes.
* There is a limit of 64 pending http_request
* There is a limit of 64 pending http_request
* '''body''' is not sent with the request unless the method is set to POST or PUT.
* {{LSLP|body}} is not sent with the request unless the method is set to <code>{{String|POST}}</code>, <code>{{String|PUT}}</code>, or [[URL_REQUEST_GRANTED]].
* Requests made at approx 0625 SLT may fail with a 503 status code, with "ERROR: The requested URL could not be retrieved", and "(111) Connection refused" in the body of the response.  This has been confirmed as expected behaviour by Kelly, due to the nightly maint & log rotation.  It does reliably impact object to object HTTP at that time, and quite probably may impact object to/from web around the same time.  The interruption in service is fairly brief, and the precise timing may vary as LL adjust their nightly maint processes, or due to server load.
* Requests may fail with a [https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_Server_Error 503 status code] for at least two possible reasons:
** if the returned body is <code>ERROR: The requested URL could not be retrieved</code>, and <code>(111) Connection refused</code>, the problem will normally last only a few minutes - this is th nightly maint & log rotation.  It does reliably impact object to object HTTP at that time, and quite probably may impact object to/from web around the same time.  The interruption in service is fairly brief, and the precise timing may vary as LL adjust their nightly maint processes, or due to server load.
** this response can also happen if too many requests are received by scripts in the region owned by the same owner. In this case, a Retry-After header is returned with a number of seconds to wait before retrying. Note that if you have many requestors, some of them may have to wait longer. A good general strategy is to wait the suggested number of seconds (possibly with a little randomness added in) before retrying, and if that fails increase how long you wait by multiplying by a small number before retrying again. If all your requestors follow some method like this, eventually they'll get through.
|examples=
|examples=
See [[LSL_http_server/examples]] for some examples from the feature design phase.
See [[LSL_http_server/examples]] for some examples from the feature design phase.
<lsl>key url_request;
{{LSL Tip|Never ever forget to release a URL again which you have requested! URLs are region resources just like prims. If you take them all you can get into big trouble with the sim owner and/or estate managers.}}
<source lang="lsl2">
string url;
key urlRequestId;
key selfCheckRequestId;
 
request_url()
{
    llReleaseURL(url);
    url = "";
 
    urlRequestId = llRequestURL();
}
 
throw_exception(string inputString)
{
    key owner = llGetOwner();
    llInstantMessage(owner, inputString);
 
    // yeah, bad way to handle exceptions by restarting.
    // However this is just a demo script...
 
    llResetScript();
}


default
default
{
{
    on_rez(integer start_param)
    {
        llResetScript();
    }
    changed(integer change)
    {
        if (change & (CHANGED_OWNER | CHANGED_INVENTORY))
        {
            llReleaseURL(url);
            url = "";
            llResetScript();
        }
        if (change & (CHANGED_REGION | CHANGED_REGION_START | CHANGED_TELEPORT))
            request_url();
    }
     state_entry()
     state_entry()
     {
     {
         url_request = llRequestURL();
         request_url();
     }
     }
     http_request(key id, string method, string body)
     http_request(key id, string method, string body)
     {
     {
         if (url_request == id)
         integer responseStatus = 400;
        string responseBody = "Unsupported method";
 
        if (method == URL_REQUEST_DENIED)
            throw_exception("The following error occurred while attempting to get a free URL for this device:\n \n" + body);
 
        else if (method == URL_REQUEST_GRANTED)
        {
            url = body;
            key owner = llGetOwner();
            llLoadURL(owner, "Click to visit my URL!", url);
 
            // check every 5 mins for dropped URL
            llSetTimerEvent(300.0);
        }
        else if (method == "GET")
         {
         {
             url_request = "";
             responseStatus = 200;
            if (method == URL_REQUEST_GRANTED)
             responseBody = "Hello world!";
            {
                llSay(0,"URL: " + body);
             }
            else if (method == URL_REQUEST_DENIED)
            {
                llSay(0, "Something went wrong, no url. " + body);
            }
         }
         }
         else
         // else if (method == "POST") ...;
        // else if (method == "PUT") ...;
        // else if (method == "DELETE") { responseStatus = 403; responseBody = "forbidden"; }
 
        llHTTPResponse(id, responseStatus, responseBody);
    }
 
    http_response(key id, integer status, list metaData, string body)
    {
        if (id == selfCheckRequestId)
         {
         {
             llHTTPResponse(id, 200, body);
             // If you're not usually doing this,
            // now is a good time to get used to doing it!
            selfCheckRequestId = NULL_KEY;
 
            if (status != 200)
                request_url();
         }
         }
        else if (id == NULL_KEY)
            throw_exception("Too many HTTP requests too fast!");
    }
    timer()
    {
        selfCheckRequestId = llHTTPRequest(url,
                                [HTTP_METHOD, "GET",
                                    HTTP_VERBOSE_THROTTLE, FALSE,
                                    HTTP_BODY_MAXLENGTH, 16384],
                                "");
     }
     }
}</lsl>
}
</source>
|helpers
|helpers
|also_articles
|also_articles
Line 55: Line 136:
{{LSL DefineRow||[[llUnescapeURL]]}}
{{LSL DefineRow||[[llUnescapeURL]]}}
|also_footer
|also_footer
|notes
|notes=
* When triggered by either [[llRequestURL]] or [[llRequestSecureURL]] the method will be either [[URL_REQUEST_GRANTED]] or [[URL_REQUEST_DENIED]]
** If the method is [[URL_REQUEST_GRANTED]] The body will contain the full url, minus the trailing forward slash.
|issues=
|issues=
{{Issue|SVC-1086|Design: LSL http_server|type=fs|status=ip}}
{{Issue/V1|SVC-1086|Design: LSL http_server|type=fs|status=ip}}
|cat1=HTTP
|cat1=HTTP
|cat2=HTTP/Server
|cat2=HTTP/Server

Latest revision as of 13:35, 29 June 2022

Description

Event: http_request( key request_id, string method, string body ){ ; }

Triggered when task receives an HTTP request.

• key request_id HTTP request id for response use, and function response identification.
• string method "GET", "POST", "PUT", "DELETE", URL_REQUEST_GRANTED, URL_REQUEST_DENIED
• string body Contents of the request.

Specification

See LSL_http_server for full specification.

Caveats

  • body is limited to 2048 bytes; anything longer will be truncated to 2048 bytes.
  • headers (accessed with llGetHTTPHeader) are limited to 255 bytes.
  • There is a limit of 64 pending http_request
  • body is not sent with the request unless the method is set to "POST", "PUT", or URL_REQUEST_GRANTED.
  • Requests may fail with a 503 status code for at least two possible reasons:
    • if the returned body is ERROR: The requested URL could not be retrieved, and (111) Connection refused, the problem will normally last only a few minutes - this is th nightly maint & log rotation. It does reliably impact object to object HTTP at that time, and quite probably may impact object to/from web around the same time. The interruption in service is fairly brief, and the precise timing may vary as LL adjust their nightly maint processes, or due to server load.
    • this response can also happen if too many requests are received by scripts in the region owned by the same owner. In this case, a Retry-After header is returned with a number of seconds to wait before retrying. Note that if you have many requestors, some of them may have to wait longer. A good general strategy is to wait the suggested number of seconds (possibly with a little randomness added in) before retrying, and if that fails increase how long you wait by multiplying by a small number before retrying again. If all your requestors follow some method like this, eventually they'll get through.


Examples

See LSL_http_server/examples for some examples from the feature design phase.

KBcaution.png Important: Never ever forget to release a URL again which you have requested! URLs are region resources just like prims. If you take them all you can get into big trouble with the sim owner and/or estate managers.
string url;
key urlRequestId;
key selfCheckRequestId;

request_url()
{
    llReleaseURL(url);
    url = "";

    urlRequestId = llRequestURL();
}

throw_exception(string inputString)
{
    key owner = llGetOwner();
    llInstantMessage(owner, inputString);

    // yeah, bad way to handle exceptions by restarting.
    // However this is just a demo script...

    llResetScript();
}

default
{
    on_rez(integer start_param)
    {
        llResetScript();
    }

    changed(integer change)
    {
        if (change & (CHANGED_OWNER | CHANGED_INVENTORY))
        {
            llReleaseURL(url);
            url = "";

            llResetScript();
        }

        if (change & (CHANGED_REGION | CHANGED_REGION_START | CHANGED_TELEPORT))
            request_url();
    }

    state_entry()
    {
        request_url();
    }

    http_request(key id, string method, string body)
    {
        integer responseStatus = 400;
        string responseBody = "Unsupported method";

        if (method == URL_REQUEST_DENIED)
            throw_exception("The following error occurred while attempting to get a free URL for this device:\n \n" + body);

        else if (method == URL_REQUEST_GRANTED)
        {
            url = body;
            key owner = llGetOwner();
            llLoadURL(owner, "Click to visit my URL!", url);

            // check every 5 mins for dropped URL
            llSetTimerEvent(300.0);
        }
        else if (method == "GET")
        {
            responseStatus = 200;
            responseBody = "Hello world!";
        }
        // else if (method == "POST") ...;
        // else if (method == "PUT") ...;
        // else if (method == "DELETE") { responseStatus = 403; responseBody = "forbidden"; }

        llHTTPResponse(id, responseStatus, responseBody);
    }

    http_response(key id, integer status, list metaData, string body)
    {
        if (id == selfCheckRequestId)
        {
            // If you're not usually doing this,
            // now is a good time to get used to doing it!
            selfCheckRequestId = NULL_KEY;

            if (status != 200)
                request_url();
        }

        else if (id == NULL_KEY)
            throw_exception("Too many HTTP requests too fast!");
    }

    timer()
    {
        selfCheckRequestId = llHTTPRequest(url,
                                [HTTP_METHOD, "GET",
                                    HTTP_VERBOSE_THROTTLE, FALSE,
                                    HTTP_BODY_MAXLENGTH, 16384],
                                "");
    }
}

Notes

See Also

Functions

•  llRequestURL Request a new LSL Server public URL
•  llRequestSecureURL Request a new LSL Server public URL
•  llReleaseURL Release a URL
•  llHTTPResponse For replying to HTTP requests
•  llGetHTTPHeader Returns the requested HTTP header's value
•  llEscapeURL
•  llUnescapeURL

Deep Notes

Issues

All Issues

~ Search JIRA for related Issues
   Design: LSL http_server

Signature

event void http_request( key request_id, string method, string body );