HTTPRequest-enabled RS

138 views
Skip to first unread message

Adam Machanic

unread,
Jun 18, 2002, 12:38:51 PM6/18/02
to
I have modified the code posted a week or two ago by GHEZ Alexandre.

It now seems to fully support asynchronous connections, and error handling
has been fixed up (MSRS_FAIL wasn't returning properly in certain
circumstances).

I have removed the code that would check browser version and use the old
MSRS instead, as it was causing some problems on my end and I don't need it
and therefore didn't want to debug it.

The only thing that I know isn't working is the cancelRequest method. That
should be easy enough to code up, but since I've never used it I don't
really want to do it ;)

Anyway, if anyone wants to check this out, test it some more, and continue
with the project, that would be nice... I've tested it on some fairly
complex operations and it seems to be working perfectly for my needs... And
maybe it's my imagination, but I swear that results are returning faster
than with the Java applet.

/******************************************************************
* Microsoft Scripting Libary
* Remote Scripting utilities for client.
*
* The Remote Scripting utilities for the client consist of
* three public methods and the RSCallObject definition.
* The public methods are RSEnableRemoteScripting, RSExecute
* and RSGetASPObject. The RSCallObject is returned from any
* remote scripting call and provides status and return value.
*
* @author Microsoft
* @version
* @copyright 1998 Microsoft Corporation. All Rights Reserved.
*****************************************************************/

/******************************************************************
*
* This version has been altered to use the HTTPRequest object instead of
the rsapplet
*
*
******************************************************************/

function RSEnableRemoteScripting(blah) {
RS = new RS_Object();
}


/**
* This is the function by which remote scripting calls are made.
* @param url url to the asp file containing remote script
* @param method name of the method to be invoked
*/
function RSExecute(url, method) {
var cb, ecb, context;
var params = new Array;
var pn = 0;
var len = RSExecute.arguments.length;
for (var i=2; i < len; i++) {
params[pn++] = RSExecute.arguments[i];
} return RS.invokeMethod(url, method, params);
}

