Skip to content

Commit

Permalink
Transparent curl_multi_* support. WIP.
Browse files Browse the repository at this point in the history
  • Loading branch information
bnoordhuis committed Jun 6, 2011
1 parent d965c6d commit 4a1b5da
Showing 1 changed file with 172 additions and 71 deletions.
243 changes: 172 additions & 71 deletions curl.cc
Expand Up @@ -15,94 +15,193 @@ namespace {

Persistent<ObjectTemplate> curlHandleTemplate;

Handle<Value> Error(const char* message) {
return ThrowException(
Exception::Error(
String::New(message)));
}

Handle<Value> TypeError(const char* message) {
return ThrowException(
Exception::TypeError(
String::New(message)));
}

template <class T> Handle<Value> CurlError(T status);

template <> Handle<Value> CurlError<CURLcode>(CURLcode status) {
return Error(curl_easy_strerror(status));
}

template <> Handle<Value> CurlError<CURLMcode>(CURLMcode status) {
return Error(curl_multi_strerror(status));
}

//
// CurlHandle definition
//
class CurlHandle: public ObjectWrap {
public:
static Handle<Object> New() {
CurlHandle* const ch = new CurlHandle();
static Handle<Object> New();
static bool IsInstanceOf(Handle<Value> val);
static CurlHandle* Unwrap(Handle<Value> handle);

// glue C++ object to a V8-managed JS object
Local<Object> handle = curlHandleTemplate->NewInstance();
handle->SetPointerInInternalField(1, reinterpret_cast<void*>(&curlHandleTemplate)); // magic cookie
ch->Wrap(handle);
void SetWriteCallback(Handle<Value> callback);
Handle<Value> InvokeWriteCallback(Buffer* data);
operator CURL*();
virtual ~CurlHandle();

return ch->handle_;
}
private:
CURL* const ch_;
Persistent<Function> read_callback_;
Persistent<Function> write_callback_;

static bool IsInstanceOf(Handle<Value> val) {
if (val->IsObject()) {
Local<Object> o = val->ToObject();
return o->InternalFieldCount() >= 2
&& o->GetPointerFromInternalField(1) == reinterpret_cast<void*>(&curlHandleTemplate);
}
else {
return false;
}
}
CurlHandle();
};

static CurlHandle* Unwrap(Handle<Value> handle) {
if (IsInstanceOf(handle)) {
return ObjectWrap::Unwrap<CurlHandle>(handle->ToObject());
}
else {
return NULL;
}
}
//
// MultiHandle definition
//
class MultiHandle {
public:
static bool Initialize();
static MultiHandle& Singleton();
Handle<Value> Add(CurlHandle& ch);
Handle<Value> Remove(CurlHandle& ch);

private:
static MultiHandle* singleton_;
unsigned num_handles_;
CURLM* const mh_;

MultiHandle();
~MultiHandle();
};

~CurlHandle() {
read_callback_.Dispose();
write_callback_.Dispose();
curl_easy_cleanup(ch_);
//
// CurlHandle implementation
//
Handle<Object> CurlHandle::New() {
CurlHandle* const ch = new CurlHandle();

// glue C++ object to a V8-managed JS object
Local<Object> handle = curlHandleTemplate->NewInstance();
handle->SetPointerInInternalField(1, reinterpret_cast<void*>(&curlHandleTemplate)); // magic cookie
ch->Wrap(handle);

return ch->handle_;
}

bool CurlHandle::IsInstanceOf(Handle<Value> val) {
if (val->IsObject()) {
Local<Object> o = val->ToObject();
return o->InternalFieldCount() >= 2
&& o->GetPointerFromInternalField(1) == reinterpret_cast<void*>(&curlHandleTemplate);
}
else {
return false;
}
}

operator CURL*() {
return ch_;
CurlHandle* CurlHandle::Unwrap(Handle<Value> handle) {
if (IsInstanceOf(handle)) {
return ObjectWrap::Unwrap<CurlHandle>(handle->ToObject());
}
else {
return NULL;
}
}

void SetWriteCallback(Handle<Value> callback) {
Local<Function> fun = Local<Function>(Function::Cast(*callback));
write_callback_.Clear();
write_callback_ = Persistent<Function>::New(fun);
CurlHandle::CurlHandle(): ch_(curl_easy_init()) {
if (ch_ == NULL) {
Error("curl_easy_init() returned NULL!");
}
}

Handle<Value> InvokeWriteCallback(Buffer* data) {
HandleScope scope;
CurlHandle::~CurlHandle() {
read_callback_.Dispose();
write_callback_.Dispose();
MultiHandle::Singleton().Remove(*this);
curl_easy_cleanup(ch_);
}

Local<Object> global = Context::GetCurrent()->Global();
Handle<Value> args[] = { data->handle_ };
Local<Value> rv = write_callback_->Call(global, 1, args);
CurlHandle::operator CURL*() {
return ch_;
}

return scope.Close(rv);
}
void CurlHandle::SetWriteCallback(Handle<Value> callback) {
Local<Function> fun = Local<Function>(Function::Cast(*callback));
write_callback_.Clear();
write_callback_ = Persistent<Function>::New(fun);
}

private:
CURL* const ch_;
Persistent<Function> read_callback_;
Persistent<Function> write_callback_;
Handle<Value> CurlHandle::InvokeWriteCallback(Buffer* data) {
HandleScope scope;

CurlHandle(): ch_(curl_easy_init()) {
if (ch_ == 0) {
// raise OOM exception?
V8::LowMemoryNotification();
}
Local<Object> global = Context::GetCurrent()->Global();
Handle<Value> args[] = { data->handle_ };
Local<Value> rv = write_callback_->Call(global, 1, args);

return scope.Close(rv);
}

//
// MultiHandle implementation
//
MultiHandle* MultiHandle::singleton_;

MultiHandle::MultiHandle(): mh_(curl_multi_init()) {
if (mh_ == 0) {
Error("curl_multi_init() returned NULL!");
}
};
}

Handle<Value> Error(const char* message) {
return ThrowException(
Exception::Error(
String::New(message)));
MultiHandle::~MultiHandle() {
// complain if there are still easy handles pending?
curl_multi_cleanup(mh_);
}

Handle<Value> TypeError(const char* message) {
return ThrowException(
Exception::TypeError(
String::New(message)));
bool MultiHandle::Initialize() {
assert(singleton_ == NULL);
singleton_ = new MultiHandle();
return singleton_->mh_ != NULL;
}

Handle<Value> CurlError(CURLcode status) {
return Error(curl_easy_strerror(status));
MultiHandle& MultiHandle::Singleton() {
return *singleton_;
}

Handle<Value> MultiHandle::Add(CurlHandle& ch) {
const CURLMcode status = curl_multi_add_handle(mh_, ch);
if (status != CURLM_OK) {
return CurlError(status);
}

if (++num_handles_ == 1) {
// start the event loop
ev_ref();
}

return Undefined();
}

Handle<Value> MultiHandle::Remove(CurlHandle& ch) {
const CURLMcode status = curl_multi_remove_handle(mh_, ch);
if (status != CURLM_OK) {
return CurlError(status);
}

if (--num_handles_ == 1) {
// stop the event loop
ev_unref();
}

return Undefined();
}

//
// helpers
//
size_t WriteFunction(char* data, size_t size, size_t nmemb, void* arg) {
CurlHandle* ch = reinterpret_cast<CurlHandle*>(arg);

Expand All @@ -118,6 +217,9 @@ size_t WriteFunction(char* data, size_t size, size_t nmemb, void* arg) {
return nmemb;
}

//
// bindings (glue)
//
Handle<Value> curl_easy_init_g(const Arguments& args) {
return CurlHandle::New();
}
Expand Down Expand Up @@ -392,15 +494,9 @@ Handle<Value> curl_easy_perform_g(const Arguments& args) {
if (!CurlHandle::IsInstanceOf(args[0])) {
return TypeError("Argument #1 must be a node-curl handle.");
}
CurlHandle* ch = CurlHandle::Unwrap(args[0]);

CURLcode status = curl_easy_perform(*ch);
if (status != CURLE_OK) {
// TODO throw a {code, message} exception
return CurlError(status);
}

return Undefined();
CurlHandle* ch = CurlHandle::Unwrap(args[0]);
return MultiHandle::Singleton().Add(*ch);
}

void RegisterModule(Handle<Object> target) {
Expand All @@ -414,6 +510,11 @@ void RegisterModule(Handle<Object> target) {
}
atexit(curl_global_cleanup);

if (!MultiHandle::Initialize()) {
Error("curl_multi_init() returned NULL!");
return;
}

target->Set(
String::NewSymbol("curl_easy_init"),
FunctionTemplate::New(curl_easy_init_g)->GetFunction());
Expand Down

0 comments on commit 4a1b5da

Please sign in to comment.