first commit
This commit is contained in:
commit
f8f934a119
18 changed files with 901 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
raw
|
||||
build
|
2
.npmignore
Normal file
2
.npmignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
raw
|
||||
build
|
36
README.md
Normal file
36
README.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
Node Curl Wrap
|
||||
Use select curl multi
|
||||
|
||||
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'))
|
||||
});
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
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_
|
||||
eg: CURLOPT_VERBOSE will be VERBOSE, CURLOPT_HEADER will be HEADER
|
||||
|
||||
Object: result
|
||||
body
|
||||
status
|
||||
info
|
||||
|
||||
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
|
||||
|
||||
|
19
examples/quick-start.js
Normal file
19
examples/quick-start.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
curl = require('../index');
|
||||
url = 'www.yahoo.com';
|
||||
curl(url, function(err, res) {
|
||||
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");
|
||||
});
|
||||
|
||||
curl(url, function(err, res) {
|
||||
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");
|
||||
});
|
6
index.js
Normal file
6
index.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
// Generated by CoffeeScript 1.1.4-3
|
||||
(function() {
|
||||
|
||||
module.exports = require('./lib/curl');
|
||||
|
||||
}).call(this);
|
1
index.toffee
Normal file
1
index.toffee
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = require './lib/curl'
|
120
lib/curl.js
Normal file
120
lib/curl.js
Normal file
|
@ -0,0 +1,120 @@
|
|||
// Generated by CoffeeScript 1.1.4-3
|
||||
(function() {
|
||||
var Curl, curl, curl_id,
|
||||
__slice = [].slice,
|
||||
__hasProp = {}.hasOwnProperty;
|
||||
|
||||
try {
|
||||
Curl = require(__dirname + '/../build/Release/node-curl').Curl;
|
||||
} catch (e) {
|
||||
Curl = require(__dirname + '/../build/default/node-curl').Curl;
|
||||
}
|
||||
|
||||
Curl.prototype.setopt = function(ooption, value) {
|
||||
var option, option_id;
|
||||
option = ooption.toUpperCase();
|
||||
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());
|
||||
} else {
|
||||
throw new Error("unsupported option " + option);
|
||||
}
|
||||
};
|
||||
|
||||
Curl.prototype.getinfo = function(oinfo) {
|
||||
var info, info_id;
|
||||
info = oinfo.toUpperCase();
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
Curl.prototype.perform = function() {
|
||||
this.perform_();
|
||||
return Curl.process();
|
||||
};
|
||||
|
||||
Curl.process = function() {
|
||||
var once;
|
||||
if (Curl.in_process) return;
|
||||
return (once = function() {
|
||||
var num;
|
||||
num = Curl.process_();
|
||||
if (num > 0) {
|
||||
Curl.in_process = true;
|
||||
return setTimeout(once, 80);
|
||||
} else {
|
||||
return Curl.in_process = false;
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
curl_id = 0;
|
||||
|
||||
curl = function() {
|
||||
var args, c, cb, chunks, k, length, options, res, 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.id = ++curl_id;
|
||||
c.setopt('FOLLOWLOCATION', 1);
|
||||
c.setopt('ACCEPT_ENCODING', 'gzip');
|
||||
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.on_end = function() {
|
||||
var chunk, data, i, position, st, _i, _len;
|
||||
data = new Buffer(length);
|
||||
position = 0;
|
||||
for (_i = 0, _len = chunks.length; _i < _len; _i++) {
|
||||
chunk = chunks[_i];
|
||||
chunk.copy(data, position);
|
||||
position += chunk.length;
|
||||
}
|
||||
st = Date.now();
|
||||
i = 0;
|
||||
while (Date.now() - st < 500) {
|
||||
++i;
|
||||
}
|
||||
res.body = data;
|
||||
res.status = res.code = c.getinfo('RESPONSE_CODE');
|
||||
res.info = function(info) {
|
||||
return c.getinfo(info);
|
||||
};
|
||||
console.info("id: " + c.id);
|
||||
return cb(null, res);
|
||||
};
|
||||
c.on_error = function(err) {
|
||||
var _this = this;
|
||||
return process.nextTick(function() {
|
||||
return cb(err, null);
|
||||
});
|
||||
};
|
||||
c.setopt('URL', url);
|
||||
return c.perform();
|
||||
};
|
||||
|
||||
curl.Curl = Curl;
|
||||
|
||||
module.exports = curl;
|
||||
|
||||
}).call(this);
|
95
lib/curl.toffee
Normal file
95
lib/curl.toffee
Normal file
|
@ -0,0 +1,95 @@
|
|||
try
|
||||
{Curl} = require __dirname + '/../build/Release/node-curl'
|
||||
catch e
|
||||
{Curl} = require __dirname + '/../build/default/node-curl'
|
||||
|
||||
Curl::setopt = (ooption, value) ->
|
||||
option = ooption.toUpperCase()
|
||||
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()
|
||||
else
|
||||
throw new Error("unsupported option #{option}")
|
||||
|
||||
Curl::getinfo = (oinfo) ->
|
||||
info = oinfo.toUpperCase()
|
||||
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}")
|
||||
|
||||
Curl::perform = ->
|
||||
@perform_()
|
||||
Curl.process()
|
||||
|
||||
Curl.process = ->
|
||||
if Curl.in_process
|
||||
return
|
||||
do once = ->
|
||||
num = Curl.process_()
|
||||
if num > 0
|
||||
Curl.in_process = true
|
||||
setTimeout once, 80
|
||||
else
|
||||
Curl.in_process = false
|
||||
|
||||
# url, [options], cb
|
||||
curl_id = 0
|
||||
curl = (args...) ->
|
||||
cb = args.pop()
|
||||
[url, options] = args
|
||||
options ?= {}
|
||||
|
||||
c = new Curl()
|
||||
c.id = ++curl_id
|
||||
c.setopt 'FOLLOWLOCATION', 1
|
||||
c.setopt 'ACCEPT_ENCODING', 'gzip'
|
||||
chunks = []
|
||||
length = 0
|
||||
res = {}
|
||||
|
||||
for own k, v of options
|
||||
c.setopt k, v
|
||||
|
||||
c.on_write = (chunk) ->
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
res.body = data #.toString()
|
||||
res.status = res.code = c.getinfo('RESPONSE_CODE')
|
||||
res.info = (info)->
|
||||
c.getinfo(info)
|
||||
# 当curl返回过快,且cb循环调用回导致堆栈溢出
|
||||
# process.nextTick!
|
||||
console.info "id: #{c.id}"
|
||||
cb null, res
|
||||
|
||||
c.on_error = (err)->
|
||||
process.nextTick!
|
||||
cb err, null
|
||||
|
||||
c.setopt('URL', url)
|
||||
c.perform()
|
||||
|
||||
curl.Curl = Curl
|
||||
module.exports = curl
|
17
package.json
Normal file
17
package.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "node-curl",
|
||||
"version": "0.1.0",
|
||||
"description": "node wrapper for multi curl",
|
||||
"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"
|
||||
},
|
||||
"engines" : { "node": ">= 0.6.0" }
|
||||
}
|
16
src/double_infos.h
Normal file
16
src/double_infos.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
// generated by generate_curl_options_list.sh at Mon, 13 Feb 2012 21:40:10 +0800
|
||||
CurlOption double_infos[] = {
|
||||
{"TOTAL_TIME", CURLINFO_TOTAL_TIME},
|
||||
{"NAMELOOKUP_TIME", CURLINFO_NAMELOOKUP_TIME},
|
||||
{"CONNECT_TIME", CURLINFO_CONNECT_TIME},
|
||||
{"PRETRANSFER_TIME", CURLINFO_PRETRANSFER_TIME},
|
||||
{"SIZE_UPLOAD", CURLINFO_SIZE_UPLOAD},
|
||||
{"SIZE_DOWNLOAD", CURLINFO_SIZE_DOWNLOAD},
|
||||
{"SPEED_DOWNLOAD", CURLINFO_SPEED_DOWNLOAD},
|
||||
{"SPEED_UPLOAD", CURLINFO_SPEED_UPLOAD},
|
||||
{"CONTENT_LENGTH_DOWNLOAD", CURLINFO_CONTENT_LENGTH_DOWNLOAD},
|
||||
{"CONTENT_LENGTH_UPLOAD", CURLINFO_CONTENT_LENGTH_UPLOAD},
|
||||
{"STARTTRANSFER_TIME", CURLINFO_STARTTRANSFER_TIME},
|
||||
{"REDIRECT_TIME", CURLINFO_REDIRECT_TIME},
|
||||
{"APPCONNECT_TIME", CURLINFO_APPCONNECT_TIME},
|
||||
};
|
19
src/generate_curl_options_list.sh
Normal file
19
src/generate_curl_options_list.sh
Normal file
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
generate() {
|
||||
name=$1
|
||||
pattern=$2
|
||||
prefix=$3
|
||||
(
|
||||
echo "// generated by $0 at $(date -R)"
|
||||
echo "CurlOption $name[] = {"
|
||||
cat /usr/include/curl/curl.h|perl -ne "/$pattern/i && print \"\t{\\\"\$1\\\", CURL${prefix}_\$1},\n\""
|
||||
echo '};'
|
||||
) > $name.h
|
||||
}
|
||||
generate integer_options 'CINIT\((\w+).*LONG' OPT
|
||||
generate string_options 'CINIT\((\w+).*OBJECT' OPT
|
||||
|
||||
generate integer_infos 'CURLINFO_(\w+).*LONG' INFO
|
||||
generate string_infos 'CURLINFO_(\w+).*STRING' INFO
|
||||
generate double_infos 'CURLINFO_(\w+).*DOUBLE' INFO
|
21
src/integer_infos.h
Normal file
21
src/integer_infos.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
// generated by generate_curl_options_list.sh at Mon, 13 Feb 2012 21:40:10 +0800
|
||||
CurlOption integer_infos[] = {
|
||||
{"RESPONSE_CODE", CURLINFO_RESPONSE_CODE},
|
||||
{"HEADER_SIZE", CURLINFO_HEADER_SIZE},
|
||||
{"REQUEST_SIZE", CURLINFO_REQUEST_SIZE},
|
||||
{"SSL_VERIFYRESULT", CURLINFO_SSL_VERIFYRESULT},
|
||||
{"FILETIME", CURLINFO_FILETIME},
|
||||
{"REDIRECT_COUNT", CURLINFO_REDIRECT_COUNT},
|
||||
{"HTTP_CONNECTCODE", CURLINFO_HTTP_CONNECTCODE},
|
||||
{"HTTPAUTH_AVAIL", CURLINFO_HTTPAUTH_AVAIL},
|
||||
{"PROXYAUTH_AVAIL", CURLINFO_PROXYAUTH_AVAIL},
|
||||
{"OS_ERRNO", CURLINFO_OS_ERRNO},
|
||||
{"NUM_CONNECTS", CURLINFO_NUM_CONNECTS},
|
||||
{"LASTSOCKET", CURLINFO_LASTSOCKET},
|
||||
{"CONDITION_UNMET", CURLINFO_CONDITION_UNMET},
|
||||
{"RTSP_CLIENT_CSEQ", CURLINFO_RTSP_CLIENT_CSEQ},
|
||||
{"RTSP_SERVER_CSEQ", CURLINFO_RTSP_SERVER_CSEQ},
|
||||
{"RTSP_CSEQ_RECV", CURLINFO_RTSP_CSEQ_RECV},
|
||||
{"PRIMARY_PORT", CURLINFO_PRIMARY_PORT},
|
||||
{"LOCAL_PORT", CURLINFO_LOCAL_PORT},
|
||||
};
|
91
src/integer_options.h
Normal file
91
src/integer_options.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
// generated by generate_curl_options_list.sh at Mon, 13 Feb 2012 21:40:10 +0800
|
||||
CurlOption integer_options[] = {
|
||||
{"PORT", CURLOPT_PORT},
|
||||
{"TIMEOUT", CURLOPT_TIMEOUT},
|
||||
{"INFILESIZE", CURLOPT_INFILESIZE},
|
||||
{"LOW_SPEED_LIMIT", CURLOPT_LOW_SPEED_LIMIT},
|
||||
{"LOW_SPEED_TIME", CURLOPT_LOW_SPEED_TIME},
|
||||
{"RESUME_FROM", CURLOPT_RESUME_FROM},
|
||||
{"CRLF", CURLOPT_CRLF},
|
||||
{"SSLVERSION", CURLOPT_SSLVERSION},
|
||||
{"TIMECONDITION", CURLOPT_TIMECONDITION},
|
||||
{"TIMEVALUE", CURLOPT_TIMEVALUE},
|
||||
{"VERBOSE", CURLOPT_VERBOSE},
|
||||
{"HEADER", CURLOPT_HEADER},
|
||||
{"NOPROGRESS", CURLOPT_NOPROGRESS},
|
||||
{"NOBODY", CURLOPT_NOBODY},
|
||||
{"FAILONERROR", CURLOPT_FAILONERROR},
|
||||
{"UPLOAD", CURLOPT_UPLOAD},
|
||||
{"POST", CURLOPT_POST},
|
||||
{"DIRLISTONLY", CURLOPT_DIRLISTONLY},
|
||||
{"APPEND", CURLOPT_APPEND},
|
||||
{"NETRC", CURLOPT_NETRC},
|
||||
{"FOLLOWLOCATION", CURLOPT_FOLLOWLOCATION},
|
||||
{"TRANSFERTEXT", CURLOPT_TRANSFERTEXT},
|
||||
{"PUT", CURLOPT_PUT},
|
||||
{"AUTOREFERER", CURLOPT_AUTOREFERER},
|
||||
{"PROXYPORT", CURLOPT_PROXYPORT},
|
||||
{"POSTFIELDSIZE", CURLOPT_POSTFIELDSIZE},
|
||||
{"HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL},
|
||||
{"SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER},
|
||||
{"MAXREDIRS", CURLOPT_MAXREDIRS},
|
||||
{"FILETIME", CURLOPT_FILETIME},
|
||||
{"MAXCONNECTS", CURLOPT_MAXCONNECTS},
|
||||
{"CLOSEPOLICY", CURLOPT_CLOSEPOLICY},
|
||||
{"FRESH_CONNECT", CURLOPT_FRESH_CONNECT},
|
||||
{"FORBID_REUSE", CURLOPT_FORBID_REUSE},
|
||||
{"CONNECTTIMEOUT", CURLOPT_CONNECTTIMEOUT},
|
||||
{"HTTPGET", CURLOPT_HTTPGET},
|
||||
{"SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST},
|
||||
{"HTTP_VERSION", CURLOPT_HTTP_VERSION},
|
||||
{"FTP_USE_EPSV", CURLOPT_FTP_USE_EPSV},
|
||||
{"SSLENGINE_DEFAULT", CURLOPT_SSLENGINE_DEFAULT},
|
||||
{"DNS_USE_GLOBAL_CACHE", CURLOPT_DNS_USE_GLOBAL_CACHE},
|
||||
{"DNS_CACHE_TIMEOUT", CURLOPT_DNS_CACHE_TIMEOUT},
|
||||
{"COOKIESESSION", CURLOPT_COOKIESESSION},
|
||||
{"BUFFERSIZE", CURLOPT_BUFFERSIZE},
|
||||
{"NOSIGNAL", CURLOPT_NOSIGNAL},
|
||||
{"PROXYTYPE", CURLOPT_PROXYTYPE},
|
||||
{"UNRESTRICTED_AUTH", CURLOPT_UNRESTRICTED_AUTH},
|
||||
{"FTP_USE_EPRT", CURLOPT_FTP_USE_EPRT},
|
||||
{"HTTPAUTH", CURLOPT_HTTPAUTH},
|
||||
{"FTP_CREATE_MISSING_DIRS", CURLOPT_FTP_CREATE_MISSING_DIRS},
|
||||
{"PROXYAUTH", CURLOPT_PROXYAUTH},
|
||||
{"FTP_RESPONSE_TIMEOUT", CURLOPT_FTP_RESPONSE_TIMEOUT},
|
||||
{"IPRESOLVE", CURLOPT_IPRESOLVE},
|
||||
{"MAXFILESIZE", CURLOPT_MAXFILESIZE},
|
||||
{"USE_SSL", CURLOPT_USE_SSL},
|
||||
{"TCP_NODELAY", CURLOPT_TCP_NODELAY},
|
||||
{"FTPSSLAUTH", CURLOPT_FTPSSLAUTH},
|
||||
{"IGNORE_CONTENT_LENGTH", CURLOPT_IGNORE_CONTENT_LENGTH},
|
||||
{"FTP_SKIP_PASV_IP", CURLOPT_FTP_SKIP_PASV_IP},
|
||||
{"FTP_FILEMETHOD", CURLOPT_FTP_FILEMETHOD},
|
||||
{"LOCALPORT", CURLOPT_LOCALPORT},
|
||||
{"LOCALPORTRANGE", CURLOPT_LOCALPORTRANGE},
|
||||
{"CONNECT_ONLY", CURLOPT_CONNECT_ONLY},
|
||||
{"SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE},
|
||||
{"SSH_AUTH_TYPES", CURLOPT_SSH_AUTH_TYPES},
|
||||
{"FTP_SSL_CCC", CURLOPT_FTP_SSL_CCC},
|
||||
{"TIMEOUT_MS", CURLOPT_TIMEOUT_MS},
|
||||
{"CONNECTTIMEOUT_MS", CURLOPT_CONNECTTIMEOUT_MS},
|
||||
{"HTTP_TRANSFER_DECODING", CURLOPT_HTTP_TRANSFER_DECODING},
|
||||
{"HTTP_CONTENT_DECODING", CURLOPT_HTTP_CONTENT_DECODING},
|
||||
{"NEW_FILE_PERMS", CURLOPT_NEW_FILE_PERMS},
|
||||
{"NEW_DIRECTORY_PERMS", CURLOPT_NEW_DIRECTORY_PERMS},
|
||||
{"POSTREDIR", CURLOPT_POSTREDIR},
|
||||
{"PROXY_TRANSFER_MODE", CURLOPT_PROXY_TRANSFER_MODE},
|
||||
{"ADDRESS_SCOPE", CURLOPT_ADDRESS_SCOPE},
|
||||
{"CERTINFO", CURLOPT_CERTINFO},
|
||||
{"TFTP_BLKSIZE", CURLOPT_TFTP_BLKSIZE},
|
||||
{"SOCKS5_GSSAPI_NEC", CURLOPT_SOCKS5_GSSAPI_NEC},
|
||||
{"PROTOCOLS", CURLOPT_PROTOCOLS},
|
||||
{"REDIR_PROTOCOLS", CURLOPT_REDIR_PROTOCOLS},
|
||||
{"FTP_USE_PRET", CURLOPT_FTP_USE_PRET},
|
||||
{"RTSP_REQUEST", CURLOPT_RTSP_REQUEST},
|
||||
{"RTSP_CLIENT_CSEQ", CURLOPT_RTSP_CLIENT_CSEQ},
|
||||
{"RTSP_SERVER_CSEQ", CURLOPT_RTSP_SERVER_CSEQ},
|
||||
{"WILDCARDMATCH", CURLOPT_WILDCARDMATCH},
|
||||
{"TRANSFER_ENCODING", CURLOPT_TRANSFER_ENCODING},
|
||||
{"GSSAPI_DELEGATION", CURLOPT_GSSAPI_DELEGATION},
|
||||
{"ACCEPTTIMEOUT_MS", CURLOPT_ACCEPTTIMEOUT_MS},
|
||||
};
|
7
src/node-curl.cc
Normal file
7
src/node-curl.cc
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include "node-curl.h"
|
||||
|
||||
extern "C"
|
||||
void init(v8::Handle<v8::Object> target)
|
||||
{
|
||||
NodeCurl::Initialize(target);
|
||||
}
|
339
src/node-curl.h
Normal file
339
src/node-curl.h
Normal file
|
@ -0,0 +1,339 @@
|
|||
#ifndef NODE_CURL_NOHE_CURL_H
|
||||
#define NODE_CURL_NOHE_CURL_H
|
||||
|
||||
|
||||
#include <unistd.h>
|
||||
#include <v8.h>
|
||||
#include <node.h>
|
||||
#include <node_buffer.h>
|
||||
#include <curl/curl.h>
|
||||
#include <map>
|
||||
|
||||
#define NODE_CURL_OPTION_NX(name, value) {#name, value}
|
||||
#define NODE_CURL_OPTION(name) NODE_CURL_OPTION_NX(name , CURLOPT_##name)
|
||||
#define NODE_CURL_EXPORT(name) export_curl_options(t, #name, name, sizeof(name) / sizeof(CurlOption));
|
||||
|
||||
|
||||
|
||||
class NodeCurl
|
||||
{
|
||||
struct CurlOption {
|
||||
const char *name;
|
||||
int value;
|
||||
};
|
||||
|
||||
static CURLM * curlm;
|
||||
static int running_handles;
|
||||
static bool is_ref;
|
||||
static std::map< CURL*, NodeCurl* > curls;
|
||||
|
||||
CURL * curl;
|
||||
v8::Persistent<v8::Object> handle;
|
||||
NodeCurl(v8::Handle<v8::Object> object)
|
||||
{
|
||||
object->SetPointerInInternalField(0, this);
|
||||
handle = v8::Persistent<v8::Object>::New(object);
|
||||
handle.MakeWeak(this, destructor);
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (!curl)
|
||||
{
|
||||
raise("curl_easy_init failed");
|
||||
}
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_function);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
|
||||
curls[curl] = this;
|
||||
}
|
||||
|
||||
~NodeCurl()
|
||||
{
|
||||
if (curl)
|
||||
{
|
||||
curl_multi_remove_handle(curlm, curl);
|
||||
curl_easy_cleanup(curl);
|
||||
curls.erase(curl);
|
||||
}
|
||||
}
|
||||
|
||||
static void destructor(v8::Persistent<v8::Value> object, void *data)
|
||||
{
|
||||
NodeCurl * curl = (NodeCurl*)object->ToObject()->GetPointerFromInternalField(0);
|
||||
delete curl;
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
handle->SetPointerInInternalField(0, NULL);
|
||||
handle.Dispose();
|
||||
delete this;
|
||||
}
|
||||
|
||||
static NodeCurl * unwrap(v8::Handle<v8::Object> value)
|
||||
{
|
||||
return (NodeCurl*)value->GetPointerFromInternalField(0);
|
||||
}
|
||||
|
||||
// curl write function mapping
|
||||
static size_t write_function(char *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
NodeCurl *nodecurl = (NodeCurl*)userdata;
|
||||
return nodecurl->on_write(ptr, size * nmemb);
|
||||
}
|
||||
|
||||
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);
|
||||
if (cb->IsFunction())
|
||||
{
|
||||
auto buffer = node::Buffer::New(data, n);
|
||||
v8::Handle<v8::Value> argv[] = { buffer->handle_ };
|
||||
cb->ToObject()->CallAsFunction(handle, 1, argv);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
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);
|
||||
if (cb->IsFunction())
|
||||
{
|
||||
v8::Handle<v8::Value> argv[] = {};
|
||||
cb->ToObject()->CallAsFunction(handle, 0, argv);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (cb->IsFunction())
|
||||
{
|
||||
v8::Handle<v8::Value> argv[] = {v8::Exception::Error(v8::String::New(curl_easy_strerror(msg->data.result)))};
|
||||
cb->ToObject()->CallAsFunction(handle, 1, argv);
|
||||
}
|
||||
}
|
||||
|
||||
// curl_easy_getinfo
|
||||
template<typename T, typename S>
|
||||
static v8::Handle<v8::Value> getinfo(const v8::Arguments &args)
|
||||
{
|
||||
T result;
|
||||
NodeCurl * node_curl = unwrap(args.This());
|
||||
CURLINFO info = (CURLINFO)args[0]->Int32Value();
|
||||
CURLcode code = curl_easy_getinfo(node_curl->curl, info, &result);
|
||||
if (code != CURLE_OK)
|
||||
{
|
||||
return raise("curl_easy_getinfo failed", curl_easy_strerror(code));
|
||||
}
|
||||
return S::New(result);
|
||||
}
|
||||
|
||||
static v8::Handle<v8::Value> getinfo_int(const v8::Arguments & args)
|
||||
{
|
||||
return getinfo<int, v8::Integer>(args);
|
||||
}
|
||||
|
||||
static v8::Handle<v8::Value> getinfo_str(const v8::Arguments & args)
|
||||
{
|
||||
return getinfo<char*,v8::String>(args);
|
||||
}
|
||||
|
||||
static v8::Handle<v8::Value> getinfo_double(const v8::Arguments & args)
|
||||
{
|
||||
return getinfo<double, v8::Number>(args);
|
||||
}
|
||||
|
||||
// curl_easy_setopt
|
||||
template<typename T>
|
||||
v8::Handle<v8::Value> setopt(v8::Handle<v8::Value> option, T value)
|
||||
{
|
||||
return v8::Integer::New(
|
||||
curl_easy_setopt(
|
||||
curl,
|
||||
(CURLoption)option->Int32Value(),
|
||||
value
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static v8::Handle<v8::Value> setopt_int(const v8::Arguments & args)
|
||||
{
|
||||
return unwrap(args.This())->setopt(args[0], args[1]->Int32Value());
|
||||
}
|
||||
|
||||
static v8::Handle<v8::Value> setopt_str(const v8::Arguments & args)
|
||||
{
|
||||
return unwrap(args.This())->setopt(args[0], *v8::String::Utf8Value(args[1]) );
|
||||
}
|
||||
|
||||
|
||||
static v8::Handle<v8::Value> raise(const char *data, const char *reason = NULL)
|
||||
{
|
||||
static char message[256];
|
||||
const char *what = data;
|
||||
if (reason)
|
||||
{
|
||||
snprintf(message, sizeof(message), "%s: %s", data, reason);
|
||||
what = message;
|
||||
}
|
||||
return v8::ThrowException(v8::Exception::Error(v8::String::New(what)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void export_curl_options(T t, const char *group_name, CurlOption *options, int len)
|
||||
{
|
||||
auto node_options = v8::Object::New();
|
||||
for (int i=0; i<len; ++i)
|
||||
{
|
||||
const auto & option = options[i];
|
||||
node_options->Set(
|
||||
v8::String::NewSymbol(option.name),
|
||||
v8::Integer::New(option.value)
|
||||
);
|
||||
}
|
||||
t->Set(v8::String::NewSymbol(group_name), node_options);
|
||||
}
|
||||
|
||||
// node js functions
|
||||
static v8::Handle<v8::Value> New(const v8::Arguments & args)
|
||||
{
|
||||
new NodeCurl(args.This());
|
||||
return args.This();
|
||||
}
|
||||
|
||||
// int process()
|
||||
static v8::Handle<v8::Value> process(const v8::Arguments & args)
|
||||
{
|
||||
if (running_handles > 0)
|
||||
{
|
||||
CURLMcode code;
|
||||
int max_fd = FD_SETSIZE;
|
||||
fd_set read_fds;
|
||||
fd_set write_fds;
|
||||
fd_set error_fds;
|
||||
FD_ZERO(&read_fds);
|
||||
FD_ZERO(&write_fds);
|
||||
FD_ZERO(&error_fds);
|
||||
// use select because of libuv didn't support sockfd well
|
||||
code = curl_multi_fdset(curlm, &read_fds, &write_fds, &error_fds, &max_fd);
|
||||
if (code != CURLM_OK)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
code = curl_multi_perform(curlm, &running_handles);
|
||||
} while ( code == CURLM_CALL_MULTI_PERFORM );
|
||||
|
||||
if (code != CURLM_OK)
|
||||
{
|
||||
return raise("curl_multi_perform failed", curl_multi_strerror(code));
|
||||
}
|
||||
|
||||
int msgs = 0;
|
||||
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];
|
||||
if (msg->data.result == CURLE_OK)
|
||||
curl->on_end(msg);
|
||||
else
|
||||
curl->on_error(msg);
|
||||
code = curl_multi_remove_handle(curlm, msg->easy_handle);
|
||||
if (code != CURLM_OK)
|
||||
{
|
||||
return raise("curl_multi_remove_handle failed", curl_multi_strerror(code));
|
||||
}
|
||||
}
|
||||
puts("done.");
|
||||
}
|
||||
}
|
||||
return v8::Integer::New(running_handles);
|
||||
}
|
||||
|
||||
// perform()
|
||||
static v8::Handle<v8::Value> perform(const v8::Arguments & args)
|
||||
{
|
||||
NodeCurl *curl = unwrap(args.This());
|
||||
CURLMcode code = curl_multi_add_handle(curlm, curl->curl);
|
||||
if (code != CURLM_OK)
|
||||
{
|
||||
return raise("curl_multi_add_handle failed", curl_multi_strerror(code));
|
||||
}
|
||||
++running_handles;
|
||||
return args.This();
|
||||
}
|
||||
|
||||
public:
|
||||
static v8::Handle<v8::Value> Initialize(v8::Handle<v8::Object> target)
|
||||
{
|
||||
// Initialize curl
|
||||
CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
|
||||
if (code != CURLE_OK)
|
||||
{
|
||||
return raise("curl_global_init faield");
|
||||
}
|
||||
|
||||
curlm = curl_multi_init();
|
||||
if (curlm == NULL)
|
||||
{
|
||||
return raise("curl_multi_init failed");
|
||||
}
|
||||
|
||||
// Initialize node-curl
|
||||
auto 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, "getinfo_int_", getinfo_int);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "getinfo_str_", getinfo_str);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "getinfo_double_", getinfo_double);
|
||||
|
||||
NODE_SET_METHOD(t, "process_", process);
|
||||
|
||||
// Set curl constants
|
||||
#include "string_options.h"
|
||||
#include "integer_options.h"
|
||||
#include "string_infos.h"
|
||||
#include "integer_infos.h"
|
||||
#include "double_infos.h"
|
||||
|
||||
NODE_CURL_EXPORT(string_options);
|
||||
NODE_CURL_EXPORT(integer_options);
|
||||
NODE_CURL_EXPORT(string_infos);
|
||||
NODE_CURL_EXPORT(integer_infos);
|
||||
NODE_CURL_EXPORT(double_infos);
|
||||
|
||||
target->Set(v8::String::NewSymbol("Curl"), t->GetFunction());
|
||||
return target;
|
||||
}
|
||||
};
|
||||
|
||||
CURLM * NodeCurl::curlm = NULL;
|
||||
int NodeCurl::running_handles = 0;
|
||||
bool NodeCurl::is_ref = false;
|
||||
std::map< CURL*, NodeCurl* > NodeCurl::curls;
|
||||
|
||||
#endif
|
11
src/string_infos.h
Normal file
11
src/string_infos.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
// generated by generate_curl_options_list.sh at Mon, 13 Feb 2012 21:40:10 +0800
|
||||
CurlOption string_infos[] = {
|
||||
{"EFFECTIVE_URL", CURLINFO_EFFECTIVE_URL},
|
||||
{"CONTENT_TYPE", CURLINFO_CONTENT_TYPE},
|
||||
{"PRIVATE", CURLINFO_PRIVATE},
|
||||
{"FTP_ENTRY_PATH", CURLINFO_FTP_ENTRY_PATH},
|
||||
{"REDIRECT_URL", CURLINFO_REDIRECT_URL},
|
||||
{"PRIMARY_IP", CURLINFO_PRIMARY_IP},
|
||||
{"RTSP_SESSION_ID", CURLINFO_RTSP_SESSION_ID},
|
||||
{"LOCAL_IP", CURLINFO_LOCAL_IP},
|
||||
};
|
84
src/string_options.h
Normal file
84
src/string_options.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
// generated by generate_curl_options_list.sh at Mon, 13 Feb 2012 21:40:10 +0800
|
||||
CurlOption string_options[] = {
|
||||
{"FILE", CURLOPT_FILE},
|
||||
{"URL", CURLOPT_URL},
|
||||
{"PROXY", CURLOPT_PROXY},
|
||||
{"USERPWD", CURLOPT_USERPWD},
|
||||
{"PROXYUSERPWD", CURLOPT_PROXYUSERPWD},
|
||||
{"RANGE", CURLOPT_RANGE},
|
||||
{"INFILE", CURLOPT_INFILE},
|
||||
{"ERRORBUFFER", CURLOPT_ERRORBUFFER},
|
||||
{"POSTFIELDS", CURLOPT_POSTFIELDS},
|
||||
{"REFERER", CURLOPT_REFERER},
|
||||
{"FTPPORT", CURLOPT_FTPPORT},
|
||||
{"USERAGENT", CURLOPT_USERAGENT},
|
||||
{"COOKIE", CURLOPT_COOKIE},
|
||||
{"HTTPHEADER", CURLOPT_HTTPHEADER},
|
||||
{"HTTPPOST", CURLOPT_HTTPPOST},
|
||||
{"SSLCERT", CURLOPT_SSLCERT},
|
||||
{"KEYPASSWD", CURLOPT_KEYPASSWD},
|
||||
{"QUOTE", CURLOPT_QUOTE},
|
||||
{"WRITEHEADER", CURLOPT_WRITEHEADER},
|
||||
{"COOKIEFILE", CURLOPT_COOKIEFILE},
|
||||
{"CUSTOMREQUEST", CURLOPT_CUSTOMREQUEST},
|
||||
{"STDERR", CURLOPT_STDERR},
|
||||
{"POSTQUOTE", CURLOPT_POSTQUOTE},
|
||||
{"WRITEINFO", CURLOPT_WRITEINFO},
|
||||
{"PROGRESSDATA", CURLOPT_PROGRESSDATA},
|
||||
{"INTERFACE", CURLOPT_INTERFACE},
|
||||
{"KRBLEVEL", CURLOPT_KRBLEVEL},
|
||||
{"CAINFO", CURLOPT_CAINFO},
|
||||
{"TELNETOPTIONS", CURLOPT_TELNETOPTIONS},
|
||||
{"RANDOM_FILE", CURLOPT_RANDOM_FILE},
|
||||
{"EGDSOCKET", CURLOPT_EGDSOCKET},
|
||||
{"COOKIEJAR", CURLOPT_COOKIEJAR},
|
||||
{"SSL_CIPHER_LIST", CURLOPT_SSL_CIPHER_LIST},
|
||||
{"SSLCERTTYPE", CURLOPT_SSLCERTTYPE},
|
||||
{"SSLKEY", CURLOPT_SSLKEY},
|
||||
{"SSLKEYTYPE", CURLOPT_SSLKEYTYPE},
|
||||
{"SSLENGINE", CURLOPT_SSLENGINE},
|
||||
{"PREQUOTE", CURLOPT_PREQUOTE},
|
||||
{"DEBUGDATA", CURLOPT_DEBUGDATA},
|
||||
{"CAPATH", CURLOPT_CAPATH},
|
||||
{"SHARE", CURLOPT_SHARE},
|
||||
{"ACCEPT_ENCODING", CURLOPT_ACCEPT_ENCODING},
|
||||
{"PRIVATE", CURLOPT_PRIVATE},
|
||||
{"HTTP200ALIASES", CURLOPT_HTTP200ALIASES},
|
||||
{"SSL_CTX_DATA", CURLOPT_SSL_CTX_DATA},
|
||||
{"NETRC_FILE", CURLOPT_NETRC_FILE},
|
||||
{"IOCTLDATA", CURLOPT_IOCTLDATA},
|
||||
{"FTP_ACCOUNT", CURLOPT_FTP_ACCOUNT},
|
||||
{"COOKIELIST", CURLOPT_COOKIELIST},
|
||||
{"FTP_ALTERNATIVE_TO_USER", CURLOPT_FTP_ALTERNATIVE_TO_USER},
|
||||
{"SOCKOPTDATA", CURLOPT_SOCKOPTDATA},
|
||||
{"SSH_PUBLIC_KEYFILE", CURLOPT_SSH_PUBLIC_KEYFILE},
|
||||
{"SSH_PRIVATE_KEYFILE", CURLOPT_SSH_PRIVATE_KEYFILE},
|
||||
{"SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5},
|
||||
{"OPENSOCKETDATA", CURLOPT_OPENSOCKETDATA},
|
||||
{"COPYPOSTFIELDS", CURLOPT_COPYPOSTFIELDS},
|
||||
{"SEEKDATA", CURLOPT_SEEKDATA},
|
||||
{"CRLFILE", CURLOPT_CRLFILE},
|
||||
{"ISSUERCERT", CURLOPT_ISSUERCERT},
|
||||
{"USERNAME", CURLOPT_USERNAME},
|
||||
{"PASSWORD", CURLOPT_PASSWORD},
|
||||
{"PROXYUSERNAME", CURLOPT_PROXYUSERNAME},
|
||||
{"PROXYPASSWORD", CURLOPT_PROXYPASSWORD},
|
||||
{"NOPROXY", CURLOPT_NOPROXY},
|
||||
{"SOCKS5_GSSAPI_SERVICE", CURLOPT_SOCKS5_GSSAPI_SERVICE},
|
||||
{"SSH_KNOWNHOSTS", CURLOPT_SSH_KNOWNHOSTS},
|
||||
{"SSH_KEYDATA", CURLOPT_SSH_KEYDATA},
|
||||
{"MAIL_FROM", CURLOPT_MAIL_FROM},
|
||||
{"MAIL_RCPT", CURLOPT_MAIL_RCPT},
|
||||
{"RTSP_SESSION_ID", CURLOPT_RTSP_SESSION_ID},
|
||||
{"RTSP_STREAM_URI", CURLOPT_RTSP_STREAM_URI},
|
||||
{"RTSP_TRANSPORT", CURLOPT_RTSP_TRANSPORT},
|
||||
{"INTERLEAVEDATA", CURLOPT_INTERLEAVEDATA},
|
||||
{"CHUNK_DATA", CURLOPT_CHUNK_DATA},
|
||||
{"FNMATCH_DATA", CURLOPT_FNMATCH_DATA},
|
||||
{"RESOLVE", CURLOPT_RESOLVE},
|
||||
{"TLSAUTH_USERNAME", CURLOPT_TLSAUTH_USERNAME},
|
||||
{"TLSAUTH_PASSWORD", CURLOPT_TLSAUTH_PASSWORD},
|
||||
{"TLSAUTH_TYPE", CURLOPT_TLSAUTH_TYPE},
|
||||
{"CLOSESOCKETDATA", CURLOPT_CLOSESOCKETDATA},
|
||||
{"DNS_SERVERS", CURLOPT_DNS_SERVERS},
|
||||
};
|
15
wscript
Normal file
15
wscript
Normal file
|
@ -0,0 +1,15 @@
|
|||
def set_options(opt):
|
||||
opt.tool_options('compiler_cxx')
|
||||
|
||||
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['LIB_CURL'] = 'curl'
|
||||
|
||||
def build(bld):
|
||||
obj = bld.new_task_gen('cxx', 'shlib', 'node_addon', uselib="CURL")
|
||||
obj.target = 'node-curl'
|
||||
obj.source = [
|
||||
'src/node-curl.cc',
|
||||
]
|
Loading…
Add table
Add a link
Reference in a new issue