Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,15 @@
{
'variables': {
'gpp': '<!(type g++ | grep "is" > /dev/null && echo "yep" || echo "no")'
},
'targets': [{
'target_name': 'sockit',
'sources': ['src/addon.c',
'src/sockit.cc'],
'conditions': [[
'"<@(gpp)"=="no"',
{ 'sources!': ['src/addon.c',
'src/sockit.cc'] }
]]
}]
}

View file

@ -0,0 +1,25 @@
'use strict';
// Import the add-on.
var sockittome = require('./');
// Create a new sockit object.
var sockit = new sockittome.Sockit();
// Connect to a host and port.
sockit.connect({ host: 'www.google.com', port: 80 });
// Write some data! Takes either a String or a node.js 'Buffer' object.
sockit.write('GET / HTTP/1.0\r\n\r\n');
//sockit.write(new Buffer('GET / HTTP/1.0\r\n\r\n'));
// Read some tasty bytes! Returns a node.js 'Buffer' object, always.
var buffer = sockit.read(1024);
// Observe amazing results.
console.log('Read ', buffer.length, ' bytes');
// And beautiful raw HTTP response.
console.log(buffer.toString());
// Close it up.
sockit.close();

View file

@ -0,0 +1,22 @@
{
"name": "sockit-to-me",
"version": "1.0.2",
"author": "Ghislain 'Aus' Lacroix <aus@mozilla.com>",
"description": "A synchronous socket API for node.js.",
"main": "build/Release/sockit",
"repository": "https://github.com/mozilla-b2g/gaia/tree/master/tests/jsmarionette/client/sockit-to-me",
"license": "MIT",
"engines": {
"node": "^4 || ^5"
},
"devDependencies": {
"node-gyp": "^3.0.1"
},
"scripts": {
"install": "CC=clang CXX=clang++ node-gyp configure build --verbose"
}
}

View file

@ -0,0 +1,15 @@
// Global includes
#include <node.h>
// Local includes
#include "sockit.h"
// So we don't spend half the time typing 'v8::'
using namespace v8;
// Initialize
void InitAll(Handle<Object> aExports) {
Sockit::Init(aExports);
}
NODE_MODULE(sockit, InitAll)

View file

