Skip to content
Browse files

Transparent curl_multi_* support. WIP.

  • Loading branch information...
1 parent d965c6d commit 4a1b5da0aad5f628797e51e9f7736c2ee8f300ac @bnoordhuis committed
Showing with 172 additions and 71 deletions.
  1. +172 −71 curl.cc
View
243 curl.cc
@@ -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);
@@ -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();
}
@@ -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) {
@@ -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());

0 comments on commit 4a1b5da

Please sign in to comment.
Something went wrong with that request. Please try again.