mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
337 lines
9.9 KiB
JavaScript
337 lines
9.9 KiB
JavaScript
|
var Cc = Components.classes;
|
||
|
var Ci = Components.interfaces;
|
||
|
|
||
|
function InputStreamBuffer() {
|
||
|
}
|
||
|
InputStreamBuffer.prototype = {
|
||
|
_data: "",
|
||
|
append: function( stuff ) {
|
||
|
this._data = this._data + stuff;
|
||
|
},
|
||
|
clear: function() {
|
||
|
this._data = "";
|
||
|
},
|
||
|
getData: function() {
|
||
|
return this._data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function SocketClient( host, port ) {
|
||
|
this._init( host, port );
|
||
|
}
|
||
|
SocketClient.prototype = {
|
||
|
__threadManager: null,
|
||
|
get _threadManager() {
|
||
|
if (!this.__threadManager)
|
||
|
this.__threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
|
||
|
return this.__threadManager;
|
||
|
},
|
||
|
__transport: null,
|
||
|
get _transport() {
|
||
|
if (!this.__transport) {
|
||
|
var transportService = Cc["@mozilla.org/network/socket-transport-service;1"].getService(Ci.nsISocketTransportService);
|
||
|
this.__transport = transportService.createTransport(['starttls'],
|
||
|
1, // ssl only
|
||
|
this._host,
|
||
|
this._port,
|
||
|
null); // proxy
|
||
|
}
|
||
|
return this.__transport;
|
||
|
},
|
||
|
|
||
|
_init: function( host, port ) {
|
||
|
this._host = host;
|
||
|
this._port = port;
|
||
|
this._contentRead = "";
|
||
|
this._buffer = null;
|
||
|
this.connect();
|
||
|
},
|
||
|
|
||
|
connect: function() {
|
||
|
var outstream = this._transport.openOutputStream( 0, // flags
|
||
|
0, // buffer size
|
||
|
0 ); // number of buffers
|
||
|
this._outstream = outstream;
|
||
|
|
||
|
var buffer = new InputStreamBuffer;
|
||
|
this._buffer = buffer;
|
||
|
|
||
|
// Wrap input stream is C only, nonscriptable, so wrap it in scriptable
|
||
|
// stream:
|
||
|
var rawInputStream = this._transport.openInputStream( 0, 0, 0 );
|
||
|
var scriptablestream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream);
|
||
|
scriptablestream.init(rawInputStream);
|
||
|
|
||
|
// input stream pump for asynchronous reading
|
||
|
var pump = Cc["@mozilla.org/network/input-stream-pump;1"].createInstance(Ci.nsIInputStreamPump);
|
||
|
pump.init(rawInputStream, -1, -1, 0, 0,
|
||
|
false); //automatically close once all data read?
|
||
|
|
||
|
// create dataListener class for callback:
|
||
|
var dataListener = {
|
||
|
data : "",
|
||
|
onStartRequest: function(request, context){
|
||
|
},
|
||
|
onStopRequest: function(request, context, status){
|
||
|
rawInputStream.close();
|
||
|
outstream.close();
|
||
|
},
|
||
|
onDataAvailable: function(request, context, inputStream, offset, count){
|
||
|
// use scriptable stream wrapper, not "real" stream.
|
||
|
// count is number of bytes available, offset is position in stream.
|
||
|
// Do stuff with data here!
|
||
|
buffer.append( scriptablestream.read( count ));
|
||
|
}
|
||
|
};
|
||
|
// register it:
|
||
|
pump.asyncRead(dataListener, null); // second argument is a context
|
||
|
//which gets passed in as the context argument to methods of dataListener
|
||
|
|
||
|
//Should be done connecting now. TODO: Catch and report errors.
|
||
|
},
|
||
|
|
||
|
send: function( messageText ) {
|
||
|
this._outstream.write( messageText, messageText.length );
|
||
|
},
|
||
|
|
||
|
getBinaryOutStream: function() {
|
||
|
var binaryOutStream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream);
|
||
|
binaryOutStream.setOutputStream( this._outstream ); // is this right?
|
||
|
return binaryOutStream;
|
||
|
},
|
||
|
|
||
|
disconnect: function() {
|
||
|
var thread = this._threadManager.currentThread;
|
||
|
while( thread.hasPendingEvents() )
|
||
|
{
|
||
|
thread.processNextEvent( true );
|
||
|
}
|
||
|
},
|
||
|
|
||
|
checkResponse: function() {
|
||
|
return this._getData();
|
||
|
},
|
||
|
|
||
|
waitForResponse: function() {
|
||
|
var thread = this._threadManager.currentThread;
|
||
|
while( this._buffer.getData().length == 0 )
|
||
|
{
|
||
|
thread.processNextEvent( true );
|
||
|
}
|
||
|
var output = this._buffer.getData();
|
||
|
this._buffer.clear();
|
||
|
return output;
|
||
|
},
|
||
|
|
||
|
startTLS: function() {
|
||
|
this._transport.securityInfo.QueryInterface(Ci.nsISSLSocketControl);
|
||
|
this._transport.securityInfo.StartTLS();
|
||
|
},
|
||
|
|
||
|
// TODO have a variant of waitForResponse that gets binary data
|
||
|
// binaryInStream = Cc["@mozilla.org/binaryinputstream;1].createInstance( Ci.nsIBinaryInputStream );
|
||
|
// binaryInStream.setInputStream( this._rawInputStream );
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
/* The interface that should be implemented by any Transport object:
|
||
|
send( messageXml );
|
||
|
setCallbackObject( object with .onIncomingData and .onTransportError );
|
||
|
connect();
|
||
|
disconnect();
|
||
|
*/
|
||
|
|
||
|
function HTTPPollingTransport( serverUrl, useKeys, interval ) {
|
||
|
/* Send HTTP requests periodically to the server using a timer.
|
||
|
HTTP POST requests with content-type application/x-www-form-urlencoded.
|
||
|
responses from the server have content-type text/xml
|
||
|
request and response are UTF-8 encoded (ignore what HTTP header says)
|
||
|
identify session by always using set-cookie header with cookie named ID
|
||
|
first request sets this to 0 to indicate new session. */
|
||
|
|
||
|
this._init( serverUrl, useKeys, interval );
|
||
|
}
|
||
|
HTTPPollingTransport.prototype = {
|
||
|
_init: function( serverUrl, useKeys, interval ) {
|
||
|
this._serverUrl = serverUrl
|
||
|
this._n = 0;
|
||
|
this._key = this._makeSeed();
|
||
|
this._latestCookie = "";
|
||
|
this._connectionId = 0;
|
||
|
this._callbackObject = null;
|
||
|
this._useKeys = useKeys;
|
||
|
this._interval = interval;
|
||
|
},
|
||
|
|
||
|
__request: null,
|
||
|
get _request() {
|
||
|
if (!this.__request)
|
||
|
this.__request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance( Ci.nsIXMLHttpRequest );
|
||
|
return this.__request;
|
||
|
},
|
||
|
|
||
|
__hasher: null,
|
||
|
get _hasher() {
|
||
|
if (!this.__hasher)
|
||
|
this.__hasher = Cc["@mozilla.org/security/hash;1"].createInstance( Ci.nsICryptoHash );
|
||
|
return this.__hasher;
|
||
|
},
|
||
|
|
||
|
__timer: null,
|
||
|
get _timer() {
|
||
|
if (!this.__timer)
|
||
|
this.__timer = Cc["@mozilla.org/timer;1"].createInstance( Ci.nsITimer );
|
||
|
return this.__timer;
|
||
|
},
|
||
|
|
||
|
_makeSeed: function() {
|
||
|
return "foo";//"MyKeyOfHorrors";
|
||
|
},
|
||
|
|
||
|
_advanceKey: function() {
|
||
|
var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
|
||
|
|
||
|
// we use UTF-8 here, you can choose other encodings.
|
||
|
converter.charset = "UTF-8";
|
||
|
// result is an out parameter,
|
||
|
// result.value will contain the array length
|
||
|
var result = {};
|
||
|
// data is an array of bytes
|
||
|
var data = converter.convertToByteArray( this._key, result);
|
||
|
|
||
|
this._n += 1;
|
||
|
this._hasher.initWithString( "SHA1" );
|
||
|
this._hasher.update( data, data.length );
|
||
|
this._key = this._hasher.finish( true ); // true means B64encode
|
||
|
},
|
||
|
|
||
|
_setIdFromCookie: function( self, cookie ) {
|
||
|
// parse connection ID out of the cookie:
|
||
|
// dump( "Cookie is " + cookie + "\n" );
|
||
|
var cookieSegments = cookie.split( ";" );
|
||
|
cookieSegments = cookieSegments[0].split( "=" );
|
||
|
var newConnectionId = cookieSegments[1];
|
||
|
switch( newConnectionId) {
|
||
|
case "0:0":
|
||
|
self._onError( "Unknown error!\n" );
|
||
|
break;
|
||
|
case "-1:0":
|
||
|
self._onError( "Server error!\n" );
|
||
|
break;
|
||
|
case "-2:0":
|
||
|
self._onError( "Bad request!\n" );
|
||
|
break;
|
||
|
case "-3:0":
|
||
|
self._onError( "Key sequence error!\n" );
|
||
|
break;
|
||
|
default :
|
||
|
self._connectionId = cookieSegments[1];
|
||
|
// dump( "Connection ID set to " + self._connectionId + "\n" );
|
||
|
break;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_onError: function( errorText ) {
|
||
|
dump( "Transport error: " + errorText + "\n" );
|
||
|
if ( this._callbackObject != null ) {
|
||
|
this._callbackObject.onTransportError( errorText );
|
||
|
}
|
||
|
this.disconnect();
|
||
|
},
|
||
|
|
||
|
_doPost: function( requestXml ) {
|
||
|
var request = this._request;
|
||
|
var callbackObj = this._callbackObject;
|
||
|
var self = this;
|
||
|
var contents = "";
|
||
|
|
||
|
|
||
|
if ( this._useKey ) {
|
||
|
this._advanceKey();
|
||
|
contents = this._connectionId + ";" + this._key + "," + requestXml;
|
||
|
} else {
|
||
|
contents = this._connectionId + "," + requestXml;
|
||
|
/* TODO:
|
||
|
Currently I get a "-3:0" error (key sequence error) from the 2nd
|
||
|
exchange if using the keys is enabled. */
|
||
|
}
|
||
|
|
||
|
_processReqChange = function( ) {
|
||
|
//Callback for XMLHTTPRequest object state change messages
|
||
|
if ( request.readyState == 4 ) {
|
||
|
if ( request.status == 200) {
|
||
|
dump( "Server says: " + request.responseText + "\n" );
|
||
|
// Look for a set-cookie header:
|
||
|
var latestCookie = request.getResponseHeader( "Set-Cookie" );
|
||
|
if ( latestCookie.length > 0 ) {
|
||
|
self._setIdFromCookie( self, latestCookie );
|
||
|
}
|
||
|
if ( callbackObj != null && request.responseText.length > 0 ) {
|
||
|
callbackObj.onIncomingData( request.responseText );
|
||
|
}
|
||
|
} else {
|
||
|
dump ( "Error! Got HTTP status code " + request.status + "\n" );
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
request.open( "POST", this._serverUrl, true ); //async = true
|
||
|
request.setRequestHeader( "Content-type",
|
||
|
"application/x-www-form-urlencoded;charset=UTF-8" );
|
||
|
request.setRequestHeader( "Content-length", contents.length );
|
||
|
request.setRequestHeader( "Connection", "close" );
|
||
|
request.onreadystatechange = _processReqChange;
|
||
|
dump( "Sending: " + contents + "\n" );
|
||
|
request.send( contents );
|
||
|
},
|
||
|
|
||
|
send: function( messageXml ) {
|
||
|
this._doPost( messageXml );
|
||
|
},
|
||
|
|
||
|
setCallbackObject: function( callbackObject ) {
|
||
|
this._callbackObject = callbackObject;
|
||
|
},
|
||
|
|
||
|
notify: function( timer ) {
|
||
|
/* having a notify method makes this object satisfy the nsITimerCallback
|
||
|
interface, so the object can be passed to timer.initWithCallback. */
|
||
|
|
||
|
/* Periodically, if we don't have anything else to post, we should
|
||
|
post an empty message just to see if the server has any queued
|
||
|
data it's waiting to give us in return. */
|
||
|
this._doPost( "" );
|
||
|
},
|
||
|
|
||
|
connect: function() {
|
||
|
/* Set up a timer to poll the server periodically. */
|
||
|
|
||
|
// TODO doPost isn't reentrant; don't try to doPost if there's
|
||
|
//already a post in progress... or can that never happen?
|
||
|
|
||
|
this._timer.initWithCallback( this,
|
||
|
this._interval,
|
||
|
this._timer.TYPE_REPEATING_SLACK );
|
||
|
},
|
||
|
|
||
|
disconnect: function () {
|
||
|
this._timer.cancel();
|
||
|
},
|
||
|
|
||
|
testKeys: function () {
|
||
|
|
||
|
this._key = "foo";
|
||
|
dump( this._key + "\n" );
|
||
|
for ( var x = 1; x < 7; x++ ) {
|
||
|
this._advanceKey();
|
||
|
dump( this._key + "\n" );
|
||
|
}
|
||
|
},
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
//transport = new HTTPPollingTransport( "http://127.0.0.1:5280/http-poll" );
|
||
|
//transport.testKeys();
|