@ -0,0 +1,629 @@
// Global includes
#include <node.h>
#include <node_buffer.h>
// Platform dependent includes.
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netdb.h>
// Local includes
#include "sockit.h"
// So we don't spend half the time typing 'v8::'
using namespace v8;
// Error Messages
#define E_ALREADY_CONNECTING \
"ALREADY CONNECTING! You must call close before calling connect again."
#define E_ALREADY_CONNECTED \
"ALREADY CONNECTED! You must call close before calling connect again."
// Clever constants
static const int MAX_LOOKUP_RETRIES = 3;
static const int POLL_TIMEOUT_MS = 60000;
// Global for debug log enable/disable at runtime.
static bool gDebugLog = false;
Persistent<Function> Sockit::constructor;
static void _debug_log(const char *aMsg) {
if(gDebugLog) {
fprintf(stderr, "[sockit-to-me] %s\n", aMsg);
}
}
Sockit::Sockit() : mSocket(0),
mPollTimeout(POLL_TIMEOUT_MS),
mIsConnecting(false),
mIsConnected(false) {
}
Sockit::~Sockit() {
// Close our socket if it's still open.
if(mSocket != 0) {
// XXXAus: We should probably report error on close.
close(mSocket);
}
}
/*static*/ void
Sockit::Init(Handle<Object> aExports) {
Isolate* isolate = Isolate::GetCurrent();
Local<FunctionTemplate> object = FunctionTemplate::New(isolate, New);
// Set the classname our object will have in JavaScript land.
object->SetClassName(String::NewFromUtf8(isolate, "Sockit"));
// Reserve space for our object instance.
object->InstanceTemplate()->SetInternalFieldCount(1);
// Add the 'Connect' function.
NODE_SET_PROTOTYPE_METHOD(object, "connect", Connect);
// Add the 'Read' function.
NODE_SET_PROTOTYPE_METHOD(object, "read", Read);
// Add the 'Write' function.
NODE_SET_PROTOTYPE_METHOD(object, "write", Write);
// Add the 'Close' function.
NODE_SET_PROTOTYPE_METHOD(object, "close", Close);
// Add the 'SetPollTimeout' function.
NODE_SET_PROTOTYPE_METHOD(object, "setPollTimeout", SetPollTimeout);
// Add the 'SetDebugLog' function.
NODE_SET_PROTOTYPE_METHOD(object, "setDebugLog", SetDebugLog);
// Add the constructor.
constructor.Reset(isolate, object->GetFunction());
aExports->Set(String::NewFromUtf8(isolate, "Sockit"),
object->GetFunction());
}
/*static*/ void
Sockit::New(const FunctionCallbackInfo<Value>& aArgs) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
if (aArgs.IsConstructCall()) {
Sockit* sockit = new Sockit();
sockit->Wrap(aArgs.This());
aArgs.GetReturnValue().Set(aArgs.This());
}
else {
const int argc = 0;
Local<Value> argv[argc] = { };
Local<Function> cons = Local<Function>::New(isolate, constructor);
aArgs.GetReturnValue().Set(cons->NewInstance(argc, argv));
}
}
/*static*/ void
Sockit::Connect(const FunctionCallbackInfo<Value>& aArgs) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
// Always return self.
aArgs.GetReturnValue().Set(aArgs.This());
if(aArgs.Length() < 1) {
isolate->ThrowException(
Exception::TypeError(String::NewFromUtf8(isolate,
"Not enough arguments.")));
return;
}
if(aArgs.Length() > 1) {
isolate->ThrowException(
Exception::TypeError(String::NewFromUtf8(isolate,
"Too many arguments.")));
return;
}
if(!aArgs[0]->IsObject()) {
isolate->ThrowException(
Exception::TypeError(
String::NewFromUtf8(isolate,
"Invalid argument. Must be an Object.")));
return;
}
Local<Object> options = aArgs[0]->ToObject();
Local<String> hostKey = String::NewFromUtf8(isolate, "host");
Local<String> portKey = String::NewFromUtf8(isolate, "port");
if(!options->Has(hostKey) ||
!options->Get(hostKey)->IsString()) {
isolate->ThrowException(
Exception::TypeError(
String::NewFromUtf8(
isolate,
"Object must have 'host' field and it must be a string.")));
return;
}
if(!options->Has(portKey) ||
!options->Get(portKey)->IsNumber()) {
isolate->ThrowException(
Exception::TypeError(
String::NewFromUtf8(
isolate,
"Object must have 'port' field and it must be a number.")));
return;
}
Sockit *sockit = ObjectWrap::Unwrap<Sockit>(aArgs.Holder());
// Ensure we're not already attempting to connect.
if(sockit->mIsConnecting) {
_debug_log(E_ALREADY_CONNECTING);
isolate->ThrowException(
Exception::Error(String::NewFromUtf8(isolate, E_ALREADY_CONNECTING)));
return;
}
// Ensure we're not already connected.
if(sockit->mIsConnected) {
_debug_log(E_ALREADY_CONNECTED);
isolate->ThrowException(
Exception::Error(String::NewFromUtf8(isolate, E_ALREADY_CONNECTED)));
return;
}
// We can consider ourselves in an attempt to connect at this point.
sockit->mIsConnecting = true;
// XXXAus: Move the DNS piece into it's own method.
String::Utf8Value optionsHost(options->Get(hostKey)->ToString());
bool shouldRetry = false;
int retryCount = 0;
struct hostent *hostEntry;
do {
hostEntry = gethostbyname(*optionsHost);
// Getting the host address failed. Let's see why.
if(hostEntry == NULL) {
// Check h_errno, we might retry if the error indicates we should.
switch(h_errno) {
case HOST_NOT_FOUND:
isolate->ThrowException(
Exception::Error(String::NewFromUtf8(isolate,
"Couldn't resolve host.")));
return;
break;
case NO_RECOVERY:
isolate->ThrowException(
Exception::Error(
String::NewFromUtf8(isolate,
"Unexpected failure during host lookup.")));
return;
break;
case NO_DATA:
isolate->ThrowException(
Exception::Error(String::NewFromUtf8(isolate,
"Host exists but has no address.")));
return;
break;
case TRY_AGAIN:
shouldRetry = true;
retryCount++;
break;
}
}
}
while(shouldRetry && retryCount <= MAX_LOOKUP_RETRIES);
if(hostEntry == NULL) {
isolate->ThrowException(
Exception::Error(String::NewFromUtf8(isolate,
"Couldn't resolve host even after retries.")));
return;
}
int socketHandle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(socketHandle < 0) {
isolate->ThrowException(
Exception::Error(String::NewFromUtf8(isolate, "Failed to create socket.")));
return;
}
sockit->mSocket = socketHandle;
// Set socket non-blocking prior to connect. We'll poll for connect success.
int flags = fcntl(socketHandle, F_GETFL, 0);
if(flags == -1) {
flags = 0;
}
int result = fcntl(socketHandle, F_SETFL, flags | O_NONBLOCK);
if(result < 0) {
isolate->ThrowException(
Exception::Error(
String::NewFromUtf8(isolate, "Failed to set non blocking to socket.")));
return;
}
// Magic socket incantation.
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
// Always use first address returned, it's good enough for our purposes.
address.sin_addr.s_addr =
inet_addr(inet_ntoa(*(struct in_addr *)hostEntry->h_addr_list[0]));
// Using static cast here to enforce short as required by IPv4 functions.
address.sin_port =
htons(static_cast<short>(options->Get(portKey)->ToNumber()->Uint32Value()));
result = connect(
sockit->mSocket, (sockaddr *) &address, sizeof(struct sockaddr)
);
if(result == -1 && errno != EINPROGRESS) {
// We failed to connect. Close to socket we created.
close(sockit->mSocket);
// In case there's another connect attempt, clean up the socket data.
sockit->mSocket = 0;
sockit->mIsConnecting = false;
// Report failure to connect.
isolate->ThrowException(
Exception::Error(String::NewFromUtf8(isolate, "Failed to connect.")));
return;
}
struct pollfd fds;
fds.fd = sockit->mSocket;
fds.events = POLLWRBAND | POLLWRNORM | POLLNVAL;
fds.revents = 0;
result = poll(&fds, 1, sockit->mPollTimeout);
// If we've failed, we'll clean-up and try and send an exception.
if(result != 1) {
// Close and clean-up.
close(sockit->mSocket);
sockit->mSocket = 0;
sockit->mIsConnecting = false;
sockit->mIsConnected = false;
if(result == 0) {
isolate->ThrowException(
Exception::Error(
String::NewFromUtf8(isolate, "Polling socket connect() timeout!")));
}
else if(result < 0 || (fds.revents & POLLERR)) {
isolate->ThrowException(
Exception::Error(
String::NewFromUtf8(isolate, "Error polling connect() socket!")));
}
else if(!((fds.revents & POLLWRBAND) || (fds.revents & POLLWRNORM)) ) {
isolate->ThrowException(
Exception::Error(
String::NewFromUtf8(isolate, "Unhandled poll event connect() socket!")));
}
return;
}
// Otherwise, we're really connected.
sockit->mIsConnected = true;
// And we're no longer attempting to connect.
sockit->mIsConnecting = false;
}
/*static*/ void
Sockit::Read(const FunctionCallbackInfo<Value>& aArgs) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
// Default to returning undefined
aArgs.GetReturnValue().Set(Undefined(isolate));
if(aArgs.Length() != 1) {
isolate->ThrowException(
Exception::TypeError(
String::NewFromUtf8(
isolate,
"Not enough arguments, read takes the number of bytes to be read from the socket.")));
return;
}
if(!aArgs[0]->IsNumber()) {
isolate->ThrowException(
Exception::TypeError(String::NewFromUtf8(isolate,
"Argument must be a Number.")));
return;
}
Sockit *sockit = ObjectWrap::Unwrap<Sockit>(aArgs.Holder());
// Make sure we're connected to something.
if(sockit->mSocket == 0) {
isolate->ThrowException(
Exception::Error(
String::NewFromUtf8(
isolate,
"Not connected. To read data you must call connect first.")));
return;
}
// Figure out how many bytes the user wants us to read from the socket.
unsigned int bytesToRead = aArgs[0]->ToNumber()->Uint32Value();
// Allocate a shiny buffer.
char *buffer = new char[bytesToRead];
// Track how many bytes we've read and total bytes read.
int totalBytesRead = 0;
int bytesRead = 0;
// Read me some tasty bytes!
struct pollfd fds;
fds.fd = sockit->mSocket;
fds.events = POLLIN;
do {
fds.revents = 0;
int result = poll(&fds, 1, sockit->mPollTimeout);
if(result == 0) {
isolate->ThrowException(
Exception::Error(String::NewFromUtf8(isolate,
"Polling socket recv() timeout!")));
break;
}
else if(result < 0 || (fds.revents & POLLERR)) {
isolate->ThrowException(
Exception::Error(String::NewFromUtf8(isolate,
"Error polling recv() socket!")));
break;
}
else if(!(fds.revents & POLLIN)) {
isolate->ThrowException(
Exception::Error(String::NewFromUtf8(isolate,
"Unhandled poll event recv() socket!")));
break;
}
bytesRead = recv(sockit->mSocket, &buffer[totalBytesRead], bytesToRead, 0);
if(bytesRead < 0) {
if(errno == EAGAIN || errno == EWOULDBLOCK) {
continue;
}
isolate->ThrowException(
Exception::Error(
String::NewFromUtf8(isolate,
"Error while reading from socket!")));
break;
}
if(bytesRead == 0) {
isolate->ThrowException(
Exception::Error(String::NewFromUtf8(isolate,
"Connection reset by peer!")));
break;
}
bytesToRead -= bytesRead;
totalBytesRead += bytesRead;
bytesRead = 0;
}
while(bytesToRead > 0);
if(totalBytesRead) {
// Create a node heap allocated buffer first.
Local<Object> heapBuffer = node::Buffer::New(isolate, totalBytesRead).ToLocalChecked();
// Copy our temporary buffer data into the heap buffer.
memcpy(node::Buffer::Data(heapBuffer), buffer, totalBytesRead);
// Done with our temporary buffer.
delete [] buffer;
// Return those tasty bytes!
aArgs.GetReturnValue().Set(heapBuffer);
return;
}
// Done with our temporary buffer.
delete [] buffer;
// Return NULL!
aArgs.GetReturnValue().Set(Null(isolate));
return;
}
/*static*/ void
Sockit::Write(const FunctionCallbackInfo<Value>& aArgs) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
if(aArgs.Length() < 1) {
isolate->ThrowException(
Exception::TypeError(String::NewFromUtf8(isolate,
"Not enough arguments.")));
return;
}
if(aArgs.Length() > 1) {
isolate->ThrowException(
Exception::TypeError(
String::NewFromUtf8(isolate,
"Too many arguments.")));
return;
}
Sockit *sockit = ObjectWrap::Unwrap<Sockit>(aArgs.Holder());
// Always return self even when we throw.
aArgs.GetReturnValue().Set(aArgs.This());
// Make sure we're connected to something.
if(sockit->mSocket == 0) {
isolate->ThrowException(
Exception::Error(
String::NewFromUtf8(
isolate,
"Not connected. To write data you must call connect first.")));
return;
}
int success = 0;
if(aArgs[0]->IsString()) {
// Convert to utf8 value before sending.
Local<String> dataString = aArgs[0]->ToString();
String::Utf8Value data(dataString);
// Write the data to the socket.
success = sockit->Write(*data, dataString->Utf8Length());
}
else if(aArgs[0]->IsObject() &&
aArgs[0]->ToObject()->GetConstructorName()->Equals(String::NewFromUtf8(isolate, "Buffer"))) {
Local<Object> buffer = aArgs[0]->ToObject();
success =
sockit->Write(node::Buffer::Data(buffer), node::Buffer::Length(buffer));
}
else {
isolate->ThrowException(
Exception::TypeError(
String::NewFromUtf8(isolate,
"Invalid argument, must be a 'String' or 'Buffer' object.")));
return;
}
if(success < 0) {
isolate->ThrowException(
Exception::Error(String::NewFromUtf8(isolate,
"Unable to write data to socket.")));
}
return;
}
int
Sockit::Write(const char *aBuffer, const int aLength) {
int bytesToWrite = aLength;
int totalBytesWritten = 0;
int bytesWritten = 0;
do {
bytesWritten =
send(mSocket,
aBuffer + (totalBytesWritten * sizeof(char)),
bytesToWrite,
0
);
if(bytesWritten < 0) {
return -1;
}
totalBytesWritten += bytesWritten;
bytesToWrite -= bytesWritten;
bytesWritten = 0;
}
while(bytesToWrite > 0);
return totalBytesWritten;
}
/*static*/ void
Sockit::Close(const FunctionCallbackInfo<Value>& aArgs) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
Sockit *sockit = ObjectWrap::Unwrap<Sockit>(aArgs.Holder());
if(sockit->mSocket != 0) {
// If close fails we have bigger problems on our hands than a stale
// socket that isn't closed.
close(sockit->mSocket);
}
sockit->mSocket = 0;
sockit->mIsConnecting = false;
sockit->mIsConnected = false;
aArgs.GetReturnValue().Set(aArgs.This());
return;
}
/*static*/ void
Sockit::SetPollTimeout(const FunctionCallbackInfo<Value>& aArgs) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
if(aArgs.Length() < 1) {
isolate->ThrowException(
Exception::TypeError(String::NewFromUtf8(isolate,
"Not enough arguments.")));
return;
}
if(aArgs.Length() > 1) {
isolate->ThrowException(
Exception::TypeError(String::NewFromUtf8(isolate,
"Too many arguments.")));
return;
}
if(!aArgs[0]->IsNumber()) {
isolate->ThrowException(
Exception::TypeError(String::NewFromUtf8(isolate,
"Argument is not a number.")));
return;
}
Sockit *sockit = ObjectWrap::Unwrap<Sockit>(aArgs.Holder());
sockit->mPollTimeout = aArgs[0]->Int32Value();
aArgs.GetReturnValue().Set(aArgs.This());
return;
}
/*static*/ void
Sockit::SetDebugLog(const FunctionCallbackInfo<Value>& aArgs) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
if(aArgs.Length() < 1) {
isolate->ThrowException(
Exception::TypeError(String::NewFromUtf8(isolate,
"Not enough arguments.")));
return;
}
if(aArgs.Length() > 1) {
isolate->ThrowException(
Exception::TypeError(String::NewFromUtf8(isolate,
"Too many arguments.")));
return;
}
if(!aArgs[0]->IsBoolean()) {
isolate->ThrowException(
Exception::TypeError(String::NewFromUtf8(isolate,
"Argument is not a boolean.")));
return;
}
gDebugLog = aArgs[0]->BooleanValue();
if(gDebugLog) {
_debug_log("debug log enabled");
}
aArgs.GetReturnValue().Set(aArgs.This());
return;
}