/**
* This function returns a server object for an ASP file
* described by its public_description.
* @param url proxy's url
* @return server information, or null if there is problem
*/
function RSGetASPObject(url) {
var cb, ecb, context;
var params = new Array;
var request = MSRS.startRequest(url, 'GetServerProxy', params, cb, ecb,
context);
//alert(request.data); // USED FOR DEBUG
if (request.status == MSRS_COMPLETED) {
var server = request.return_value;
if (typeof(Function) == 'function') {
for (var name in server) {
server[name] = Function('return MSRS.invokeMethod(this.location,"' +
name + '",this.' + name + '.arguments);');
}
}
else {
// JavaScript 1.0 does not support Function ( IE3.0 )
for (var name in server) {
server[name] = eval('function
t(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF) { return
MSRS.invokeMethod(this.location,"' + name + '",this.' + name +
'.arguments);} t');
}
}
server.location = url;
return server;
}
alert('Failed to create ASP object for : ' + url);
return null;
}

/**
* The RSCallObject is returned for every remote scripting
* invocation. It contains the return value and status.
* @param cb an optional callback routine for async.
* @param ecb an optional error callback routine for async.
* @param context an optional user context
*/
function RSCallObject(cb, ecb, context) {
/* unique id of request */
this.id = RS.nextRequestID++;

/*
* status of request, one of
* MSRS_COMPLETED
* MSRS_FAIL
* MSRS_PENDING
* MSRS_PARTIAL
*/
this.status = MSRS_PENDING;

/* message associated with status */
this.message = '';

/* raw data returned from server */
this.data = '';

/* evaluated value returned from server */
this.return_value = '';

/* user provided callback ( optional ) */
this.callback = cb;

/* user provided callback ( optional ) */
this.error_callback = ecb;

/* user provided context ( optional ) */
this.context = context;

this.wait = RSCallObject_wait;
this.cancel = RSCallObject_cancel;

RS.requestList[this.id] = this;
}

/**
* The RSCallObject_wait method can be called from an asynchronous
* request to wait for it to complete. If the request has finished
* this call returns immediately.
* this = RSCallObject instance to wait for
*/
function RSCallObject_wait(requestid) {
if (this.status != MSRS_PENDING) {
return;
}
while (true) {
// wait synchronously for response
if (RS.waitForResponse(requestid)) {
if (RS.hasResponse(requestid)) {
RS.handleResponse(requestid);
// DO NOT CHANGE THIS CODE
// Solves Java to Script discrepancies between IE3 and Navigator
var strrid = String(requestid);
if (strrid == null) {
strrid = requestid;
}
if (strrid == this.id) {
// this response completed
break;
}
} else {
this.status = MSRS_FAIL;
this.message = 'Request not handled.'
break;
}
}
else {
this.status = MSRS_FAIL;
this.message = 'Request not handled.'
break;
}
}
}

/**
* The RSCallObject_cancel method can be called from an
* asynchronous request to cancel it. If the request has
* finished this call returns immediately.
* this = RSCallObject instance to cancel
*/
function RSCallObject_cancel() {
if (this.status == MSRS_PENDING) {
MSRS.cancelRequest(this.id);
this.status = MSRS_FAIL;
this.message = 'Request cancelled.'
}
}


/************* IMPLEMENTATION SANS APPLET JAVA **********************
*******************************************************************
* Remote Scripting Object -- private implementation
*
* The following code is private glue code that contains the
* implementation of a JSObject to enable remote scripting
* functionality on the client. This private object is utilized
* by the public Remote Scripting methods defined above.
*
*****************************************************************
*****************************************************************/

//*****************************************************************
// function RS_Object
//
// This is the JSObject that interacts with the RSAspProxy
// applet to synchronously/asynchronously retrieve data via
// an ASP file.
//*****************************************************************

function RS_Object() {
MSRS_FAIL = -1;
MSRS_COMPLETED = "OK";
MSRS_PENDING = 1;
MSRS_PARTIAL = 2;

this.REQUEST_MODE_COMPLETE = 0;
this.POLLING_PERIOD = 100;

this.pollID = 0;
this.pollCount = 0;
this.nextRequestID = 1;
this.requestList = new Array;

///AJOUT, tableau qui va contenir les objets utilisés
this.connectionList = new Array;

this.startRequest = RS_startRequest;
this.invokeMethod = RS_invokeMethod;
this.handleResponse = RS_handleResponse;
this.evaluateRequest = RS_evaluateRequest;
this.setRequestPoll = RS_setRequestPoll;
this.requestPollHandler = RS_requestPollHandler;
this.buildURL = RS_buildURL;

this.waitForResponse = RS_waitForResponse;
this.hasResponse = RS_hasResponse;
this.cancelRequest = RS_cancelRequest;
}

//*****************************************************************
// function RS_startRequest(url,method,args,cb,ecb,context)
//
// This is key function for initiating a request for data.
// The url to the ASP file is required. The callback,
// error_callback, and user context parameters are optional.
//*****************************************************************
function RS_startRequest(url,method,args,cb,ecb,context) {
var request = new RSCallObject(cb,ecb,context);
var boolResult

if (request.status != MSRS_FAIL) {
url = this.buildURL(url,method,args);
url_context = window.location.href; // May not be
'window.location.pathname'


this.connectionList[request.id] = new
ActiveXObject("Microsoft.XMLHTTP");

if (typeof(cb) == 'function') {
this.connectionList[request.id].open("GET",url, true);
} else {
this.connectionList[request.id].open("GET",url, false);
}

this.connectionList[request.id].send();


//this.rsapplet.startRequest(request.id,url_context,url,this.REQUEST_MODE_CO
MPLETE);


if (typeof(cb) == 'function') {
if (this.pollCount++ == 0) {
this.setRequestPoll(request.id);
}
}
else {
// wait synchronously for response
request.wait(request.id);
}
} return request;
}


//*****************************************************************
// function RS_invokeMethod(url,method,args)
// This is the function by which remote scripting calls are
// via a server object retrieved with the GetASPObject call.
// The caller provides the following :
// url : url to the asp
// method : name of the method to be invoked
// args : an array containing method parameters
// and the optional cb, ecb, context parameters
//*****************************************************************
function RS_invokeMethod(url,method,args) {
var cb, ecb, context;
var params = new Array;
var pn = 0;
var i = 0;
for (var i=0; i < args.length; i++) {
if (typeof(args[i]) == 'function') {
pn = -1; // no more params
if (typeof(cb) == 'undefined') {
cb = args[i];
}
else {
ecb = args[i];
}
}
else if (pn != -1) {
params[pn++] = args[i];
}
else {
context = args[i];
}
}

return RS.startRequest(url,method,params,cb,ecb,context);
}


//*****************************************************************
// function RS_handleResponse(requestid)
//
// This function will handle the response for a given request.
// If the response is complete or failed, then the associated
// request object will be updated and the appropriate callback
// invoked, if provided.
// NOTE: incremental data retrieval is not yet supported
//*****************************************************************
function RS_handleResponse(requestid) {


var request = this.requestList[requestid];

if (typeof(request) == 'undefined') {
alert('Unknown request id.');
return;
}


var lo_Connexion = this.connectionList[requestid];


request.status = lo_Connexion.statusText;

if (request.status == MSRS_COMPLETED) {
request.data = lo_Connexion.responseText;
request.message = "Completed";
//request.message = this.rsapplet.getMessage();
this.evaluateRequest(request);
if (request.status != MSRS_COMPLETED) {
if (typeof(request.error_callback) == 'function') {
this.pollCount--;
request.error_callback(request);
}
else {
alert('Remote Scripting Error\n' + request.message);
}
}
else {
if (typeof(request.callback) == 'function') {
this.pollCount--;
request.callback(request);
}
}

this.connectionList[requestid].abort();
this.connectionList[requestid] = null;
this.requestList[request.id] = null;
}
else if (request.status != MSRS_COMPLETED) {
request.message = "";
//request.message = this.rsapplet.getMessage();
if (typeof(request.error_callback) == 'function') {
this.pollCount--;
request.error_callback(request);
}

this.connectionList[requestid].abort();
this.connectionList[requestid] = null;
this.requestList[request.id] = null;
}
//else if (request.status == MSRS_PARTIAL) {} // not handling partial data
retrieval yet
//else if (request.status == MSRS_PENDING) {} // do nothing
}


//*****************************************************************
// function RS_evaluateRequest(request)
//
// This function evaluates the data returned to the request.
// Marshalled jscript objects are re-evaluated on the client.
//*****************************************************************
function RS_evaluateRequest(request) {
var data = request.data;
var start_index = 0;
var end_index = 0;
var start_key = '<' + 'RETURN_VALUE';
var end_key = '<' + '/RETURN_VALUE>';

if ((start_index = data.indexOf(start_key)) != -1) {
var data_start_index = data.indexOf('>',start_index) + 1;
end_index = data.indexOf(end_key,data_start_index);
if (end_index == -1) {
end_index = data.length;
}
var metatag = data.substring(start_index,data_start_index);
if (metatag.indexOf('TYPE=SIMPLE') != -1) {
request.return_value =
unescape(data.substring(data_start_index,end_index));
//alert('TYPE=SIMPLE:\n' + data); // USED FOR DEBUG
}
else if (metatag.indexOf('TYPE=EVAL_OBJECT') != -1) {
request.return_value = data.substring(data_start_index,end_index);
//alert('TYPE=EVAL_OBJECT:\n' + data); // USED FOR DEBUG
request.return_value = eval(unescape(request.return_value));
}
else if (metatag.indexOf('TYPE=ERROR') != -1) {
request.status = MSRS_FAIL;
request.message = unescape(data.substring(data_start_index,end_index));
}
}
else {
request.status = MSRS_FAIL;
request.message = 'REMOTE SCRIPTING ERROR: Page invoked does not support
remote scripting.';
}
}


//*****************************************************************
// function RS_setRequestPoll()
//
// Due to limitations in calling back into JScript from a worker
// thread of an applet, a polling mechanism is used to determine
// when a request has been completed. This function sets up
// a timer to kick the requestPollHandler at a later time.
//*****************************************************************
function RS_setRequestPoll(requestid) {
this.pollID =
window.setTimeout('RS.requestPollHandler('+requestid+')',this.POLLING_PERIOD
,'javascript');
}

//*****************************************************************
// function RS_requestPollHandler()
//
// Due to limitations in calling back into JScript from a worker
// thread of an applet, a polling mechanism is used to determine
// when a request has been completed. This function is the
// handler which is kicked by a timer. It polls the applet to
// see if a response to a prior request is available.
//*****************************************************************
function RS_requestPollHandler(requestid) {
var connection = this.connectionList[requestid]
if (connection.readyState == 4) {
this.handleResponse(requestid);
} else {
this.setRequestPoll(requestid);
}
}


//*****************************************************************
// function RS_buildURL(url,method,args)
//
// This builds the proper url entry point into the ASP page
// such that the intended server method with parameters gets
// invoked.
//*****************************************************************
function RS_buildURL(url,method,args) {
if (url == '') {
url = window.location.pathname;
}
if (typeof(method) == 'string') {
url += '?_method=' + method;
url += '&_mtype=execute';
var params = '&pcount=0';
if (typeof(args) != 'undefined' && args.length) {
// add parameters
params = '&pcount=' + args.length
for (var i = 0; i < args.length; i++) {
var arg = args[i];
params += '&p' + i + '=' + escape(arg);
}
} url += params;
} return url;
}


function RS_waitForResponse(requestid)
{
var connection = this.connectionList[requestid]
if (connection.readyState == 4) {
return(true)
} else {
return(false)
}
}

function RS_hasResponse(requestid)
{
var connection = this.connectionList[requestid]
if (connection.status == 500) {
return(false)
} else {
return(true)
}
}

function RS_cancelRequest(li_Id)
{
//MUST BE IMMPLEMENTED WITH THE METHOD ABORT
return(0);
}


Adam Machanic

unread,
Jun 19, 2002, 9:38:22 AM6/19/02
to
I enabled the cancelRequest method and fixed three bugs:

1) The asynchronous pool wasn't working properly -- only one query would go
through if more than one was instantiated at once.

