Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Ryan French
committed
May 8, 2012
0 parents
commit 9510638
Showing
7 changed files
with
539 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# curler | ||
A native c++ node.js module for asynchronous http requests via libcurl. | ||
|
||
## Install | ||
<pre> | ||
$ npm install curler | ||
</pre> | ||
|
||
## request(options, callback[err, res, bodyData]) | ||
|
||
### Options | ||
- `url`: request url. (required) | ||
- `method`: HTTP method type. Defaults to `GET`. (can be anything) | ||
- `headers`: Optional JSON key/value array of the request headers. | ||
- `data`: Optional request body data. | ||
- `timeout`: Total request timeout (connection/response) in milliseconds. | ||
- `connectionTimeout`: Connection timeout in milliseconds. | ||
|
||
## Examples | ||
|
||
### GET request | ||
``` js | ||
var curler = require("curler").Curler; | ||
var curlClient = new curler(); | ||
|
||
var options = { | ||
method: "GET", | ||
url: 'http://www.google.com' | ||
}; | ||
|
||
var startDate = Date.now(); | ||
curlClient.request(options, function(err, res, bodyData) { | ||
var duration = (Date.now() - startDate); | ||
if (err) { | ||
console.log(err); | ||
} | ||
else { | ||
console.log('statusCode: %s', res.statusCode); | ||
console.log('bodyData: %s', bodyData); | ||
} | ||
|
||
console.log("curler (libcurl) performed http request in %s ms. dnsTime: %s, connectTime: %s, preTransferTime: %s, startTransferTime: %s, totalTime: %s", duration, res.dnsTime, res.connectTime, res.preTransferTime, res.startTransferTime, res.totalTime); | ||
}); | ||
``` | ||
|
||
### POST request (body data) | ||
``` js | ||
var curler = require("curler").Curler; | ||
var curlClient = new curler(); | ||
|
||
var data = JSON.stringify({ hello: 'world' }); | ||
|
||
var options = { | ||
method: "POST", | ||
url: 'http://www.example.com/', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Connection': 'Keep-Alive' | ||
}, | ||
data: data, | ||
timeout: 5000, | ||
connectionTimeout: 5000 | ||
}; | ||
|
||
var startDate = Date.now(); | ||
curlClient.request(options, function(err, res, bodyData) { | ||
var duration = (Date.now() - startDate); | ||
if (err) { | ||
console.log(err); | ||
} | ||
else { | ||
console.log('statusCode: %s', res.statusCode); | ||
console.log('bodyData: %s', bodyData); | ||
} | ||
|
||
console.log("curler (libcurl) performed http request in %s ms. dnsTime: %s, connectTime: %s, preTransferTime: %s, startTransferTime: %s, totalTime: %s", duration, res.dnsTime, res.connectTime, res.preTransferTime, res.startTransferTime, res.totalTime); | ||
}); | ||
``` | ||
|
||
## 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! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"name": "curler", | ||
"author": "Ryan French <frenchrya@gmail.com>", | ||
"version": "0.0.1", | ||
"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/*" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
#define CURL_CLIENT | ||
#include "curl_client.h" | ||
|
||
CurlClient::CurlClient() {} | ||
|
||
CurlClient::~CurlClient() {} | ||
|
||
curl_response CurlClient::Request(curl_request request) { | ||
curl_response response; | ||
|
||
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()); | ||
} | ||
|
||
/* 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; | ||
} | ||
|
||
size_t CurlClient::HeaderCallback(void *buffer, size_t size, size_t nmemb, void *userp) { | ||
int result = size * nmemb; | ||
|
||
std::string data = (char*) buffer; | ||
std::string key, value; | ||
|
||
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)); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
size_t CurlClient::WriteCallback(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); | ||
|
||
response->data = response->data + data; | ||
} | ||
|
||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#ifndef CURLCLIENT_H | ||
#define CURLCLIENT_H | ||
#include <list> | ||
#include <string> | ||
#include <map> | ||
#include <iostream> | ||
#include <curl/curl.h> | ||
#include <curl/easy.h> | ||
|
||
using namespace std; | ||
|
||
typedef struct { | ||
string method; | ||
string url; | ||
map<string, string> headers; | ||
string bodyData; | ||
long timeout; | ||
long connectionTimeout; | ||
} curl_request; | ||
|
||
typedef struct { | ||
int statusCode; | ||
map<string, string> headers; | ||
string data; | ||
string error; | ||
double dnsTime; | ||
double connectTime; | ||
double preTransferTime; | ||
double startTransferTime; | ||
double totalTime; | ||
} curl_response; | ||
|
||
class CurlClient { | ||
public: | ||
curl_response Request(curl_request request); | ||
~CurlClient(); | ||
CurlClient(); | ||
|
||
private: | ||
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp); | ||
static size_t HeaderCallback(void *buffer, size_t size, size_t nmemb, void *userp); | ||
}; | ||
#endif |
Oops, something went wrong.