View file

@ -0,0 +1,39 @@
#ifndef __SOCKIT_H__
#define __SOCKIT_H__
#include <node.h>
#include <node_object_wrap.h>
class Sockit : public node::ObjectWrap {
public:
static void Init(v8::Handle<v8::Object>);
private:
Sockit();
~Sockit();
static v8::Persistent<v8::Function> constructor;
static void New(const v8::FunctionCallbackInfo<v8::Value>&);
static void Connect(const v8::FunctionCallbackInfo<v8::Value>&);
static void Read(const v8::FunctionCallbackInfo<v8::Value>&);
static void Write(const v8::FunctionCallbackInfo<v8::Value>&);
int Write(const char *, const int);
static void Close(const v8::FunctionCallbackInfo<v8::Value>&);
static void SetPollTimeout(const v8::FunctionCallbackInfo<v8::Value>&);
static void SetDebugLog(const v8::FunctionCallbackInfo<v8::Value>&);
int mSocket;
int mPollTimeout;
bool mIsConnecting;
bool mIsConnected;
};
#endif

View file

@ -0,0 +1,380 @@
'use strict';
var assert = require('assert');
var ChildProcess = require('child_process');
// Always use debug binary during testing.
var Sockit = require('..');
suite('sockit-to-me', function() {
// Firing up a process can be slow on Travis CI so we allow a longer
// timeout for our tests.
this.timeout(10000);
var host = '127.0.0.1';
var port = 44321;
var helo = 'HELO\r\n';
var ackn = 'ACK\r\n';
var server = null;
var subject = null;
// global flag set to true if we kill the server
// needed for the close / disconnect
var server_killed = false;
function startServer(callback) {
server_killed = false;
// Start the child process.
server = ChildProcess.fork(__dirname + '/../test_support/server.js');
// Register listener
server.on('message', function(message) {
// Server has actually started listening on the expected host and port.
if(message.reply == 'started') {
// Setup complete.
callback();
}
});
// Ask server to start.
server.send({ command: 'start' });
}
function stopServer(callback) {
// Register listener to shutdown child process once the server has
// successfully closed it's listening socket.
server.on('message', function(message) {
// Server has actually stopped.
if(message.reply == 'stopped') {
// Indicate server was killed.
server_killed = true;
// Disconnect child process.
server.disconnect();
// Teardown complete.
callback();
}
});
// Ask server to stop.
server.send({ command: 'stop' });
}
setup(function(done) {
subject = new Sockit.Sockit();
startServer(done);
});
teardown(function(done) {
// Close connection to server.
subject.close();
if (!server_killed) {
stopServer(done);
} else {
done();
}
});
suite('#connect', function() {
test('successful connections', function(done) {
// Register a listener to ensure that we really did connect to the server
// as we expected to.
server.on('message', function(message) {
if(message.reply == 'connected') {
done();
}
});
// Connect throws on error and is synchronous.
subject.connect({ host: host, port: port });
});
suite('argument shape errors', function() {
test('not enough arguments', function() {
var err;
try {
assert.ok(subject.connect() instanceof Error);
} catch(e) {
err = e;
}
assert.ok(err instanceof Error);
});
test('argument of wrong type', function() {
var err;
try {
assert.ok(subject.connect(23) instanceof Error);
} catch(e) {
err = e;
}
assert.ok(err instanceof Error);
});
test('argument without string "host" attribute', function() {
var err;
try {
subject.connect({ port: 23 });
} catch(e) {
err = e;
}
assert.ok(err instanceof Error);
});
test('argument without number "port" attribute', function() {
var err;
try {
subject.connect({ host: 'host' });
} catch(e) {
err = e;
}
assert.ok(err instanceof Error);
});
});
test('error is thrown when connection cannot be established',
function() {
var err;
// Connect throws on error and is synchronous.
try {
subject.connect({ host: host + 'garbage', port: port });
} catch(e) {
err = e;
}
assert.ok(err instanceof Error);
});
test('connection will timeout when expected when using setPollTimeout',
function() {
subject.setPollTimeout(1000);
var err;
var start = Date.now();
try {
subject.connect({ host: '1.2.3.4', port: 31337 });
} catch(e) {
err = e;
}
assert.ok(Date.now() - start >= 1000);
assert.ok(err instanceof Error);
});
test('calling connect twice will fail', function() {
var err;
try {
subject.connect({ host: host, port: port });
subject.connect({ host: host, port: port });
} catch(e) {
err = e;
}
assert.ok(err instanceof Error);
assert.ok(err.message.indexOf('ALREADY CONNECTED') != -1);
});
});
suite('#read', function() {
test('correct data is returned', function(done) {
// Register a listener to ensure that the server really is ready to write
// data when we ask it to.
server.on('message', function(message) {
// Connected, ask server to send data.
if(message.reply == 'connected') {
// Ask server to send 'helo'.
server.send({ command: 'send', data: helo });
// Read the response.
var response = subject.read(helo.length);
// Ensure we got the response we expected.
assert.equal(helo, response.toString());
done();
}
});
// Connect to server.
subject.connect({ host: host, port: port });
});
test('error is thrown when not connected', function() {
var err;
try {
subject.read(1);
} catch(e) {
err = e;
}
assert.ok(err instanceof Error);
});
test('error is thrown when a number is not specified', function() {
var err;
subject.connect({ host: host, port: port });
try {
subject.read();
} catch(e) {
err = e;
}
assert.ok(err instanceof Error);
});
test('1s timeout if no data', function(done) {
subject.setPollTimeout(1000);
// Register a listener to ensure that the server really is ready
server.on('message', function(message) {
// Connected, ask server to send NOTHING.
if(message.reply == 'connected') {
var err;
try {
// Read the response.
subject.read(helo.length);
}
catch(e) {
err = e;
}
assert.ok(err instanceof Error);
done();
}
});
// Connect to server.
subject.connect({ host: host, port: port });
});
test('socket closing', function(done) {
subject.setPollTimeout(1000);
// Register a listener to ensure that the server really is ready
server.on('message', function(message) {
// Connected, ask server to send NOTHING.
if(message.reply == 'connected') {
var err;
server.kill('SIGKILL');
server_killed = true;
try {
// Read the response.
subject.read(helo.length);
}
catch(e) {
err = e;
}
assert.ok(err instanceof Error);
done();
}
});
// Connect to server.
subject.connect({ host: host, port: port });
});
});
suite('#write', function() {
test('specified data is written', function(done) {
// Register a litener to ensure that the server is really ready to read
// data when we ask it to and to verify that it received what we sent.
server.on('message', function(message) {
if(message.reply == 'connected') {
// Tell the server to expect data from the client.
server.send({ command: 'recv', data: ackn });
// Send data to server.
subject.write(ackn);
}
else if(message.reply == 'expected') {
done();
}
});
// Connect to server.
subject.connect({ host: host, port: port });
});
test('error is thrown when not connected', function() {
var err;
try {
subject.write('data');
} catch(e) {
err = e;
}
assert.ok(err instanceof Error);
});
test('error is thrown when no argument is specified', function() {
var err;
subject.connect({ host: host, port: port });
try {
subject.write();
} catch(e) {
err = e;
}
assert.ok(err instanceof Error);
});
test('error is thrown when invalid argument is specified', function() {
var err;
subject.connect({ host: host, port: port });
try {
subject.write({});
} catch(e) {
err = e;
}
assert.ok(err instanceof Error);
});
});
suite('#setDebugLog', function() {
test('succeeds', function() {
subject.setDebugLog(true);
});
test('error is thrown with invalid argument count', function() {
var err;
try {
subject.setDebugLog();
} catch(e) {
err = e;
}
assert.ok(err instanceof Error);
});
test('error is thrown with bad argument type', function() {
var err;
try {
subject.setDebugLog('a string is not the right type');
} catch(e) {
err = e;
}
assert.ok(err instanceof Error);
});
});
});

