diff --git a/binding.gyp b/binding.gyp index 98e03e5..22b610e 100644 --- a/binding.gyp +++ b/binding.gyp @@ -2,8 +2,8 @@ 'targets': [ { 'target_name': 'node-curl', - 'cflags': ['-Wall', '-O1', '-fno-inline-functions'], - 'cflags_cc': ['-Wall', '-O1', '-fno-inline-functions'], + 'cflags': ['-Wall', '-O1', '-g', '-fno-inline-functions'], + 'cflags_cc': ['-Wall', '-O1', '-g', '-fno-inline-functions'], 'sources': ['src/node-curl.cc'], 'libraries': ['-lcurl'] } diff --git a/examples/curl.js b/examples/curl.js index dbfa67a..bbe7a43 100644 --- a/examples/curl.js +++ b/examples/curl.js @@ -1,6 +1,6 @@ -// Generated by ToffeeScript 1.4.0 +// Generated by ToffeeScript 1.6.2 (function() { - var curl, p, + var curl, err, p, res, _this = this; curl = require('../index'); @@ -10,10 +10,8 @@ curl(process.argv[2], { VERBOSE: 1, DEBUG: 1 - }, function(_$$_err, _$$_res) { - var err, res; - err = _$$_err; - res = _$$_res; + }, function() { + err = arguments[0], res = arguments[1]; if (err) { return p(err); } else { diff --git a/examples/low-level.js b/examples/low-level.js index d5e228b..43baa87 100644 --- a/examples/low-level.js +++ b/examples/low-level.js @@ -21,7 +21,6 @@ curl.on('data', function(chunk) { curl.on('error', function(e) { p("error: " + e.message); curl.close(); - once(); }); diff --git a/examples/post-multi-part.js b/examples/post-multi-part.js new file mode 100644 index 0000000..0a4ff5e --- /dev/null +++ b/examples/post-multi-part.js @@ -0,0 +1,27 @@ +// Generated by ToffeeScript 1.6.2 +(function() { + var curl, p; + + curl = require('../index'); + + p = console.log; + + p('start'); + + curl('127.0.0.1/upload.php', { + multipart: [ + { + name: 'file', + file: '/home/miao/test.js', + type: 'text/html' + }, { + name: 'sumbit', + value: 'send' + } + ] + }, function(e) { + console.log(e); + return console.log(curl.body); + }); + +}).call(this); diff --git a/examples/post-multi-part.toffee b/examples/post-multi-part.toffee new file mode 100644 index 0000000..17e3746 --- /dev/null +++ b/examples/post-multi-part.toffee @@ -0,0 +1,12 @@ +curl = require '../index' +p = console.log +p 'start' + +curl '127.0.0.1/upload.php', { + multipart: [ + {name: 'file', file: '/home/miao/test.js', type: 'text/html'}, + {name: 'sumbit', value: 'send'} + ], +}, (e) -> + console.log e + console.log curl.body diff --git a/examples/test.js b/examples/test.js index 137c883..836edaf 100644 --- a/examples/test.js +++ b/examples/test.js @@ -1,6 +1,6 @@ -// Generated by ToffeeScript 1.4.0 +// Generated by ToffeeScript 1.6.2 (function() { - var cookieFile, curl, fs, options, p, util, + var cookieFile, curl, err, fs, options, p, stream, util, _this = this; curl = require('../index'); @@ -25,14 +25,12 @@ curl.setDefaultOptions(options); - curl('www.google.com', function(_$$_err) { - var err; - err = _$$_err; + curl('www.google.com', function() { + err = arguments[0]; p("\x1b[33m" + util.inspect(curl.info('COOKIELIST')) + "\x1b[0m"); curl.reset(); - return curl('www.yahoo.com', function(_$$_err) { - var stream; - err = _$$_err; + curl('www.yahoo.com', function() { + err = arguments[0]; p("\x1b[33m" + util.inspect(curl.info('COOKIELIST')) + "\x1b[0m"); p("body length " + curl.body.length); p("\x1b[33mText in " + cookieFile + "\x1b[0m"); @@ -44,7 +42,7 @@ p("----"); curl.close(); p("deleting " + cookieFile); - return fs.unlink(cookieFile, function() { + fs.unlink(cookieFile, function() { return p("done."); }); }); diff --git a/lib/Curl.js b/lib/Curl.js index 0101caa..de08b88 100644 --- a/lib/Curl.js +++ b/lib/Curl.js @@ -1,10 +1,12 @@ -// Generated by ToffeeScript 1.4.0 +// Generated by ToffeeScript 1.6.2 (function() { - var Curl, curls, id, m, p; + var Curl, curls, e, id, m, p, + __hasProp = {}.hasOwnProperty; try { Curl = require(__dirname + '/../build/Release/node-curl').Curl; - } catch (e) { + } catch (_error) { + e = _error; Curl = require(__dirname + '/../build/default/node-curl').Curl; } @@ -12,11 +14,45 @@ return this.options[option_id] = value; }; + Curl.prototype.setopt_httppost = function(rows) { + var cols, k, option_id, row, v; + this.httppost = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = rows.length; _i < _len; _i++) { + row = rows[_i]; + cols = []; + for (k in row) { + if (!__hasProp.call(row, k)) continue; + v = row[k]; + k = k.toUpperCase(); + if ((option_id = Curl.httppost_options[k]) != null) { + cols.push(option_id); + if (!(v instanceof Buffer)) { + v = new Buffer(v.toString()); + } + cols.push(v); + } else { + throw new Error("invalid http post option " + k); + } + } + _results.push(cols); + } + return _results; + })(); + this.setopt_httppost_(this.httppost); + return this; + }; + Curl.prototype.setopt = function(option_name, value) { var option, option_id; option = option_name.toUpperCase(); if ((option_id = Curl.user_options[option]) != null) { - this.setopt_user_(option_id, value); + if (option === 'MULTIPART') { + this.setopt_httppost(value); + } else { + this.setopt_user_(option_id, value); + } } else if ((option_id = Curl.slist_options[option]) != null) { this.setopt_slist_(option_id, value); } else if ((option_id = Curl.integer_options[option]) != null) { @@ -50,7 +86,8 @@ Curl.user_options = { RAW: 'RAW', - DEBUG: 'DEBUG' + DEBUG: 'DEBUG', + MULTIPART: 'MULTIPART' }; id = 0; diff --git a/lib/Curl.toffee b/lib/Curl.toffee index b06d7bf..1ff83cf 100644 --- a/lib/Curl.toffee +++ b/lib/Curl.toffee @@ -6,13 +6,38 @@ catch e Curl::setopt_user_ = (option_id, value) -> @options[option_id] = value +Curl::setopt_httppost = (rows) -> + # convert object-rows to array-rows + # format + # [ + # [OPTION_ID, VALUE] + # ] + @httppost = for row in rows + cols = [] + for own k, v of row + k = k.toUpperCase() + if (option_id = Curl.httppost_options[k])? + cols.push option_id + unless v instanceof Buffer + v = new Buffer(v.toString()) + cols.push v + else + throw new Error("invalid http post option #{k}") + cols + @setopt_httppost_(@httppost) + @ + + Curl::setopt = (option_name, value) -> option = option_name.toUpperCase() # 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 + if (option == 'MULTIPART') + @setopt_httppost value + else + @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])? @@ -41,6 +66,7 @@ Curl::getinfo = (oinfo) -> Curl.user_options = RAW: 'RAW' DEBUG: 'DEBUG' + MULTIPART: 'MULTIPART' id = 0 curls = {} diff --git a/lib/CurlBuilder.js b/lib/CurlBuilder.js index ed6448f..f09aed3 100644 --- a/lib/CurlBuilder.js +++ b/lib/CurlBuilder.js @@ -1,17 +1,17 @@ -// Generated by ToffeeScript 1.4.0 +// Generated by ToffeeScript 1.6.2 (function() { - var Curl, CurlBuilder, + var Curl, CurlBuilder, e, __hasProp = {}.hasOwnProperty, __slice = [].slice; try { Curl = require(__dirname + '/Curl'); - } catch (e) { + } catch (_error) { + e = _error; Curl = require(__dirname + '/Curl'); } CurlBuilder = (function() { - function CurlBuilder() {} CurlBuilder.curls = {}; @@ -95,7 +95,7 @@ curl.body = data.toString(); } curl.status = curl.code = c.getinfo('RESPONSE_CODE'); - return process.nextTick(function() { + process.nextTick(function() { return cb.call(curl, null, curl); }); }); @@ -103,7 +103,7 @@ var _this = this; curl.log("receive failed: " + err.message); curl.running = false; - return process.nextTick(function() { + process.nextTick(function() { return cb.call(curl, err, null); }); }); diff --git a/src/node-curl.h b/src/node-curl.h index e7460ae..927eb71 100644 --- a/src/node-curl.h +++ b/src/node-curl.h @@ -8,9 +8,84 @@ #include #include #include +#include #define NODE_CURL_EXPORT(name) export_curl_options(t, #name, name, sizeof(name) / sizeof(CurlOption)); +class NodeCurlHttppost +{ + public: + curl_httppost *first; + curl_httppost *last; + + public: + NodeCurlHttppost() + : first(NULL), last(NULL) + { + reset(); + } + + void reset() + { + curl_httppost *cur = first; + while (cur) { + curl_httppost *next = cur->next; + if (cur->contenttype) + free(cur->contenttype); + if (cur->contents && cur->flags & HTTPPOST_FILENAME) + free(cur->contents); + if (cur->buffer) + free(cur->buffer); + if (cur->name) + free(cur->buffer); + free(cur); + cur = next; + } + first = NULL; + last = NULL; + } + + void append() + { + if (!first) { + first = (curl_httppost*)calloc(1, sizeof(curl_httppost)); + last = first; + } else { + last->next = (curl_httppost*)calloc(1, sizeof(curl_httppost)); + last = last->next; + } + } + + enum { + NAME, + FILE, + VALUE, + TYPE + }; + + void set(int field, char *value, long length) + { + switch (field) { + case NAME: + value = strndup(value, length); + last->name = value; + last->namelength = length; + break; + case TYPE: + value = strndup(value, length); + last->contenttype = value; + break; + case FILE: + value = strndup(value, length); + last->flags |= HTTPPOST_FILENAME; + case VALUE: + last->contents = value; + last->contentslength = length; + break; + } + } +}; + class NodeCurl { struct CurlOption @@ -32,6 +107,7 @@ class NodeCurl bool in_curlm; std::vector slists; std::map strings; + NodeCurlHttppost httppost; NodeCurl(v8::Handle object) : in_curlm(false) @@ -242,6 +318,30 @@ class NodeCurl return node_curl->setopt(args[0], slist); } + static v8::Handle setopt_httppost(const v8::Arguments & args) + { + NodeCurl * node_curl = unwrap(args.This()); + NodeCurlHttppost &httppost = node_curl->httppost; + v8::Handle rows = v8::Handle::Cast(args[0]); + httppost.reset(); + for (uint32_t i=0, len = rows->Length(); i cols = v8::Handle::Cast(rows->Get(i)); + uint32_t j=0, cols_len = cols->Length(); + httppost.append(); + while (jGet(j++)->Int32Value(); + v8::Handle buffer = cols->Get(j++)->ToObject(); + char *value = node::Buffer::Data(buffer); + int length = node::Buffer::Length(buffer); + httppost.set(field, value, length); + } + } + curl_easy_setopt(node_curl->curl, CURLOPT_HTTPPOST, node_curl->httppost.first); + return args.This(); + } + static curl_slist * value_to_slist(v8::Handle value) { curl_slist * slist = NULL; @@ -389,6 +489,7 @@ class NodeCurl 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 , "setopt_httppost_" , setopt_httppost); NODE_SET_PROTOTYPE_METHOD(t , "getinfo_int_" , getinfo_int); NODE_SET_PROTOTYPE_METHOD(t , "getinfo_str_" , getinfo_str); @@ -434,6 +535,15 @@ class NodeCurl X(SSL_ENGINES), X(COOKIELIST) }; + + #undef X + #define X(name) {#name, NodeCurlHttppost::name} + CurlOption httppost_options[] = { + X(NAME), + X(FILE), + X(VALUE), + X(TYPE) + }; #undef X NODE_CURL_EXPORT(string_options); @@ -445,6 +555,8 @@ class NodeCurl NODE_CURL_EXPORT(double_infos); NODE_CURL_EXPORT(slist_infos); + NODE_CURL_EXPORT(httppost_options); + target->Set(v8::String::NewSymbol("Curl"), t->GetFunction()); return target; }