Context not passed in when makeWebRequest encounters system-level errors

When using the new `context` option to chain requests, the callback is expected to accept a new `context` argument. For application-level errors, like a 404, the `context` gets passed in correctly. However, for OS-level errors (with negative error codes), the `context` doesn't get passed in, causing a `Not Enough Arguments Error` to be raised.

The following PoC below will trigger it.

The basic idea is to generate a system-level error by disabling BLE and Wifi in the simulator and making a web-request. Our callback will be called with the wrong number of arguments, generating the error.

This is observed in the beta 1 version of ConnectIQ 3.0.

using Toybox.Application;

using Toybox.Communications;




class ContextBugApp extends Application.AppBase {




function initialize() {

AppBase.initialize();

}




// Trigger bug by setting the following in simulator:

// Wifi: Not Connected

// BLE: Not Connected

//

// Expected Result: responseCode = -104 with `context` passed in

// Actual Result: `Not Enough Arguments Error`

function onResponse(responseCode, data, context) {

System.println("onResponse: " + { "responseCode" => responseCode,

"data" => data,

"context" => context });

}




function onStart(state) {

var url = "jsonplaceholder.typicode.com/.../1";

var parameters = {};

var options = {

:method => Communications.HTTP_REQUEST_METHOD_GET,

:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON,

:context => { :foo => "bar" }

};

var responseCallback = method(:onResponse);

Communications.makeWebRequest(url, parameters, options, responseCallback);

}




function onStop(state) {

}




function getInitialView() {

return [ new ContextBugView() ];

}




}


Side-note: Changing the arity of the callback depending on whether a `context` is used seems like a really brittle design due to these kinds of bugs. Seems like it'd be better for an `onResponse` callback to have a fixed specification, something like:

// Unlike canonical spec, `context` is *always* passed in, it's just null if a context wasn't used.
function onResponse(responseCode, data, context) {
if (context == null) {
// Do something
} else {
// Do something else
}
}
  • This looks like a bug. Apparently this bit was added for some features that we are working on, but it is no longer necessary and is likely to get removed. You should be able to achieve the same result but without the bug by using a web request delegate...

    class WebRequestDelegate
    {
        hidden var mCallback; // function always takes 3 arguments
        hidden var mContext; // this is the 3rd argument
    
        function initialize(context) {
            mContext = context;
        }
    
        function makeWebRequest(url, params, options, callback) {
            mCallback = callback;
            Communications.makeWebRequest(url, params, options, self.method(:onWebResponse));
        }
    
        function onWebResponse(code, data) {
            mCallback.invoke(code, data, mContext);
        }
    }
    
    
    class MyDelegate extends Ui.BehaviorDelegate
    {
        function onSelect() {
            var delegate = new WebRequestDelegate(999);
    
            var url = "jsonplaceholder.typicode.com/.../1";
    
            var params = {
            };
    
            var options = {
                :method => Communications.HTTP_REQUEST_METHOD_GET,
                :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
            };
    
            delegate.makeWebRequest(url, params, options, self.method(:onWebResponse));
        }
    
        function onWebResponse(code, data, context) {
            System.println(context);
        }
    }
    


    Travis

  • Two years have passed, but the bug has not been fixed.

  • I'm not sure why the documentation hasn't been updated, but as I said two years ago don't use the context parameter. The above does everything you need.

  • What if we want to make two webrequests one followed by the other, with different contexts? If we set context for the first one and make the request but then set the context for the second (before the first request finished) the context is going to be wrong

  • probably you have to use twice x = new WebRequestDelegate(...)

    but I've did it differently:

    function onReceive(ResponseCode, d, context)

    {

    }

    function onReceive_1    (ResponseCode, d){onReceive(ResponseCode, d, 1);}
    function onReceive_2    (ResponseCode, d){onReceive(ResponseCode, d, 2);}

    function makeReqs()

    {

    COM.makeWebRequest(..., method(:onReceive_1));

    COM.makeWebRequest(..., method(:onReceive_2));

    }

  • sure we can make two function to receive the information, but I think context would be a better solution, I don't know why they dont implement this fix

  • Generally, when you do makeWebRequests, you want to do them one at a time.  Do the first request, and in the callback for that, do the second request.  I've not tried it in a while, but is used to be Android would only handle one at a time.  iOS would handle 3 at a time.

  • Not true :)

    I do 4 reqs in makeReqs and without any problem receive data (android).

  • yes of course context is batter (and just was created for such thing) but unfortunately there is the bug.

    but maybe it is fixed - I haven't tested it from months.

  • The thing is, my callback invokes a method on my view class, and there is where the webRequest truly ends. I don't think adding a new callback on that file would be good practice, when I have a file and a class to handle all the communications. Also this two request are independent from each other so I would rather let them run and whichever ends first ends first. All I want is for the method to tell the difference from each request, so having something attached to the request itself but for internal use makes complete sense to me.

    And since the implementation already exists, we are not asking for new functionality, they should just fix this simple problem: when there is a context and the OS invokes the callback because there was an error ( for lack of connection) an error is thrown because this invocation only has 2 arguments it does not accomodate for the context, and thus the IQ! symbol appears.

    Sorry for any mistakes english is not my mother tongue.