View file

@ -0,0 +1,65 @@
'use strict';
var net = require('net');
var server = {
_port: 44321,
_host: '127.0.0.1',
_server: null,
_client: null,
_accumulator: null,
start: function() {
this._server = net.createServer(this.connection.bind(this));
this._server.listen(this._port, this._host, function() {
process.send({ reply: 'started' });
});
},
connection: function(socket) {
this._client = socket;
this._accumulator = '';
process.send({ reply: 'connected' });
},
send: function(message) {
this._client.write(message);
},
recv: function(expected) {
this._client.setEncoding('utf8');
this._client.on('data', function(buffer) {
this._accumulator += buffer;
if(this._accumulator == expected) {
process.send({ reply: 'expected' });
}
}.bind(this));
},
stop: function() {
this._server.close(function() {
process.send({ reply: 'stopped' });
});
}
};
process.on('message', function(message) {
switch(message.command) {
case 'start':
server.start();
break;
case 'send':
server.send(message.data);
break;
case 'recv':
server.recv(message.data);
break;
case 'stop':
server.stop();
break;
}
});

View file

@ -0,0 +1,70 @@
#!/usr/bin/env node
'use strict';
var exec = require('child_process').exec;
/**
* Checks which sockit-to-me binary we need. This will be null unless
*
* 1. Platform is linux
* 2. gcc not installed
* 3. Linux version is ubuntu 12.04
*
* @param {Function} callback will be invoked with null, 'i386', or 'amd64'.
*/
function getRequiredVersion(callback) {
if (process.platform !== 'linux') {
console.log('[sockit-to-me] platform not linux');
return callback(null);
}
exec('type g++', function(err, stdout) {
if (stdout.indexOf('g++ is') !== -1) {
console.log('[sockit-to-me] found g++');
return callback(null);
}
exec('lsb_release -a', function(err, stdout) {
if (stdout.indexOf('precise') === -1) {
console.log('[sockit-to-me] distribution not precise');
return callback(null);
}
// Choose between 32 and 64 bit versions.
exec('uname -a', function(err, stdout) {
var result;
if (stdout.indexOf('i386') !== -1) {
result = 'i386';
} else if (stdout.indexOf('x86_64') !== -1) {
result = 'amd64';
} else {
result = null;
}
return callback(result);
});
});
});
}
function main() {
getRequiredVersion(function(version) {
if (version === null) {
// We can build on the fly.
process.exit(1);
}
// Copy an existing binary into build/Release.
console.log('[sockit-to-me] will use existing binary');
console.log('[sockit-to-me] ' + version);
exec('mkdir -p build', function() {
var source = 'bin/ubuntu-12.04.3-' + version;
var dest = 'build/Release';
var cp = ['cp', '-r', source, dest].join(' ');
exec(cp, function() {});
});
});
}
if (require.main === module) {
main();
}

