Permalink
Browse files

proxy/redirect/user agent support, use node-gyp, and migrate to libuv

  • Loading branch information...
1 parent 550e1e6 commit 52377a6b6aeaa5308c9e0272e2be8c4d99a2ad6d @rfrench committed Feb 7, 2014
Showing with 448 additions and 391 deletions.
  1. +1 −0 .gitignore
  2. +7 −5 README.md
  3. +30 −0 binding.gyp
  4. +20 −20 package.json
  5. +130 −111 src/curl_client.cc
  6. +22 −19 src/curl_client.h
  7. +224 −202 src/curler.cc
  8. +14 −13 test.js
  9. +0 −21 wscript
View
@@ -0,0 +1 @@
+build/
View
@@ -12,6 +12,8 @@ A native c++ node.js module for asynchronous http requests via libcurl.
- `url`: request url. (required)
- `method`: HTTP method type. Defaults to `GET`. (can be anything)
- `headers`: Optional JSON key/value array of the request headers.
+ - `userAgent`: Optional custom user agent.
+ - `proxy`: Optional proxy support. (i.e. http://proxy.example.com:80)
- `data`: Optional request body data.
- `timeout`: Total request timeout (connection/response) in milliseconds.
- `connectionTimeout`: Connection timeout in milliseconds.
@@ -21,15 +23,15 @@ A native c++ node.js module for asynchronous http requests via libcurl.
### GET request
``` js
var curler = require("curler").Curler;
-var curlClient = new curler();
+var curl = new curler();
var options = {
method: "GET",
url: 'http://www.google.com'
};
var startDate = Date.now();
-curlClient.request(options, function(err, res, bodyData) {
+curl.request(options, function(err, res, bodyData) {
var duration = (Date.now() - startDate);
if (err) {
console.log(err);
@@ -46,13 +48,14 @@ curlClient.request(options, function(err, res, bodyData) {
### POST request (body data)
``` js
var curler = require("curler").Curler;
-var curlClient = new curler();
+var curl = new curler();
var data = JSON.stringify({ hello: 'world' });
var options = {
method: "POST",
url: 'http://www.example.com/',
+ userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3',
headers: {
'Content-Type': 'application/json',
'Connection': 'Keep-Alive'
@@ -63,7 +66,7 @@ var options = {
};
var startDate = Date.now();
-curlClient.request(options, function(err, res, bodyData) {
+curl.request(options, function(err, res, bodyData) {
var duration = (Date.now() - startDate);
if (err) {
console.log(err);
@@ -78,6 +81,5 @@ curlClient.request(options, function(err, res, bodyData) {
```
## TODO
-- Proxy support (http/https)
- Allow Expect: 100-Continue to be configurable, rather than always off
- Load a queue of curl handles when the module loads (ghetto connection pooling). Need a deconstructor in curler.cc that works first!
View
@@ -0,0 +1,30 @@
+{
+ 'link_settings': {
+ 'library_dirs': [
+ '/usr/local/lib/',
+ '/usr/lib/'
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'curler',
+ 'include_dirs': [
+ '/usr/local/include/curl/',
+ '/usr/include/curl/'
+ ],
+ 'libraries': ['-lcurl'],
+ 'sources': ['src/curl_client.cc',
+ 'src/curler.cc'],
+ 'cflags!': [ '-fno-exceptions' ],
+ 'cflags_cc!': [ '-fno-exceptions' ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'xcode_settings': {
+ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
+ 'MACOSX_DEPLOYMENT_TARGET': '10.5',
+ }
+ }]
+ ]
+ }
+ ]
+}
View
@@ -1,22 +1,22 @@
{
- "name": "curler",
- "author": "Ryan French <frenchrya@gmail.com>",
- "version": "0.0.2",
- "description": "A native c++ node.js module for asynchronous http requests via libcurl.",
- "homepage": "http://github.com/rfrench/curler",
- "keywords": [
- "curl",
- "libcurl",
- "http"
- ],
- "repository": {
- "type": "git",
- "url": "git://github.com/rfrench/curler.git"
- },
- "main": "curler.node",
- "engines" : ["node >= 0.4.7"],
- "scripts": {
- "install" : "node-waf configure build install",
- "preuninstall": "rm -rf build/*"
- }
+ "name": "curler",
+ "author": "Ryan French <frenchrya@gmail.com>",
+ "version": "0.0.3",
+ "description": "A native c++ node.js module for asynchronous http requests via libcurl.",
+ "homepage": "http://github.com/rfrench/curler",
+ "keywords": [
+ "curl",
+ "libcurl",
+ "http"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/rfrench/curler.git"
+ },
+ "main": "curler.node",
+ "engines" : ["node >= 0.4.7"],
+ "scripts": {
+ "install" : "node-gyp configure build install",
+ "preuninstall": "rm -rf build/*"
+ }
}
View
@@ -5,128 +5,147 @@ CurlClient::CurlClient() {}
CurlClient::~CurlClient() {}
-curl_response CurlClient::Request(curl_request request) {
- curl_response response;
+curl_response CurlClient::request(curl_request request) {
+ curl_response response;
- CURL *curl_handle = curl_easy_init();
+ CURL *curl_handle = curl_easy_init();
- /* set request URL */
- curl_easy_setopt(curl_handle, CURLOPT_URL, request.url.c_str());
-
- /* set method type */
- if (request.method == "HEAD") {
- curl_easy_setopt(curl_handle, CURLOPT_NOBODY, 1); //no need to get response data if it's a HEAD request
- }
- else {
- curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, request.method.c_str());
- }
-
- /* no signal. should stop curl from killing the thread */
- curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L);
-
- /* set timeout */
- if (request.timeout > 0) {
- curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT_MS, request.timeout);
- }
-
- /* set connection timeout */
- if (request.connectionTimeout > 0) {
- curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT_MS, request.connectionTimeout);
- }
-
- /* add custom headers */
- struct curl_slist *list = NULL;
- if (request.headers.size() > 0) {
- typedef std::map<string, string>::iterator it_type;
- for(it_type iterator = request.headers.begin(); iterator != request.headers.end(); iterator++) {
- string header = iterator->first + ": " + iterator->second;
- list = curl_slist_append(list, header.c_str());
- }
- }
-
- /* no expect header. not required but may be beneficial in some cases, but I don't need it. */
- /* todo: allow this to be configurable. */
- list = curl_slist_append(list, "Expect:");
- curl_easy_setopt (curl_handle, CURLOPT_HTTPHEADER, list);
-
- /* send body data. Illegal to send body data with a GET request (even though libcurl will send it). */
- if (request.method != "GET") {
- if (request.bodyData.size() > 0) {
- curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, request.bodyData.c_str());
- }
- }
-
- /* send all data to this function */
- curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteCallback);
- curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &response);
-
- /* we want the headers to this file handle */
- curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, HeaderCallback);
- curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, &response.headers);
-
- /* set error buffer */
- char errorBuffer[CURL_ERROR_SIZE];
- curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
-
- /* no progress meter please */
- curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
-
- /* todo: proxy support */
- /* do not verify peer or host */
- curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
-
- /* let's do it */
- CURLcode res = curl_easy_perform(curl_handle);
- if (res != 0) {
- response.error = errorBuffer;
- }
-
- /* lets get some stats on the request */
- curl_easy_getinfo(curl_handle, CURLINFO_TOTAL_TIME, &response.totalTime);
- curl_easy_getinfo(curl_handle, CURLINFO_NAMELOOKUP_TIME, &response.dnsTime);
- curl_easy_getinfo(curl_handle, CURLINFO_PRETRANSFER_TIME, &response.preTransferTime);
- curl_easy_getinfo(curl_handle, CURLINFO_CONNECT_TIME, &response.connectTime);
- curl_easy_getinfo(curl_handle, CURLINFO_STARTTRANSFER_TIME, &response.startTransferTime);
-
- /* get status code */
- response.statusCode = 0;
- curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response.statusCode);
-
- /* cleanup curl handle */
- curl_easy_cleanup(curl_handle);
-
- return response;
+ /* set request URL */
+ curl_easy_setopt(curl_handle, CURLOPT_URL, request.url.c_str());
+
+ /* set method type */
+ if (request.method == "HEAD") {
+ curl_easy_setopt(curl_handle, CURLOPT_NOBODY, 1); //no need to get response data if it's a HEAD request
+ }
+ else {
+ curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, request.method.c_str());
+ }
+
+ /* no signal. should stop curl from killing the thread */
+ curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L);
+
+ /* set timeout */
+ if (request.timeout > 0) {
+ curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT_MS, request.timeout);
+ }
+
+ /* set connection timeout */
+ if (request.connectionTimeout > 0) {
+ curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT_MS, request.connectionTimeout);
+ }
+
+ /* add custom headers */
+ struct curl_slist *list = NULL;
+ if (request.headers.size() > 0) {
+ typedef std::map<string, string>::iterator it_type;
+ for(it_type iterator = request.headers.begin(); iterator != request.headers.end(); iterator++) {
+ string header = iterator->first + ": " + iterator->second;
+ list = curl_slist_append(list, header.c_str());
+ }
+ }
+
+ /* no expect header. not required but may be beneficial in some cases, but I don't need it. */
+ /* todo: allow this to be configurable. */
+ list = curl_slist_append(list, "Expect:");
+ curl_easy_setopt (curl_handle, CURLOPT_HTTPHEADER, list);
+
+ /* send body data. Illegal to send body data with a GET request (even though libcurl will send it). */
+ if (request.method != "GET") {
+ if (request.bodyData.size() > 0) {
+ curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, request.bodyData.c_str());
+ }
+ }
+
+ /* send all data to this function */
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &response);
+
+ /* we want the headers to this file handle */
+ curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_callback);
+ curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, &response.headers);
+
+ /* set error buffer */
+ char errorBuffer[CURL_ERROR_SIZE];
+ curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
+
+ /* no progress meter please */
+ curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
+
+ /* custom user agent */
+ if (!request.userAgent.empty()) {
+ curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, request.userAgent.c_str());
+ }
+
+ /* proxy support */
+ if (!request.proxy.empty()) {
+ curl_easy_setopt(curl_handle, CURLOPT_PROXY, request.proxy.c_str());
+ }
+
+ /* follow redirects? */
+ if (request.maxRedirects > 0) {
+ curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
+ curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, request.maxRedirects);
+ }
+
+ /* do not verify peer or host */
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
+
+ /* CVE-2013-0249 */
+ curl_easy_setopt(curl_handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ curl_easy_setopt(curl_handle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+
+ /* let's do it */
+ CURLcode res = curl_easy_perform(curl_handle);
+ if (res != 0) {
+ response.error = errorBuffer;
+ }
+
+ /* lets get some stats on the request */
+ curl_easy_getinfo(curl_handle, CURLINFO_TOTAL_TIME, &response.totalTime);
+ curl_easy_getinfo(curl_handle, CURLINFO_NAMELOOKUP_TIME, &response.dnsTime);
+ curl_easy_getinfo(curl_handle, CURLINFO_PRETRANSFER_TIME, &response.preTransferTime);
+ curl_easy_getinfo(curl_handle, CURLINFO_CONNECT_TIME, &response.connectTime);
+ curl_easy_getinfo(curl_handle, CURLINFO_STARTTRANSFER_TIME, &response.startTransferTime);
+
+ /* get status code */
+ response.statusCode = 0;
+ curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response.statusCode);
+
+ /* cleanup curl handle */
+ curl_easy_cleanup(curl_handle);
+
+ return response;
}
-size_t CurlClient::HeaderCallback(void *buffer, size_t size, size_t nmemb, void *userp) {
- int result = size * nmemb;
+size_t CurlClient::header_callback(void *buffer, size_t size, size_t nmemb, void *userp) {
+ int result = size * nmemb;
- std::string data = (char*) buffer;
- std::string key, value;
+ std::string data = (char*) buffer;
+ std::string key, value;
- size_t p = data.find(":");
+ size_t p = data.find(":");
- if((int)p > 0) {
- key = data.substr(0, p);
- value = data.substr(p + 2, (result - key.size() - 4)); //4 = 2 (aka ": " between header name and value) + carriage return
-
- map<string, string> *headers = (map<string, string>*)userp;
- headers->insert(pair<string,string>(key, value));
- }
+ if((int)p > 0) {
+ key = data.substr(0, p);
+ value = data.substr(p + 2, (result - key.size() - 4)); //4 = 2 (aka ": " between header name and value) + carriage return
+
+ map<string, string> *headers = (map<string, string>*)userp;
+ headers->insert(pair<string,string>(key, value));
+ }
- return result;
+ return result;
}
-size_t CurlClient::WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
- int result = size * nmemb;
+size_t CurlClient::write_callback(void *contents, size_t size, size_t nmemb, void *userp) {
+ int result = size * nmemb;
- if (result > 0) {
- curl_response *response = (curl_response*)userp;
- string data = string((char*)contents, result);
+ if (result > 0) {
+ curl_response *response = (curl_response*)userp;
+ string data = string((char*)contents, result);
- response->data = response->data + data;
- }
+ response->data = response->data + data;
+ }
- return result;
+ return result;
}
Oops, something went wrong.

0 comments on commit 52377a6

Please sign in to comment.