Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan French committed May 8, 2012
0 parents commit 9510638
Show file tree
Hide file tree
Showing 7 changed files with 539 additions and 0 deletions.
83 changes: 83 additions & 0 deletions README.md
@@ -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!
22 changes: 22 additions & 0 deletions package.json
@@ -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/*"
}
}
129 changes: 129 additions & 0 deletions src/curl_client.cc
@@ -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;
}
43 changes: 43 additions & 0 deletions src/curl_client.h
@@ -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

0 comments on commit 9510638

Please sign in to comment.