2) Status, when successful, was returning as 'OK' instead of 0.

3) There were some misnamed object references in the RSGetASPObject method
(I haven't been able to test that method).


Here's the updated code:

var request = RS.startRequest(url, 'GetServerProxy', params, cb, ecb,


context);
//alert(request.data); // USED FOR DEBUG
if (request.status == MSRS_COMPLETED) {
var server = request.return_value;
if (typeof(Function) == 'function') {
for (var name in server) {

server[name] = Function('return RS.invokeMethod(this.location,"' + name


+ '",this.' + name + '.arguments);');
}
}
else {
// JavaScript 1.0 does not support Function ( IE3.0 )
for (var name in server) {
server[name] = eval('function
t(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF) { return

RS.requestList[this.id] = this;
}

this.status = 0


var strrid = String(requestid);
if (strrid == null) {
strrid = requestid;
}
if (strrid == this.id) {
// this response completed
break;
}
} else {
this.status = MSRS_FAIL;
this.message = 'Request not handled.'
break;
}
}
else {
this.status = MSRS_FAIL;
this.message = 'Request not handled.'
break;
}
}
}

/**
* The RSCallObject_cancel method can be called from an
* asynchronous request to cancel it. If the request has
* finished this call returns immediately.
* this = RSCallObject instance to cancel
*/
function RSCallObject_cancel() {
if (this.status == MSRS_PENDING) {

this.connectionList[request.id].send();

return RS.startRequest(url,method,params,cb,ecb,context);
}


request.status = lo_Connexion.statusText;

function RS_cancelRequest(requestid)


{
var connection = this.connectionList[requestid]

connection.abort()
return(0);
}


r h

unread,
Jul 19, 2002, 7:54:29 AM7/19/02
to
adam, the pieces seemed to be running a lot faster. it
did not work with netscape 4.7x (my lowest common
denominator). it did not understand the createActiveXObjext.


"Adam Machanic" <amac...@protegent.com> wrote in message news:<ONoM9X5FCHA.2240@tkmsftngp05>...

Reply all
Reply to author
Forward
0 new messages