use c++ instead of c++0x to compatible with old compiler
This commit is contained in:
parent
f8f934a119
commit
314d43203f
11 changed files with 454 additions and 106 deletions
90
README.md
90
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
|
||||
===========
|
||||
-----------
|
||||
|
||||
* 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}, function(err, res) {
|
||||
console.info(res.status)
|
||||
console.info('-----')
|
||||
console.info(res.body)
|
||||
console.info('-----')
|
||||
console.info(res.info('SIZE_DOWNLOAD'))
|
||||
curl('www.google.com', {VERBOSE: 1, RAW: 1}, function(err, res) {
|
||||
console.info(res);
|
||||
res.close();
|
||||
});
|
||||
|
||||
Usage
|
||||
=====
|
||||
-----
|
||||
|
||||
* curl
|
||||
|
||||
Function: curl
|
||||
curl(url, [options = {}], callback)
|
||||
callback includes 2 parameters (error, result)
|
||||
|
||||
Curl Options:
|
||||
options is on the list at http://curl.haxx.se/libcurl/c/curl_easy_setopt.html without CURLOPT_
|
||||
* result in callback
|
||||
|
||||
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
|
||||
|
||||
Object: result
|
||||
body
|
||||
status
|
||||
info
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
|
30
examples/pressure.js
Normal file
30
examples/pressure.js
Normal file
|
@ -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);
|
16
examples/pressure.toffee
Normal file
16
examples/pressure.toffee
Normal file
|
@ -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()
|
|
@ -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);
|
||||
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'));
|
||||
console.info("\033[33meffetcive url: " + res.info('EFFECTIVE_URL') + "\033[0m");
|
||||
});
|
||||
*/
|
||||
|
|
40
examples/test.js
Normal file
40
examples/test.js
Normal file
|
@ -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);
|
22
examples/test.toffee
Normal file
22
examples/test.toffee
Normal file
|
@ -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'
|
79
lib/curl.js
79
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.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);
|
||||
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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
{
|
||||
"name": "node-curl",
|
||||
"version": "0.1.0",
|
||||
"description": "node wrapper for multi curl",
|
||||
"author" : "Jiang Miao <jiangfriend@gmail.com>",
|
||||
"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 <jiangfriend@gmail.com>",
|
||||
"main" : "./lib",
|
||||
"scripts" : {
|
||||
"install" : "node-waf configure build || true"
|
||||
|
|
155
src/node-curl.h
155
src/node-curl.h
|
@ -8,6 +8,7 @@
|
|||
#include <node_buffer.h>
|
||||
#include <curl/curl.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#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<v8::Object> handle;
|
||||
bool in_curlm;
|
||||
std::vector<curl_slist*> slists;
|
||||
NodeCurl(v8::Handle<v8::Object> object)
|
||||
: in_curlm(false)
|
||||
{
|
||||
++count;
|
||||
v8::V8::AdjustAmountOfExternalAllocatedMemory(2*4096);
|
||||
object->SetPointerInInternalField(0, this);
|
||||
handle = v8::Persistent<v8::Object>::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)
|
||||
{
|
||||
if (in_curlm)
|
||||
curl_multi_remove_handle(curlm, curl);
|
||||
curl_easy_cleanup(curl);
|
||||
curls.erase(curl);
|
||||
}
|
||||
|
||||
for (std::vector<curl_slist*>::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<v8::Value> object, void *data)
|
||||
static void destructor(v8::Persistent<v8::Value> value, void *data)
|
||||
{
|
||||
NodeCurl * curl = (NodeCurl*)object->ToObject()->GetPointerFromInternalField(0);
|
||||
delete curl;
|
||||
v8::Handle<v8::Object> object = value->ToObject();
|
||||
NodeCurl * curl = (NodeCurl*)object->GetPointerFromInternalField(0);
|
||||
curl->close();
|
||||
}
|
||||
|
||||
void close()
|
||||
|
@ -68,6 +89,15 @@ class NodeCurl
|
|||
delete this;
|
||||
}
|
||||
|
||||
static v8::Handle<v8::Value> 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<v8::Object> 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<v8::String>::New(v8::String::NewSymbol("on_write"));
|
||||
auto cb = handle->Get(SYM_ON_WRITE);
|
||||
static v8::Persistent<v8::String> SYM_ON_WRITE = v8::Persistent<v8::String>::New(v8::String::NewSymbol("on_write"));
|
||||
v8::Handle<v8::Value> 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<v8::Value> 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<v8::String>::New(v8::String::NewSymbol("on_end"));
|
||||
auto cb = handle->Get(SYM_ON_END);
|
||||
static v8::Persistent<v8::String> SYM_ON_END = v8::Persistent<v8::String>::New(v8::String::NewSymbol("on_end"));
|
||||
v8::Handle<v8::Value> cb = handle->Get(SYM_ON_END);
|
||||
if (cb->IsFunction())
|
||||
{
|
||||
v8::Handle<v8::Value> argv[] = {};
|
||||
|
@ -107,8 +136,8 @@ class NodeCurl
|
|||
|
||||
void on_error(CURLMsg *msg)
|
||||
{
|
||||
static auto SYM_ON_ERROR = v8::Persistent<v8::String>::New(v8::String::NewSymbol("on_error"));
|
||||
auto cb = handle->Get(SYM_ON_ERROR);
|
||||
static v8::Persistent<v8::String> SYM_ON_ERROR = v8::Persistent<v8::String>::New(v8::String::NewSymbol("on_error"));
|
||||
v8::Handle<v8::Value> cb = handle->Get(SYM_ON_ERROR);
|
||||
if (cb->IsFunction())
|
||||
{
|
||||
v8::Handle<v8::Value> argv[] = {v8::Exception::Error(v8::String::New(curl_easy_strerror(msg->data.result)))};
|
||||
|
@ -117,10 +146,10 @@ class NodeCurl
|
|||
}
|
||||
|
||||
// curl_easy_getinfo
|
||||
template<typename T, typename S>
|
||||
template<typename CType, typename JsClass>
|
||||
static v8::Handle<v8::Value> 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<v8::Value> getinfo_int(const v8::Arguments & args)
|
||||
|
@ -146,6 +175,30 @@ class NodeCurl
|
|||
return getinfo<double, v8::Number>(args);
|
||||
}
|
||||
|
||||
static v8::Handle<v8::Value> 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<v8::Array> 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<typename T>
|
||||
v8::Handle<v8::Value> setopt(v8::Handle<v8::Value> option, T value)
|
||||
|
@ -169,6 +222,32 @@ class NodeCurl
|
|||
return unwrap(args.This())->setopt(args[0], *v8::String::Utf8Value(args[1]) );
|
||||
}
|
||||
|
||||
static v8::Handle<v8::Value> 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<v8::Value> value)
|
||||
{
|
||||
curl_slist * slist = NULL;
|
||||
if (!value->IsArray())
|
||||
{
|
||||
slist = curl_slist_append(slist, *v8::String::Utf8Value(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
|
||||
for (uint32_t i=0, len = array->Length(); i<len; ++i)
|
||||
{
|
||||
slist = curl_slist_append(slist, *v8::String::Utf8Value(array->Get(i)));
|
||||
}
|
||||
}
|
||||
return slist;
|
||||
}
|
||||
|
||||
|
||||
static v8::Handle<v8::Value> raise(const char *data, const char *reason = NULL)
|
||||
{
|
||||
|
@ -185,10 +264,10 @@ class NodeCurl
|
|||
template<typename T>
|
||||
static void export_curl_options(T t, const char *group_name, CurlOption *options, int len)
|
||||
{
|
||||
auto node_options = v8::Object::New();
|
||||
v8::Handle<v8::Object> node_options = v8::Object::New();
|
||||
for (int i=0; i<len; ++i)
|
||||
{
|
||||
const auto & option = options[i];
|
||||
const CurlOption & option = options[i];
|
||||
node_options->Set(
|
||||
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<v8::Value> get_count(const v8::Arguments & args )
|
||||
{
|
||||
return v8::Integer::New(count);
|
||||
}
|
||||
|
||||
public:
|
||||
static v8::Handle<v8::Value> Initialize(v8::Handle<v8::Object> target)
|
||||
{
|
||||
|
@ -299,19 +381,24 @@ class NodeCurl
|
|||
}
|
||||
|
||||
// Initialize node-curl
|
||||
auto t = v8::FunctionTemplate::New(New);
|
||||
v8::Handle<v8::FunctionTemplate> 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
|
||||
|
|
2
wscript
2
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):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue