In a Thunderbird extension I use the following code to wait for an incoming TCP connection:
function MyExtension_OnNewConnection(srvSock, newSock)
{
var block = Components.interfaces.nsITransport.OPEN_BLOCKING |
Components.interfaces.nsITransport.OPEN_UNBUFFERED;
var istream = newSock.openInputStream(block, 0, 0);
var ostream = newSock.openOutputStream(block, 0, 0);
var cstream = Components.
classes["#mozilla.org/scriptableinputstream;1"].
createInstance(Components.interfaces.nsIScriptableInputStream);
cstream.init(istream);
var y = cstream.read(1);
cstream.close();
if(y == "")
{
var y = "The socket is Tx-Only!\r\n";
ostream.write(y, y.length);
}
istream.close();
ostream.close();
}
...
waitSocket = Components.classes["#mozilla.org/network/server-socket;1"].
createInstance(Components.interfaces.nsIServerSocket);
waitSocket.init(-1, false, -1);
waitSocket.asyncListen({
onSocketAccepted: MyExtension_OnNewConnection,
onStopListening: function(socket, status) {}
});
According to the documentation cstream.read() can only return an empty string when the TCP socket is closed.
The ostream.write() function will send some data which is only possible if the TCP socket is not closed, yet.
However cstream.read() returns an empty string, ostream.write() sends some data and the other end of the TCP connection receives the string!
This means that the TCP connection is monodirectional (from the beginning!).
Questions:
Why?
How can I make the TCP conenction bidirectional so I can receive data?
What I already tried:
not to use OPEN_BLOCKING nor OPEN_UNBUFFERED flags => No effect.
checking istream.available() in a loop instead of using cstream => Will always return 0 even if data has been sent by the other end of the TCP connection.
I analyzed the source code of Thunderbird:
Obviously openInputStream uses a quite "simple" implementation if OPEN_UNBUFFERED is set and OPEN_BLOCKING is not set while a rather complex implementation is used for the three other combinations.
The simple implementation works perfectly however blocking socket access is not possible then.
Another page says that Javascript functions should be written in a non-blocking way so I decided to use the following implementation:
function CalledOnTimer()
{
try
{
while(true)
{
data = cstream.read(NumberOfBytes);
if(data == "")
{
/* Socket has been closed */
cstream.close();
istream.close();
ostream.close();
...
return;
}
/* Process the data */
...
}
}
catch(dummy)
{
/* Currently no (more) data on the socket */
restartTheTimer();
}
}
...
var istream = newSock.openInputStream(
Components.interfaces.nsITransport.OPEN_UNBUFFERED, 0, 0);
...
cstream.init(istream);
...
startTheTimer();
Related
I am unable to use cancellation tokens to stop a TCP Listener. The first code extract is an example where I can successfully stop a test while loop in a method from another class. So I don't understand why I cant apply this similar logic to the TCP Listener Class. Spent many days reading convoluted answers on this topic and cannot find a suitable solution.
My software application requires that the TCP Listener must give the user the ability to stop it from the server end, not the client. If a user wants to re-configure the port number for this listener then they would currently have to shutdown the software in order for Windows to close the underlying socket, this is no good as would affect the other services running in my app.
This first extract of code is just an example where I am able to stop a while loop from running, this works OK but is not that relevant other than the faat I would expect this to work for my TCP Listener:
public void Cancel(CancellationToken cancelToken) // EXAMPLE WHICH IS WORKING
{
Task.Run(async () =>
{
while (!cancelToken.IsCancellationRequested)
{
await Task.Delay(500);
log.Info("Test Message!");
}
}, cancelToken);
}
Now below is the actual TCP Listener code I am struggling with
public void TcpServerIN(string inboundEncodingType, string inboundIpAddress, string inboundLocalPortNumber, CancellationToken cancelToken)
{
TcpListener listener = null;
Task.Run(() =>
{
while (!cancelToken.IsCancellationRequested)
{
try
{
IPAddress localAddr = IPAddress.Parse(inboundIpAddress);
int port = int.Parse(inboundLocalPortNumber);
listener = new TcpListener(localAddr, port);
// Start listening for client requests.
listener.Start();
log.Info("TcpListenerIN listener started");
// Buffer for reading data
Byte[] bytes = new Byte[1024];
String data = null;
// Enter the listening loop.
while (true)
{
// Perform a blocking call to accept client requests.
TcpClient client = listener.AcceptTcpClient();
// Once each client has connected, start a new task with included parameters.
var task = Task.Run(() =>
{
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
data = null;
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Select Encoding format set by string inboundEncodingType parameter.
if (inboundEncodingType == "UTF8") { data = Encoding.UTF8.GetString(bytes, 0, i); }
if (inboundEncodingType == "ASCII") { data = Encoding.ASCII.GetString(bytes, 0, i); }
// Use this if you want to echo each message directly back to TCP Client
//stream.Write(msg, 0, msg.Length);
// If any TCP Clients are connected then pass the appended string through
// the rules engine for processing, if not don't send.
if ((listConnectedClients != null) && (listConnectedClients.Any()))
{
// Pass the appended message string through the SSSCRulesEngine
SendMessageToAllClients(data);
}
}
// When the remote client disconnetcs, close/release the socket on the TCP Server.
client.Close();
});
}
}
catch (SocketException ex)
{
log.Error(ex);
}
finally
{
// If statement is required to prevent an en exception thrown caused by the user
// entering an invalid IP Address or Port number.
if (listener != null)
{
// Stop listening for new clients.
listener.Stop();
}
}
}
MessageBox.Show("CancellationRequested");
log.Info("TCP Server IN CancellationRequested");
}, cancelToken);
}
Interesting to see that no one had come back with any solutions, admittedly it took me a long while to figure out a solution. The key to stopping the TCP Listener when using a synchronous blocking mode like the example below is to register the Cancellation Token with the TCP Listener itself, as well the TCP Client that may have already been connected at the time the Cancellation Token was fired. (see comments that are marked as IMPORTANT)
The example code may differ slightly in your own environment and I have extracted some code bloat that is unique to my project, but you'll get the idea in what we're doing here. In my project this TCP Server is started as a background service using NET Core 5.0 IHosted Services. My code below was adapted from the notes on MS Docs: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcplistener?view=net-5.0
The main difference between the MS Docs and my example below is I wanted to allow multiple TCP Clients to connect hence the reason why I start up a new inner Task each time a new TCP Client connects.
/// <summary>
/// </summary>
/// <param name="server"></param>
/// <param name="port"></param>
/// <param name="logger"></param>
/// <param name="cancelToken"></param>
public void TcpServerRun(
int pluginId,
string pluginName,
string encoding,
int bufferForReadingData,
string ipAddress,
int port,
bool logEvents,
IServiceScopeFactory _scopeFactory,
CancellationToken cancelToken)
{
IPAddress localAddrIN = IPAddress.Parse(ipAddress);
TcpListener listener = new TcpListener(localAddrIN, port);
Task.Run(() =>
{
// Dispose the DbContext instance when the task has completed. 'using' = dispose when finished...
using var scope = _scopeFactory.CreateScope();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<TcpServer>>();
try
{
listener.Start();
cancelToken.Register(listener.Stop); // THIS IS IMPORTANT!
string logData = "TCP Server with name [" + pluginName + "] started Succesfully";
// Custom Logger - you would use your own logging method here...
WriteLogEvent("Information", "TCP Servers", "Started", pluginName, logData, null, _scopeFactory);
while (!cancelToken.IsCancellationRequested)
{
TcpClient client = listener.AcceptTcpClient();
logData = "A TCP Client with IP Address [" + client.Client.RemoteEndPoint.ToString() + "] connected to the TCP Server with name: [" + pluginName + "]";
// Custom Logger - you would use your own logging method here...
WriteLogEvent("Information", "TCP Servers", "Connected", pluginName, logData, null, _scopeFactory);
// Once each client has connected, start a new task with included parameters.
var task = Task.Run(async () =>
{
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
// Buffer for reading data
Byte[] bytes = new Byte[bufferForReadingData]; // Bytes variable
String data = null;
int i;
cancelToken.Register(client.Close); // THIS IS IMPORTANT!
// Checks CanRead to verify that the NetworkStream is readable.
if (stream.CanRead)
{
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0 & !cancelToken.IsCancellationRequested)
{
data = Encoding.ASCII.GetString(bytes, 0, i);
logData = "TCP Server with name [" + pluginName + "] received data [" + data + "] from a TCP Client with IP Address [" + client.Client.RemoteEndPoint.ToString() + "]";
// Custom Logger - you would use your own logging method here...
WriteLogEvent("Information", "TCP Servers", "Receive", pluginName, logData, null, _scopeFactory);
}
// Shutdown and end connection
client.Close();
logData = "A TCP Client disconnected from the TCP Server with name: [" + pluginName + "]";
// Custom Logger - you would use your own logging method here...
WriteLogEvent("Information", "TCP Servers", "Disconnected", pluginName, logData, null, _scopeFactory);
}
}, cancelToken);
}
}
catch (SocketException ex)
{
// When the cancellation token is called, we will always encounter
// a socket exception for the listener.AcceptTcpClient(); blocking
// call in the while loop thread. We want to catch this particular exception
// and mark the exception as an accepted event without logging it as an error.
// A cancellation token is passed usually when the running thread is manually stopped
// by the user from the UI, or will occur when the IHosted service Stop Method
// is called during a system shutdown.
// For all other unexpected socket exceptions we provide en error log underneath
// in the else statement block.
if (ex.SocketErrorCode == SocketError.Interrupted)
{
string logData = "TCP Server with name [" + pluginName + "] was stopped due to a CancellationTokenSource cancellation. This event is triggered when the SMTP Server is manually stopped from the UI by the user or during a system shutdown.";
WriteLogEvent("Information", "TCP Servers", "Stopped", pluginName, logData, null, _scopeFactory);
}
else
{
string logData = "TCP Server with name [" + pluginName + "] encountered a socket exception error and exited the running thread.";
WriteLogEvent("Error", "TCP Servers", "Socket Exception", pluginName, logData, ex, _scopeFactory);
}
}
finally
{
// Call the Stop method to close the TcpListener.
// Closing the listener does not close any exisiting connections,
// simply stops listening for new connections, you are responsible
// closing the existing connections which we achieve by registering
// the cancel token with the listener.
listener.Stop();
}
});
}
I use ConnectivityPlugin (CrossConnectivity.Current.IsConnected field) to check the Internet connection. The problem is that it only checks if the internet connection button is pressed. That is, if I am connected to the mobile network, but there is no Internet itself (for example, problems from the operator), then the CrossConnectivity.Current.IsConnected field for such a situation returns true (although there is no connection). Question: how to check whether there is access to the Internet? Thanks
You can use the Device.StartTimer(TimeSpan, Func) method.
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
//insert checking of internet connection
}
Assuming "google.com" is always up and running:
if (CrossConnectivity.Current.IsConnected)
{
try {
Ping ping = new Ping();
String host = "google.com";
byte[] buffer = new byte[32];
int timeout = 1000;
PingOptions pingOptions = new PingOptions();
PingReply reply = ping.Send(host, timeout, buffer, pingOptions);
if (reply.Status == IPStatus.Success){
// Your code here...
}
}
catch (Exception) {
return false;
}
}
I recommend to add the following code to your App.xaml.cs class OnStart method and that take care of it.
CrossConnectivity.Current.ConnectivityChanged += (sender, e) =>
{
if (!CrossConnectivity.Current.IsConnected)
{
Console.WriteLine("Internet connectivity lost");
}
};
I'm trying to use Rust-Websocket to create a simple chatroom where multiple people can talk to each other.
I looked at the examples and the 'server.rs' and 'websockets.html' looked like a decent starting point to me. So I just tried starting it up and connecting from web. Everything works but I can only communicate with myself and not with other connections (since it sends the message back directly to sender and not to every connection).
So I'm trying to get a vector with all senders/clients so I can just iterate through them and send the message to each one but this seems to be problematic. I cannot communicate the sender or client since It's not thread safe and I cannot copy any of these either.
I'm not sure if I just don't understand the whole borrowing 100% or if it's not intended to do cross-connection communication like this.
server.rs:
https://github.com/cyderize/rust-websocket/blob/master/examples/server.rs
websockets.html:
https://github.com/cyderize/rust-websocket/blob/master/examples/websockets.html
I might be approaching this from the wrong direction. It might be easier to share a received message with all other threads. I thought about this a little bit but the only thing I can think of is sending a message from inside a thread to outside using channels. Is there any way to broadcast messages directly between the threads? All I would need to do is send a string from one thread to the other.
So this is not quite as straight-forward as one might think.
Basically I used a dispatcher thread that would act like a control center for all the connected clients. So whenever a client receives a message it's sent to the dispatcher and this then distributes the message to every connected client.
I also had to receive the messages in another thread because there is no non-blocking way to receive messages in rust-websocket. Then I ways able to just use a permanent loop that checks both for new messages received from the websocket and from the dispatcher.
Here's how my code looked like in the end:
extern crate websocket;
use std::str;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::thread;
use websocket::{Server, Message, Sender, Receiver};
use websocket::header::WebSocketProtocol;
use websocket::message::Type;
fn main() {
let server = Server::bind("0.0.0.0:2794").unwrap();
let (dispatcher_tx, dispatcher_rx) = mpsc::channel::<String>();
let client_senders: Arc<Mutex<Vec<mpsc::Sender<String>>>> = Arc::new(Mutex::new(vec![]));
// dispatcher thread
{
let client_senders = client_senders.clone();
thread::spawn(move || {
while let Ok(msg) = dispatcher_rx.recv() {
for sender in client_senders.lock().unwrap().iter() {
sender.send(msg.clone()).unwrap();
}
}
});
}
// client threads
for connection in server {
let dispatcher = dispatcher_tx.clone();
let (client_tx, client_rx) = mpsc::channel();
client_senders.lock().unwrap().push(client_tx);
// Spawn a new thread for each connection.
thread::spawn(move || {
let request = connection.unwrap().read_request().unwrap(); // Get the request
let headers = request.headers.clone(); // Keep the headers so we can check them
request.validate().unwrap(); // Validate the request
let mut response = request.accept(); // Form a response
if let Some(&WebSocketProtocol(ref protocols)) = headers.get() {
if protocols.contains(&("rust-websocket".to_string())) {
// We have a protocol we want to use
response.headers.set(WebSocketProtocol(vec!["rust-websocket".to_string()]));
}
}
let mut client = response.send().unwrap(); // Send the response
let ip = client.get_mut_sender()
.get_mut()
.peer_addr()
.unwrap();
println!("Connection from {}", ip);
let message: Message = Message::text("SERVER: Connected.".to_string());
client.send_message(&message).unwrap();
let (mut sender, mut receiver) = client.split();
let(tx, rx) = mpsc::channel::<Message>();
thread::spawn(move || {
for message in receiver.incoming_messages() {
tx.send(message.unwrap()).unwrap();
}
});
loop {
if let Ok(message) = rx.try_recv() {
match message.opcode {
Type::Close => {
let message = Message::close();
sender.send_message(&message).unwrap();
println!("Client {} disconnected", ip);
return;
},
Type::Ping => {
let message = Message::pong(message.payload);
sender.send_message(&message).unwrap();
},
_ => {
let payload_bytes = &message.payload;
let payload_string = match str::from_utf8(payload_bytes) {
Ok(v) => v,
Err(e) => panic!("Invalid UTF-8 sequence: {}", e),
};
let msg_string = format!("MESSAGE: {}: ", payload_string);
dispatcher.send(msg_string).unwrap();
}
}
}
if let Ok(message) = client_rx.try_recv() {
let message: Message = Message::text(message);
sender.send_message(&message).unwrap();
}
}
});
}
}
http://pastebin.com/H9McWLrH
I am writing a kind of chat server app where a message received from one websocket client is sent out to all other websocket clients. To do this, I keep the connected clients in a list. When a client disconnects, I need to remove it from the list (so that future "sends" do not fail).
However, sometimes when a client disconnects, the server just gets an exception "connection reset by peer", and the code does not get chance to remove from the client list. Is there a way to guarantee a "nice" notification that the connection has been reset?
My code is:
void WsRequestHandler::handleRequest(HTTPServerRequest &req, HTTPServerResponse &resp)
{
int n;
Poco::Timespan timeOut(5,0);
try
{
req.set("Connection","Upgrade"); // knock out any extra tokens firefox may send such as "keep-alive"
ws = new WebSocket(req, resp);
ws->setKeepAlive(false);
connectedSockets->push_back(this);
do
{
flags = 0;
if (!ws->poll(timeOut,Poco::Net::Socket::SELECT_READ || Poco::Net::Socket::SELECT_ERROR))
{
// cout << ".";
}
else
{
n = ws->receiveFrame(buffer, sizeof(buffer), flags);
if (n > 0)
{
if ((flags & WebSocket::FRAME_OP_BITMASK) == WebSocket::FRAME_OP_BINARY)
{
// process and send out to all other clients
DoReceived(ws, buffer, n);
}
}
}
}
while ((flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE);
// client has closed, so remove from list
for (vector<WsRequestHandler *>::iterator it = connectedSockets->begin() ; it != connectedSockets->end(); ++it)
{
if (*it == this)
{
connectedSockets->erase(it);
logger->information("Connection closed %s", ws->peerAddress().toString());
break;
}
}
delete(ws);
ws = NULL;
}
catch (WebSocketException& exc)
{
//never gets called
}
}
See receiveFrame() documentation:
Returns the number of bytes received. A return value of 0 means that the peer has shut down or closed the connection.
So if receiveFrame() call returns zero, you can act acordingly.
I do not know if this is an answer to the question, but the implementation you have done does not deal with PING frames. This is currently (as of my POCO version: 1.7.5) not done automatically by the POCO framework. I put up a question about that recently. According to the RFC (6465), the ping and pong frames are used (among others) as a keep-alive function. This may therefore be critical to get right in order to get your connection stable over time. Much of this is guess-work from my side as I am experimenting with this now myself.
#Alex, you are a main developer of POCO I believe, a comment on my answer would be much appreciated.
I extended the catch, to do some exception handling for "Connection reset by peer".
catch (Poco::Net::WebSocketException& exc)
{
// Do something
}
catch (Poco::Exception& e)
{
// This is where the "Connection reset by peer" lands
}
A bit late to the party here... but I am using Poco and Websockets as well - and properly handling disconnects was tricky.
I ended up implementing a simple ping functionality myself where the client side sends an ACK message for every WS Frame it receives. A separate thread on the server side tries to read the ACK messages - and it will now detect when the client has disconnected by looking at flags | WebSocket::FRAME_OP_CLOSE.
//Serverside - POCO. Start thread for receiving ACK packages. Needed in order to detect when websocket is closed!
thread t0([&]()->void{
while((!KillFlag && ws!= nullptr && flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE && machineConnection != nullptr){
try{
if(ws == nullptr){
return;
}
if(ws->available() > 0){
int len = ws->receiveFrame(buffer, sizeof(buffer), flags);
}
else{
Util::Sleep(10);
}
}
catch(Poco::Exception &pex){
flags = flags | WebSocket::FRAME_OP_CLOSE;
return;
}
catch(...){
//log::info(string("Unknown exception in ACK Thread drained"));
return;
}
}
log::debug("OperatorWebHandler::HttpRequestHandler() Websocket Acking thread DONE");
});
on the client side I just send a dummy "ACK" message back to the server (JS) every time I receive a WS frame from the server (POCO).
websocket.onmessage = (evt) => {
_this.receivedData = JSON.parse(evt.data);
websocket.send("ACK");
};
It is not about disconnect handling, rather about the stability of the connection.
Had some issues with POCO Websocket server in StreamSocket mode and C# client. Sometimes the client sends Pong messages with zero length payload and disconnect occurs so I added Ping and Pong handling code.
int WebSocketImpl::receiveBytes(void* buffer, int length, int)
{
char mask[4];
bool useMask;
_frameFlags = 0;
for (;;) {
int payloadLength = receiveHeader(mask, useMask);
int frameOp = _frameFlags & WebSocket::FRAME_OP_BITMASK;
if (frameOp == WebSocket::FRAME_OP_PONG || frameOp ==
WebSocket::FRAME_OP_PING) {
std::vector<char> tmp(payloadLength);
if (payloadLength != 0) {
receivePayload(tmp.data(), payloadLength, mask, useMask);
}
if (frameOp == WebSocket::FRAME_OP_PING) {
sendBytes(tmp.data(), payloadLength, WebSocket::FRAME_OP_PONG);
}
continue;
}
if (payloadLength <= 0)
return payloadLength;
if (payloadLength > length)
throw WebSocketException(Poco::format("Insufficient buffer for
payload size %d", payloadLength),
WebSocket::WS_ERR_PAYLOAD_TOO_BIG);
return receivePayload(reinterpret_cast<char*>(buffer), payloadLength,
mask, useMask);
}
}
PLEASE READ THE UPDATE #2 BELOW IF YOU ARE INTERESTED IN THIS PROBLEM ;)
Say I put this code into the JS of my extension.
var reader = {
onInputStreamReady : function(input) {
var sin = Cc["#mozilla.org/scriptableinputstream;1"]
.createInstance(Ci.nsIScriptableInputStream);
sin.init(input);
sin.available();
var request = '';
while (sin.available()) {
request = request + sin.read(512);
}
console.log('Received: ' + request);
input.asyncWait(reader,0,0,null);
}
}
var listener = {
onSocketAccepted: function(serverSocket, clientSocket) {
console.log("Accepted connection on "+clientSocket.host+":"+clientSocket.port);
input = clientSocket.openInputStream(0, 0, 0).QueryInterface(Ci.nsIAsyncInputStream);
output = clientSocket.openOutputStream(Ci.nsITransport.OPEN_BLOCKING, 0, 0);
input.asyncWait(reader,0,0,null);
}
}
var serverSocket = Cc["#mozilla.org/network/server-socket;1"].
createInstance(Ci.nsIServerSocket);
serverSocket.init(-1, true, 5);
console.log("Opened socket on " + serverSocket.port);
serverSocket.asyncListen(listener);
Then I run Firefox and connect to the socket via telnet
telnet localhost PORT
I send 5 messages and they get printed out, but when I try to send 6th message I get
firefox-bin: Fatal IO error 11 (Resource temporarily unavailable) on X server :0.0.
Even worse, when I try to put this same code into an XPCOM component (because that's where I actually need it), after I try sending a message via telnet I get
Segmentation fault
or sometimes
GLib-ERROR **: /build/buildd/glib2.0-2.24.1/glib/gmem.c:137: failed to allocate 32 bytes
aborting...
Aborted
printed to the terminal from which I launched firefox.
This is really weird stuff.. Can you spot something wrong with the code I've pasted or is smth wrong with my firefox/system or is the nsIServerSocket interface deprecated?
I'm testing with Firefox 3.6.6.
I would really appreciate some answer. Perhaps you could point me to a good example of using Sockets within an XPCOM component. I haven't seen many of those around.
UPDATE
I just realised that it used to work so now I think that my Console
component breaks it. I have no idea how this is related. But if I
don't use this component the sockets are working fine.
Here is the code of my Console component. I will try to figure out
what's wrong and why it interferes and I'll post my findings later.
Likely I'm doing something terribly wrong here to cause Segmentation
faults with my javascript =)
Voodoo..
components/Console.js:
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
Console.prototype = (function() {
var win;
var initialized = false;
var ready = false;
var _log = function(m, level, location) {
if (initialized&&ready) {
var prefix = "INFO: ";
switch (level) {
case "empty":
prefix = ""
break;
case "error":
prefix = "ERORR: "
break;
case "warning":
prefix = "WARNING: "
break;
}
win.document.getElementById(location).value =
win.document.getElementById(location).value + prefix + m + "\n";
win.focus();
} else if (initialized&&!ready) {
// Now it is time to create the timer...
var timer = Components.classes["#mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
// ... and to initialize it, we want to call
event.notify() ...
// ... one time after exactly ten second.
timer.initWithCallback(
{ notify: function() { log(m); } },
10,
Components.interfaces.nsITimer.TYPE_ONE_SHOT
);
} else {
init();
log(m);
}
}
var log = function(m, level) {
_log(m, level, 'debug');
}
var poly = function(m, level) {
_log(m, "empty", 'polyml');
}
var close = function() {
win.close();
}
var setReady = function() {
ready = true;
}
var init = function() {
initialized = true;
var ww = Components.classes["#mozilla.org/embedcomp/window-
watcher;1"]
.getService(Components.interfaces.nsIWindowWatcher);
win = ww.openWindow(null, "chrome://polymlext/content/
console.xul",
"console", "chrome,centerscreen,
resizable=no", null);
win.onload = setReady;
return win;
}
return {
init: init,
log : log,
poly : poly,
}
}());
// turning Console Class into an XPCOM component
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function Console() {
this.wrappedJSObject = this;
}
prototype2 = {
classDescription: "A special Console for PolyML extension",
classID: Components.ID("{483aecbc-42e7-456e-b5b3-2197ea7e1fb4}"),
contractID: "#ed.ac.uk/poly/console;1",
QueryInterface: XPCOMUtils.generateQI(),
}
//add the required XPCOM glue into the Poly class
for (attr in prototype2) {
Console.prototype[attr] = prototype2[attr];
}
var components = [Console];
function NSGetModule(compMgr, fileSpec) {
return XPCOMUtils.generateModule(components);
}
I'm using this component like this:
console = Cc["#ed.ac.uk/poly/console;1"].getService().wrappedJSObject;
console.log("something");
And this breaks the sockets :-S =)
UPDATE #2
Ok, if anyone is interested in checking this thing out I would really
appreciate it + I think this is likely some kind of bug (Seg fault
from javascript shouldn't happen)
I've made a minimal version of the extension that causes the problem,
you can install it from here:
http://dl.dropbox.com/u/645579/segfault.xpi
The important part is chrome/content/main.js:
http://pastebin.com/zV0e73Na
The way my friend and me can reproduce the error is by launching the
firefox, then a new window should appear saying "Opened socket on
9999". Connect using "telnet localhost 9999" and send a few messages.
After 2-6 messages you get one of the following printed out in the
terminal where firefox was launched:
1 (most common)
Segmentation fault
2 (saw multiple times)
firefox-bin: Fatal IO error 11 (Resource temporarily unavailable) on
X
server :0.0.
3 (saw a couple of times)
GLib-ERROR **: /build/buildd/glib2.0-2.24.1/glib/gmem.c:137: failed
to
allocate 32 bytes
aborting...
Aborted
4 (saw once)
firefox-bin: ../../src/xcb_io.c:249: process_responses: Assertion
`(((long) (dpy->last_request_read) - (long) (dpy->request)) <= 0)'
failed.
Aborted
If you need any more info or could point me to where to post a bug
report :-/ I'll be glad to do that.
I know this is just one of the many bugs... but perhaps you have an
idea of what should I do differently to avoid this? I would like to
use that "console" of mine in such way.
I'll try doing it with buffer/flushing/try/catch as people are suggesting, but I wonder whether try/catch will catch the Seg fault...
This is a thread problem. The callback onInputStreamReady happened to be executed in a different thread and accessing UI / DOM is only allowed from the main thread.
Solution is really simple:
change
input.asyncWait(reader,0,0,null);
to
var tm = Cc["#mozilla.org/thread-manager;1"].getService();
input.asyncWait(reader,0,0,tm.mainThread);