diff --git a/README.md b/README.md index 615be06..d03e99a 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,88 @@ -Node Curl Wrap -Use select curl multi +node-curl +========= + +node cURL wrapper, support all options and infos. Quick Start -=========== +----------- -curl = require('node-curl') -curl('www.google.com', {VERBOSE: 1}, function(err, res) { - console.info(res.status) - console.info('-----') - console.info(res.body) - console.info('-----') - console.info(res.info('SIZE_DOWNLOAD')) -}); +* quick start + + curl = require('node-curl'); + curl('www.google.com', function(err, res) { + console.info(res.status); + console.info('-----'); + console.info(res.body); + console.info('-----'); + console.info(res.info('SIZE_DOWNLOAD')); + res.close(); + }); + + +* with options + + curl = require('node-curl') + curl('www.google.com', {VERBOSE: 1, RAW: 1}, function(err, res) { + console.info(res); + res.close(); + }); Usage -===== +----- -Function: curl -curl(url, [options = {}], callback) -callback includes 2 parameters (error, result) +* curl -Curl Options: -options is on the list at http://curl.haxx.se/libcurl/c/curl_easy_setopt.html without CURLOPT_ -eg: CURLOPT_VERBOSE will be VERBOSE, CURLOPT_HEADER will be HEADER + curl(url, [options = {}], callback) + callback includes 2 parameters (error, result) -Object: result -body -status -info +* result in callback -Curl Infos: -infos is on the list at http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html without CURLINFO_ -eg: CURLINFO_EFFECTIVE_URL will be EFFETCTIVE_URL + members: + status - Http Response code + body - Http body + + methods: + info(name) - Get information of result, see 'info' section + +Options +------- +* Any Curl Easy Options + + eg: CURLOPT_VERBOSE will be VERBOSE, CURLOPT_HEADER will be HEADER + + Full list at http://curl.haxx.se/libcurl/c/curl_easy_setopt.html + +* node-curl Extra Options + + RAW - Returns Buffer instead of String in result.body + +* About slist parameters + + node-curl support slist which map to Javascript Array + + eg: + HTTP_HEADER: ['FOO', 'BAR'] + HTTP_HEADER: 'FOO' + + any non-array parameter will convert to [ parameter.toString() ] + +Infos +----- +* Any Curl Info options + + eg: CURLINFO_EFFECTIVE_URL will be EFFETCTIVE_URL + + full list at http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html +* About slist + + slist will be returns in Array + eg: CURLINFO_COOKIELIST + + +Hints +----- +close the result to release resource of curl immediately. + +or the resource will not release until gc performed. diff --git a/examples/pressure.js b/examples/pressure.js new file mode 100644 index 0000000..d29605e --- /dev/null +++ b/examples/pressure.js @@ -0,0 +1,30 @@ +// Generated by ToffeeScript 1.1.4-4 +(function() { + var assert, curl, i, j, next, once, _i; + + curl = require('../index'); + + assert = require('assert'); + + j = 0; + + (next = function() { + console.info("curl instances: ", curl.get_count()); + return setTimeout(next, 1000); + })(); + + for (i = _i = 1; _i <= 100; i = ++_i) { + (once = function() { + var _this = this; + return curl('localhost/test.html', function(_$$_err, _$$_res) { + var err, res; + err = _$$_err; + res = _$$_res; + assert.equal(res.body.length, 1468); + if (++j % 100 === 0) console.info(j); + if (j < 20000) return once(); + }); + })(); + } + +}).call(this); diff --git a/examples/pressure.toffee b/examples/pressure.toffee new file mode 100644 index 0000000..f9a7cd1 --- /dev/null +++ b/examples/pressure.toffee @@ -0,0 +1,16 @@ +curl = require '../index' +assert = require 'assert' +j = 0; +do next = -> + console.info "curl instances: ", curl.get_count() + setTimeout next, 1000 + +for i in [1..100] + do once = -> + err, res = curl! 'localhost/test.html' + assert.equal res.body.length, 1468 + if ++j % 100 == 0 + console.info j + if j < 20000 + once() + # res.close() diff --git a/examples/quick-start.js b/examples/quick-start.js index a60d162..61ff590 100644 --- a/examples/quick-start.js +++ b/examples/quick-start.js @@ -1,19 +1,27 @@ curl = require('../index'); -url = 'www.yahoo.com'; -curl(url, function(err, res) { +url = 'www.nodejs.org'; +options = {CONNECTTIMEOUT: 2, VERBOSE: 1}; +curl(url, options, function(err, res) { + console.info("\x1b[33meffetcive url: " + res.info('EFFECTIVE_URL') + "\x1b[0m"); console.info("body length: " + res.body.length); - console.info('-----'); - console.info("status: " + res.status); - console.info('-----'); - console.info("size download: " + res.info('SIZE_DOWNLOAD')); - console.info("\033[33meffetcive url: " + res.info('EFFECTIVE_URL') + "\033[0m"); + res.close(); }); -curl(url, function(err, res) { +url = 'www.yahoo.com' +curl(url, options, function(err, res) { + console.info("\x1b[33meffetcive url: " + res.info('EFFECTIVE_URL') + "\x1b[0m"); console.info("body length: " + res.body.length); - console.info('-----'); - console.info("status: " + res.status); - console.info('-----'); - console.info("size download: " + res.info('SIZE_DOWNLOAD')); - console.info("\033[33meffetcive url: " + res.info('EFFECTIVE_URL') + "\033[0m"); + res.close(); }); + +curl('www.google.com', {VERBOSE: 1, RAW: 1}, function(err, res) { + console.info(res); + res.close(); +}); + +/* +console.info('-----'); +console.info("status: " + res.status); +console.info('-----'); +console.info("size download: " + res.info('SIZE_DOWNLOAD')); +*/ diff --git a/examples/test.js b/examples/test.js new file mode 100644 index 0000000..23b227f --- /dev/null +++ b/examples/test.js @@ -0,0 +1,40 @@ +// Generated by ToffeeScript 1.1.4-4 +(function() { + var curl, fs, p, url, util, + _this = this; + + curl = require('../index'); + + fs = require('fs'); + + util = require('util'); + + p = console.info; + + url = 'www.google.com'; + + curl(url, { + HTTPHEADER: 'BAR', + VERBOSE: 1, + COOKIEFILE: 'node-curl-cookie.txt' + }, function(_$$_err, _$$_res) { + var err, res; + err = _$$_err; + res = _$$_res; + p("\x1b[33m" + util.inspect(res.info('COOKIELIST')) + "\x1b[0m"); + url = 'www.yahoo.com'; + return curl(url, { + HTTPHEADER: ['foo', 'bar'], + VERBOSE: 1, + COOKIEFILE: 'node-curl-cookie.txt', + RAW: 1 + }, function(_$$_err, _$$_res) { + err = _$$_err; + res = _$$_res; + p("\x1b[33m" + util.inspect(res.info('COOKIELIST')) + "\x1b[0m"); + p(res.body); + return fs.unlink('node-curl-cookie.txt'); + }); + }); + +}).call(this); diff --git a/examples/test.toffee b/examples/test.toffee new file mode 100644 index 0000000..ee7d1ef --- /dev/null +++ b/examples/test.toffee @@ -0,0 +1,22 @@ +curl = require '../index' +fs = require 'fs' +util = require 'util' +p = console.info + +url = 'www.google.com' +err, res = curl! url, + HTTPHEADER: 'BAR' + VERBOSE: 1 + COOKIEFILE: 'node-curl-cookie.txt' + +p "\x1b[33m" + util.inspect(res.info('COOKIELIST')) + "\x1b[0m" +url = 'www.yahoo.com' +err, res = curl! url, + HTTPHEADER: ['foo', 'bar'] + VERBOSE: 1 + COOKIEFILE: 'node-curl-cookie.txt' + RAW: 1 + +p "\x1b[33m" + util.inspect(res.info('COOKIELIST')) + "\x1b[0m" +p res.body +fs.unlink 'node-curl-cookie.txt' diff --git a/lib/curl.js b/lib/curl.js index 1205042..3d60d06 100644 --- a/lib/curl.js +++ b/lib/curl.js @@ -1,6 +1,6 @@ -// Generated by CoffeeScript 1.1.4-3 +// Generated by ToffeeScript 1.1.4-4 (function() { - var Curl, curl, curl_id, + var Curl, curl, curl_id, curls, __slice = [].slice, __hasProp = {}.hasOwnProperty; @@ -10,10 +10,18 @@ Curl = require(__dirname + '/../build/default/node-curl').Curl; } + Curl.prototype.setopt_user_ = function(option_id, value) { + return this.options[option_id] = value; + }; + Curl.prototype.setopt = function(ooption, value) { var option, option_id; option = ooption.toUpperCase(); - if ((option_id = Curl.integer_options[option]) != null) { + if ((option_id = Curl.user_options[option]) != null) { + return this.setopt_user_(option_id, value); + } else if ((option_id = Curl.slist_options[option]) != null) { + return this.setopt_slist_(option_id, value); + } else if ((option_id = Curl.integer_options[option]) != null) { return this.setopt_int_(option_id, value >> 0); } else if ((option_id = Curl.string_options[option]) != null) { return this.setopt_str_(option_id, value.toString()); @@ -25,14 +33,16 @@ Curl.prototype.getinfo = function(oinfo) { var info, info_id; info = oinfo.toUpperCase(); - if ((info_id = Curl.integer_infos[info]) != null) { + if ((info_id = Curl.slist_infos[info]) != null) { + return this.getinfo_slist_(info_id); + } else if ((info_id = Curl.integer_infos[info]) != null) { return this.getinfo_int_(info_id); } else if ((info_id = Curl.string_infos[info]) != null) { return this.getinfo_str_(info_id); } else if ((info_id = Curl.double_infos[info]) != null) { return this.getinfo_double_(info_id); } else { - throw new Error("unsupproted info " + oinfo); + throw new Error("unsupported info " + oinfo); } }; @@ -41,6 +51,10 @@ return Curl.process(); }; + Curl.user_options = { + RAW: 'RAW' + }; + Curl.process = function() { var once; if (Curl.in_process) return; @@ -58,63 +72,84 @@ curl_id = 0; + curls = {}; + curl = function() { - var args, c, cb, chunks, k, length, options, res, url, v; + var args, c, cb, k, length, options, url, v; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; cb = args.pop(); url = args[0], options = args[1]; if (options == null) options = {}; c = new Curl(); + c.options = {}; c.id = ++curl_id; + curls[c.id] = c; c.setopt('FOLLOWLOCATION', 1); c.setopt('ACCEPT_ENCODING', 'gzip'); - chunks = []; + c.chunks = []; length = 0; - res = {}; for (k in options) { if (!__hasProp.call(options, k)) continue; v = options[k]; c.setopt(k, v); } c.on_write = function(chunk) { - chunks.push(chunk); - length += chunk.length; - return console.info("on_write " + c.id + " " + chunk.length); + c.chunks.push(chunk); + return length += chunk.length; }; c.on_end = function() { - var chunk, data, i, position, st, _i, _len; + var chunk, data, position, res, _i, _len, _ref, + _this = this; data = new Buffer(length); position = 0; - for (_i = 0, _len = chunks.length; _i < _len; _i++) { - chunk = chunks[_i]; + _ref = c.chunks; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + chunk = _ref[_i]; chunk.copy(data, position); position += chunk.length; } - st = Date.now(); - i = 0; - while (Date.now() - st < 500) { - ++i; + c.chunks = []; + res = {}; + res.curl_ = c; + delete curls[c.id]; + if (c.options.RAW) { + res.body = data; + } else { + res.body = data.toString(); } - res.body = data; res.status = res.code = c.getinfo('RESPONSE_CODE'); res.info = function(info) { - return c.getinfo(info); + if (this.curl_ == null) throw new Error('curl is closed'); + return this.curl_.getinfo(info); }; - console.info("id: " + c.id); - return cb(null, res); + res.close = function() { + if (this.curl_ != null) this.curl_.close(); + this.curl_ = null; + return this.body = null; + }; + return process.nextTick(function() { + return cb(null, res); + }); }; c.on_error = function(err) { var _this = this; + curls[c.id].close(); + delete curls[c.id]; return process.nextTick(function() { return cb(err, null); }); }; c.setopt('URL', url); - return c.perform(); + c.perform(); + return c; }; curl.Curl = Curl; + curl.get_count = function() { + return Curl.get_count(); + }; + module.exports = curl; }).call(this); diff --git a/lib/curl.toffee b/lib/curl.toffee index eda4a21..782863f 100644 --- a/lib/curl.toffee +++ b/lib/curl.toffee @@ -3,9 +3,19 @@ try catch e {Curl} = require __dirname + '/../build/default/node-curl' +Curl::setopt_user_ = (option_id, value) -> + @options[option_id] = value + Curl::setopt = (ooption, value) -> option = ooption.toUpperCase() - if (option_id = Curl.integer_options[option])? + + # slist must be at the top of condition + # the option exists in string_options too + if (option_id = Curl.user_options[option])? + @setopt_user_ option_id, value + else if (option_id = Curl.slist_options[option])? + @setopt_slist_ option_id, value + else if (option_id = Curl.integer_options[option])? @setopt_int_ option_id, value >> 0 else if (option_id = Curl.string_options[option])? @setopt_str_ option_id, value.toString() @@ -14,19 +24,24 @@ Curl::setopt = (ooption, value) -> Curl::getinfo = (oinfo) -> info = oinfo.toUpperCase() - if (info_id = Curl.integer_infos[info])? + if (info_id = Curl.slist_infos[info])? + @getinfo_slist_(info_id) + else if (info_id = Curl.integer_infos[info])? @getinfo_int_(info_id) else if (info_id = Curl.string_infos[info])? @getinfo_str_(info_id) else if (info_id = Curl.double_infos[info])? @getinfo_double_(info_id) else - throw new Error("unsupproted info #{oinfo}") + throw new Error("unsupported info #{oinfo}") Curl::perform = -> @perform_() Curl.process() +Curl.user_options = + RAW: 'RAW' + Curl.process = -> if Curl.in_process return @@ -40,56 +55,75 @@ Curl.process = -> # url, [options], cb curl_id = 0 +curls = {} curl = (args...) -> cb = args.pop() [url, options] = args options ?= {} c = new Curl() + c.options = {} c.id = ++curl_id + # after the scope c will not valid any more, so add to curls to keep c alive + curls[c.id] = c + c.setopt 'FOLLOWLOCATION', 1 c.setopt 'ACCEPT_ENCODING', 'gzip' - chunks = [] + c.chunks = [] length = 0 - res = {} for own k, v of options c.setopt k, v c.on_write = (chunk) -> - chunks.push chunk + c.chunks.push chunk length += chunk.length - console.info "on_write #{c.id} #{chunk.length}" c.on_end = -> data = new Buffer(length) position = 0 - for chunk in chunks + for chunk in c.chunks chunk.copy data, position position += chunk.length - # Strange Issue - # use data.toString() will cause parallel http request terminated? eg yahoo.com - st = Date.now() - i = 0 - while Date.now() - st < 500 - ++i + c.chunks = [] + res = {} + # now the c is in res.curl, delete curl in curls + # if res destruct, curl will be destruct after gc + res.curl_ = c + delete curls[c.id] - res.body = data #.toString() + if c.options.RAW + res.body = data + else + res.body = data.toString() #.toString() res.status = res.code = c.getinfo('RESPONSE_CODE') + res.info = (info)-> - c.getinfo(info) - # 当curl返回过快,且cb循环调用回导致堆栈溢出 - # process.nextTick! - console.info "id: #{c.id}" + unless @curl_? + throw new Error('curl is closed') + @curl_.getinfo(info) + + res.close = -> + @curl_.close() if @curl_? + @curl_ = null + @body = null + + # if curl returns to fast, avoid cb recursive call + process.nextTick! cb null, res c.on_error = (err)-> + curls[c.id].close() + delete curls[c.id] process.nextTick! cb err, null c.setopt('URL', url) c.perform() + c curl.Curl = Curl +curl.get_count = -> + Curl.get_count() module.exports = curl diff --git a/package.json b/package.json index 17bc5db..6716a7d 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { "name": "node-curl", "version": "0.1.0", - "description": "node wrapper for multi curl", + "author" : "Jiang Miao ", + "description": "node wrapper for multi curl, full implemented.", "keywords" : ["node-curl", "curl", "multi-curl", "mcurl"], "homepage": "http://github.com/jiangmiao/node-curl", "repository" : { "type" : "git", "url" : "git://github.com/jiangmiao/node-curl.git" }, - "author" : "Jiang Miao ", "main" : "./lib", "scripts" : { "install" : "node-waf configure build || true" diff --git a/src/node-curl.h b/src/node-curl.h index 7a3904b..e960bcf 100644 --- a/src/node-curl.h +++ b/src/node-curl.h @@ -8,6 +8,7 @@ #include #include #include +#include #define NODE_CURL_OPTION_NX(name, value) {#name, value} #define NODE_CURL_OPTION(name) NODE_CURL_OPTION_NX(name , CURLOPT_##name) @@ -26,11 +27,17 @@ class NodeCurl static int running_handles; static bool is_ref; static std::map< CURL*, NodeCurl* > curls; + static int count; CURL * curl; v8::Persistent handle; + bool in_curlm; + std::vector slists; NodeCurl(v8::Handle object) + : in_curlm(false) { + ++count; + v8::V8::AdjustAmountOfExternalAllocatedMemory(2*4096); object->SetPointerInInternalField(0, this); handle = v8::Persistent::New(object); handle.MakeWeak(this, destructor); @@ -39,6 +46,7 @@ class NodeCurl if (!curl) { raise("curl_easy_init failed"); + return; } curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_function); curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); @@ -47,18 +55,31 @@ class NodeCurl ~NodeCurl() { + --count; + v8::V8::AdjustAmountOfExternalAllocatedMemory(-2*4096); if (curl) { - curl_multi_remove_handle(curlm, curl); + if (in_curlm) + curl_multi_remove_handle(curlm, curl); curl_easy_cleanup(curl); curls.erase(curl); } + + for (std::vector::iterator i = slists.begin(), e = slists.end(); i != e; ++i) + { + curl_slist * slist = *i; + if (slist) + { + curl_slist_free_all(slist); + } + } } - static void destructor(v8::Persistent object, void *data) + static void destructor(v8::Persistent value, void *data) { - NodeCurl * curl = (NodeCurl*)object->ToObject()->GetPointerFromInternalField(0); - delete curl; + v8::Handle object = value->ToObject(); + NodeCurl * curl = (NodeCurl*)object->GetPointerFromInternalField(0); + curl->close(); } void close() @@ -68,6 +89,15 @@ class NodeCurl delete this; } + static v8::Handle close(const v8::Arguments & args) + { + NodeCurl * node_curl = unwrap(args.This()); + if (node_curl) + node_curl->close(); + return args.This(); + } + + static NodeCurl * unwrap(v8::Handle value) { return (NodeCurl*)value->GetPointerFromInternalField(0); @@ -82,12 +112,11 @@ class NodeCurl size_t on_write(char *data, size_t n) { - v8::HandleScope scope; - static auto SYM_ON_WRITE = v8::Persistent::New(v8::String::NewSymbol("on_write")); - auto cb = handle->Get(SYM_ON_WRITE); + static v8::Persistent SYM_ON_WRITE = v8::Persistent::New(v8::String::NewSymbol("on_write")); + v8::Handle cb = handle->Get(SYM_ON_WRITE); if (cb->IsFunction()) { - auto buffer = node::Buffer::New(data, n); + node::Buffer * buffer = node::Buffer::New(data, n); v8::Handle argv[] = { buffer->handle_ }; cb->ToObject()->CallAsFunction(handle, 1, argv); } @@ -96,8 +125,8 @@ class NodeCurl void on_end(CURLMsg *msg) { - static auto SYM_ON_END = v8::Persistent::New(v8::String::NewSymbol("on_end")); - auto cb = handle->Get(SYM_ON_END); + static v8::Persistent SYM_ON_END = v8::Persistent::New(v8::String::NewSymbol("on_end")); + v8::Handle cb = handle->Get(SYM_ON_END); if (cb->IsFunction()) { v8::Handle argv[] = {}; @@ -107,8 +136,8 @@ class NodeCurl void on_error(CURLMsg *msg) { - static auto SYM_ON_ERROR = v8::Persistent::New(v8::String::NewSymbol("on_error")); - auto cb = handle->Get(SYM_ON_ERROR); + static v8::Persistent SYM_ON_ERROR = v8::Persistent::New(v8::String::NewSymbol("on_error")); + v8::Handle cb = handle->Get(SYM_ON_ERROR); if (cb->IsFunction()) { v8::Handle argv[] = {v8::Exception::Error(v8::String::New(curl_easy_strerror(msg->data.result)))}; @@ -117,10 +146,10 @@ class NodeCurl } // curl_easy_getinfo - template + template static v8::Handle getinfo(const v8::Arguments &args) { - T result; + CType result; NodeCurl * node_curl = unwrap(args.This()); CURLINFO info = (CURLINFO)args[0]->Int32Value(); CURLcode code = curl_easy_getinfo(node_curl->curl, info, &result); @@ -128,7 +157,7 @@ class NodeCurl { return raise("curl_easy_getinfo failed", curl_easy_strerror(code)); } - return S::New(result); + return JsClass::New(result); } static v8::Handle getinfo_int(const v8::Arguments & args) @@ -146,6 +175,30 @@ class NodeCurl return getinfo(args); } + static v8::Handle getinfo_slist(const v8::Arguments & args) + { + curl_slist * slist, * cur; + NodeCurl* node_curl = unwrap(args.This()); + CURLINFO info = (CURLINFO)args[0]->Int32Value(); + CURLcode code = curl_easy_getinfo(node_curl->curl, info, &slist); + if (code != CURLE_OK) + { + return raise("curl_easy_getinfo failed", curl_easy_strerror(code)); + } + v8::Handle array = v8::Array::New(); + if (slist) + { + cur = slist; + while (cur) + { + array->Set(array->Length(), v8::String::New(slist->data)); + cur = cur->next; + } + curl_slist_free_all(slist); + } + return array; + } + // curl_easy_setopt template v8::Handle setopt(v8::Handle option, T value) @@ -169,6 +222,32 @@ class NodeCurl return unwrap(args.This())->setopt(args[0], *v8::String::Utf8Value(args[1]) ); } + static v8::Handle setopt_slist(const v8::Arguments & args) + { + NodeCurl * node_curl = unwrap(args.This()); + curl_slist * slist = value_to_slist(args[1]); + node_curl->slists.push_back(slist); + return node_curl->setopt(args[0], slist); + } + + static curl_slist * value_to_slist(v8::Handle value) + { + curl_slist * slist = NULL; + if (!value->IsArray()) + { + slist = curl_slist_append(slist, *v8::String::Utf8Value(value)); + } + else + { + v8::Handle array = v8::Handle::Cast(value); + for (uint32_t i=0, len = array->Length(); iGet(i))); + } + } + return slist; + } + static v8::Handle raise(const char *data, const char *reason = NULL) { @@ -185,10 +264,10 @@ class NodeCurl template static void export_curl_options(T t, const char *group_name, CurlOption *options, int len) { - auto node_options = v8::Object::New(); + v8::Handle node_options = v8::Object::New(); for (int i=0; iSet( v8::String::NewSymbol(option.name), v8::Integer::New(option.value) @@ -223,12 +302,10 @@ class NodeCurl { return raise("curl_multi_fdset failed", curl_multi_strerror(code)); } - printf("maxfd returns %d\n", max_fd); if (max_fd > 0) { timeval tv = {0}; int n = select(max_fd+1, &read_fds, &write_fds, &error_fds, &tv); - printf("selecting returns %d\n", n); if (n == 0) { return v8::Integer::New(running_handles); @@ -249,21 +326,20 @@ class NodeCurl CURLMsg * msg = NULL; while ( (msg = curl_multi_info_read(curlm, &msgs)) ) { - printf("msgs: %d, running_handles: %d\n", msgs, running_handles); if (msg->msg == CURLMSG_DONE) { - auto curl = curls[msg->easy_handle]; + NodeCurl * curl = curls[msg->easy_handle]; if (msg->data.result == CURLE_OK) curl->on_end(msg); else curl->on_error(msg); code = curl_multi_remove_handle(curlm, msg->easy_handle); + curl->in_curlm = false; if (code != CURLM_OK) { return raise("curl_multi_remove_handle failed", curl_multi_strerror(code)); } } - puts("done."); } } return v8::Integer::New(running_handles); @@ -274,6 +350,7 @@ class NodeCurl { NodeCurl *curl = unwrap(args.This()); CURLMcode code = curl_multi_add_handle(curlm, curl->curl); + curl->in_curlm = true; if (code != CURLM_OK) { return raise("curl_multi_add_handle failed", curl_multi_strerror(code)); @@ -282,6 +359,11 @@ class NodeCurl return args.This(); } + static v8::Handle get_count(const v8::Arguments & args ) + { + return v8::Integer::New(count); + } + public: static v8::Handle Initialize(v8::Handle target) { @@ -299,19 +381,24 @@ class NodeCurl } // Initialize node-curl - auto t = v8::FunctionTemplate::New(New); + v8::Handle t = v8::FunctionTemplate::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); // Set prototype methods NODE_SET_PROTOTYPE_METHOD(t, "perform_", perform); NODE_SET_PROTOTYPE_METHOD(t, "setopt_int_", setopt_int); NODE_SET_PROTOTYPE_METHOD(t, "setopt_str_", setopt_str); + NODE_SET_PROTOTYPE_METHOD(t, "setopt_slist_", setopt_slist); NODE_SET_PROTOTYPE_METHOD(t, "getinfo_int_", getinfo_int); NODE_SET_PROTOTYPE_METHOD(t, "getinfo_str_", getinfo_str); NODE_SET_PROTOTYPE_METHOD(t, "getinfo_double_", getinfo_double); + NODE_SET_PROTOTYPE_METHOD(t, "getinfo_slist_", getinfo_slist); + + NODE_SET_PROTOTYPE_METHOD(t, "close", close); NODE_SET_METHOD(t, "process_", process); + NODE_SET_METHOD(t, "get_count", get_count); // Set curl constants #include "string_options.h" @@ -320,11 +407,34 @@ class NodeCurl #include "integer_infos.h" #include "double_infos.h" + #define X(name) {#name, CURLOPT_##name} + CurlOption slist_options[] = { + X(HTTPHEADER), + X(HTTP200ALIASES), + X(MAIL_RCPT), + X(QUOTE), + X(POSTQUOTE), + X(PREQUOTE), + X(RESOLVE), + X(TELNETOPTIONS) + }; + #undef X + + #define X(name) {#name, CURLINFO_##name} + CurlOption slist_infos[] = { + X(SSL_ENGINES), + X(COOKIELIST) + }; + #undef X + NODE_CURL_EXPORT(string_options); NODE_CURL_EXPORT(integer_options); + NODE_CURL_EXPORT(slist_options); + NODE_CURL_EXPORT(string_infos); NODE_CURL_EXPORT(integer_infos); NODE_CURL_EXPORT(double_infos); + NODE_CURL_EXPORT(slist_infos); target->Set(v8::String::NewSymbol("Curl"), t->GetFunction()); return target; @@ -335,5 +445,6 @@ CURLM * NodeCurl::curlm = NULL; int NodeCurl::running_handles = 0; bool NodeCurl::is_ref = false; std::map< CURL*, NodeCurl* > NodeCurl::curls; +int NodeCurl::count = 0; #endif diff --git a/wscript b/wscript index dbeab4a..4c7ad97 100644 --- a/wscript +++ b/wscript @@ -4,7 +4,7 @@ def set_options(opt): def configure(conf): conf.check_tool('compiler_cxx') conf.check_tool('node_addon') - conf.env.append_unique('CXXFLAGS', ['-Wall', '-O2', '-std=c++0x']) + conf.env.append_unique('CXXFLAGS', ['-Wall', '-O2']) conf.env['LIB_CURL'] = 'curl' def build(bld):