forked from cory/tildefriends
sandboxos => tildefriends
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3157 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
190
src/Database.cpp
Normal file
190
src/Database.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
#include "Database.h"
|
||||
|
||||
#include "Task.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <sstream>
|
||||
|
||||
int Database::_count = 0;
|
||||
|
||||
Database::Database(Task* task) {
|
||||
++_count;
|
||||
|
||||
_task = task;
|
||||
|
||||
v8::Handle<v8::External> data = v8::External::New(task->getIsolate(), this);
|
||||
|
||||
v8::Local<v8::ObjectTemplate> databaseTemplate = v8::ObjectTemplate::New(task->getIsolate());
|
||||
databaseTemplate->SetInternalFieldCount(1);
|
||||
databaseTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "get"), v8::FunctionTemplate::New(task->getIsolate(), get, data));
|
||||
databaseTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "set"), v8::FunctionTemplate::New(task->getIsolate(), set, data));
|
||||
databaseTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "remove"), v8::FunctionTemplate::New(task->getIsolate(), remove, data));
|
||||
databaseTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "getAll"), v8::FunctionTemplate::New(task->getIsolate(), getAll, data));
|
||||
|
||||
v8::Local<v8::Object> databaseObject = databaseTemplate->NewInstance();
|
||||
databaseObject->SetInternalField(0, v8::External::New(task->getIsolate(), this));
|
||||
_object = v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> >(task->getIsolate(), databaseObject);
|
||||
}
|
||||
|
||||
Database::~Database() {
|
||||
--_count;
|
||||
}
|
||||
|
||||
void Database::create(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope handleScope(args.GetIsolate());
|
||||
if (Database* database = new Database(Task::get(args.GetIsolate()))) {
|
||||
if (database->open(args.GetIsolate(), *v8::String::Utf8Value(args[0].As<v8::String>()))) {
|
||||
v8::Handle<v8::Object> result = v8::Local<v8::Object>::New(args.GetIsolate(), database->_object);
|
||||
args.GetReturnValue().Set(result);
|
||||
}
|
||||
database->release();
|
||||
}
|
||||
}
|
||||
|
||||
bool Database::checkError(const char* command, int result) {
|
||||
bool isError = false;
|
||||
if (result != MDB_SUCCESS) {
|
||||
isError = true;
|
||||
|
||||
std::ostringstream buffer;
|
||||
buffer << command << " failed (" << result << "): " << mdb_strerror(result);
|
||||
_task->getIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(_task->getIsolate(), buffer.str().c_str())));
|
||||
}
|
||||
return isError;
|
||||
}
|
||||
|
||||
bool Database::open(v8::Isolate* isolate, const char* path) {
|
||||
int result = mdb_env_create(&_environment);
|
||||
if (checkError("mdb_env_create", result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
result = mdb_env_set_maxdbs(_environment, 10);
|
||||
checkError("mdb_env_set_maxdbs", result);
|
||||
|
||||
result = mdb_env_open(_environment, path, 0, 0644);
|
||||
if (!checkError("mdb_env_open", result)) {
|
||||
result = mdb_txn_begin(_environment, 0, 0, &_transaction);
|
||||
if (!checkError("mdb_txn_begin", result)) {
|
||||
result = mdb_dbi_open(_transaction, NULL, MDB_CREATE, &_database);
|
||||
if (!checkError("mdb_dbi_open", result)) {
|
||||
result = mdb_txn_commit(_transaction);
|
||||
checkError("mdb_txn_commit", result);
|
||||
}
|
||||
}
|
||||
|
||||
if (result != MDB_SUCCESS) {
|
||||
mdb_txn_abort(_transaction);
|
||||
}
|
||||
}
|
||||
|
||||
if (result != MDB_SUCCESS) {
|
||||
mdb_env_close(_environment);
|
||||
}
|
||||
|
||||
return result == MDB_SUCCESS;
|
||||
}
|
||||
|
||||
void Database::get(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Database* database = Database::get(args.Data())) {
|
||||
int result = mdb_txn_begin(database->_environment, 0, MDB_RDONLY, &database->_transaction);
|
||||
if (!database->checkError("mdb_txn_begin", result)) {
|
||||
MDB_val key;
|
||||
MDB_val value;
|
||||
v8::String::Utf8Value keyString(args[0].As<v8::String>());
|
||||
key.mv_data = *keyString;
|
||||
key.mv_size = keyString.length();
|
||||
if (mdb_get(database->_transaction, database->_database, &key, &value) == MDB_SUCCESS) {
|
||||
args.GetReturnValue().Set(v8::String::NewFromUtf8(args.GetIsolate(), reinterpret_cast<const char*>(value.mv_data), v8::String::kNormalString, value.mv_size));
|
||||
}
|
||||
mdb_txn_reset(database->_transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Database::set(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Database* database = Database::get(args.Data())) {
|
||||
int result = mdb_txn_begin(database->_environment, 0, 0, &database->_transaction);
|
||||
if (!database->checkError("mdb_txn_begin", result)) {
|
||||
MDB_val key;
|
||||
MDB_val data;
|
||||
v8::String::Utf8Value keyString(args[0].As<v8::String>());
|
||||
key.mv_data = *keyString;
|
||||
key.mv_size = keyString.length();
|
||||
v8::String::Utf8Value valueString(args[1]->ToString(args.GetIsolate()));
|
||||
data.mv_data = *valueString;
|
||||
data.mv_size = valueString.length();
|
||||
result = mdb_put(database->_transaction, database->_database, &key, &data, 0);
|
||||
database->checkError("mdb_put", result);
|
||||
mdb_txn_commit(database->_transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Database::remove(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Database* database = Database::get(args.Data())) {
|
||||
int result = mdb_txn_begin(database->_environment, 0, 0, &database->_transaction);
|
||||
if (!database->checkError("mdb_txn_begin", result)) {
|
||||
MDB_val key;
|
||||
v8::String::Utf8Value keyString(args[0].As<v8::String>());
|
||||
key.mv_data = *keyString;
|
||||
key.mv_size = keyString.length();
|
||||
result = mdb_del(database->_transaction, database->_database, &key, 0);
|
||||
database->checkError("mdb_del", result);
|
||||
mdb_txn_commit(database->_transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Database::getAll(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Database* database = Database::get(args.Data())) {
|
||||
int result = mdb_txn_begin(database->_environment, 0, MDB_RDONLY, &database->_transaction);
|
||||
if (!database->checkError("mdb_txn_begin", result)) {
|
||||
MDB_cursor* cursor;
|
||||
result = mdb_cursor_open(database->_transaction, database->_database, &cursor);
|
||||
if (!database->checkError("mdb_cursor_open", result)) {
|
||||
int expectedCount = 0;
|
||||
MDB_stat statistics;
|
||||
if (mdb_stat(database->_transaction, database->_database, &statistics) == 0) {
|
||||
expectedCount = statistics.ms_entries;
|
||||
}
|
||||
v8::Local<v8::Array> array = v8::Array::New(args.GetIsolate(), expectedCount);
|
||||
|
||||
MDB_val key;
|
||||
int index = 0;
|
||||
while ((result = mdb_cursor_get(cursor, &key, 0, MDB_NEXT)) == 0) {
|
||||
array->Set(index++, v8::String::NewFromUtf8(args.GetIsolate(), reinterpret_cast<const char*>(key.mv_data), v8::String::kNormalString, key.mv_size));
|
||||
}
|
||||
if (result == MDB_NOTFOUND) {
|
||||
args.GetReturnValue().Set(array);
|
||||
} else {
|
||||
database->checkError("mdb_cursor_get", result);
|
||||
}
|
||||
mdb_cursor_close(cursor);
|
||||
}
|
||||
mdb_txn_reset(database->_transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Database::onRelease(const v8::WeakCallbackData<v8::Object, Database>& data) {
|
||||
data.GetParameter()->_object.Reset();
|
||||
delete data.GetParameter();
|
||||
}
|
||||
|
||||
void Database::ref() {
|
||||
if (++_refCount == 1) {
|
||||
_object.ClearWeak();
|
||||
}
|
||||
}
|
||||
|
||||
void Database::release() {
|
||||
assert(_refCount >= 1);
|
||||
if (--_refCount == 0) {
|
||||
_object.SetWeak(this, onRelease);
|
||||
}
|
||||
}
|
||||
|
||||
Database* Database::get(v8::Handle<v8::Value> databaseObject) {
|
||||
return reinterpret_cast<Database*>(v8::Handle<v8::External>::Cast(databaseObject)->Value());
|
||||
}
|
44
src/Database.h
Normal file
44
src/Database.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef INCLUDED_Database
|
||||
#define INCLUDED_Database
|
||||
|
||||
#include <lmdb.h>
|
||||
#include <v8.h>
|
||||
|
||||
class Task;
|
||||
|
||||
class Database {
|
||||
public:
|
||||
static void create(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static int getCount() { return _count; }
|
||||
|
||||
private:
|
||||
Database(Task* task);
|
||||
~Database();
|
||||
|
||||
Task* _task;
|
||||
int _refCount = 1;
|
||||
v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> > _object;
|
||||
|
||||
MDB_env* _environment;
|
||||
MDB_dbi _database;
|
||||
MDB_txn* _transaction;
|
||||
|
||||
static int _count;
|
||||
|
||||
static Database* get(v8::Handle<v8::Value> databaseObject);
|
||||
static void onRelease(const v8::WeakCallbackData<v8::Object, Database>& data);
|
||||
|
||||
static void get(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void set(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void remove(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void getAll(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
bool open(v8::Isolate* isolate, const char* path);
|
||||
|
||||
bool checkError(const char* command, int result);
|
||||
|
||||
void ref();
|
||||
void release();
|
||||
};
|
||||
|
||||
#endif
|
129
src/File.cpp
Normal file
129
src/File.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
#include "File.h"
|
||||
|
||||
#include "Task.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <uv.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
void File::configure(v8::Isolate* isolate, v8::Handle<v8::ObjectTemplate> global) {
|
||||
v8::Local<v8::ObjectTemplate> fileTemplate = v8::ObjectTemplate::New(isolate);
|
||||
fileTemplate->Set(v8::String::NewFromUtf8(isolate, "readFile"), v8::FunctionTemplate::New(isolate, readFile));
|
||||
fileTemplate->Set(v8::String::NewFromUtf8(isolate, "readDirectory"), v8::FunctionTemplate::New(isolate, readDirectory));
|
||||
fileTemplate->Set(v8::String::NewFromUtf8(isolate, "makeDirectory"), v8::FunctionTemplate::New(isolate, makeDirectory));
|
||||
fileTemplate->Set(v8::String::NewFromUtf8(isolate, "writeFile"), v8::FunctionTemplate::New(isolate, writeFile));
|
||||
fileTemplate->Set(v8::String::NewFromUtf8(isolate, "renameFile"), v8::FunctionTemplate::New(isolate, renameFile));
|
||||
fileTemplate->Set(v8::String::NewFromUtf8(isolate, "unlinkFile"), v8::FunctionTemplate::New(isolate, unlinkFile));
|
||||
global->Set(v8::String::NewFromUtf8(isolate, "File"), fileTemplate);
|
||||
}
|
||||
|
||||
void File::readFile(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
v8::Handle<v8::String> fileName = args[0]->ToString();
|
||||
|
||||
v8::String::Utf8Value utf8FileName(fileName);
|
||||
std::ifstream file(*utf8FileName, std::ios_base::in | std::ios_base::binary | std::ios_base::ate);
|
||||
std::streampos fileSize = file.tellg();
|
||||
if (fileSize >= 0 && fileSize < 4 * 1024 * 1024) {
|
||||
file.seekg(0, std::ios_base::beg);
|
||||
char* buffer = new char[fileSize];
|
||||
file.read(buffer, fileSize);
|
||||
std::string contents(buffer, buffer + fileSize);
|
||||
args.GetReturnValue().Set(v8::String::NewFromOneByte(args.GetIsolate(), reinterpret_cast<const uint8_t*>(buffer), v8::String::kNormalString, fileSize));
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
|
||||
void File::writeFile(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
v8::Handle<v8::String> fileName = args[0]->ToString();
|
||||
v8::Handle<v8::String> contents = args[1]->ToString();
|
||||
|
||||
v8::String::Utf8Value utf8FileName(fileName);
|
||||
std::ofstream file(*utf8FileName, std::ios_base::out | std::ios_base::binary);
|
||||
|
||||
if (contents->ContainsOnlyOneByte()) {
|
||||
std::vector<uint8_t> bytes(contents->Length());
|
||||
contents->WriteOneByte(bytes.data(), 0, bytes.size(), v8::String::NO_NULL_TERMINATION);
|
||||
if (!file.write(reinterpret_cast<const char*>(bytes.data()), bytes.size())) {
|
||||
args.GetReturnValue().Set(v8::Integer::New(args.GetIsolate(), -1));
|
||||
}
|
||||
} else {
|
||||
v8::String::Utf8Value utf8Contents(contents);
|
||||
if (!file.write(*utf8Contents, utf8Contents.length())) {
|
||||
args.GetReturnValue().Set(v8::Integer::New(args.GetIsolate(), -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void File::renameFile(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
|
||||
v8::String::Utf8Value oldName(args[0]->ToString());
|
||||
v8::String::Utf8Value newName(args[1]->ToString());
|
||||
|
||||
uv_fs_t req;
|
||||
int result = uv_fs_rename(task->getLoop(), &req, *oldName, *newName, 0);
|
||||
args.GetReturnValue().Set(v8::Integer::New(args.GetIsolate(), result));
|
||||
}
|
||||
|
||||
void File::unlinkFile(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
|
||||
v8::String::Utf8Value fileName(args[0]->ToString());
|
||||
|
||||
uv_fs_t req;
|
||||
int result = uv_fs_unlink(task->getLoop(), &req, *fileName, 0);
|
||||
args.GetReturnValue().Set(v8::Integer::New(args.GetIsolate(), result));
|
||||
}
|
||||
|
||||
void File::readDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
v8::Handle<v8::String> directory = args[0]->ToString();
|
||||
|
||||
v8::Handle<v8::Array> array = v8::Array::New(args.GetIsolate(), 0);
|
||||
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATA find;
|
||||
std::string pattern = *v8::String::Utf8Value(directory);
|
||||
pattern += "\\*";
|
||||
HANDLE handle = FindFirstFile(pattern.c_str(), &find);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
int index = 0;
|
||||
do {
|
||||
array->Set(v8::Integer::New(args.GetIsolate(), index++), v8::String::NewFromUtf8(args.GetIsolate(), find.cFileName));
|
||||
} while (FindNextFile(handle, &find) != 0);
|
||||
FindClose(handle);
|
||||
}
|
||||
#else
|
||||
if (DIR* dir = opendir(*v8::String::Utf8Value(directory))) {
|
||||
int index = 0;
|
||||
while (struct dirent* entry = readdir(dir)) {
|
||||
array->Set(v8::Integer::New(args.GetIsolate(), index++), v8::String::NewFromUtf8(args.GetIsolate(), entry->d_name));
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
#endif
|
||||
|
||||
args.GetReturnValue().Set(array);
|
||||
}
|
||||
|
||||
void File::makeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Task* task = Task::get(args.GetIsolate());
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
v8::Handle<v8::String> directory = args[0]->ToString();
|
||||
|
||||
uv_fs_t req;
|
||||
int result = uv_fs_mkdir(task->getLoop(), &req, *v8::String::Utf8Value(directory), 0755, 0);
|
||||
args.GetReturnValue().Set(result);
|
||||
}
|
19
src/File.h
Normal file
19
src/File.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef INCLUDED_File
|
||||
#define INCLUDED_File
|
||||
|
||||
#include <v8.h>
|
||||
|
||||
class File {
|
||||
public:
|
||||
static void configure(v8::Isolate* isolate, v8::Handle<v8::ObjectTemplate> global);
|
||||
|
||||
private:
|
||||
static void readFile(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void writeFile(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void readDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void makeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void unlinkFile(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void renameFile(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
};
|
||||
|
||||
#endif
|
32
src/Mutex.cpp
Normal file
32
src/Mutex.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "Mutex.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <assert.h>
|
||||
|
||||
Mutex::Mutex() {
|
||||
int result = uv_mutex_init(&_mutex);
|
||||
if (result != 0) {
|
||||
assert("Mutex lock failed.");
|
||||
}
|
||||
}
|
||||
|
||||
Mutex::~Mutex() {
|
||||
uv_mutex_destroy(&_mutex);
|
||||
}
|
||||
|
||||
void Mutex::lock() {
|
||||
uv_mutex_lock(&_mutex);
|
||||
}
|
||||
|
||||
void Mutex::unlock() {
|
||||
uv_mutex_unlock(&_mutex);
|
||||
}
|
||||
|
||||
Lock::Lock(Mutex& mutex)
|
||||
: _mutex(mutex) {
|
||||
_mutex.lock();
|
||||
}
|
||||
|
||||
Lock::~Lock() {
|
||||
_mutex.unlock();
|
||||
}
|
26
src/Mutex.h
Normal file
26
src/Mutex.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef INCLUDED_Mutex
|
||||
#define INCLUDED_Mutex
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
class Mutex {
|
||||
public:
|
||||
Mutex();
|
||||
~Mutex();
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
uv_mutex_t _mutex;
|
||||
};
|
||||
|
||||
class Lock {
|
||||
public:
|
||||
Lock(Mutex& mutex);
|
||||
~Lock();
|
||||
private:
|
||||
Mutex& _mutex;
|
||||
};
|
||||
|
||||
#endif
|
85
src/PacketStream.cpp
Normal file
85
src/PacketStream.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
#include "PacketStream.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
PacketStream::PacketStream()
|
||||
: _onReceive(0),
|
||||
_onReceiveUserData(0) {
|
||||
}
|
||||
|
||||
PacketStream::~PacketStream() {
|
||||
_onReceive = 0;
|
||||
_onReceiveUserData = 0;
|
||||
close();
|
||||
}
|
||||
|
||||
void PacketStream::close() {
|
||||
if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(&_stream))) {
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&_stream), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void PacketStream::start() {
|
||||
_stream.data = this;
|
||||
uv_read_start(reinterpret_cast<uv_stream_t*>(&_stream), onAllocate, onRead);
|
||||
}
|
||||
|
||||
void PacketStream::send(int packetType, char* begin, size_t length) {
|
||||
size_t bufferLength = sizeof(uv_write_t) + sizeof(packetType) + sizeof(length) + length;
|
||||
char* buffer = new char[bufferLength];
|
||||
uv_write_t* request = reinterpret_cast<uv_write_t*>(buffer);
|
||||
buffer += sizeof(uv_write_t);
|
||||
memcpy(buffer, &packetType, sizeof(packetType));
|
||||
memcpy(buffer + sizeof(packetType), &length, sizeof(length));
|
||||
memcpy(buffer + sizeof(packetType) + sizeof(length), begin, length);
|
||||
uv_buf_t writeBuffer;
|
||||
writeBuffer.base = buffer;
|
||||
writeBuffer.len = sizeof(packetType) + sizeof(length) + length;
|
||||
uv_write(request, reinterpret_cast<uv_stream_t*>(&_stream), &writeBuffer, 1, onWrite);
|
||||
}
|
||||
|
||||
void PacketStream::setOnReceive(OnReceive* onReceiveCallback, void* userData) {
|
||||
_onReceive = onReceiveCallback;
|
||||
_onReceiveUserData = userData;
|
||||
}
|
||||
|
||||
void PacketStream::onWrite(uv_write_t* request, int status) {
|
||||
delete[] reinterpret_cast<char*>(request);
|
||||
}
|
||||
|
||||
void PacketStream::onAllocate(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buffer) {
|
||||
buffer->base = new char[suggestedSize];
|
||||
buffer->len = suggestedSize;
|
||||
}
|
||||
|
||||
void PacketStream::onRead(uv_stream_t* handle, ssize_t count, const uv_buf_t* buffer) {
|
||||
PacketStream* owner = reinterpret_cast<PacketStream*>(handle->data);
|
||||
if (count >= 0) {
|
||||
if (count > 0) {
|
||||
owner->_buffer.insert(owner->_buffer.end(), buffer->base, buffer->base + count);
|
||||
owner->processMessages();
|
||||
}
|
||||
delete[] reinterpret_cast<char*>(buffer->base);
|
||||
} else {
|
||||
owner->close();
|
||||
}
|
||||
}
|
||||
|
||||
void PacketStream::processMessages() {
|
||||
int packetType = 0;
|
||||
size_t length = 0;
|
||||
while (_buffer.size() >= sizeof(packetType) + sizeof(length)) {
|
||||
memcpy(&packetType, &*_buffer.begin(), sizeof(packetType));
|
||||
memcpy(&length, &*_buffer.begin() + sizeof(packetType), sizeof(length));
|
||||
|
||||
if (_buffer.size() >= sizeof(packetType) + sizeof(length) + length) {
|
||||
if (_onReceive) {
|
||||
_onReceive(packetType, &*_buffer.begin() + sizeof(length) + sizeof(packetType), length, _onReceiveUserData);
|
||||
}
|
||||
_buffer.erase(_buffer.begin(), _buffer.begin() + sizeof(length) + sizeof(packetType) + length);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
34
src/PacketStream.h
Normal file
34
src/PacketStream.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef INCLUDED_PacketStream
|
||||
#define INCLUDED_PacketStream
|
||||
|
||||
#include <uv.h>
|
||||
#include <vector>
|
||||
|
||||
class PacketStream {
|
||||
public:
|
||||
PacketStream();
|
||||
~PacketStream();
|
||||
|
||||
void start();
|
||||
|
||||
typedef void (OnReceive)(int packetType, const char* begin, size_t length, void* userData);
|
||||
void send(int packetType, char* begin, size_t length);
|
||||
void setOnReceive(OnReceive* onReceiveCallback, void* userData);
|
||||
void close();
|
||||
|
||||
uv_pipe_t& getStream() { return _stream; }
|
||||
|
||||
private:
|
||||
OnReceive* _onReceive;
|
||||
void* _onReceiveUserData;
|
||||
uv_pipe_t _stream;
|
||||
std::vector<char> _buffer;
|
||||
|
||||
void processMessages();
|
||||
|
||||
static void onAllocate(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buffer);
|
||||
static void onRead(uv_stream_t* handle, ssize_t count, const uv_buf_t* buffer);
|
||||
static void onWrite(uv_write_t* request, int status);
|
||||
};
|
||||
|
||||
#endif
|
210
src/Serialize.cpp
Normal file
210
src/Serialize.cpp
Normal file
@ -0,0 +1,210 @@
|
||||
#include "Serialize.h"
|
||||
|
||||
#include "Task.h"
|
||||
#include "TaskStub.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
void Serialize::writeInt8(std::vector<char>& buffer, int8_t value) {
|
||||
buffer.insert(buffer.end(), value);
|
||||
}
|
||||
|
||||
void Serialize::writeInt32(std::vector<char>& buffer, int32_t value) {
|
||||
const char* p = reinterpret_cast<char*>(&value);
|
||||
buffer.insert(buffer.end(), p, p + sizeof(value));
|
||||
}
|
||||
|
||||
void Serialize::writeUint32(std::vector<char>& buffer, uint32_t value) {
|
||||
const char* p = reinterpret_cast<char*>(&value);
|
||||
buffer.insert(buffer.end(), p, p + sizeof(value));
|
||||
}
|
||||
|
||||
void Serialize::writeDouble(std::vector<char>& buffer, double value) {
|
||||
const char* p = reinterpret_cast<char*>(&value);
|
||||
buffer.insert(buffer.end(), p, p + sizeof(value));
|
||||
}
|
||||
|
||||
int8_t Serialize::readInt8(const std::vector<char>& buffer, int& offset) {
|
||||
int8_t result;
|
||||
std::memcpy(&result, &*buffer.begin() + offset, sizeof(result));
|
||||
offset += sizeof(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t Serialize::readInt32(const std::vector<char>& buffer, int& offset) {
|
||||
int32_t result;
|
||||
std::memcpy(&result, &*buffer.begin() + offset, sizeof(result));
|
||||
offset += sizeof(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t Serialize::readUint32(const std::vector<char>& buffer, int& offset) {
|
||||
uint32_t result;
|
||||
std::memcpy(&result, &*buffer.begin() + offset, sizeof(result));
|
||||
offset += sizeof(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
double Serialize::readDouble(const std::vector<char>& buffer, int& offset) {
|
||||
double result;
|
||||
std::memcpy(&result, &*buffer.begin() + offset, sizeof(result));
|
||||
offset += sizeof(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Serialize::store(Task* task, std::vector<char>& buffer, v8::Handle<v8::Value> value) {
|
||||
return storeInternal(task, buffer, value, 0);
|
||||
}
|
||||
|
||||
bool Serialize::storeInternal(Task* task, std::vector<char>& buffer, v8::Handle<v8::Value> value, int depth) {
|
||||
if (value.IsEmpty()) {
|
||||
return false;
|
||||
} else if (value->IsUndefined()) {
|
||||
writeInt32(buffer, kUndefined);
|
||||
} else if (value->IsNull()) {
|
||||
writeInt32(buffer, kNull);
|
||||
} else if (value->IsBoolean()) {
|
||||
writeInt32(buffer, kBoolean);
|
||||
writeInt8(buffer, value->IsTrue() ? 1 : 0);
|
||||
} else if (value->IsInt32()) {
|
||||
writeInt32(buffer, kInt32);
|
||||
writeInt32(buffer, value->Int32Value());
|
||||
} else if (value->IsUint32()) {
|
||||
writeInt32(buffer, kUint32);
|
||||
writeInt32(buffer, value->Uint32Value());
|
||||
} else if (value->IsNumber()) {
|
||||
writeInt32(buffer, kNumber);
|
||||
writeDouble(buffer, value->NumberValue());
|
||||
} else if (value->IsString()) {
|
||||
writeInt32(buffer, kString);
|
||||
v8::String::Utf8Value utf8(value->ToString());
|
||||
writeInt32(buffer, utf8.length());
|
||||
buffer.insert(buffer.end(), *utf8, *utf8 + utf8.length());
|
||||
} else if (value->IsArray()) {
|
||||
writeInt32(buffer, kArray);
|
||||
v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
|
||||
writeInt32(buffer, array->Length());
|
||||
for (size_t i = 0; i < array->Length(); ++i) {
|
||||
storeInternal(task, buffer, array->Get(i), depth + 1);
|
||||
}
|
||||
} else if (value->IsFunction()) {
|
||||
writeInt32(buffer, kFunction);
|
||||
exportid_t exportId = task->exportFunction(v8::Handle<v8::Function>::Cast(value));
|
||||
writeInt32(buffer, exportId);
|
||||
} else if (value->IsNativeError()) {
|
||||
storeInternal(task, buffer, storeMessage(task, v8::Exception::CreateMessage(value)), depth);
|
||||
} else if (value->IsObject()) {
|
||||
writeInt32(buffer, kObject);
|
||||
v8::Handle<v8::Object> object = value->ToObject();
|
||||
|
||||
// XXX: For some reason IsNativeError isn't working reliably. Catch an
|
||||
// object that still looks like an error object and treat it as such.
|
||||
if (object->GetOwnPropertyNames()->Length() == 0
|
||||
&& !object->Get(v8::String::NewFromUtf8(task->getIsolate(), "stackTrace")).IsEmpty()) {
|
||||
object = v8::Handle<v8::Object>::Cast(storeMessage(task, v8::Exception::CreateMessage(value)));
|
||||
}
|
||||
|
||||
v8::Handle<v8::Array> keys = object->GetOwnPropertyNames();
|
||||
writeInt32(buffer, keys->Length());
|
||||
for (size_t i = 0; i < keys->Length(); ++i) {
|
||||
v8::Handle<v8::Value> key = keys->Get(i);
|
||||
storeInternal(task, buffer, key, depth + 1);
|
||||
storeInternal(task, buffer, object->Get(key), depth + 1);
|
||||
}
|
||||
} else {
|
||||
writeInt32(buffer, kString);
|
||||
v8::String::Utf8Value utf8(value->ToString());
|
||||
writeInt32(buffer, utf8.length());
|
||||
buffer.insert(buffer.end(), *utf8, *utf8 + utf8.length());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
v8::Handle<v8::Value> Serialize::store(Task* task, v8::TryCatch& tryCatch) {
|
||||
return storeMessage(task, tryCatch.Message());
|
||||
}
|
||||
|
||||
v8::Handle<v8::Object> Serialize::storeMessage(Task* task, v8::Handle<v8::Message> message) {
|
||||
v8::Handle<v8::Object> error = v8::Object::New(task->getIsolate());
|
||||
error->Set(v8::String::NewFromUtf8(task->getIsolate(), "message"), message->Get());
|
||||
error->Set(v8::String::NewFromUtf8(task->getIsolate(), "fileName"), message->GetScriptResourceName());
|
||||
error->Set(v8::String::NewFromUtf8(task->getIsolate(), "lineNumber"), v8::Integer::New(task->getIsolate(), message->GetLineNumber()));
|
||||
error->Set(v8::String::NewFromUtf8(task->getIsolate(), "sourceLine"), message->GetSourceLine());
|
||||
if (!message->GetStackTrace().IsEmpty()) {
|
||||
error->Set(v8::String::NewFromUtf8(task->getIsolate(), "stackTrace"), message->GetStackTrace()->AsArray());
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
v8::Handle<v8::Value> Serialize::load(Task* task, TaskStub* from, const std::vector<char>& buffer) {
|
||||
int offset = 0;
|
||||
return loadInternal(task, from, buffer, offset, 0);
|
||||
}
|
||||
|
||||
v8::Handle<v8::Value> Serialize::loadInternal(Task* task, TaskStub* from, const std::vector<char>& buffer, int& offset, int depth) {
|
||||
if (static_cast<size_t>(offset) >= buffer.size()) {
|
||||
return v8::Undefined(task->getIsolate());
|
||||
} else {
|
||||
int32_t type = readInt32(buffer, offset);
|
||||
v8::Handle<v8::Value> result;
|
||||
|
||||
switch (type) {
|
||||
case kUndefined:
|
||||
result = v8::Undefined(task->getIsolate());
|
||||
break;
|
||||
case kNull:
|
||||
result = v8::Null(task->getIsolate());
|
||||
break;
|
||||
case kBoolean:
|
||||
result = v8::Boolean::New(task->getIsolate(), readInt8(buffer, offset) != 0);
|
||||
break;
|
||||
case kInt32:
|
||||
result = v8::Int32::New(task->getIsolate(), readInt32(buffer, offset));
|
||||
break;
|
||||
case kUint32:
|
||||
result = v8::Uint32::New(task->getIsolate(), readUint32(buffer, offset));
|
||||
break;
|
||||
case kNumber:
|
||||
result = v8::Number::New(task->getIsolate(), readDouble(buffer, offset));
|
||||
break;
|
||||
case kString:
|
||||
{
|
||||
int32_t length = readInt32(buffer, offset);
|
||||
result = v8::String::NewFromUtf8(task->getIsolate(), &*buffer.begin() + offset, v8::String::kNormalString, length);
|
||||
offset += length;
|
||||
}
|
||||
break;
|
||||
case kArray:
|
||||
{
|
||||
int32_t length = readInt32(buffer, offset);
|
||||
v8::Handle<v8::Array> array = v8::Array::New(task->getIsolate());
|
||||
for (int i = 0; i < length; ++i) {
|
||||
v8::Handle<v8::Value> value = loadInternal(task, from, buffer, offset, depth + 1);
|
||||
array->Set(i, value);
|
||||
}
|
||||
result = array;
|
||||
}
|
||||
break;
|
||||
case kFunction:
|
||||
{
|
||||
exportid_t exportId = readInt32(buffer, offset);
|
||||
result = task->addImport(from->getId(), exportId);
|
||||
}
|
||||
break;
|
||||
case kObject:
|
||||
{
|
||||
int32_t length = readInt32(buffer, offset);
|
||||
v8::Handle<v8::Object> object = v8::Object::New(task->getIsolate());
|
||||
for (int i = 0; i < length; ++i) {
|
||||
v8::Handle<v8::Value> key = loadInternal(task, from, buffer, offset, depth + 1);
|
||||
v8::Handle<v8::Value> value = loadInternal(task, from, buffer, offset, depth + 1);
|
||||
object->Set(key, value);
|
||||
}
|
||||
result = object;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
47
src/Serialize.h
Normal file
47
src/Serialize.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef INCLUDED_Serialize
|
||||
#define INCLUDED_Serialize
|
||||
|
||||
#include <v8.h>
|
||||
#include <vector>
|
||||
|
||||
class Task;
|
||||
class TaskStub;
|
||||
|
||||
class Serialize {
|
||||
public:
|
||||
static bool store(Task* task, std::vector<char>& buffer, v8::Handle<v8::Value> value);
|
||||
static v8::Handle<v8::Value> load(Task* task, TaskStub* from, const std::vector<char>& buffer);
|
||||
|
||||
static v8::Handle<v8::Value> store(Task* task, v8::TryCatch& tryCatch);
|
||||
static v8::Handle<v8::Object> storeMessage(Task* task, v8::Handle<v8::Message> message);
|
||||
|
||||
private:
|
||||
static bool storeInternal(Task* task, std::vector<char>& buffer, v8::Handle<v8::Value> value, int depth);
|
||||
static v8::Handle<v8::Value> loadInternal(Task* task, TaskStub* from, const std::vector<char>& buffer, int& offse, int deptht);
|
||||
|
||||
static void writeInt8(std::vector<char>& buffer, int8_t value);
|
||||
static void writeInt32(std::vector<char>& buffer, int32_t value);
|
||||
static void writeUint32(std::vector<char>& buffer, uint32_t value);
|
||||
static void writeDouble(std::vector<char>& buffer, double value);
|
||||
|
||||
static int8_t readInt8(const std::vector<char>& buffer, int& offset);
|
||||
static int32_t readInt32(const std::vector<char>& buffer, int& offset);
|
||||
static uint32_t readUint32(const std::vector<char>& buffer, int& offset);
|
||||
static double readDouble(const std::vector<char>& buffer, int& offset);
|
||||
|
||||
enum Types {
|
||||
kUndefined,
|
||||
kNull,
|
||||
kBoolean,
|
||||
kInt32,
|
||||
kUint32,
|
||||
kNumber,
|
||||
kString,
|
||||
kArray,
|
||||
kObject,
|
||||
kFunction,
|
||||
kError,
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
653
src/Socket.cpp
Normal file
653
src/Socket.cpp
Normal file
@ -0,0 +1,653 @@
|
||||
#include "Socket.h"
|
||||
|
||||
#include "Task.h"
|
||||
#include "TaskTryCatch.h"
|
||||
#include "Tls.h"
|
||||
#include "TlsContextWrapper.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cstring>
|
||||
#include <uv.h>
|
||||
|
||||
int Socket::_count = 0;
|
||||
int Socket::_openCount = 0;
|
||||
TlsContext* Socket::_defaultTlsContext = 0;
|
||||
|
||||
struct SocketResolveData {
|
||||
uv_getaddrinfo_t resolver;
|
||||
Socket* socket;
|
||||
promiseid_t promise;
|
||||
};
|
||||
|
||||
Socket::Socket(Task* task) {
|
||||
v8::HandleScope scope(task->getIsolate());
|
||||
++_count;
|
||||
|
||||
v8::Handle<v8::External> data = v8::External::New(task->getIsolate(), this);
|
||||
|
||||
v8::Local<v8::ObjectTemplate> socketTemplate = v8::ObjectTemplate::New(task->getIsolate());
|
||||
socketTemplate->SetInternalFieldCount(1);
|
||||
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "bind"), v8::FunctionTemplate::New(task->getIsolate(), bind, data));
|
||||
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "connect"), v8::FunctionTemplate::New(task->getIsolate(), connect, data));
|
||||
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "listen"), v8::FunctionTemplate::New(task->getIsolate(), listen, data));
|
||||
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "accept"), v8::FunctionTemplate::New(task->getIsolate(), accept, data));
|
||||
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "startTls"), v8::FunctionTemplate::New(task->getIsolate(), startTls, data));
|
||||
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "stopTls"), v8::FunctionTemplate::New(task->getIsolate(), stopTls, data));
|
||||
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "shutdown"), v8::FunctionTemplate::New(task->getIsolate(), shutdown, data));
|
||||
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "close"), v8::FunctionTemplate::New(task->getIsolate(), close, data));
|
||||
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "read"), v8::FunctionTemplate::New(task->getIsolate(), read, data));
|
||||
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "onError"), v8::FunctionTemplate::New(task->getIsolate(), onError, data));
|
||||
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "write"), v8::FunctionTemplate::New(task->getIsolate(), write, data));
|
||||
socketTemplate->SetAccessor(v8::String::NewFromUtf8(task->getIsolate(), "peerName"), getPeerName, 0, data);
|
||||
socketTemplate->SetAccessor(v8::String::NewFromUtf8(task->getIsolate(), "peerCertificate"), getPeerCertificate, 0, data);
|
||||
socketTemplate->SetAccessor(v8::String::NewFromUtf8(task->getIsolate(), "isConnected"), isConnected, 0, data);
|
||||
|
||||
v8::Local<v8::Object> socketObject = socketTemplate->NewInstance();
|
||||
socketObject->SetInternalField(0, v8::External::New(task->getIsolate(), this));
|
||||
_object = v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> >(task->getIsolate(), socketObject);
|
||||
|
||||
uv_tcp_init(task->getLoop(), &_socket);
|
||||
++_openCount;
|
||||
_socket.data = this;
|
||||
_task = task;
|
||||
}
|
||||
|
||||
Socket::~Socket() {
|
||||
if (_tls) {
|
||||
delete _tls;
|
||||
_tls = 0;
|
||||
}
|
||||
--_count;
|
||||
}
|
||||
|
||||
void Socket::close() {
|
||||
if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(&_socket))) {
|
||||
if (!_onRead.IsEmpty()) {
|
||||
_onRead.Reset();
|
||||
}
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&_socket), onClose);
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::reportError(const char* error) {
|
||||
v8::Handle<v8::Value> exception = v8::Exception::Error(v8::String::NewFromUtf8(_task->getIsolate(), error));
|
||||
if (!_onError.IsEmpty()) {
|
||||
v8::Handle<v8::Function> callback = v8::Local<v8::Function>::New(_task->getIsolate(), _onError);
|
||||
callback->Call(callback, 1, &exception);
|
||||
} else {
|
||||
_task->getIsolate()->ThrowException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::reportTlsErrors() {
|
||||
char buffer[4096];
|
||||
while (_tls && _tls->getError(buffer, sizeof(buffer))) {
|
||||
reportError(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::startTls(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Socket* socket = Socket::get(args.Data())) {
|
||||
if (!socket->_tls) {
|
||||
TlsContext* context = 0;
|
||||
|
||||
if (args.Length() > 0 && !args[0].IsEmpty() && !args[0]->IsUndefined()) {
|
||||
if (TlsContextWrapper* wrapper = TlsContextWrapper::get(args[0])) {
|
||||
context = wrapper->getContext();
|
||||
}
|
||||
} else {
|
||||
if (!_defaultTlsContext) {
|
||||
_defaultTlsContext = TlsContext::create();
|
||||
}
|
||||
context = _defaultTlsContext;
|
||||
}
|
||||
|
||||
if (context) {
|
||||
socket->_tls = context->createSession();
|
||||
}
|
||||
|
||||
if (socket->_tls) {
|
||||
socket->_tls->setHostname(socket->_peerName.c_str());
|
||||
if (socket->_direction == kAccept) {
|
||||
socket->_tls->startAccept();
|
||||
} else if (socket->_direction == kConnect) {
|
||||
socket->_tls->startConnect();
|
||||
}
|
||||
socket->_startTlsPromise = socket->_task->allocatePromise();
|
||||
socket->processOutgoingTls();
|
||||
args.GetReturnValue().Set(socket->_task->getPromise(socket->_startTlsPromise));
|
||||
} else {
|
||||
args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), "Failed to get TLS context")));
|
||||
}
|
||||
} else {
|
||||
args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), "startTls with TLS already started")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::stopTls(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Socket* socket = Socket::get(args.Data())) {
|
||||
if (socket->_tls) {
|
||||
socket->processOutgoingTls();
|
||||
delete socket->_tls;
|
||||
socket->_tls = 0;
|
||||
} else {
|
||||
args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), "stopTls with TLS already stopped")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Socket::processSomeOutgoingTls(promiseid_t promise, uv_write_cb callback) {
|
||||
char buffer[8192];
|
||||
int result = _tls->readEncrypted(buffer, sizeof(buffer));
|
||||
if (result > 0) {
|
||||
char* rawBuffer = new char[sizeof(uv_write_t) + result];
|
||||
uv_write_t* request = reinterpret_cast<uv_write_t*>(rawBuffer);
|
||||
std::memset(request, 0, sizeof(*request));
|
||||
request->data = reinterpret_cast<void*>(promise);
|
||||
rawBuffer += sizeof(uv_write_t);
|
||||
std::memcpy(rawBuffer, buffer, result);
|
||||
|
||||
uv_buf_t writeBuffer;
|
||||
writeBuffer.base = rawBuffer;
|
||||
writeBuffer.len = result;
|
||||
|
||||
int writeResult = uv_write(request, reinterpret_cast<uv_stream_t*>(&_socket), &writeBuffer, 1, callback);
|
||||
if (writeResult != 0) {
|
||||
std::string error = "uv_write: " + std::string(uv_strerror(writeResult));
|
||||
reportError(error.c_str());
|
||||
}
|
||||
} else {
|
||||
reportTlsErrors();
|
||||
}
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
void Socket::processOutgoingTls() {
|
||||
while (processSomeOutgoingTls(-1, onWrite)) {}
|
||||
}
|
||||
|
||||
void Socket::bind(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Socket* socket = Socket::get(args.Data())) {
|
||||
v8::String::Utf8Value node(args[0]->ToString());
|
||||
v8::String::Utf8Value port(args[1]->ToString());
|
||||
|
||||
SocketResolveData* data = new SocketResolveData();
|
||||
std::memset(data, 0, sizeof(*data));
|
||||
struct addrinfo hints;
|
||||
hints.ai_family = PF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_flags = 0;
|
||||
data->resolver.data = data;
|
||||
data->socket = socket;
|
||||
data->promise = socket->_task->allocatePromise();
|
||||
|
||||
int result = uv_getaddrinfo(socket->_task->getLoop(), &data->resolver, onResolvedForBind, *node, *port, &hints);
|
||||
if (result != 0) {
|
||||
std::string error = "uv_getaddrinfo: " + std::string(uv_strerror(result));
|
||||
socket->_task->rejectPromise(data->promise, v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), error.c_str())));
|
||||
delete data;
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(socket->_task->getPromise(data->promise));
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::onResolvedForBind(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result) {
|
||||
SocketResolveData* data = reinterpret_cast<SocketResolveData*>(resolver->data);
|
||||
if (status != 0) {
|
||||
std::string error = "uv_getaddrinfo: " + std::string(uv_strerror(status));
|
||||
data->socket->_task->rejectPromise(data->promise, v8::Exception::Error(v8::String::NewFromUtf8(data->socket->_task->getIsolate(), error.c_str())));
|
||||
} else {
|
||||
int bindResult = uv_tcp_bind(&data->socket->_socket, result->ai_addr, 0);
|
||||
if (bindResult != 0) {
|
||||
std::string error = "uv_tcp_bind: " + std::string(uv_strerror(bindResult));
|
||||
data->socket->_task->rejectPromise(data->promise, v8::Exception::Error(v8::String::NewFromUtf8(data->socket->_task->getIsolate(), error.c_str())));
|
||||
} else {
|
||||
data->socket->_task->resolvePromise(data->promise, v8::Undefined(data->socket->_task->getIsolate()));
|
||||
}
|
||||
}
|
||||
delete data;
|
||||
}
|
||||
|
||||
void Socket::connect(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Socket* socket = Socket::get(args.Data())) {
|
||||
socket->_direction = kConnect;
|
||||
v8::String::Utf8Value node(args[0]->ToString());
|
||||
v8::String::Utf8Value port(args[1]->ToString());
|
||||
|
||||
socket->_peerName = *node;
|
||||
|
||||
promiseid_t promise = socket->_task->allocatePromise();
|
||||
|
||||
SocketResolveData* data = new SocketResolveData();
|
||||
std::memset(data, 0, sizeof(*data));
|
||||
struct addrinfo hints;
|
||||
hints.ai_family = PF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_flags = 0;
|
||||
data->resolver.data = data;
|
||||
data->socket = socket;
|
||||
data->promise = promise;
|
||||
|
||||
int result = uv_getaddrinfo(socket->_task->getLoop(), &data->resolver, onResolvedForConnect, *node, *port, &hints);
|
||||
if (result != 0) {
|
||||
std::string error = "uv_getaddrinfo: " + std::string(uv_strerror(result));
|
||||
socket->_task->rejectPromise(promise, v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), error.c_str())));
|
||||
delete data;
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(socket->_task->getPromise(promise));
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::onResolvedForConnect(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result) {
|
||||
SocketResolveData* data = reinterpret_cast<SocketResolveData*>(resolver->data);
|
||||
if (status != 0) {
|
||||
std::string error = "uv_getaddrinfo: " + std::string(uv_strerror(status));
|
||||
data->socket->_task->rejectPromise(data->promise, v8::Exception::Error(v8::String::NewFromUtf8(data->socket->_task->getIsolate(), error.c_str())));
|
||||
} else {
|
||||
uv_connect_t* request = new uv_connect_t();
|
||||
std::memset(request, 0, sizeof(*request));
|
||||
request->data = reinterpret_cast<void*>(data->promise);
|
||||
int connectResult = uv_tcp_connect(request, &data->socket->_socket, result->ai_addr, onConnect);
|
||||
if (connectResult != 0) {
|
||||
std::string error("uv_tcp_connect: " + std::string(uv_strerror(connectResult)));
|
||||
data->socket->_task->rejectPromise(data->promise, v8::Exception::Error(v8::String::NewFromUtf8(data->socket->_task->getIsolate(), error.c_str())));
|
||||
}
|
||||
}
|
||||
delete data;
|
||||
}
|
||||
|
||||
|
||||
void Socket::onConnect(uv_connect_t* request, int status) {
|
||||
promiseid_t promise = reinterpret_cast<intptr_t>(request->data);
|
||||
if (promise != -1) {
|
||||
Socket* socket = reinterpret_cast<Socket*>(request->handle->data);
|
||||
if (status == 0) {
|
||||
socket->_connected = true;
|
||||
socket->_task->resolvePromise(promise, v8::Integer::New(socket->_task->getIsolate(), status));
|
||||
} else {
|
||||
std::string error("uv_tcp_connect: " + std::string(uv_strerror(status)));
|
||||
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(socket->_task->getIsolate(), error.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::listen(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Socket* socket = Socket::get(args.Data())) {
|
||||
int backlog = args[0]->ToInteger()->Value();
|
||||
if (socket->_onConnect.IsEmpty()) {
|
||||
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > callback(args.GetIsolate(), args[1].As<v8::Function>());
|
||||
socket->_onConnect = callback;
|
||||
int result = uv_listen(reinterpret_cast<uv_stream_t*>(&socket->_socket), backlog, onNewConnection);
|
||||
if (result != 0) {
|
||||
std::string error = "uv_listen: " + std::string(uv_strerror(result));
|
||||
args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), error.c_str(), v8::String::kNormalString, error.size())));
|
||||
}
|
||||
args.GetReturnValue().Set(v8::Integer::New(args.GetIsolate(), result));
|
||||
} else {
|
||||
args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), "listen: Already listening.")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::onNewConnection(uv_stream_t* server, int status) {
|
||||
if (Socket* socket = reinterpret_cast<Socket*>(server->data)) {
|
||||
v8::HandleScope handleScope(socket->_task->getIsolate());
|
||||
TaskTryCatch tryCatch(socket->_task);
|
||||
if (!socket->_onConnect.IsEmpty()) {
|
||||
v8::Handle<v8::Function> callback = v8::Local<v8::Function>::New(socket->_task->getIsolate(), socket->_onConnect);
|
||||
callback->Call(callback, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::accept(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Socket* socket = Socket::get(args.Data())) {
|
||||
v8::HandleScope handleScope(args.GetIsolate());
|
||||
Socket* client = new Socket(socket->_task);
|
||||
client->_direction = kAccept;
|
||||
promiseid_t promise = socket->_task->allocatePromise();
|
||||
v8::Handle<v8::Value> promiseObject = socket->_task->getPromise(promise);
|
||||
v8::Handle<v8::Object> result = v8::Local<v8::Object>::New(args.GetIsolate(), client->_object);
|
||||
int status = uv_accept(reinterpret_cast<uv_stream_t*>(&socket->_socket), reinterpret_cast<uv_stream_t*>(&client->_socket));
|
||||
if (status == 0) {
|
||||
client->_connected = true;
|
||||
socket->_task->resolvePromise(promise, result);
|
||||
} else {
|
||||
std::string error = "uv_accept: " + std::string(uv_strerror(status));
|
||||
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(args.GetIsolate(), error.c_str(), v8::String::kNormalString, error.size()));
|
||||
}
|
||||
args.GetReturnValue().Set(promiseObject);
|
||||
client->release();
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::close(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Socket* socket = Socket::get(args.Data())) {
|
||||
if (socket->_closePromise == -1) {
|
||||
socket->_closePromise = socket->_task->allocatePromise();
|
||||
args.GetReturnValue().Set(socket->_task->getPromise(socket->_closePromise));
|
||||
socket->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::shutdown(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Socket* socket = Socket::get(args.Data())) {
|
||||
if (socket->_tls) {
|
||||
promiseid_t promise = socket->_task->allocatePromise();
|
||||
socket->processTlsShutdown(promise);
|
||||
args.GetReturnValue().Set(socket->_task->getPromise(promise));
|
||||
} else {
|
||||
promiseid_t promise = socket->_task->allocatePromise();
|
||||
socket->shutdownInternal(promise);
|
||||
args.GetReturnValue().Set(socket->_task->getPromise(promise));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::shutdownInternal(promiseid_t promise) {
|
||||
uv_shutdown_t* request = new uv_shutdown_t();
|
||||
std::memset(request, 0, sizeof(*request));
|
||||
request->data = reinterpret_cast<void*>(promise);
|
||||
int result = uv_shutdown(request, reinterpret_cast<uv_stream_t*>(&_socket), onShutdown);
|
||||
if (result != 0) {
|
||||
std::string error = "uv_shutdown: " + std::string(uv_strerror(result));
|
||||
_task->rejectPromise(promise, v8::Exception::Error(v8::String::NewFromUtf8(_task->getIsolate(), error.c_str())));
|
||||
delete request;
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::processTlsShutdown(promiseid_t promise) {
|
||||
_tls->shutdown();
|
||||
if (!processSomeOutgoingTls(promise, onTlsShutdown)) {
|
||||
shutdownInternal(promise);
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::onTlsShutdown(uv_write_t* request, int status) {
|
||||
if (Socket* socket = reinterpret_cast<Socket*>(request->handle->data)) {
|
||||
promiseid_t promise = reinterpret_cast<intptr_t>(request->data);
|
||||
socket->processTlsShutdown(promise);
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::onError(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Socket* socket = Socket::get(args.Data())) {
|
||||
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > callback(args.GetIsolate(), args[0].As<v8::Function>());
|
||||
socket->_onError = callback;
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::read(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Socket* socket = Socket::get(args.Data())) {
|
||||
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > callback(args.GetIsolate(), args[0].As<v8::Function>());
|
||||
socket->_onRead = callback;
|
||||
int result = uv_read_start(reinterpret_cast<uv_stream_t*>(&socket->_socket), allocateBuffer, onRead);
|
||||
promiseid_t promise = socket->_task->allocatePromise();
|
||||
if (result != 0) {
|
||||
std::string error = "uv_read_start: " + std::string(uv_strerror(result));
|
||||
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(socket->_task->getIsolate(), error.c_str(), v8::String::kNormalString, error.size()));
|
||||
} else {
|
||||
socket->_task->resolvePromise(promise, v8::Undefined(socket->_task->getIsolate()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::allocateBuffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf) {
|
||||
*buf = uv_buf_init(new char[suggestedSize], suggestedSize);
|
||||
}
|
||||
|
||||
void Socket::onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffer) {
|
||||
if (Socket* socket = reinterpret_cast<Socket*>(stream->data)) {
|
||||
v8::HandleScope handleScope(socket->_task->getIsolate());
|
||||
TaskTryCatch tryCatch(socket->_task);
|
||||
v8::Handle<v8::Value> data;
|
||||
|
||||
if (readSize <= 0) {
|
||||
socket->_connected = false;
|
||||
v8::Local<v8::Function> callback = v8::Local<v8::Function>::New(socket->_task->getIsolate(), socket->_onRead);
|
||||
if (!callback.IsEmpty()) {
|
||||
data = v8::Undefined(socket->_task->getIsolate());
|
||||
callback->Call(callback, 1, &data);
|
||||
}
|
||||
socket->close();
|
||||
} else {
|
||||
if (socket->_tls) {
|
||||
socket->reportTlsErrors();
|
||||
socket->_tls->writeEncrypted(buffer->base, readSize);
|
||||
if (socket->_startTlsPromise != -1) {
|
||||
TlsSession::HandshakeResult result = socket->_tls->handshake();
|
||||
if (result == TlsSession::kDone) {
|
||||
promiseid_t promise = socket->_startTlsPromise;
|
||||
socket->_startTlsPromise = -1;
|
||||
socket->_task->resolvePromise(promise, v8::Undefined(socket->_task->getIsolate()));
|
||||
} else if (result == TlsSession::kFailed) {
|
||||
promiseid_t promise = socket->_startTlsPromise;
|
||||
socket->_startTlsPromise = -1;
|
||||
char buffer[8192];
|
||||
if (socket->_tls->getError(buffer, sizeof(buffer))) {
|
||||
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(socket->_task->getIsolate(), buffer));
|
||||
} else {
|
||||
socket->_task->rejectPromise(promise, v8::Undefined(socket->_task->getIsolate()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
char plain[8192];
|
||||
int result = socket->_tls->readPlain(plain, sizeof(plain));
|
||||
if (result > 0) {
|
||||
v8::Local<v8::Function> callback = v8::Local<v8::Function>::New(socket->_task->getIsolate(), socket->_onRead);
|
||||
if (!callback.IsEmpty()) {
|
||||
data = v8::String::NewFromOneByte(socket->_task->getIsolate(), reinterpret_cast<const uint8_t*>(plain), v8::String::kNormalString, result);
|
||||
callback->Call(callback, 1, &data);
|
||||
}
|
||||
} else if (result == TlsSession::kReadFailed) {
|
||||
socket->reportTlsErrors();
|
||||
socket->close();
|
||||
break;
|
||||
} else if (result == TlsSession::kReadZero) {
|
||||
v8::Local<v8::Function> callback = v8::Local<v8::Function>::New(socket->_task->getIsolate(), socket->_onRead);
|
||||
if (!callback.IsEmpty()) {
|
||||
data = v8::Undefined(socket->_task->getIsolate());
|
||||
callback->Call(callback, 1, &data);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (socket->_tls) {
|
||||
socket->processOutgoingTls();
|
||||
}
|
||||
} else {
|
||||
v8::Local<v8::Function> callback = v8::Local<v8::Function>::New(socket->_task->getIsolate(), socket->_onRead);
|
||||
if (!callback.IsEmpty()) {
|
||||
data = v8::String::NewFromOneByte(socket->_task->getIsolate(), reinterpret_cast<const uint8_t*>(buffer->base), v8::String::kNormalString, readSize);
|
||||
callback->Call(callback, 1, &data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] buffer->base;
|
||||
}
|
||||
|
||||
void Socket::write(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (Socket* socket = Socket::get(args.Data())) {
|
||||
promiseid_t promise = socket->_task->allocatePromise();
|
||||
args.GetReturnValue().Set(socket->_task->getPromise(promise));
|
||||
v8::Handle<v8::String> value = args[0].As<v8::String>();
|
||||
if (!value.IsEmpty() && value->IsString()) {
|
||||
if (socket->_tls) {
|
||||
socket->reportTlsErrors();
|
||||
int result;
|
||||
int length;
|
||||
if (value->ContainsOnlyOneByte()) {
|
||||
length = value->Length();
|
||||
std::vector<uint8_t> bytes(length);
|
||||
value->WriteOneByte(bytes.data(), 0, bytes.size(), v8::String::NO_NULL_TERMINATION);
|
||||
result = socket->_tls->writePlain(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
||||
} else {
|
||||
v8::String::Utf8Value utf8(value);
|
||||
length = utf8.length();
|
||||
result = socket->_tls->writePlain(*utf8, utf8.length());
|
||||
}
|
||||
char buffer[8192];
|
||||
if (result <= 0 && socket->_tls->getError(buffer, sizeof(buffer))) {
|
||||
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(args.GetIsolate(), buffer));
|
||||
} else if (result < length) {
|
||||
socket->_task->rejectPromise(promise, v8::Integer::New(socket->_task->getIsolate(), result));
|
||||
} else {
|
||||
socket->_task->resolvePromise(promise, v8::Integer::New(socket->_task->getIsolate(), result));
|
||||
}
|
||||
socket->processOutgoingTls();
|
||||
} else {
|
||||
v8::String::Utf8Value utf8(value);
|
||||
int length;
|
||||
char* rawBuffer = 0;
|
||||
if (value->ContainsOnlyOneByte()) {
|
||||
length = value->Length();
|
||||
rawBuffer = new char[sizeof(uv_write_t) + length];
|
||||
value->WriteOneByte(reinterpret_cast<uint8_t*>(rawBuffer) + sizeof(uv_write_t), 0, length, v8::String::NO_NULL_TERMINATION);
|
||||
} else {
|
||||
v8::String::Utf8Value utf8(value);
|
||||
length = utf8.length();
|
||||
rawBuffer = new char[sizeof(uv_write_t) + length];
|
||||
std::memcpy(rawBuffer + sizeof(uv_write_t), *utf8, length);
|
||||
}
|
||||
uv_write_t* request = reinterpret_cast<uv_write_t*>(rawBuffer);
|
||||
|
||||
uv_buf_t buffer;
|
||||
buffer.base = rawBuffer + sizeof(uv_write_t);
|
||||
buffer.len = length;
|
||||
|
||||
request->data = reinterpret_cast<void*>(promise);
|
||||
int result = uv_write(request, reinterpret_cast<uv_stream_t*>(&socket->_socket), &buffer, 1, onWrite);
|
||||
if (result != 0) {
|
||||
std::string error = "uv_write: " + std::string(uv_strerror(result));
|
||||
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(args.GetIsolate(), error.c_str(), v8::String::kNormalString, error.size()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
socket->_task->rejectPromise(promise, v8::Integer::New(args.GetIsolate(), -2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::onWrite(uv_write_t* request, int status) {
|
||||
if (Socket* socket = reinterpret_cast<Socket*>(request->handle->data)) {
|
||||
v8::HandleScope handleScope(socket->_task->getIsolate());
|
||||
promiseid_t promise = reinterpret_cast<intptr_t>(request->data);
|
||||
if (promise != -1) {
|
||||
if (status == 0) {
|
||||
socket->_task->resolvePromise(promise, v8::Integer::New(socket->_task->getIsolate(), status));
|
||||
} else {
|
||||
std::string error = "uv_write: " + std::string(uv_strerror(status));
|
||||
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(socket->_task->getIsolate(), error.c_str(), v8::String::kNormalString, error.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] reinterpret_cast<char*>(request);
|
||||
}
|
||||
|
||||
void Socket::onClose(uv_handle_t* handle) {
|
||||
--_openCount;
|
||||
if (Socket* socket = reinterpret_cast<Socket*>(handle->data)) {
|
||||
if (socket->_closePromise != -1) {
|
||||
v8::HandleScope scope(socket->_task->getIsolate());
|
||||
promiseid_t promise = socket->_closePromise;
|
||||
socket->_closePromise = -1;
|
||||
socket->_connected = false;
|
||||
socket->_task->resolvePromise(promise, v8::Integer::New(socket->_task->getIsolate(), 0));
|
||||
}
|
||||
if (socket->_object.IsEmpty()) {
|
||||
delete socket;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::onShutdown(uv_shutdown_t* request, int status) {
|
||||
if (Socket* socket = reinterpret_cast<Socket*>(request->handle->data)) {
|
||||
promiseid_t promise = reinterpret_cast<intptr_t>(request->data);
|
||||
if (status == 0) {
|
||||
socket->_task->resolvePromise(promise, v8::Undefined(socket->_task->getIsolate()));
|
||||
} else {
|
||||
std::string error = "uv_shutdown: " + std::string(uv_strerror(status));
|
||||
socket->_task->rejectPromise(promise, v8::Exception::Error(v8::String::NewFromUtf8(socket->_task->getIsolate(), error.c_str())));
|
||||
}
|
||||
}
|
||||
delete request;
|
||||
}
|
||||
|
||||
void Socket::getPeerName(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
if (Socket* socket = Socket::get(info.Data())) {
|
||||
struct sockaddr_in6 addr;
|
||||
int nameLength = sizeof(addr);
|
||||
if (uv_tcp_getpeername(&socket->_socket, reinterpret_cast<sockaddr*>(&addr), &nameLength) == 0) {
|
||||
char name[1024];
|
||||
if (static_cast<size_t>(nameLength) > sizeof(struct sockaddr_in)) {
|
||||
if (uv_ip6_name(&addr, name, sizeof(name)) == 0) {
|
||||
info.GetReturnValue().Set(v8::String::NewFromUtf8(info.GetIsolate(), name));
|
||||
}
|
||||
} else {
|
||||
if (uv_ip4_name(reinterpret_cast<struct sockaddr_in*>(&addr), name, sizeof(name)) == 0) {
|
||||
info.GetReturnValue().Set(v8::String::NewFromUtf8(info.GetIsolate(), name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::getPeerCertificate(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
if (Socket* socket = Socket::get(info.Data())) {
|
||||
if (socket->_tls) {
|
||||
std::vector<char> buffer(128 * 1024);
|
||||
int result = socket->_tls->getPeerCertificate(buffer.data(), buffer.size());
|
||||
if (result > 0) {
|
||||
info.GetReturnValue().Set(v8::String::NewFromUtf8(info.GetIsolate(), buffer.data(), v8::String::kNormalString, result));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::isConnected(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
if (Socket* socket = Socket::get(info.Data())) {
|
||||
info.GetReturnValue().Set(v8::Boolean::New(socket->_task->getIsolate(), socket->_connected));
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::create(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope handleScope(args.GetIsolate());
|
||||
if (Socket* socket = new Socket(Task::get(args.GetIsolate()))) {
|
||||
v8::Handle<v8::Object> result = v8::Local<v8::Object>::New(args.GetIsolate(), socket->_object);
|
||||
args.GetReturnValue().Set(result);
|
||||
socket->release();
|
||||
}
|
||||
}
|
||||
|
||||
Socket* Socket::get(v8::Handle<v8::Value> socketObject) {
|
||||
return reinterpret_cast<Socket*>(v8::Handle<v8::External>::Cast(socketObject)->Value());
|
||||
}
|
||||
|
||||
void Socket::ref() {
|
||||
if (++_refCount == 1) {
|
||||
_object.ClearWeak();
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::release() {
|
||||
assert(_refCount >= 1);
|
||||
if (--_refCount == 0) {
|
||||
_object.SetWeak(this, onRelease);
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::onRelease(const v8::WeakCallbackData<v8::Object, Socket>& data) {
|
||||
data.GetParameter()->_object.Reset();
|
||||
data.GetParameter()->close();
|
||||
}
|
90
src/Socket.h
Normal file
90
src/Socket.h
Normal file
@ -0,0 +1,90 @@
|
||||
#ifndef INCLUDED_Socket
|
||||
#define INCLUDED_Socket
|
||||
|
||||
#include <string>
|
||||
#include <uv.h>
|
||||
#include <v8.h>
|
||||
|
||||
typedef int promiseid_t;
|
||||
class Task;
|
||||
class TlsContext;
|
||||
class TlsSession;
|
||||
|
||||
class Socket {
|
||||
public:
|
||||
static void create(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
void close();
|
||||
|
||||
static int getCount() { return _count; }
|
||||
static int getOpenCount() { return _openCount; }
|
||||
|
||||
private:
|
||||
Socket(Task* task);
|
||||
~Socket();
|
||||
|
||||
Task* _task;
|
||||
uv_tcp_t _socket;
|
||||
TlsSession* _tls = 0;
|
||||
promiseid_t _startTlsPromise = -1;
|
||||
promiseid_t _closePromise = -1;
|
||||
int _refCount = 1;
|
||||
bool _connected = false;
|
||||
std::string _peerName;
|
||||
|
||||
enum Direction { kUndetermined, kAccept, kConnect };
|
||||
Direction _direction = kUndetermined;
|
||||
|
||||
static int _count;
|
||||
static int _openCount;
|
||||
|
||||
static TlsContext* _defaultTlsContext;
|
||||
|
||||
v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> > _object;
|
||||
|
||||
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _onConnect;
|
||||
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _onRead;
|
||||
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _onError;
|
||||
|
||||
static void startTls(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void stopTls(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void bind(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void connect(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void listen(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void accept(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void close(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void shutdown(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void read(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void onError(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void write(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void getPeerName(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info);
|
||||
static void getPeerCertificate(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info);
|
||||
static void isConnected(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info);
|
||||
|
||||
static Socket* get(v8::Handle<v8::Value> socketObject);
|
||||
static void onClose(uv_handle_t* handle);
|
||||
static void onShutdown(uv_shutdown_t* request, int status);
|
||||
static void onResolvedForBind(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result);
|
||||
static void onResolvedForConnect(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result);
|
||||
static void onConnect(uv_connect_t* request, int status);
|
||||
static void onNewConnection(uv_stream_t* server, int status);
|
||||
|
||||
static void allocateBuffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buffer);
|
||||
static void onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffer);
|
||||
static void onWrite(uv_write_t* request, int status);
|
||||
static void onRelease(const v8::WeakCallbackData<v8::Object, Socket>& data);
|
||||
|
||||
void processTlsShutdown(promiseid_t promise);
|
||||
static void onTlsShutdown(uv_write_t* request, int status);
|
||||
void shutdownInternal(promiseid_t promise);
|
||||
|
||||
bool processSomeOutgoingTls(promiseid_t promise, uv_write_cb callback);
|
||||
void processOutgoingTls();
|
||||
void reportTlsErrors();
|
||||
void reportError(const char* error);
|
||||
|
||||
void ref();
|
||||
void release();
|
||||
};
|
||||
|
||||
#endif
|
749
src/Task.cpp
Normal file
749
src/Task.cpp
Normal file
@ -0,0 +1,749 @@
|
||||
#include "Task.h"
|
||||
|
||||
#include "Database.h"
|
||||
#include "File.h"
|
||||
#include "Serialize.h"
|
||||
#include "Socket.h"
|
||||
#include "TaskStub.h"
|
||||
#include "TaskTryCatch.h"
|
||||
#include "TlsContextWrapper.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <libplatform/libplatform.h>
|
||||
#include <map>
|
||||
#include <sys/types.h>
|
||||
#include <uv.h>
|
||||
#include <v8.h>
|
||||
#include <v8-platform.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
static const int STDIN_FILENO = 0;
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
extern v8::Platform* gPlatform;
|
||||
int gNextTaskId = 1;
|
||||
|
||||
int Task::_count;
|
||||
|
||||
struct ExportRecord {
|
||||
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _persistent;
|
||||
int _useCount;
|
||||
|
||||
ExportRecord(v8::Isolate* isolate, v8::Handle<v8::Function> function)
|
||||
: _persistent(isolate, function),
|
||||
_useCount(0) {
|
||||
}
|
||||
|
||||
void ref() {
|
||||
++_useCount;
|
||||
}
|
||||
|
||||
bool release() {
|
||||
return --_useCount == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct ImportRecord {
|
||||
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _persistent;
|
||||
exportid_t _export;
|
||||
taskid_t _task;
|
||||
Task* _owner;
|
||||
int _useCount;
|
||||
|
||||
ImportRecord(v8::Isolate* isolate, v8::Handle<v8::Function> function, exportid_t exportId, taskid_t taskId, Task* owner)
|
||||
: _persistent(isolate, function),
|
||||
_export(exportId),
|
||||
_task(taskId),
|
||||
_owner(owner),
|
||||
_useCount(0) {
|
||||
_persistent.SetWeak(this, ImportRecord::onRelease);
|
||||
}
|
||||
|
||||
void ref() {
|
||||
if (_useCount++ == 0) {
|
||||
// Make a strong ref again until an in-flight function call is finished.
|
||||
_persistent.ClearWeak();
|
||||
}
|
||||
}
|
||||
|
||||
void release() {
|
||||
if (--_useCount == 0) {
|
||||
// All in-flight calls are finished. Make weak.
|
||||
_persistent.SetWeak(this, ImportRecord::onRelease);
|
||||
}
|
||||
}
|
||||
|
||||
static void onRelease(const v8::WeakCallbackData<v8::Function, ImportRecord >& data) {
|
||||
ImportRecord* import = data.GetParameter();
|
||||
import->_owner->releaseExport(import->_task, import->_export);
|
||||
for (size_t i = 0; i < import->_owner->_imports.size(); ++i) {
|
||||
if (import->_owner->_imports[i] == import) {
|
||||
import->_owner->_imports.erase(import->_owner->_imports.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
import->_persistent.Reset();
|
||||
delete import;
|
||||
}
|
||||
};
|
||||
|
||||
Task::Task() {
|
||||
_loop = uv_loop_new();
|
||||
++_count;
|
||||
v8::Isolate::CreateParams options;
|
||||
options.array_buffer_allocator = &_allocator;
|
||||
_isolate = v8::Isolate::New(options);
|
||||
_isolate->SetData(0, this);
|
||||
_isolate->SetCaptureStackTraceForUncaughtExceptions(true, 16);
|
||||
}
|
||||
|
||||
Task::~Task() {
|
||||
{
|
||||
v8::Isolate::Scope isolateScope(_isolate);
|
||||
v8::HandleScope handleScope(_isolate);
|
||||
_context.Reset();
|
||||
}
|
||||
|
||||
_isolate->Dispose();
|
||||
_isolate = 0;
|
||||
|
||||
uv_loop_delete(_loop);
|
||||
--_count;
|
||||
}
|
||||
|
||||
v8::Handle<v8::Context> Task::getContext() {
|
||||
return v8::Local<v8::Context>::New(_isolate, _context);
|
||||
}
|
||||
|
||||
void Task::run() {
|
||||
{
|
||||
v8::Isolate::Scope isolateScope(_isolate);
|
||||
v8::HandleScope handleScope(_isolate);
|
||||
v8::Context::Scope contextScope(v8::Local<v8::Context>::New(_isolate, _context));
|
||||
uv_run(_loop, UV_RUN_DEFAULT);
|
||||
}
|
||||
_promises.clear();
|
||||
_exports.clear();
|
||||
_imports.clear();
|
||||
}
|
||||
|
||||
v8::Handle<v8::String> Task::loadFile(v8::Isolate* isolate, const char* fileName) {
|
||||
v8::Handle<v8::String> value;
|
||||
std::ifstream file(fileName, std::ios_base::in | std::ios_base::binary | std::ios_base::ate);
|
||||
std::streampos fileSize = file.tellg();
|
||||
if (fileSize >= 0) {
|
||||
file.seekg(0, std::ios_base::beg);
|
||||
char* buffer = new char[fileSize];
|
||||
file.read(buffer, fileSize);
|
||||
std::string contents(buffer, buffer + fileSize);
|
||||
value = v8::String::NewFromOneByte(isolate, reinterpret_cast<const uint8_t*>(buffer), v8::String::kNormalString, fileSize);
|
||||
delete[] buffer;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void Task::activate() {
|
||||
v8::Isolate::Scope isolateScope(_isolate);
|
||||
v8::HandleScope handleScope(_isolate);
|
||||
|
||||
v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
|
||||
|
||||
if (!_importObject.IsEmpty()) {
|
||||
v8::Local<v8::Object> imports(_importObject.Get(_isolate));
|
||||
v8::Handle<v8::Array> keys = imports->GetOwnPropertyNames();
|
||||
for (size_t i = 0; i < keys->Length(); ++i) {
|
||||
global->SetAccessor(keys->Get(i).As<v8::String>(), getImportProperty);
|
||||
}
|
||||
}
|
||||
|
||||
global->Set(v8::String::NewFromUtf8(_isolate, "print"), v8::FunctionTemplate::New(_isolate, print));
|
||||
global->Set(v8::String::NewFromUtf8(_isolate, "setTimeout"), v8::FunctionTemplate::New(_isolate, setTimeout));
|
||||
global->Set(v8::String::NewFromUtf8(_isolate, "require"), v8::FunctionTemplate::New(_isolate, require));
|
||||
global->SetAccessor(v8::String::NewFromUtf8(_isolate, "parent"), parent);
|
||||
global->Set(v8::String::NewFromUtf8(_isolate, "exit"), v8::FunctionTemplate::New(_isolate, exit));
|
||||
global->Set(v8::String::NewFromUtf8(_isolate, "utf8Length"), v8::FunctionTemplate::New(_isolate, utf8Length));
|
||||
global->SetAccessor(v8::String::NewFromUtf8(_isolate, "exports"), getExports, setExports);
|
||||
global->SetAccessor(v8::String::NewFromUtf8(_isolate, "imports"), getImports);
|
||||
global->SetAccessor(v8::String::NewFromUtf8(_isolate, "version"), version);
|
||||
global->SetAccessor(v8::String::NewFromUtf8(_isolate, "statistics"), statistics);
|
||||
if (_trusted) {
|
||||
global->Set(v8::String::NewFromUtf8(_isolate, "Database"), v8::FunctionTemplate::New(_isolate, Database::create));
|
||||
global->Set(v8::String::NewFromUtf8(_isolate, "Socket"), v8::FunctionTemplate::New(_isolate, Socket::create));
|
||||
global->Set(v8::String::NewFromUtf8(_isolate, "Task"), v8::FunctionTemplate::New(_isolate, TaskStub::create));
|
||||
global->Set(v8::String::NewFromUtf8(_isolate, "TlsContext"), v8::FunctionTemplate::New(_isolate, TlsContextWrapper::create));
|
||||
File::configure(_isolate, global);
|
||||
}
|
||||
|
||||
v8::Local<v8::Context> context = v8::Context::New(_isolate, 0, global);
|
||||
_context = v8::Persistent<v8::Context, v8::CopyablePersistentTraits<v8::Context> >(_isolate, context);
|
||||
}
|
||||
|
||||
void Task::activate(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Task* task = Task::get(args.GetIsolate());
|
||||
task->activate();
|
||||
}
|
||||
|
||||
void Task::print(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
||||
v8::Handle<v8::Object> json = context->Global()->Get(v8::String::NewFromUtf8(args.GetIsolate(), "JSON"))->ToObject();
|
||||
v8::Handle<v8::Function> stringify = v8::Handle<v8::Function>::Cast(json->Get(v8::String::NewFromUtf8(args.GetIsolate(), "stringify")));
|
||||
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
||||
TaskTryCatch tryCatch(task);
|
||||
std::cout << "Task[" << task << ':' << task->_scriptName << "]>";
|
||||
for (int i = 0; i < args.Length(); i++) {
|
||||
std::cout << ' ';
|
||||
v8::Handle<v8::Value> arg = args[i];
|
||||
if (arg->IsNativeError()) {
|
||||
arg = Serialize::storeMessage(task, v8::Exception::CreateMessage(arg));
|
||||
}
|
||||
v8::String::Utf8Value value(stringify->Call(json, 1, &arg));
|
||||
std::cout << (*value ? *value : "(null)");
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
struct TimeoutData {
|
||||
Task* _task;
|
||||
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _callback;
|
||||
};
|
||||
|
||||
void Task::setTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
||||
|
||||
TimeoutData* timeout = new TimeoutData();
|
||||
timeout->_task = task;
|
||||
|
||||
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > function(args.GetIsolate(), v8::Handle<v8::Function>::Cast(args[0]));
|
||||
timeout->_callback = function;
|
||||
|
||||
uv_timer_t* timer = new uv_timer_t();
|
||||
uv_timer_init(task->_loop, timer);
|
||||
timer->data = timeout;
|
||||
uv_timer_start(timer, timeoutCallback, static_cast<uint64_t>(args[1].As<v8::Number>()->Value()), 0);
|
||||
}
|
||||
|
||||
void Task::timeoutCallback(uv_timer_t* handle) {
|
||||
TimeoutData* timeout = reinterpret_cast<TimeoutData*>(handle->data);
|
||||
TaskTryCatch tryCatch(timeout->_task);
|
||||
v8::HandleScope scope(timeout->_task->_isolate);
|
||||
v8::Handle<v8::Function> function = v8::Local<v8::Function>::New(timeout->_task->_isolate, timeout->_callback);
|
||||
function->Call(v8::Undefined(timeout->_task->_isolate), 0, 0);
|
||||
delete timeout;
|
||||
}
|
||||
|
||||
void Task::utf8Length(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
||||
TaskTryCatch tryCatch(task);
|
||||
v8::HandleScope scope(task->_isolate);
|
||||
args.GetReturnValue().Set(v8::Integer::New(args.GetIsolate(), args[0].As<v8::String>()->Utf8Length()));
|
||||
}
|
||||
|
||||
void Task::exit(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
::exit(args[0]->Int32Value());
|
||||
}
|
||||
|
||||
void Task::kill() {
|
||||
if (!_killed && _isolate) {
|
||||
_killed = true;
|
||||
v8::V8::TerminateExecution(_isolate);
|
||||
}
|
||||
}
|
||||
|
||||
void Task::execute(const char* fileName) {
|
||||
v8::Isolate::Scope isolateScope(_isolate);
|
||||
v8::HandleScope handleScope(_isolate);
|
||||
v8::Context::Scope contextScope(v8::Local<v8::Context>::New(_isolate, _context));
|
||||
|
||||
v8::Handle<v8::String> name = v8::String::NewFromUtf8(_isolate, fileName);
|
||||
|
||||
v8::Handle<v8::String> source = loadFile(_isolate, fileName);
|
||||
std::cout << "Running script " << fileName << "\n";
|
||||
if (!_scriptName.size()) {
|
||||
_scriptName = fileName;
|
||||
}
|
||||
if (!source.IsEmpty()) {
|
||||
v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
|
||||
if (!script.IsEmpty()) {
|
||||
script->Run();
|
||||
std::cout << "Script " << fileName << " completed\n";
|
||||
} else {
|
||||
std::cerr << "Failed to compile: " << fileName << ".\n";
|
||||
}
|
||||
} else {
|
||||
std::string message;
|
||||
message = "Failed to load file: ";
|
||||
message += fileName;
|
||||
_isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(_isolate, message.c_str())));
|
||||
}
|
||||
}
|
||||
|
||||
void Task::invokeExport(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Task* sender = Task::get(args.GetIsolate());
|
||||
TaskTryCatch tryCatch(sender);
|
||||
v8::Handle<v8::Object> data = v8::Handle<v8::Object>::Cast(args.Data());
|
||||
exportid_t exportId = data->Get(v8::String::NewFromUtf8(args.GetIsolate(), "export"))->Int32Value();
|
||||
taskid_t recipientId = data->Get(v8::String::NewFromUtf8(args.GetIsolate(), "task"))->Int32Value();
|
||||
|
||||
for (size_t i = 0; i < sender->_imports.size(); ++i) {
|
||||
if (sender->_imports[i]->_task == recipientId && sender->_imports[i]->_export == exportId) {
|
||||
sender->_imports[i]->ref();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
v8::Local<v8::Array> array = v8::Array::New(args.GetIsolate(), args.Length() + 1);
|
||||
array->Set(0, args.This());
|
||||
for (int i = 0; i < args.Length(); ++i) {
|
||||
array->Set(i + 1, args[i]);
|
||||
}
|
||||
|
||||
if (TaskStub* recipient = sender->get(recipientId)) {
|
||||
promiseid_t promise = sender->allocatePromise();
|
||||
sendPromiseExportMessage(sender, recipient, kInvokeExport, promise, exportId, array);
|
||||
args.GetReturnValue().Set(sender->getPromise(promise));
|
||||
} else {
|
||||
args.GetReturnValue().Set(args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), "Invoking a function on a nonexistant task."))));
|
||||
}
|
||||
}
|
||||
|
||||
v8::Handle<v8::Value> Task::invokeExport(TaskStub* from, Task* to, exportid_t exportId, const std::vector<char>& buffer) {
|
||||
v8::Handle<v8::Value> result;
|
||||
if (to->_exports[exportId]) {
|
||||
v8::Handle<v8::Array> arguments = v8::Handle<v8::Array>::Cast(Serialize::load(to, from, buffer));
|
||||
std::vector<v8::Handle<v8::Value> > argumentArray;
|
||||
for (size_t i = 1; i < arguments->Length(); ++i) {
|
||||
argumentArray.push_back(arguments->Get(i));
|
||||
}
|
||||
v8::Handle<v8::Function> function = v8::Local<v8::Function>::New(to->_isolate, to->_exports[exportId]->_persistent);
|
||||
v8::Handle<v8::Value>* argumentPointer = 0;
|
||||
if (argumentArray.size()) {
|
||||
argumentPointer = &*argumentArray.begin();
|
||||
}
|
||||
result = function->Call(v8::Handle<v8::Object>::Cast(arguments->Get(0)), argumentArray.size(), argumentPointer);
|
||||
} else {
|
||||
std::cout << to->_scriptName << ": That's not an export we have (exportId=" << exportId << ", exports = " << to->_exports.size() << ")\n";
|
||||
}
|
||||
from->getStream().send(kReleaseImport, reinterpret_cast<char*>(&exportId), sizeof(exportId));
|
||||
return result;
|
||||
}
|
||||
|
||||
void Task::sendPromiseResolve(Task* from, TaskStub* to, promiseid_t promise, v8::Handle<v8::Value> result) {
|
||||
if (!result.IsEmpty() && result->IsPromise()) {
|
||||
// We're not going to serialize/deserialize a promise...
|
||||
v8::Handle<v8::Object> data = v8::Object::New(from->_isolate);
|
||||
data->Set(v8::String::NewFromUtf8(from->_isolate, "task"), v8::Int32::New(from->_isolate, to->getId()));
|
||||
data->Set(v8::String::NewFromUtf8(from->_isolate, "promise"), v8::Int32::New(from->_isolate, promise));
|
||||
v8::Handle<v8::Promise> promise = v8::Handle<v8::Promise>::Cast(result);
|
||||
v8::Handle<v8::Function> then = v8::Function::New(from->_isolate, invokeThen, data);
|
||||
promise->Then(then);
|
||||
v8::Handle<v8::Function> catchCallback = v8::Function::New(from->_isolate, invokeCatch, data);
|
||||
promise->Catch(catchCallback);
|
||||
from->_isolate->RunMicrotasks();
|
||||
} else {
|
||||
sendPromiseMessage(from, to, kResolvePromise, promise, result);
|
||||
}
|
||||
}
|
||||
|
||||
void Task::sendPromiseReject(Task* from, TaskStub* to, promiseid_t promise, v8::Handle<v8::Value> result) {
|
||||
if (!result.IsEmpty() && result->IsPromise()) {
|
||||
// We're not going to serialize/deserialize a promise...
|
||||
v8::Handle<v8::Object> data = v8::Object::New(from->_isolate);
|
||||
data->Set(v8::String::NewFromUtf8(from->_isolate, "task"), v8::Int32::New(from->_isolate, to->getId()));
|
||||
data->Set(v8::String::NewFromUtf8(from->_isolate, "promise"), v8::Int32::New(from->_isolate, promise));
|
||||
v8::Handle<v8::Promise> promise = v8::Handle<v8::Promise>::Cast(result);
|
||||
v8::Handle<v8::Function> then = v8::Function::New(from->_isolate, invokeThen, data);
|
||||
promise->Then(then);
|
||||
v8::Handle<v8::Function> catchCallback = v8::Function::New(from->_isolate, invokeCatch, data);
|
||||
promise->Catch(catchCallback);
|
||||
from->_isolate->RunMicrotasks();
|
||||
} else {
|
||||
sendPromiseMessage(from, to, kRejectPromise, promise, result);
|
||||
}
|
||||
}
|
||||
|
||||
void Task::sendPromiseMessage(Task* from, TaskStub* to, MessageType messageType, promiseid_t promise, v8::Handle<v8::Value> result) {
|
||||
if (to) {
|
||||
std::vector<char> buffer;
|
||||
buffer.insert(buffer.end(), reinterpret_cast<char*>(&promise), reinterpret_cast<char*>(&promise) + sizeof(promise));
|
||||
if (!result.IsEmpty() && !result->IsUndefined() && !result->IsNull()) {
|
||||
Serialize::store(from, buffer, result);
|
||||
}
|
||||
to->getStream().send(messageType, &*buffer.begin(), buffer.size());
|
||||
} else {
|
||||
std::cerr << "Sending to a NULL task.\n";
|
||||
}
|
||||
}
|
||||
|
||||
void Task::sendPromiseExportMessage(Task* from, TaskStub* to, MessageType messageType, promiseid_t promise, exportid_t exportId, v8::Handle<v8::Value> result) {
|
||||
std::vector<char> buffer;
|
||||
buffer.insert(buffer.end(), reinterpret_cast<char*>(&promise), reinterpret_cast<char*>(&promise) + sizeof(promise));
|
||||
buffer.insert(buffer.end(), reinterpret_cast<char*>(&exportId), reinterpret_cast<char*>(&exportId) + sizeof(exportId));
|
||||
if (!result.IsEmpty() && !result->IsUndefined() && !result->IsNull()) {
|
||||
Serialize::store(from, buffer, result);
|
||||
}
|
||||
to->getStream().send(messageType, &*buffer.begin(), buffer.size());
|
||||
}
|
||||
|
||||
TaskStub* Task::get(taskid_t taskId) {
|
||||
return taskId == kParentId ? _parent : _children[taskId];
|
||||
}
|
||||
|
||||
void Task::invokeThen(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Task* from = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
||||
v8::Handle<v8::Object> data = v8::Handle<v8::Object>::Cast(args.Data());
|
||||
TaskStub* to = from->get(data->Get(v8::String::NewFromUtf8(args.GetIsolate(), "task"))->Int32Value());
|
||||
promiseid_t promise = data->Get(v8::String::NewFromUtf8(args.GetIsolate(), "promise"))->Int32Value();
|
||||
sendPromiseMessage(from, to, kResolvePromise, promise, args[0]);
|
||||
}
|
||||
|
||||
void Task::invokeCatch(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Task* from = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
||||
v8::Handle<v8::Object> data = v8::Handle<v8::Object>::Cast(args.Data());
|
||||
TaskStub* to = from->get(data->Get(v8::String::NewFromUtf8(args.GetIsolate(), "task"))->Int32Value());
|
||||
promiseid_t promise = data->Get(v8::String::NewFromUtf8(args.GetIsolate(), "promise"))->Int32Value();
|
||||
sendPromiseMessage(from, to, kRejectPromise, promise, args[0]);
|
||||
}
|
||||
|
||||
void Task::parent(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
||||
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
||||
if (task->_parent) {
|
||||
args.GetReturnValue().Set(task->_parent->getTaskObject());
|
||||
} else {
|
||||
args.GetReturnValue().Set(v8::Undefined(task->_isolate));
|
||||
}
|
||||
}
|
||||
|
||||
void Task::version(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
||||
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
||||
args.GetReturnValue().Set(v8::String::NewFromUtf8(task->_isolate, v8::V8::GetVersion()));
|
||||
}
|
||||
|
||||
void Task::getImportProperty(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
||||
v8::Local<v8::Object> imports = Task::get(args.GetIsolate())->_importObject.Get(args.GetIsolate());
|
||||
args.GetReturnValue().Set(imports->Get(property));
|
||||
}
|
||||
|
||||
void Task::getImports(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
||||
args.GetReturnValue().Set(v8::Local<v8::Object>::New(args.GetIsolate(), Task::get(args.GetIsolate())->_importObject));
|
||||
}
|
||||
|
||||
void Task::getExports(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
||||
args.GetReturnValue().Set(v8::Local<v8::Object>::New(args.GetIsolate(), Task::get(args.GetIsolate())->_exportObject));
|
||||
}
|
||||
|
||||
void Task::setExports(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& args) {
|
||||
Task::get(args.GetIsolate())->_exportObject = v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> >(args.GetIsolate(), v8::Handle<v8::Object>::Cast(value));
|
||||
}
|
||||
|
||||
Task* Task::get(v8::Isolate* isolate) {
|
||||
return reinterpret_cast<Task*>(isolate->GetData(0));
|
||||
}
|
||||
|
||||
promiseid_t Task::allocatePromise() {
|
||||
promiseid_t promiseId;
|
||||
do {
|
||||
promiseId = _nextPromise++;
|
||||
} while (_promises.find(promiseId) != _promises.end());
|
||||
v8::Persistent<v8::Promise::Resolver, v8::NonCopyablePersistentTraits<v8::Promise::Resolver> > promise(_isolate, v8::Promise::Resolver::New(_isolate));
|
||||
_promises[promiseId] = promise;
|
||||
return promiseId;
|
||||
}
|
||||
|
||||
v8::Handle<v8::Promise::Resolver> Task::getPromise(promiseid_t promise) {
|
||||
v8::Handle<v8::Promise::Resolver> result;
|
||||
if (!_promises[promise].IsEmpty()) {
|
||||
result = v8::Local<v8::Promise::Resolver>::New(_isolate, _promises[promise]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Task::resolvePromise(promiseid_t promise, v8::Handle<v8::Value> value) {
|
||||
TaskTryCatch tryCatch(this);
|
||||
if (!_promises[promise].IsEmpty()) {
|
||||
v8::HandleScope handleScope(_isolate);
|
||||
v8::Handle<v8::Promise::Resolver> resolver = v8::Local<v8::Promise::Resolver>::New(_isolate, _promises[promise]);
|
||||
resolver->Resolve(value);
|
||||
_isolate->RunMicrotasks();
|
||||
_promises[promise].Reset();
|
||||
_promises.erase(promise);
|
||||
}
|
||||
}
|
||||
|
||||
void Task::rejectPromise(promiseid_t promise, v8::Handle<v8::Value> value) {
|
||||
TaskTryCatch tryCatch(this);
|
||||
if (!_promises[promise].IsEmpty()) {
|
||||
v8::HandleScope handleScope(_isolate);
|
||||
v8::Handle<v8::Promise::Resolver> resolver = v8::Local<v8::Promise::Resolver>::New(_isolate, _promises[promise]);
|
||||
resolver->Reject(value);
|
||||
_isolate->RunMicrotasks();
|
||||
_promises[promise].Reset();
|
||||
_promises.erase(promise);
|
||||
}
|
||||
}
|
||||
|
||||
exportid_t Task::exportFunction(v8::Handle<v8::Function> function) {
|
||||
exportid_t exportId = -1;
|
||||
v8::Handle<v8::String> exportName = v8::String::NewFromUtf8(_isolate, "export");
|
||||
|
||||
v8::Local<v8::Value> value = function->GetHiddenValue(exportName);
|
||||
if (!value.IsEmpty() && value->IsNumber())
|
||||
{
|
||||
exportid_t foundId = value->ToInteger(_isolate)->Int32Value();
|
||||
if (_exports[foundId]) {
|
||||
exportId = foundId;
|
||||
}
|
||||
}
|
||||
|
||||
if (exportId == -1) {
|
||||
do {
|
||||
exportId = _nextExport++;
|
||||
} while (_exports[_nextExport]);
|
||||
ExportRecord* record = new ExportRecord(_isolate, function);
|
||||
function->SetHiddenValue(exportName, v8::Integer::New(_isolate, exportId));
|
||||
_exports[exportId] = record;
|
||||
}
|
||||
|
||||
if (_exports[exportId]) {
|
||||
_exports[exportId]->ref();
|
||||
}
|
||||
|
||||
return exportId;
|
||||
}
|
||||
|
||||
void Task::releaseExport(taskid_t taskId, exportid_t exportId) {
|
||||
if (TaskStub* task = get(taskId)) {
|
||||
std::vector<char> buffer;
|
||||
buffer.insert(buffer.end(), reinterpret_cast<char*>(&exportId), reinterpret_cast<char*>(&exportId) + sizeof(exportId));
|
||||
task->getStream().send(kReleaseExport, &*buffer.begin(), buffer.size());
|
||||
}
|
||||
}
|
||||
|
||||
v8::Handle<v8::Function> Task::addImport(taskid_t taskId, exportid_t exportId) {
|
||||
v8::Local<v8::Object> data = v8::Object::New(_isolate);
|
||||
data->Set(v8::String::NewFromUtf8(_isolate, "export"), v8::Int32::New(_isolate, exportId));
|
||||
data->Set(v8::String::NewFromUtf8(_isolate, "task"), v8::Int32::New(_isolate, taskId));
|
||||
v8::Local<v8::Function> function = v8::Function::New(_isolate, Task::invokeExport, data);
|
||||
_imports.push_back(new ImportRecord(_isolate, function, exportId, taskId, this));
|
||||
return function;
|
||||
}
|
||||
|
||||
void Task::statistics(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
||||
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
||||
args.GetReturnValue().Set(task->getStatistics());
|
||||
}
|
||||
|
||||
v8::Handle<v8::Object> Task::getStatistics() {
|
||||
v8::Handle<v8::Object> result = v8::Object::New(_isolate);
|
||||
result->Set(v8::String::NewFromUtf8(_isolate, "sockets"), v8::Integer::New(_isolate, Socket::getCount()));
|
||||
result->Set(v8::String::NewFromUtf8(_isolate, "openSockets"), v8::Integer::New(_isolate, Socket::getOpenCount()));
|
||||
result->Set(v8::String::NewFromUtf8(_isolate, "promises"), v8::Integer::New(_isolate, _promises.size()));
|
||||
result->Set(v8::String::NewFromUtf8(_isolate, "exports"), v8::Integer::New(_isolate, _exports.size()));
|
||||
result->Set(v8::String::NewFromUtf8(_isolate, "imports"), v8::Integer::New(_isolate, _imports.size()));
|
||||
result->Set(v8::String::NewFromUtf8(_isolate, "tlsContexts"), v8::Integer::New(_isolate, TlsContextWrapper::getCount()));
|
||||
|
||||
uv_rusage_t usage;
|
||||
if (uv_getrusage(&usage) == 0) {
|
||||
result->Set(v8::String::NewFromUtf8(_isolate, "utime"), v8::Number::New(_isolate, usage.ru_utime.tv_sec + usage.ru_utime.tv_usec / 1000000.0));
|
||||
result->Set(v8::String::NewFromUtf8(_isolate, "stime"), v8::Number::New(_isolate, usage.ru_stime.tv_sec + usage.ru_stime.tv_usec / 1000000.0));
|
||||
result->Set(v8::String::NewFromUtf8(_isolate, "maxrss"), v8::Number::New(_isolate, usage.ru_maxrss));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Task::onReceivePacket(int packetType, const char* begin, size_t length, void* userData) {
|
||||
TaskStub* stub = reinterpret_cast<TaskStub*>(userData);
|
||||
TaskStub* from = stub;
|
||||
Task* to = stub->getOwner();
|
||||
|
||||
TaskTryCatch tryCatch(to);
|
||||
v8::HandleScope scope(to->_isolate);
|
||||
|
||||
switch (static_cast<MessageType>(packetType)) {
|
||||
case kStatistics:
|
||||
{
|
||||
promiseid_t promise;
|
||||
std::memcpy(&promise, begin, sizeof(promise));
|
||||
v8::Handle<v8::Value> result = to->getStatistics();
|
||||
sendPromiseResolve(to, from, promise, result);
|
||||
}
|
||||
break;
|
||||
case kInvokeExport:
|
||||
{
|
||||
promiseid_t promise;
|
||||
exportid_t exportId;
|
||||
std::memcpy(&promise, begin, sizeof(promise));
|
||||
std::memcpy(&exportId, begin + sizeof(promise), sizeof(exportId));
|
||||
|
||||
v8::TryCatch tryCatch;
|
||||
v8::Handle<v8::Value> result = invokeExport(from, to, exportId, std::vector<char>(begin + sizeof(promiseid_t) + sizeof(exportid_t), begin + length));
|
||||
if (tryCatch.HasCaught()) {
|
||||
sendPromiseReject(to, from, promise, Serialize::store(to, tryCatch));
|
||||
} else {
|
||||
sendPromiseResolve(to, from, promise, result);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kResolvePromise:
|
||||
case kRejectPromise:
|
||||
{
|
||||
v8::Handle<v8::Value> arg;
|
||||
promiseid_t promise;
|
||||
std::memcpy(&promise, begin, sizeof(promiseid_t));
|
||||
if (length > sizeof(promiseid_t)) {
|
||||
arg = Serialize::load(to, from, std::vector<char>(begin + sizeof(promiseid_t), begin + length));
|
||||
}
|
||||
else {
|
||||
arg = v8::Undefined(to->_isolate);
|
||||
}
|
||||
if (static_cast<MessageType>(packetType) == kResolvePromise) {
|
||||
to->resolvePromise(promise, arg);
|
||||
}
|
||||
else {
|
||||
to->rejectPromise(promise, arg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kReleaseExport:
|
||||
assert(length == sizeof(exportid_t));
|
||||
exportid_t exportId;
|
||||
memcpy(&exportId, begin, sizeof(exportId));
|
||||
if (to->_exports[exportId]) {
|
||||
if (to->_exports[exportId]->release()) {
|
||||
to->_exports.erase(exportId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kReleaseImport:
|
||||
{
|
||||
assert(length == sizeof(exportid_t));
|
||||
exportid_t exportId;
|
||||
memcpy(&exportId, begin, sizeof(exportId));
|
||||
for (size_t i = 0; i < to->_imports.size(); ++i) {
|
||||
if (to->_imports[i]->_task == from->getId() && to->_imports[i]->_export == exportId) {
|
||||
to->_imports[i]->release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kSetTrusted:
|
||||
{
|
||||
assert(length == sizeof(bool));
|
||||
bool trusted = false;
|
||||
memcpy(&trusted, begin, sizeof(bool));
|
||||
to->_trusted = trusted;
|
||||
}
|
||||
break;
|
||||
case kActivate:
|
||||
to->activate();
|
||||
break;
|
||||
case kExecute:
|
||||
{
|
||||
assert(length >= sizeof(promiseid_t));
|
||||
v8::Handle<v8::Value> arg;
|
||||
promiseid_t promise;
|
||||
std::memcpy(&promise, begin, sizeof(promiseid_t));
|
||||
arg = Serialize::load(to, from, std::vector<char>(begin + sizeof(promiseid_t), begin + length));
|
||||
v8::TryCatch tryCatch(to->_isolate);
|
||||
tryCatch.SetCaptureMessage(true);
|
||||
tryCatch.SetVerbose(true);
|
||||
to->execute(*v8::String::Utf8Value(arg));
|
||||
if (tryCatch.HasCaught()) {
|
||||
sendPromiseReject(to, from, promise, Serialize::store(to, tryCatch));
|
||||
}
|
||||
else {
|
||||
sendPromiseResolve(to, from, promise, v8::Undefined(to->_isolate));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kKill:
|
||||
::exit(1);
|
||||
break;
|
||||
case kSetImports:
|
||||
{
|
||||
v8::Handle<v8::Object> result = v8::Handle<v8::Object>::Cast(Serialize::load(to, from, std::vector<char>(begin, begin + length)));
|
||||
to->_importObject = v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> >(to->_isolate, result);
|
||||
}
|
||||
break;
|
||||
case kGetExports:
|
||||
promiseid_t promise;
|
||||
assert(length == sizeof(promise));
|
||||
std::memcpy(&promise, begin, sizeof(promiseid_t));
|
||||
v8::Handle<v8::Object> result = v8::Local<v8::Object>::New(to->_isolate, to->_exportObject);
|
||||
sendPromiseResolve(to, from, promise, result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Task::configureFromStdin() {
|
||||
_parent = TaskStub::createParent(this, STDIN_FILENO);
|
||||
}
|
||||
|
||||
std::string Task::resolveRequire(const std::string& require) {
|
||||
std::string result;
|
||||
std::string path = _scriptName;
|
||||
size_t position = path.rfind('/');
|
||||
if (position != std::string::npos) {
|
||||
path.resize(position + 1);
|
||||
std::cout << "Looking in " << path << " for " << require << "\n";
|
||||
if (require.find("..") == std::string::npos && require.find('/') == std::string::npos) {
|
||||
result = path + require;
|
||||
}
|
||||
if (result.size() && require.rfind(".js") != require.size() - 3) {
|
||||
result += ".js";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Task::require(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
Task* task = Task::get(args.GetIsolate());
|
||||
v8::String::Utf8Value pathValue(args[0]);
|
||||
if (*pathValue) {
|
||||
std::string unresolved(*pathValue, *pathValue + pathValue.length());
|
||||
std::string path = task->resolveRequire(unresolved);
|
||||
if (!path.size()) {
|
||||
args.GetReturnValue().Set(args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), ("require(): Unable to resolve module: " + unresolved).c_str()))));
|
||||
} else {
|
||||
ScriptExportMap::iterator it = task->_scriptExports.find(path);
|
||||
if (it != task->_scriptExports.end()) {
|
||||
v8::Handle<v8::Object> exports = v8::Local<v8::Object>::New(args.GetIsolate(), it->second);
|
||||
args.GetReturnValue().Set(exports);
|
||||
} else {
|
||||
v8::Handle<v8::Object> exports = v8::Object::New(args.GetIsolate());
|
||||
task->_scriptExports[path] = v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> >(args.GetIsolate(), exports);
|
||||
|
||||
v8::Handle<v8::String> name = v8::String::NewFromUtf8(args.GetIsolate(), path.c_str());
|
||||
v8::Handle<v8::String> source = loadFile(args.GetIsolate(), path.c_str());
|
||||
std::cout << "Requiring script " << path << "\n";
|
||||
if (!source.IsEmpty()) {
|
||||
v8::Handle<v8::Object> global = args.GetIsolate()->GetCurrentContext()->Global();
|
||||
v8::Handle<v8::Value> oldExports = global->Get(v8::String::NewFromUtf8(args.GetIsolate(), "exports"));
|
||||
global->Set(v8::String::NewFromUtf8(args.GetIsolate(), "exports"), exports);
|
||||
v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
|
||||
if (!script.IsEmpty()) {
|
||||
script->Run();
|
||||
std::cout << "Script " << path << " completed\n";
|
||||
} else {
|
||||
std::cerr << "Failed to compile script.\n";
|
||||
}
|
||||
global->Set(v8::String::NewFromUtf8(args.GetIsolate(), "exports"), oldExports);
|
||||
args.GetReturnValue().Set(exports);
|
||||
} else {
|
||||
std::cerr << "Failed to load " << path << ".\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
args.GetReturnValue().Set(args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), "require(): No module specified."))));
|
||||
}
|
||||
}
|
163
src/Task.h
Normal file
163
src/Task.h
Normal file
@ -0,0 +1,163 @@
|
||||
#ifndef INCLUDED_Task
|
||||
#define INCLUDED_Task
|
||||
|
||||
#include "PacketStream.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <v8.h>
|
||||
#include <v8-platform.h>
|
||||
#include <vector>
|
||||
|
||||
struct ExportRecord;
|
||||
struct ImportRecord;
|
||||
class Task;
|
||||
class TaskStub;
|
||||
|
||||
struct uv_loop_s;
|
||||
typedef struct uv_loop_s uv_loop_t;
|
||||
|
||||
typedef int taskid_t;
|
||||
typedef int promiseid_t;
|
||||
typedef int exportid_t;
|
||||
|
||||
enum MessageType {
|
||||
kResolvePromise,
|
||||
kRejectPromise,
|
||||
kInvokeExport,
|
||||
kReleaseExport,
|
||||
kReleaseImport,
|
||||
kSetTrusted,
|
||||
kActivate,
|
||||
kExecute,
|
||||
kKill,
|
||||
kStatistics,
|
||||
kSetImports,
|
||||
kGetExports,
|
||||
};
|
||||
|
||||
class NewArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
|
||||
public:
|
||||
void* Allocate(size_t length) {
|
||||
char* bytes = new char[length];
|
||||
std::memset(bytes, 0, length);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void* AllocateUninitialized(size_t length) {
|
||||
return new char[length];
|
||||
}
|
||||
|
||||
void Free(void* data, size_t length) {
|
||||
delete[] reinterpret_cast<char*>(data);
|
||||
}
|
||||
};
|
||||
|
||||
class Task {
|
||||
public:
|
||||
Task();
|
||||
~Task();
|
||||
|
||||
const std::string& getName() const { return _scriptName; }
|
||||
v8::Isolate* getIsolate() { return _isolate; }
|
||||
uv_loop_t* getLoop() { return _loop; }
|
||||
v8::Handle<v8::Context> getContext();
|
||||
void kill();
|
||||
|
||||
promiseid_t allocatePromise();
|
||||
v8::Handle<v8::Promise::Resolver> getPromise(promiseid_t promise);
|
||||
void resolvePromise(promiseid_t promise, v8::Handle<v8::Value> value);
|
||||
void rejectPromise(promiseid_t promise, v8::Handle<v8::Value> value);
|
||||
|
||||
void configureFromStdin();
|
||||
void setTrusted(bool trusted) { _trusted = trusted; }
|
||||
void execute(const char* fileName);
|
||||
void activate();
|
||||
void run();
|
||||
|
||||
static int getCount() { return _count; }
|
||||
static Task* get(v8::Isolate* isolate);
|
||||
TaskStub* get(taskid_t taskId);
|
||||
|
||||
exportid_t exportFunction(v8::Handle<v8::Function> function);
|
||||
static void invokeExport(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
v8::Handle<v8::Function> addImport(taskid_t taskId, exportid_t exportId);
|
||||
void releaseExport(taskid_t taskId, exportid_t exportId);
|
||||
|
||||
private:
|
||||
static int _count;
|
||||
|
||||
TaskStub* _stub = 0;
|
||||
TaskStub* _parent = 0;
|
||||
taskid_t _nextTask = 1;
|
||||
static const taskid_t kParentId = 0;
|
||||
std::map<taskid_t, TaskStub*> _children;
|
||||
|
||||
typedef std::map<std::string, v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> > > ScriptExportMap;
|
||||
ScriptExportMap _scriptExports;
|
||||
|
||||
bool _trusted = false;
|
||||
bool _killed = false;
|
||||
std::string _scriptName;
|
||||
NewArrayBufferAllocator _allocator;
|
||||
v8::Isolate* _isolate = 0;
|
||||
|
||||
std::map<promiseid_t, v8::Persistent<v8::Promise::Resolver, v8::CopyablePersistentTraits<v8::Promise::Resolver> > > _promises;
|
||||
promiseid_t _nextPromise = 0;
|
||||
uv_loop_t* _loop = 0;
|
||||
|
||||
std::map<exportid_t, ExportRecord*> _exports;
|
||||
exportid_t _nextExport = 0;
|
||||
|
||||
v8::Persistent<v8::Context, v8::CopyablePersistentTraits<v8::Context> > _context;
|
||||
|
||||
std::vector<ImportRecord*> _imports;
|
||||
|
||||
v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> > _importObject;
|
||||
v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> > _exportObject;
|
||||
|
||||
v8::Handle<v8::Object> getStatistics();
|
||||
|
||||
std::string resolveRequire(const std::string& require);
|
||||
|
||||
static void activate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void exit(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void print(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void require(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
static void setTimeout(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void timeoutCallback(uv_timer_t* handle);
|
||||
|
||||
static void invokeThen(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void invokeCatch(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
static void parent(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
|
||||
static void version(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
|
||||
static void statistics(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
|
||||
|
||||
static void utf8Length(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
static void getImportProperty(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
|
||||
static void getImports(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
|
||||
static void getExports(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
|
||||
static void setExports(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& args);
|
||||
|
||||
static v8::Handle<v8::Value> invokeExport(TaskStub* from, Task* to, exportid_t exportId, const std::vector<char>& buffer);
|
||||
static void sendPromiseResolve(Task* from, TaskStub* to, promiseid_t promise, v8::Handle<v8::Value> result);
|
||||
static void sendPromiseReject(Task* from, TaskStub* to, promiseid_t promise, v8::Handle<v8::Value> result);
|
||||
|
||||
static void onReceivePacket(int packetType, const char* begin, size_t length, void* userData);
|
||||
|
||||
static void sendPromiseMessage(Task* from, TaskStub* to, MessageType messageType, promiseid_t promise, v8::Handle<v8::Value> result);
|
||||
static void sendPromiseExportMessage(Task* from, TaskStub* to, MessageType messageType, promiseid_t promiseId, exportid_t exportId, v8::Handle<v8::Value> result);
|
||||
|
||||
static v8::Handle<v8::String> loadFile(v8::Isolate* isolate, const char* fileName);
|
||||
|
||||
friend struct ImportRecord;
|
||||
friend class TaskStub;
|
||||
};
|
||||
|
||||
#endif
|
254
src/TaskStub.cpp
Normal file
254
src/TaskStub.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
#include "TaskStub.h"
|
||||
|
||||
#include "PacketStream.h"
|
||||
#include "Serialize.h"
|
||||
#include "Task.h"
|
||||
#include "TaskTryCatch.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
static const int STDIN_FILENO = 0;
|
||||
static const int STDOUT_FILENO = 1;
|
||||
static const int STDERR_FILENO = 2;
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
bool TaskStub::_determinedExecutable = false;
|
||||
char TaskStub::_executable[1024];
|
||||
|
||||
void TaskStub::initialize() {
|
||||
if (!_determinedExecutable) {
|
||||
size_t size = sizeof(_executable);
|
||||
uv_exepath(_executable, &size);
|
||||
_determinedExecutable = true;
|
||||
}
|
||||
}
|
||||
|
||||
TaskStub::TaskStub() {
|
||||
initialize();
|
||||
std::memset(&_process, 0, sizeof(_process));
|
||||
}
|
||||
|
||||
void TaskStub::ref() {
|
||||
if (++_refCount == 1) {
|
||||
_taskObject.ClearWeak();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskStub::release() {
|
||||
if (--_refCount == 0) {
|
||||
_taskObject.SetWeak(this, onRelease);
|
||||
}
|
||||
}
|
||||
|
||||
TaskStub* TaskStub::createParent(Task* task, uv_file file) {
|
||||
v8::Isolate::Scope isolateScope(task->_isolate);
|
||||
v8::HandleScope scope(task->_isolate);
|
||||
|
||||
v8::Local<v8::Context> context = v8::Context::New(task->_isolate, 0);
|
||||
context->Enter();
|
||||
|
||||
v8::Handle<v8::ObjectTemplate> parentTemplate = v8::ObjectTemplate::New(task->_isolate);
|
||||
parentTemplate->SetInternalFieldCount(1);
|
||||
|
||||
v8::Handle<v8::Object> parentObject = parentTemplate->NewInstance();
|
||||
TaskStub* parentStub = new TaskStub();
|
||||
parentStub->_taskObject.Reset(task->_isolate, v8::Local<v8::Object>::New(task->_isolate, parentObject));
|
||||
parentObject->SetInternalField(0, v8::External::New(task->_isolate, parentStub));
|
||||
parentStub->_owner = task;
|
||||
parentStub->_id = Task::kParentId;
|
||||
|
||||
if (uv_pipe_init(task->_loop, &parentStub->_stream.getStream(), 1) != 0) {
|
||||
std::cerr << "uv_pipe_init failed\n";
|
||||
}
|
||||
parentStub->_stream.setOnReceive(Task::onReceivePacket, parentStub);
|
||||
if (uv_pipe_open(&parentStub->_stream.getStream(), file) != 0) {
|
||||
std::cerr << "uv_pipe_open failed\n";
|
||||
}
|
||||
parentStub->_stream.start();
|
||||
|
||||
return parentStub;
|
||||
}
|
||||
|
||||
void TaskStub::create(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Task* parent = Task::get(args.GetIsolate());
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
|
||||
TaskStub* stub = new TaskStub();
|
||||
v8::Handle<v8::External> data = v8::External::New(args.GetIsolate(), stub);
|
||||
|
||||
v8::Handle<v8::ObjectTemplate> taskTemplate = v8::ObjectTemplate::New(args.GetIsolate());
|
||||
taskTemplate->SetAccessor(v8::String::NewFromUtf8(args.GetIsolate(), "trusted"), getTrusted, setTrusted, data);
|
||||
taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "setImports"), v8::FunctionTemplate::New(args.GetIsolate(), setImports, data));
|
||||
taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "getExports"), v8::FunctionTemplate::New(args.GetIsolate(), getExports, data));
|
||||
taskTemplate->SetAccessor(v8::String::NewFromUtf8(args.GetIsolate(), "onExit"), getOnExit, setOnExit, data);
|
||||
taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "activate"), v8::FunctionTemplate::New(args.GetIsolate(), TaskStub::activate, data));
|
||||
taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "execute"), v8::FunctionTemplate::New(args.GetIsolate(), TaskStub::execute, data));
|
||||
taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "kill"), v8::FunctionTemplate::New(args.GetIsolate(), TaskStub::kill, data));
|
||||
taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "statistics"), v8::FunctionTemplate::New(args.GetIsolate(), TaskStub::statistics, data));
|
||||
taskTemplate->SetInternalFieldCount(1);
|
||||
|
||||
v8::Handle<v8::Object> taskObject = taskTemplate->NewInstance();
|
||||
stub->_taskObject.Reset(args.GetIsolate(), taskObject);
|
||||
taskObject->SetInternalField(0, v8::External::New(args.GetIsolate(), stub));
|
||||
stub->_owner = parent;
|
||||
|
||||
taskid_t id = 0;
|
||||
if (parent) {
|
||||
do {
|
||||
id = parent->_nextTask++;
|
||||
if (parent->_nextTask == Task::kParentId) {
|
||||
++parent->_nextTask;
|
||||
}
|
||||
} while (parent->_children.find(id) != parent->_children.end());
|
||||
parent->_children[id] = stub;
|
||||
}
|
||||
stub->_id = id;
|
||||
|
||||
char arg1[] = "--child";
|
||||
char* argv[] = { _executable, arg1, 0 };
|
||||
|
||||
uv_pipe_t* pipe = reinterpret_cast<uv_pipe_t*>(&stub->_stream.getStream());
|
||||
std::memset(pipe, 0, sizeof(*pipe));
|
||||
if (uv_pipe_init(parent->getLoop(), pipe, 1) != 0) {
|
||||
std::cerr << "uv_pipe_init failed\n";
|
||||
}
|
||||
|
||||
uv_stdio_container_t io[3];
|
||||
io[0].flags = static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE);
|
||||
io[0].data.stream = reinterpret_cast<uv_stream_t*>(pipe);
|
||||
io[1].flags = UV_INHERIT_FD;
|
||||
io[1].data.fd = STDOUT_FILENO;
|
||||
io[2].flags = UV_INHERIT_FD;
|
||||
io[2].data.fd = STDERR_FILENO;
|
||||
|
||||
uv_process_options_t options = {0};
|
||||
options.args = argv;
|
||||
options.exit_cb = onProcessExit;
|
||||
options.stdio = io;
|
||||
options.stdio_count = sizeof(io) / sizeof(*io);
|
||||
options.file = argv[0];
|
||||
|
||||
stub->_process.data = stub;
|
||||
int result = uv_spawn(parent->getLoop(), &stub->_process, &options);
|
||||
if (result == 0) {
|
||||
stub->_stream.setOnReceive(Task::onReceivePacket, stub);
|
||||
stub->_stream.start();
|
||||
|
||||
args.GetReturnValue().Set(taskObject);
|
||||
} else {
|
||||
std::cerr << "uv_spawn failed: " << uv_strerror(result) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void TaskStub::onProcessExit(uv_process_t* process, int64_t status, int terminationSignal) {
|
||||
TaskStub* stub = reinterpret_cast<TaskStub*>(process->data);
|
||||
if (!stub->_onExit.IsEmpty()) {
|
||||
TaskTryCatch tryCatch(stub->_owner);
|
||||
v8::HandleScope scope(stub->_owner->_isolate);
|
||||
v8::Handle<v8::Function> callback = v8::Local<v8::Function>::New(stub->_owner->_isolate, stub->_onExit);
|
||||
v8::Handle<v8::Value> args[2];
|
||||
args[0] = v8::Integer::New(stub->_owner->_isolate, status);
|
||||
args[1] = v8::Integer::New(stub->_owner->_isolate, terminationSignal);
|
||||
callback->Call(callback, 2, &args[0]);
|
||||
}
|
||||
stub->_stream.close();
|
||||
stub->_owner->_children.erase(stub->_id);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(process), 0);
|
||||
}
|
||||
|
||||
void TaskStub::onRelease(const v8::WeakCallbackData<v8::Object, TaskStub>& data) {
|
||||
}
|
||||
|
||||
void TaskStub::getTrusted(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
||||
args.GetReturnValue().Set(v8::Boolean::New(args.GetIsolate(), false));
|
||||
}
|
||||
|
||||
void TaskStub::setTrusted(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& args) {
|
||||
if (TaskStub* stub = TaskStub::get(args.Data())) {
|
||||
bool trusted = value->BooleanValue();
|
||||
stub->_stream.send(kSetTrusted, reinterpret_cast<char*>(&trusted), sizeof(trusted));
|
||||
}
|
||||
}
|
||||
|
||||
void TaskStub::getExports(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (TaskStub* stub = TaskStub::get(args.Data())) {
|
||||
TaskTryCatch tryCatch(stub->_owner);
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
|
||||
promiseid_t promise = stub->_owner->allocatePromise();
|
||||
Task::sendPromiseMessage(stub->_owner, stub, kGetExports, promise, v8::Undefined(args.GetIsolate()));
|
||||
args.GetReturnValue().Set(stub->_owner->getPromise(promise));
|
||||
}
|
||||
}
|
||||
|
||||
void TaskStub::setImports(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (TaskStub* stub = TaskStub::get(args.Data())) {
|
||||
std::vector<char> buffer;
|
||||
Serialize::store(Task::get(args.GetIsolate()), buffer, args[0]);
|
||||
stub->_stream.send(kSetImports, &*buffer.begin(), buffer.size());
|
||||
}
|
||||
}
|
||||
|
||||
void TaskStub::getOnExit(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
||||
TaskTryCatch tryCatch(TaskStub::get(args.Data())->_owner);
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
args.GetReturnValue().Set(v8::Local<v8::Function>::New(args.GetIsolate(), TaskStub::get(args.Data())->_onExit));
|
||||
}
|
||||
|
||||
void TaskStub::setOnExit(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& args) {
|
||||
TaskTryCatch tryCatch(TaskStub::get(args.Data())->_owner);
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > function(args.GetIsolate(), v8::Handle<v8::Function>::Cast(value));
|
||||
TaskStub::get(args.Data())->_onExit = function;
|
||||
}
|
||||
|
||||
TaskStub* TaskStub::get(v8::Handle<v8::Value> object) {
|
||||
return reinterpret_cast<TaskStub*>(v8::Handle<v8::External>::Cast(object)->Value());
|
||||
}
|
||||
|
||||
v8::Handle<v8::Object> TaskStub::getTaskObject() {
|
||||
return v8::Local<v8::Object>::New(_owner->getIsolate(), _taskObject);
|
||||
}
|
||||
|
||||
void TaskStub::activate(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (TaskStub* stub = TaskStub::get(args.Data())) {
|
||||
TaskTryCatch tryCatch(stub->_owner);
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
v8::String::Utf8Value fileName(args[0]->ToString(args.GetIsolate()));
|
||||
stub->_stream.send(kActivate, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TaskStub::execute(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (TaskStub* stub = TaskStub::get(args.Data())) {
|
||||
TaskTryCatch tryCatch(stub->_owner);
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
|
||||
promiseid_t promise = stub->_owner->allocatePromise();
|
||||
Task::sendPromiseMessage(stub->_owner, stub, kExecute, promise, args[0]);
|
||||
args.GetReturnValue().Set(stub->_owner->getPromise(promise));
|
||||
}
|
||||
}
|
||||
|
||||
void TaskStub::kill(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (TaskStub* stub = TaskStub::get(args.Data())) {
|
||||
uv_process_kill(&stub->_process, SIGTERM);
|
||||
}
|
||||
}
|
||||
|
||||
void TaskStub::statistics(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (TaskStub* stub = TaskStub::get(args.Data())) {
|
||||
TaskTryCatch tryCatch(stub->_owner);
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
|
||||
promiseid_t promise = stub->_owner->allocatePromise();
|
||||
Task::sendPromiseMessage(stub->_owner, stub, kStatistics, promise, v8::Undefined(args.GetIsolate()));
|
||||
args.GetReturnValue().Set(stub->_owner->getPromise(promise));
|
||||
}
|
||||
}
|
63
src/TaskStub.h
Normal file
63
src/TaskStub.h
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef INCLUDED_TaskStub
|
||||
#define INCLUDED_TaskStub
|
||||
|
||||
#include "PacketStream.h"
|
||||
|
||||
#include <v8.h>
|
||||
|
||||
class Task;
|
||||
|
||||
typedef int taskid_t;
|
||||
|
||||
class TaskStub {
|
||||
public:
|
||||
void ref();
|
||||
void release();
|
||||
|
||||
static void create(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static TaskStub* createParent(Task* task, uv_file file);
|
||||
static void initialize();
|
||||
|
||||
taskid_t getId() { return _id; }
|
||||
Task* getOwner() { return _owner; }
|
||||
v8::Handle<v8::Object> getTaskObject();
|
||||
PacketStream& getStream() { return _stream; }
|
||||
|
||||
private:
|
||||
v8::Persistent<v8::Object> _taskObject;
|
||||
int _refCount = 1;
|
||||
|
||||
Task* _owner = 0;
|
||||
PacketStream _stream;
|
||||
taskid_t _id = -1;
|
||||
uv_process_t _process;
|
||||
|
||||
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _onExit;
|
||||
|
||||
static bool _determinedExecutable;
|
||||
static char _executable[1024];
|
||||
|
||||
TaskStub();
|
||||
|
||||
static TaskStub* get(v8::Handle<v8::Value> object);
|
||||
|
||||
static void getTrusted(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
|
||||
static void setTrusted(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& args);
|
||||
|
||||
static void getExports(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void setImports(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
static void getOnExit(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
|
||||
static void setOnExit(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& args);
|
||||
|
||||
static void activate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void execute(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void kill(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void statistics(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
static void onRelease(const v8::WeakCallbackData<v8::Object, TaskStub>& data);
|
||||
|
||||
static void onProcessExit(uv_process_t* process, int64_t status, int terminationSignal);
|
||||
};
|
||||
|
||||
#endif
|
57
src/TaskTryCatch.cpp
Normal file
57
src/TaskTryCatch.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
#include "TaskTryCatch.h"
|
||||
|
||||
#include "Task.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
const char* TaskTryCatch::toString(const v8::String::Utf8Value& value) {
|
||||
return *value ? *value : "(null)";
|
||||
}
|
||||
|
||||
TaskTryCatch::TaskTryCatch(Task* task) {
|
||||
_tryCatch.SetCaptureMessage(true);
|
||||
_tryCatch.SetVerbose(true);
|
||||
}
|
||||
|
||||
TaskTryCatch::~TaskTryCatch() {
|
||||
if (_tryCatch.HasCaught()) {
|
||||
if (v8::Isolate* isolate = v8::Isolate::GetCurrent()) {
|
||||
if (Task* task = reinterpret_cast<Task*>(isolate->GetData(0))) {
|
||||
std::cerr << "Task[" << task << ':' << task->getName() << "] ";
|
||||
}
|
||||
}
|
||||
std::cerr << "Exception:\n";
|
||||
|
||||
v8::Handle<v8::Message> message(_tryCatch.Message());
|
||||
if (!message.IsEmpty()) {
|
||||
std::cerr
|
||||
<< toString(v8::String::Utf8Value(message->GetScriptResourceName()))
|
||||
<< ':'
|
||||
<< message->GetLineNumber()
|
||||
<< ": "
|
||||
<< toString(v8::String::Utf8Value(_tryCatch.Exception()))
|
||||
<< '\n';
|
||||
std::cerr << toString(v8::String::Utf8Value(message->GetSourceLine())) << '\n';
|
||||
|
||||
for (int i = 0; i < message->GetStartColumn(); ++i) {
|
||||
std::cerr << ' ';
|
||||
}
|
||||
for (int i = message->GetStartColumn(); i < message->GetEndColumn(); ++i) {
|
||||
std::cerr << '^';
|
||||
}
|
||||
if (!message->GetStackTrace().IsEmpty()) {
|
||||
for (int i = 0; i < message->GetStackTrace()->GetFrameCount(); ++i) {
|
||||
std::cerr << "oops " << i << "\n";
|
||||
}
|
||||
}
|
||||
std::cerr << '\n';
|
||||
} else {
|
||||
std::cerr << toString(v8::String::Utf8Value(_tryCatch.Exception())) << '\n';
|
||||
}
|
||||
|
||||
v8::String::Utf8Value stackTrace(_tryCatch.StackTrace());
|
||||
if (stackTrace.length() > 0) {
|
||||
std::cerr << *stackTrace << '\n';
|
||||
}
|
||||
}
|
||||
}
|
18
src/TaskTryCatch.h
Normal file
18
src/TaskTryCatch.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef INCLUDED_TaskTryCatch
|
||||
#define INCLUDED_TaskTryCatch
|
||||
|
||||
#include <v8.h>
|
||||
|
||||
class Task;
|
||||
|
||||
class TaskTryCatch {
|
||||
public:
|
||||
TaskTryCatch(Task* task);
|
||||
~TaskTryCatch();
|
||||
|
||||
private:
|
||||
v8::TryCatch _tryCatch;
|
||||
static const char* toString(const v8::String::Utf8Value& value);
|
||||
};
|
||||
|
||||
#endif
|
1126
src/Tls.cpp
Normal file
1126
src/Tls.cpp
Normal file
File diff suppressed because it is too large
Load Diff
50
src/Tls.h
Normal file
50
src/Tls.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef INCLUDED_Tls
|
||||
#define INCLUDED_Tls
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
class TlsSession;
|
||||
|
||||
class TlsContext {
|
||||
public:
|
||||
static TlsContext* create();
|
||||
virtual ~TlsContext() {}
|
||||
|
||||
virtual TlsSession* createSession() { return 0; }
|
||||
virtual bool setCertificate(const char* certificate) { return false; }
|
||||
virtual bool setPrivateKey(const char* privateKey) { return false; }
|
||||
virtual bool addTrustedCertificate(const char* certificate) { return false; }
|
||||
};
|
||||
|
||||
class TlsSession {
|
||||
public:
|
||||
virtual ~TlsSession() {}
|
||||
|
||||
virtual void setHostname(const char* hostname) {}
|
||||
virtual void startAccept() = 0;
|
||||
virtual void startConnect() = 0;
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
virtual int getPeerCertificate(char* buffer, size_t bytes) { return -1; }
|
||||
|
||||
enum HandshakeResult {
|
||||
kDone,
|
||||
kMore,
|
||||
kFailed,
|
||||
};
|
||||
virtual HandshakeResult handshake() = 0;
|
||||
|
||||
enum ReadResult {
|
||||
kReadZero = -1,
|
||||
kReadFailed = -2,
|
||||
};
|
||||
virtual int readPlain(char* buffer, size_t bytes) = 0;
|
||||
virtual int writePlain(const char* buffer, size_t bytes) = 0;
|
||||
|
||||
virtual int readEncrypted(char* buffer, size_t bytes) = 0;
|
||||
virtual int writeEncrypted(const char* buffer, size_t bytes) = 0;
|
||||
|
||||
virtual bool getError(char* buffer, size_t bytes) { return false; }
|
||||
};
|
||||
|
||||
#endif
|
115
src/TlsContextWrapper.cpp
Normal file
115
src/TlsContextWrapper.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
#include "TlsContextWrapper.h"
|
||||
|
||||
#include "Task.h"
|
||||
#include "Tls.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
int TlsContextWrapper::_count = 0;
|
||||
|
||||
void TlsContextWrapper::create(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope handleScope(args.GetIsolate());
|
||||
if (TlsContextWrapper* wrapper = new TlsContextWrapper(Task::get(args.GetIsolate()))) {
|
||||
v8::Handle<v8::Object> result = v8::Local<v8::Object>::New(args.GetIsolate(), wrapper->_object);
|
||||
args.GetReturnValue().Set(result);
|
||||
wrapper->release();
|
||||
}
|
||||
}
|
||||
|
||||
TlsContextWrapper::TlsContextWrapper(Task* task) {
|
||||
++_count;
|
||||
v8::HandleScope scope(task->getIsolate());
|
||||
v8::Handle<v8::External> identifier = v8::External::New(task->getIsolate(), reinterpret_cast<void*>(&TlsContextWrapper::create));
|
||||
v8::Handle<v8::External> data = v8::External::New(task->getIsolate(), this);
|
||||
|
||||
v8::Local<v8::ObjectTemplate> wrapperTemplate = v8::ObjectTemplate::New(task->getIsolate());
|
||||
wrapperTemplate->SetInternalFieldCount(2);
|
||||
wrapperTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "setCertificate"), v8::FunctionTemplate::New(task->getIsolate(), setCertificate, data));
|
||||
wrapperTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "setPrivateKey"), v8::FunctionTemplate::New(task->getIsolate(), setPrivateKey, data));
|
||||
wrapperTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "addTrustedCertificate"), v8::FunctionTemplate::New(task->getIsolate(), addTrustedCertificate, data));
|
||||
|
||||
v8::Local<v8::Object> wrapperObject = wrapperTemplate->NewInstance();
|
||||
wrapperObject->SetInternalField(0, identifier);
|
||||
wrapperObject->SetInternalField(1, data);
|
||||
_object.Reset(task->getIsolate(), wrapperObject);
|
||||
|
||||
_context = TlsContext::create();
|
||||
_task = task;
|
||||
}
|
||||
|
||||
TlsContextWrapper::~TlsContextWrapper() {
|
||||
close();
|
||||
--_count;
|
||||
}
|
||||
|
||||
void TlsContextWrapper::close() {
|
||||
if (_context) {
|
||||
delete _context;
|
||||
_context = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TlsContextWrapper::onRelease(const v8::WeakCallbackData<v8::Object, TlsContextWrapper>& data) {
|
||||
data.GetParameter()->_object.Reset();
|
||||
delete data.GetParameter();
|
||||
}
|
||||
|
||||
TlsContextWrapper* TlsContextWrapper::get(v8::Handle<v8::Value> value) {
|
||||
TlsContextWrapper* result = 0;
|
||||
|
||||
if (!value.IsEmpty()
|
||||
&& value->IsObject())
|
||||
{
|
||||
v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value);
|
||||
if (object->InternalFieldCount() == 2
|
||||
&& v8::Handle<v8::External>::Cast(object->GetInternalField(0))->Value() == &TlsContextWrapper::create)
|
||||
{
|
||||
result = reinterpret_cast<TlsContextWrapper*>(v8::Handle<v8::External>::Cast(object->GetInternalField(1))->Value());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TlsContextWrapper* TlsContextWrapper::get(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
return reinterpret_cast<TlsContextWrapper*>(v8::Handle<v8::External>::Cast(args.Data())->Value());
|
||||
}
|
||||
|
||||
void TlsContextWrapper::ref() {
|
||||
if (++_refCount == 1) {
|
||||
_object.ClearWeak();
|
||||
}
|
||||
}
|
||||
|
||||
void TlsContextWrapper::release() {
|
||||
assert(_refCount >= 1);
|
||||
if (--_refCount == 0) {
|
||||
_object.SetWeak(this, onRelease);
|
||||
}
|
||||
}
|
||||
|
||||
void TlsContextWrapper::setCertificate(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (TlsContextWrapper* wrapper = TlsContextWrapper::get(args)) {
|
||||
v8::String::Utf8Value value(args[0]->ToString(args.GetIsolate()));
|
||||
wrapper->_context->setCertificate(*value);
|
||||
}
|
||||
}
|
||||
|
||||
void TlsContextWrapper::setPrivateKey(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (TlsContextWrapper* wrapper = TlsContextWrapper::get(args)) {
|
||||
v8::String::Utf8Value value(args[0]->ToString(args.GetIsolate()));
|
||||
wrapper->_context->setPrivateKey(*value);
|
||||
}
|
||||
}
|
||||
|
||||
void TlsContextWrapper::addTrustedCertificate(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (TlsContextWrapper* wrapper = TlsContextWrapper::get(args)) {
|
||||
v8::String::Utf8Value value(args[0]->ToString(args.GetIsolate()));
|
||||
wrapper->_context->addTrustedCertificate(*value);
|
||||
}
|
||||
}
|
||||
|
||||
int TlsContextWrapper::getCount()
|
||||
{
|
||||
return _count;
|
||||
}
|
42
src/TlsContextWrapper.h
Normal file
42
src/TlsContextWrapper.h
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef INCLUDED_TlsContextWrapper
|
||||
#define INCLUDED_TlsContextWrapper
|
||||
|
||||
#include <v8.h>
|
||||
|
||||
class Task;
|
||||
class TlsContext;
|
||||
|
||||
class TlsContextWrapper {
|
||||
public:
|
||||
static void create(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
void close();
|
||||
|
||||
static TlsContextWrapper* get(v8::Handle<v8::Value> value);
|
||||
|
||||
static void setCertificate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void setPrivateKey(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void addTrustedCertificate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
static void onRelease(const v8::WeakCallbackData<v8::Object, TlsContextWrapper>& data);
|
||||
|
||||
TlsContext* getContext() { return _context; }
|
||||
|
||||
static int getCount();
|
||||
|
||||
private:
|
||||
TlsContextWrapper(Task* task);
|
||||
~TlsContextWrapper();
|
||||
|
||||
static TlsContextWrapper* get(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
TlsContext* _context = 0;
|
||||
Task* _task = 0;
|
||||
v8::Persistent<v8::Object> _object;
|
||||
int _refCount = 1;
|
||||
static int _count;
|
||||
|
||||
void ref();
|
||||
void release();
|
||||
};
|
||||
|
||||
#endif
|
75
src/main.cpp
Normal file
75
src/main.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
#include "Task.h"
|
||||
#include "TaskStub.h"
|
||||
#include "TaskTryCatch.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <libplatform/libplatform.h>
|
||||
#include <uv.h>
|
||||
#include <v8.h>
|
||||
#include <v8-platform.h>
|
||||
|
||||
#if !defined (_WIN32) && !defined (__MACH__)
|
||||
#include <signal.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
v8::Platform* gPlatform = 0;
|
||||
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
uv_setup_args(argc, argv);
|
||||
TaskStub::initialize();
|
||||
v8::V8::InitializeICU();
|
||||
gPlatform = v8::platform::CreateDefaultPlatform();
|
||||
v8::V8::InitializePlatform(gPlatform);
|
||||
v8::V8::Initialize();
|
||||
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
|
||||
|
||||
bool isChild = false;
|
||||
const char* coreTask = "core/core.js";
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (!std::strcmp(argv[i], "--child")) {
|
||||
isChild = true;
|
||||
} else {
|
||||
coreTask = argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined (_WIN32)
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
||||
perror("signal");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (isChild) {
|
||||
#if !defined (_WIN32) && !defined (__MACH__)
|
||||
prctl(PR_SET_PDEATHSIG, SIGHUP);
|
||||
#endif
|
||||
Task task;
|
||||
task.configureFromStdin();
|
||||
task.activate();
|
||||
task.run();
|
||||
} else {
|
||||
#if !defined (_WIN32) && !defined (__MACH__)
|
||||
setpgid(0, 0);
|
||||
#endif
|
||||
Task task;
|
||||
task.setTrusted(true);
|
||||
task.activate();
|
||||
|
||||
{
|
||||
v8::Isolate::Scope isolateScope(task.getIsolate());
|
||||
v8::HandleScope handleScope(task.getIsolate());
|
||||
v8::Context::Scope contextScope(task.getContext());
|
||||
TaskTryCatch tryCatch(&task);
|
||||
task.execute(coreTask);
|
||||
}
|
||||
task.run();
|
||||
}
|
||||
|
||||
v8::V8::Dispose();
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user