Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Initial commit.
  • Loading branch information
strange committed Feb 10, 2010
0 parents commit e52b6cf
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
@@ -0,0 +1,2 @@
build/*
lib/ceoip.node
13 changes: 13 additions & 0 deletions examples/lookup.js
@@ -0,0 +1,13 @@
var sys = require('sys'),
ceoip = require('../lib/ceoip');

con = new ceoip.Connection();
con.addListener('connected', function () {
con.addListener('result', function(result) {
for (var attr in result) {
sys.puts(attr + ' : ' + result[attr]);
}
});
con.query('216.236.135.152');
});
con.connect('/usr/local/share/GeoIP/GeoLiteCity.dat');
12 changes: 12 additions & 0 deletions examples/lookup2.js
@@ -0,0 +1,12 @@
var sys = require('sys'),
geoip = require('../lib/geoip');

con = new geoip.Connection('/usr/local/share/GeoIP/GeoLiteCity.dat');

con.query('216.236.135.152').addCallback(function (result) {
for (var attr in result) {
sys.puts(attr + ' : ' + result[attr]);
}
});

con.close();
57 changes: 57 additions & 0 deletions lib/geoip.js
@@ -0,0 +1,57 @@
var sys = require('sys'),
ceoip = require('./ceoip');

function Connection(dbpath) {
this.con = new ceoip.Connection();
this.queue = [];
this.connected = false;
this.currentQuery = null;

var self = this;

this.con.addListener('closed', function() {
self.connected = false;
self.emit('closed');
});

this.con.addListener('connected', function() {
self.connected = true;
self.processQueue();
});

this.con.addListener('result', function(result) {
self.currentQuery[0].emitSuccess(result);
self.currentQuery = null;
self.processQueue();
});

this.con.connect(dbpath);
}

sys.inherits(Connection, process.EventEmitter);

Connection.prototype.addJob = function(promise, ipAddress) {
this.queue.push([promise, ipAddress]);
this.processQueue();
};

Connection.prototype.query = function(ipAddress) {
var promise = new process.Promise;
this.addJob(promise, ipAddress);
return promise;
};

Connection.prototype.processQueue = function () {
if (!this.queue.length || !this.connected || this.currentQuery) {
return;
}
this.currentQuery = this.queue.shift();
this.con.query(this.currentQuery[1]);
};

Connection.prototype.close = function () {
// Emit failure on all promises in queue?
this.con.close();
};

exports.Connection = Connection;
164 changes: 164 additions & 0 deletions src/ceoip.cc
@@ -0,0 +1,164 @@
#include <GeoIP.h>
#include <GeoIPCity.h>

#include <v8.h>
#include <node.h>
#include <node_events.h>
#include <assert.h>

using namespace v8;
using namespace node;

static Persistent<String> connected_symbol;
static Persistent<String> closed_symbol;
static Persistent<String> error_symbol;
static Persistent<String> result_symbol;

class Connection : public EventEmitter {
public:
static void
Initialize (v8::Handle<v8::Object> target)
{
Local<FunctionTemplate> t = FunctionTemplate::New(Connection::New);
t->Inherit(EventEmitter::constructor_template);
t->InstanceTemplate()->SetInternalFieldCount(1);

closed_symbol = NODE_PSYMBOL("closed");
connected_symbol = NODE_PSYMBOL("connected");
error_symbol = NODE_PSYMBOL("error");
result_symbol = NODE_PSYMBOL("result");

NODE_SET_PROTOTYPE_METHOD(t, "connect", Connect);
NODE_SET_PROTOTYPE_METHOD(t, "close", Close);
NODE_SET_PROTOTYPE_METHOD(t, "query", Query);

target->Set(String::NewSymbol("Connection"), t->GetFunction());
}

void Connect(const char *dbpath)
{
HandleScope scope;

gi = GeoIP_open(dbpath, GEOIP_INDEX_CACHE);

Emit((gi ? connected_symbol : error_symbol), 0, NULL);
}

void Close()
{
HandleScope scope;

if (gi != NULL) {
GeoIP_delete(gi);
gi = NULL;
}

Emit(closed_symbol, 0, NULL);
}

void Query(const char *query)
{
HandleScope scope;

assert(gi);
GeoIPRecord *record;
record = GeoIP_record_by_addr(gi, query);


if (record) {
Local<Value> result = BuildResult(record);
GeoIPRecord_delete(record);
Emit(result_symbol, 1, &result);
} else {
Emit(result_symbol, 0, NULL);
}
}

protected:
static Handle<Value> New(const Arguments& args)
{
HandleScope scope;

Connection *connection = new Connection();
connection->Wrap(args.This());

return args.This();
}

static Handle<Value> Connect(const Arguments &args)
{
HandleScope scope;

if (args.Length() < 1 || !args[0]->IsString()) {
return ThrowException(
Exception::TypeError(
String::New("Required argument: path to database.")));
}

String::Utf8Value dbpath(args[0]->ToString());

Connection *connection = ObjectWrap::Unwrap<Connection>(args.This());
connection->Connect(*dbpath);

return Undefined();
}

static Handle<Value> Close(const Arguments &args)
{
HandleScope scope;

Connection *connection = ObjectWrap::Unwrap<Connection>(args.This());
connection->Close();

return Undefined();
}

static Handle<Value> Query(const Arguments &args)
{
HandleScope scope;

if (args.Length() < 1 || !args[0]->IsString()) {
return ThrowException(
Exception::TypeError(
String::New("Required argument: ip address.")));
}

String::Utf8Value query(args[0]->ToString());

Connection *connection = ObjectWrap::Unwrap<Connection>(args.This());
connection->Query(*query);

return Undefined();
}

private:
Local<Value>BuildResult(GeoIPRecord *record)
{
HandleScope scope;

Local<Array> result = Array::New(8);

result->Set(String::New("longitude"), Number::New(record->longitude));
result->Set(String::New("latitude"), Number::New(record->latitude));
result->Set(String::New("country_code"),
String::New(record->country_code));
result->Set(String::New("continent_code"),
String::New(record->continent_code));
result->Set(String::New("metro_code"),
Number::New(record->metro_code));
result->Set(String::New("country"), String::New(record->country_name));
result->Set(String::New("city"), String::New(record->city));
result->Set(String::New("area_code"), Number::New(record->area_code));

return scope.Close(result);
}

GeoIP *gi;
};

extern "C" void
init (Handle<Object> target)
{
HandleScope scope;
Connection::Initialize(target);
}
35 changes: 35 additions & 0 deletions wscript
@@ -0,0 +1,35 @@
import os

srcdir = os.path.abspath('.')
blddir = 'build'
target = 'ceoip'
VERSION = '0.0.1'

def set_options(opt):
opt.tool_options('compiler_cxx')

def configure(conf):
conf.check_tool('compiler_cxx')
conf.check_tool('node_addon')
conf.check_cxx(lib='GeoIP', mandatory=True, uselib_store='GeoIP')

def build(context, target=target):
obj = context.new_task_gen('cxx', 'shlib', 'node_addon')

obj.uselib = 'GeoIP'
obj.source = 'src/ceoip.cc'
obj.target = target

context.add_post_fun(move_addon)

def move_addon(context, target=target):
"""Move the compiled addon to the local lib directory. Previously compiled
versions of the addon are overwritten.
"""
filename = '%s.node' % target
from_path = os.path.join(srcdir, blddir, 'default', filename)
to_path = os.path.join(srcdir, 'lib', filename)

if os.path.exists(from_path):
os.rename(from_path, to_path)

0 comments on commit e52b6cf

Please sign in to comment.