I'm creating a Firefox add-on which contains server listening for TCP connections on one port. The problem is that it does not always close the connection: after sending a FIN-ACK, and receiving an ACK, the TCP session is left open if the client does not send in return a FIN-ACK.
Only some connections are not closed completely. But after a while, they are too many TC connections hanging, and Firefox cannot open an new file handle, or receive any new connection.
TCP localhost.localdomain:commtact-https->localhost.localdomain:46951 (CLOSE_WAIT)
I could not find a way, preferably in the add-on (but I also tried on the client side) to make sure all TCP connections are closed correctly. Here is what the server functionality looks like in the add-on:
init_server: function(port) {
server.result = {};
server.listener = {
onSocketAccepted : function(serverSocket, transport) {
server.result.sout = transport.openOutputStream(0,0,0);
var instream = transport.openInputStream(0,0,0);
server.result.sin = Components.classes["#mozilla.org/binaryinputstream;1"].
createInstance(Components.interfaces.nsIBinaryInputStream);
server.result.sin.setInputStream(instream);
server.debug("New incoming connection");
var dataListener = {
onStartRequest: function(request, context) {
server.debug("onStartRequest");
},
onStopRequest: function(request, context, status) {
server.debug("onStopRequest");
// instream.close();
// delete instream;
// server.result.sout.close();
// server.result.sin.close();
},
onDataAvailable: function(request, context, inputStream, offset, count) {
var sis = Components.classes ["#mozilla.org/scriptableinputstream;1"].createInstance (Components.interfaces.nsIScriptableInputStream);
sis.init(inputStream);
var str = sis.read(count);
var suc = Components.classes ["#mozilla.org/intlscriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
suc.charset = "utf-8";
var line = suc. ConvertToUnicode (str);
server.debug(line);
[ ... process data ... ]
// instream.close();
// server.result.sout.close();
// server.result.sin.close();
// delete instream;
},
};
// Listen to new data
var pump = Components.classes["#mozilla.org/network/input-stream-pump;1"].createInstance(Components.interfaces.nsIInputStreamPump);
pump.init(instream, -1, -1, 0, 0, false); //screenserver.result.sin
pump.asyncRead(dataListener, null);
},
onStopListening : function(serverSocket, status){
server.debug("Client dead?");
server.init_server(server.PORT);
},
};
server.socket = null;
server.socket = Components.classes["#mozilla.org/network/server-socket;1"].getService(Components.interfaces.nsIServerSocket);
server.socket.init(port, true, -1);
server.socket.asyncListen(server.listener);
}
I think you need to save the transport you're passed in call onSocketAccepted and then invoke close() on that.
Related
How to detect in Deno that remote has closed (aborted) the TCP/IP (HTTP) connection?
const server = Deno.listen({ port: 8080 });
for await (const conn of server) {
conn.on('abort', () => { // <-- API I expect but doesn't exist
// ...
});
const httpConn = Deno.serveHttp(conn);
for await (const requestEvent of httpConn) {
//
}
}
While Deno does not provide an API to know when a connection was closed, the most reliable way to detect a connection closure is to attempt to write to it, which will throw an error if it's closed.
The following snippet that tries to perform a zero-length write periodically will solve your issue:
const server = Deno.listen({ port: 8080 });
for await (const conn of server) {
const httpConn = Deno.serveHttp(conn);
for await (const requestEvent of httpConn) {
let interval;
const stream = new ReadableStream({
start(controller) {
interval = setInterval(() =>
// attempt to write a 0 length buffer, it will fail if
// connection is closed
controller.enqueue(new Uint8Array(0)),
500); // tune interval depending on your needs
},
async pull(controller) {
/*
const result = await someComputation();
// in case you want to return some response
controller.enqueue(result);
// cleanup
clearInterval(interval);
controller.close();
*/
},
});
requestEvent.respondWith(new Response(stream))
.catch((err) => {
clearInterval(interval);
// check for <connection closed> error
if (err.message.includes('connection closed before message completed')) {
// stop your operation
console.log('connection closed');
}
});
}
}
The error logic can also be added to ReadableStreamDefaultController cancel method:
const stream = new ReadableStream({
start(controller) {
// ..
},
async pull(controller) {
// ...
},
cancel(reason) {
clearInterval(interval);
if (reason && reason.message.includes('connection closed before message completed')) {
// stop your operation
console.log('connection closed');
}
}
});
AFAIK there's not an event-oriented API, but when the connection's ReadableStream closes, you'll know that the connection has closed. This will also be reflected in Deno's internal resource map. Consider the following self-contained example:
A TCP listener is started, and is closed after 500ms. While it is open, three connections are created and closed (once every 100ms). When each connection is established:
The current TCP entries from Deno's resource map are printed to the console.
A reader is acquired on the connection's readable stream and a read is performed. Because the connection is closed from the client without any data being written, the first read is the final read (reflected in the read result's done property being true).
The reader's lock on the stream is released. The stream is closed.
The current TCP entries from Deno's resource map are printed to the console. Note that none appear at this point.
so-74228364.ts:
import { delay } from "https://deno.land/std#0.161.0/async/delay.ts";
function getTCPConnectionResources() {
return Object.fromEntries(
Object.entries(Deno.resources()).filter(([, type]) => type === "tcpStream"),
);
}
async function startServer(options: Deno.ListenOptions, signal: AbortSignal) {
const listener = Deno.listen(options);
signal.addEventListener("abort", () => listener.close());
for await (const conn of listener) {
console.log("Resources after open:", getTCPConnectionResources());
const reader = conn.readable.getReader();
reader.read()
.then(({ done }) => console.log({ done }))
.then(() => {
reader.releaseLock();
console.log("Resources after final read:", getTCPConnectionResources());
});
}
}
const controller = new AbortController();
delay(500).then(() => controller.abort());
const options: Deno.ListenOptions = {
hostname: "localhost",
port: 8080,
};
startServer(options, controller.signal);
for (let i = 0; i < 3; i += 1) {
await delay(100);
(await Deno.connect(options)).close();
}
% deno --version
deno 1.27.0 (release, x86_64-apple-darwin)
v8 10.8.168.4
typescript 4.8.3
% deno run --allow-net=localhost so-74228364.ts
Resources after open: { "7": "tcpStream" }
{ done: true }
Resources after final read: {}
Resources after open: { "10": "tcpStream" }
{ done: true }
Resources after final read: {}
Resources after open: { "13": "tcpStream" }
{ done: true }
Resources after final read: {}
I am trying to get a final speech transcription/recognition result from a Fleck websocket audio stream. The method OnOpen executes code when the websocket connection is first established and the OnBinary method executes code whenever binary data is received from the client. I have tested the websocket by echoing the voice into the websocket and writing the same binary data back into the websocket at the same rate. This test worked so I know that the binary data is being sent correctly (640 byte messages with a 20ms frame size).
Therefore, my code is failing and not the service. My aim is to do the following:
When the websocket connection is created, send the initial audio config request to the API with SingleUtterance == true
Run a background task that listens for the streaming results waiting for isFinal == true
Send each binary message received to the API for transcription
When background task recognises isFinal == true, stop current streaming request and create a new request - repeating steps 1 through 4
The context of this project is transcribing all single utterances in a live phone call.
socket.OnOpen = () =>
{
firstMessage = true;
};
socket.OnBinary = async binary =>
{
var speech = SpeechClient.Create();
var streamingCall = speech.StreamingRecognize();
if (firstMessage == true)
{
await streamingCall.WriteAsync(
new StreamingRecognizeRequest()
{
StreamingConfig = new StreamingRecognitionConfig()
{
Config = new RecognitionConfig()
{
Encoding = RecognitionConfig.Types.AudioEncoding.Linear16,
SampleRateHertz = 16000,
LanguageCode = "en",
},
SingleUtterance = true,
}
});
Task getUtterance = Task.Run(async () =>
{
while (await streamingCall.ResponseStream.MoveNext(
default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream.Current.Results)
{
if (result.IsFinal == true)
{
Console.WriteLine("This test finally worked");
}
}
}
});
firstMessage = false;
}
else if (firstMessage == false)
{
streamingCall.WriteAsync(new StreamingRecognizeRequest()
{
AudioContent = Google.Protobuf.ByteString.CopyFrom(binary, 0, 640)
}).Wait();
}
};
.Wait() is a blocking call being called in an async/await. They don't mix well and can lead to deadlocks.
Simply keep the code async all the way through
//...omitted for brevity
else if (firstMessage == false) {
await streamingCall.WriteAsync(new StreamingRecognizeRequest() {
AudioContent = Google.Protobuf.ByteString.CopyFrom(binary, 0, 640)
});
}
I'm trying to send some data over TCP connection from my TCP client for Firefox OS. Initially I started with filling loginBytes with the data which should help me to login to the service specified by ip and port (array's size is 28 bytes), now I'm trying to send an empty array. In any case the outcome is the same: in console log of Firefox Web IDE I can see the following message: uncaught exception: out of memory. And among the messages I tried to use for debugging only "Started sending data" is printed. So I come to a conclusion that the failure takes place during the execution of the send command. But what can be wrong with it?
(function() {
var sendButton = document.querySelector('#send');
var notes = document.querySelector('#notes');
var options = {binaryType: 'arraybuffer'};
var socket = navigator.mozTCPSocket.open(ip, port, options);
sendButton.addEventListener('click', function() {
var loginBytes = [];
var Int8View = new Uint8Array(loginBytes);
socket.ondata = function(event) {
console.log("Started receiving data");
console.log(event.data);
console.log("Received successfully");
}
socket.onerror = function(event) {
console.log("Everything is bad");
}
console.log("Started sending data");
socket.send(Int8View);
console.log("Sent successfully");
});
})();
I think
var Int8View = new Uint8Array(loginBytes)
needs to be changed to:
var Int8View = new Uint8Array(loginBytes).buffer
Uint8Array by itself does not return an ArrayBuffer object.
I have a form that was created on it's own UI thread running in the system tray which I need to manipulate with a signalR connection from the server which I believe to be running on a background thread. I'm aware of the need to invoke controls when not accessing them from their UI thread. I am able to manipulate (make popup in my case) using the following code that is called on form load but would like a sanity check as I'm fairly new to async:
private void WireUpTransport()
{
// connect up to the signalR server
var connection = new HubConnection("http://localhost:32957/");
var messageHub = connection.CreateProxy("message");
var uiThreadScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var backgroundTask = connection.Start().ContinueWith(task =>
{
if (task.IsFaulted)
{
Console.WriteLine("There was an error opening the connection: {0}", task.Exception.GetBaseException());
}
else
{
Console.WriteLine("The connection was opened successfully");
}
});
// subscribe to the servers Broadcast method
messageHub.On<Domain.Message>("Broadcast", message =>
{
// do our work on the UI thread
var uiTask = backgroundTask.ContinueWith(t =>
{
popupNotifier.TitleText = message.Title + ", Priority: " + message.Priority.ToString();
popupNotifier.ContentText = message.Body;
popupNotifier.Popup();
}, uiThreadScheduler);
});
}
Does this look OK? It's working on my local machine but this has the potential to be rolled out on every user machine in our business and I need to get it right.
Technically you should hook up to all notifications (using On<T>) before you Start listening. As far as your async work I'm not quite sure what you were trying to do, but for some reason your chaining the notification to your UI in On<T> to the backgroundTask variable which is the Task that was returned to you by the call to Start. There's no reason for that to be involved there.
So this is probably what you want:
private void WireUpTransport()
{
// connect up to the signalR server
var connection = new HubConnection("http://localhost:32957/");
var messageHub = connection.CreateProxy("message");
var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
// subscribe to the servers Broadcast method
messageHub.On<Domain.Message>("Broadcast", message =>
{
// do our work on the UI thread
Task.Factory.StartNew(
() =>
{
popupNotifier.TitleText = message.Title + ", Priority: " + message.Priority.ToString();
popupNotifier.ContentText = message.Body;
popupNotifier.Popup();
},
CancellationToken.None,
TaskCreationOptions.None,
uiTaskScheduler);
});
connection.Start().ContinueWith(task =>
{
if (task.IsFaulted)
{
Console.WriteLine("There was an error opening the connection: {0}", task.Exception.GetBaseException());
}
else
{
Console.WriteLine("The connection was opened successfully");
}
});
}
I am using node-http-proxy. However, in addition to relaying HTTP requests, I also need to listen to the incoming and outgoing data.
Intercepting the response data is where I'm struggling. Node's ServerResponse object (and more generically the WritableStream interface) doesn't broadcast a 'data' event. http-proxy seems to create it's own internal request, which produces a ClientResponse object (which does broadcast the 'data' event) however this object is not exposed publically outside the proxy.
Any ideas how to solve this without monkey-patching node-http-proxy or creating a wrapper around the response object?
Related issue in issues of node-http-proxy on Github seems to imply this is not possible. For future attempts by others, here is how I hacked the issue:
you'll quickly find out that the proxy is only calling writeHead(), write() and end() methods of the res object
since res is already an EventEmitter, you can start emitting new custom events
listen for these new events to assemble the response data and then use it
var eventifyResponse = function(res) {
var methods = ['writeHead', 'write', 'end'];
methods.forEach(function(method){
var oldMethod = res[method]; // remember original method
res[method] = function() { // replace with a wrapper
oldMethod.apply(this, arguments); // call original method
arguments = Array.prototype.slice.call(arguments, 0);
arguments.unshift("method_" + method);
this.emit.apply(this, arguments); // broadcast the event
};
});
};
res = eventifyResponse(res), outputData = '';
res.on('method_writeHead', function(statusCode, headers) { saveHeaders(); });
res.on('method_write', function(data) { outputData += data; });
res.on('method_end', function(data) { use_data(outputData + data); });
proxy.proxyRequest(req, res, options)
This is a simple proxy server sniffing the traffic and writing it to console:
var http = require('http'),
httpProxy = require('http-proxy');
//
// Create a proxy server with custom application logic
//
var proxy = httpProxy.createProxyServer({});
// assign events
proxy.on('proxyRes', function (proxyRes, req, res) {
// collect response data
var proxyResData='';
proxyRes.on('data', function (chunk) {
proxyResData +=chunk;
});
proxyRes.on('end',function () {
var snifferData =
{
request:{
data:req.body,
headers:req.headers,
url:req.url,
method:req.method},
response:{
data:proxyResData,
headers:proxyRes.headers,
statusCode:proxyRes.statusCode}
};
console.log(snifferData);
});
// console.log('RAW Response from the target', JSON.stringify(proxyRes.headers, true, 2));
});
proxy.on('proxyReq', function(proxyReq, req, res, options) {
// collect request data
req.body='';
req.on('data', function (chunk) {
req.body +=chunk;
});
req.on('end', function () {
});
});
proxy.on('error',
function(err)
{
console.error(err);
});
// run the proxy server
var server = http.createServer(function(req, res) {
// every time a request comes proxy it:
proxy.web(req, res, {
target: 'http://localhost:4444'
});
});
console.log("listening on port 5556")
server.listen(5556);
I tried your hack but it didn't work for me. My use case is simple: I want to log the in- and outgoing traffic from an Android app to our staging server which is secured by basic auth.
https://github.com/greim/hoxy/
was the solution for me. My node-http-proxy always returned 500 (while the direct request to stage did not). Maybe the authorization headers would not be forwarded correctly or whatever.
Hoxy worked fine right from the start.
npm install hoxy [-g]
hoxy --port=<local-port> --stage=<your stage host>:<port>
As rules for logging I specified:
request: $aurl.log()
request: #log-headers()
request: $method.log()
request: $request-body.log()
response: $url.log()
response: $status-code.log()
response: $response-body.log()
Beware, this prints any binary content.