View file

@ -0,0 +1,28 @@
#!/usr/bin/env node
'use strict';
var exec = require('child_process').exec;
function main() {
var info = {
__dirname: __dirname,
__filename: __filename,
process: {
arch: process.arch,
config: process.config,
cwd: process.cwd(),
env: process.env,
execArgv: process.execArgv,
platform: process.platform
}
};
exec('whoami', function(err, stdout, stderr) {
info.whoami = stdout.replace(/\s+/g, '');
console.log('[sockit-to-me] ' + JSON.stringify(info, null, 4));
process.exit(1);
});
}
if (require.main === module) {
main();
}

View file

@ -0,0 +1,19 @@
#!/usr/bin/env bash
# curl -L https://raw.github.com/mozilla-b2g/sockit-to-me/master/tools/prebuild.sh | bash -s stable
# This script prebuilds nodejs and sockit-to-me on ubuntu since we can't do
# it on the fly on the build machines (they don't have gcc).
UBUNTU=/home/ubuntu
sudo apt-get -y update --fix-missing
sudo apt-get -y install g++ gcc git make
cd $UBUNTU && git clone https://github.com/joyent/node
cd $UBUNTU && git clone https://github.com/mozilla-b2g/sockit-to-me
# Build nodejs.
cd $UBUNTU/node && git checkout v0.10.21 && ./configure && make && sudo make install
# Install node-gyp.
sudo npm install -g node-gyp
# Build sockit-to-me.
cd $UBUNTU/sockit-to-me && node-gyp configure build