THRIFT-1033: node.js target and lib
git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1056613 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/src/generate/t_js_generator.cc b/compiler/cpp/src/generate/t_js_generator.cc
index bb1033c..4aa6811 100644
--- a/compiler/cpp/src/generate/t_js_generator.cc
+++ b/compiler/cpp/src/generate/t_js_generator.cc
@@ -41,9 +41,18 @@
const std::map<std::string, std::string>& parsed_options,
const std::string& option_string) :
t_oop_generator(program) {
- (void) parsed_options;
(void) option_string;
- out_dir_base_ = "gen-js";
+
+ std::map<std::string, std::string>::const_iterator iter;
+
+ iter = parsed_options.find("node");
+ gen_node_ = (iter != parsed_options.end());
+
+ if (gen_node_) {
+ out_dir_base_ = "gen-nodejs";
+ } else {
+ out_dir_base_ = "gen-js";
+ }
}
/**
@@ -64,6 +73,8 @@
void generate_xception (t_struct* txception);
void generate_service (t_service* tservice);
+ std::string render_recv_throw(std::string var);
+ std::string render_recv_return(std::string var);
std::string render_const_value(t_type* type, t_const_value* value);
@@ -72,7 +83,7 @@
* Structs!
*/
void generate_js_struct(t_struct* tstruct, bool is_exception);
- void generate_js_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false);
+ void generate_js_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false, bool is_exported=true);
void generate_js_struct_reader(std::ofstream& out, t_struct* tstruct);
void generate_js_struct_writer(std::ofstream& out, t_struct* tstruct);
void generate_js_function_helpers(t_function* tfunction);
@@ -147,7 +158,7 @@
std::string js_includes();
std::string declare_field(t_field* tfield, bool init=false, bool obj=false);
- std::string function_signature(t_function* tfunction, std::string prefix="");
+ std::string function_signature(t_function* tfunction, std::string prefix="", bool include_callback=false);
std::string argument_list(t_struct* tstruct);
std::string type_to_enum(t_type* ttype);
@@ -180,6 +191,20 @@
return pieces;
}
+ std::string js_type_namespace(t_program* p) {
+ if (gen_node_) {
+ return "ttypes.";
+ }
+ return js_namespace(p);
+ }
+
+ std::string js_export_namespace(t_program* p) {
+ if (gen_node_) {
+ return "exports.";
+ }
+ return js_namespace(p);
+ }
+
std::string js_namespace(t_program* p) {
std::string ns = p->get_namespace("js");
if (ns.size() > 0) {
@@ -193,6 +218,11 @@
private:
/**
+ * True iff we should generate NodeJS-friendly RPC services.
+ */
+ bool gen_node_;
+
+ /**
* File streams
*/
std::ofstream f_types_;
@@ -219,12 +249,16 @@
// Print header
f_types_ <<
autogen_comment() <<
- js_includes();
+ js_includes() << endl;
+ if (gen_node_) {
+ f_types_ << "var ttypes = module.exports = {};" << endl;
+ }
string pns;
//setup the namespace
+ // TODO should the namespace just be in the directory structure for node?
vector<string> ns_pieces = js_namespace_pieces( program_ );
if( ns_pieces.size() > 0){
f_types_ << "var " << ns_pieces[0] << " = {}"<<endl;
@@ -244,6 +278,9 @@
* Prints standard js imports
*/
string t_js_generator::js_includes() {
+ if (gen_node_) {
+ return string("var Thrift = require('thrift').Thrift;");
+ }
string inc;
return inc;
@@ -274,8 +311,7 @@
* @param tenum The enumeration
*/
void t_js_generator::generate_enum(t_enum* tenum) {
-
- f_types_ << js_namespace(tenum->get_program())<<tenum->get_name()<<" = { "<<endl;
+ f_types_ << js_type_namespace(tenum->get_program())<<tenum->get_name()<<" = { "<<endl;
vector<t_enum_value*> constants = tenum->get_constants();
vector<t_enum_value*>::iterator c_iter;
@@ -298,7 +334,7 @@
string name = tconst->get_name();
t_const_value* value = tconst->get_value();
- f_types_ << js_namespace(program_) << name << " = ";
+ f_types_ << js_type_namespace(program_) << name << " = ";
f_types_ << render_const_value(type, value) << endl;
}
@@ -340,7 +376,7 @@
} else if (type->is_enum()) {
out << value->get_integer();
} else if (type->is_struct() || type->is_xception()) {
- out << "new " << js_namespace(type->get_program()) << type->get_name() << "({" << endl;
+ out << "new " << js_type_namespace(type->get_program()) << type->get_name() << "({" << endl;
indent_up();
const vector<t_field*>& fields = ((t_struct*)type)->get_members();
vector<t_field*>::const_iterator f_iter;
@@ -441,16 +477,34 @@
*/
void t_js_generator::generate_js_struct_definition(ofstream& out,
t_struct* tstruct,
- bool is_exception) {
+ bool is_exception,
+ bool is_exported) {
const vector<t_field*>& members = tstruct->get_members();
vector<t_field*>::const_iterator m_iter;
- out << js_namespace(tstruct->get_program()) << tstruct->get_name() <<" = function(args){\n";
+ indent_up();
+ if (gen_node_) {
+ if (is_exported) {
+ out << "var " << js_namespace(tstruct->get_program()) << tstruct->get_name() << " = " <<
+ "module.exports." << js_namespace(tstruct->get_program()) << tstruct->get_name() << " = function(args){\n";
+ } else {
+ out << "var " << js_namespace(tstruct->get_program()) << tstruct->get_name() << " = function(args){\n";
+ }
+ } else {
+ out << js_namespace(tstruct->get_program()) << tstruct->get_name() <<" = function(args){\n";
+ }
+
+ if (gen_node_ && is_exception) {
+ out << indent() << "Thrift.TException.call(this, \"" <<
+ js_namespace(tstruct->get_program()) << tstruct->get_name() << "\")" << endl;
+ out << indent() << "this.name = \"" <<
+ js_namespace(tstruct->get_program()) << tstruct->get_name() << "\"" << endl;
+ }
//members with arguments
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
- string dval = declare_field(*m_iter,true,true);
+ string dval = declare_field(*m_iter,false,true);
t_type* t = get_true_type((*m_iter)->get_type());
if ((*m_iter)->get_value() != NULL && !(t->is_struct() || t->is_xception())) {
dval = render_const_value((*m_iter)->get_type(), (*m_iter)->get_value());
@@ -487,12 +541,20 @@
out << "}\n";
if (is_exception) {
- out << "for (var property in Thrift.Exception)"<<endl<<
- js_namespace(tstruct->get_program())<<tstruct->get_name()<<"[property] = Thrift.Exception[property]"<<endl;
+ if (gen_node_) {
+ out << "require('util').inherits(" <<
+ js_namespace(tstruct->get_program()) <<
+ tstruct->get_name() << ", Thrift.TException)" << endl;
+ } else {
+ out << "for (var property in Thrift.TException)"<<endl<<
+ js_namespace(tstruct->get_program())<<tstruct->get_name()<<"[property] = Thrift.TException[property]"<<endl;
+ }
}
- //init prototype
- out << js_namespace(tstruct->get_program())<<tstruct->get_name() <<".prototype = {}\n";
+ if (!gen_node_) {
+ //init prototype
+ out << js_namespace(tstruct->get_program())<<tstruct->get_name() <<".prototype = {}\n";
+ }
generate_js_struct_reader(out, tstruct);
@@ -632,10 +694,23 @@
string f_service_name = get_out_dir()+service_name_+".js";
f_service_.open(f_service_name.c_str());
+ f_service_ <<
+ autogen_comment() <<
+ js_includes() << endl;
+
+ if (gen_node_) {
+ f_service_ <<
+ "var ttypes = require('./" + program_->get_name() + "_types.js');" << endl;
+ }
+
generate_service_helpers(tservice);
generate_service_interface(tservice);
generate_service_client(tservice);
+ if (gen_node_) {
+ generate_service_processor(tservice);
+ }
+
f_service_.close();
}
@@ -645,7 +720,45 @@
* @param tservice The service to generate a server for.
*/
void t_js_generator::generate_service_processor(t_service* tservice) {
- (void) tservice;
+ vector<t_function*> functions = tservice->get_functions();
+ vector<t_function*>::iterator f_iter;
+
+ f_service_ <<
+ "var " << js_namespace(tservice->get_program()) << service_name_ << "Processor = " <<
+ "exports.Processor = function(handler) ";
+
+ scope_up(f_service_);
+
+ f_service_ << indent() << "this._handler = handler" << endl;
+
+ scope_down(f_service_);
+
+ // Generate the server implementation
+ indent(f_service_) <<
+ js_namespace(tservice->get_program()) << service_name_ << "Processor.prototype.process = function(input, output) ";
+
+ scope_up(f_service_);
+
+ f_service_ << indent() << "var r = input.readMessageBegin()" << endl
+ << indent() << "if (this['process_' + r.fname]) {" << endl
+ << indent() << " return this['process_' + r.fname].call(this, r.rseqid, input, output)" << endl
+ << indent() << "} else {" << endl
+ << indent() << " input.skip(Thrift.Type.STRUCT)" << endl
+ << indent() << " input.readMessageEnd()" << endl
+ << indent() << " var x = new Thrift.TApplicationException(Thrift.TApplicationExceptionType.UNKNOWN_METHOD, 'Unknown function ' + r.fname)" << endl
+ << indent() << " output.writeMessageBegin(r.fname, Thrift.MessageType.Exception, r.rseqid)" << endl
+ << indent() << " x.write(output)" << endl
+ << indent() << " output.writeMessageEnd()" << endl
+ << indent() << " output.flush()" << endl
+ << indent() << "}" << endl;
+
+ scope_down(f_service_);
+ f_service_ << endl;
+
+ // Generate the process subfunctions
+ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+ generate_process_function(tservice, *f_iter);
+ }
}
/**
@@ -655,8 +768,70 @@
*/
void t_js_generator::generate_process_function(t_service* tservice,
t_function* tfunction) {
- (void) tservice;
- (void) tfunction;
+ indent(f_service_) <<
+ js_namespace(tservice->get_program()) << service_name_ << "Processor.prototype.process_" + tfunction->get_name() + " = function(seqid, input, output) ";
+
+ scope_up(f_service_);
+
+ string argsname = js_namespace(program_)+ service_name_ + "_" + tfunction->get_name() + "_args";
+ string resultname = js_namespace(program_)+ service_name_ + "_" + tfunction->get_name() + "_result";
+
+ f_service_ <<
+ indent() << "var args = new " << argsname << "()" << endl <<
+ indent() << "args.read(input)" << endl <<
+ indent() << "input.readMessageEnd()" << endl;
+
+ // Declare result for non oneway function
+ if (!tfunction->is_oneway()) {
+ f_service_ <<
+ indent() << "var result = new " << resultname << "()" << endl;
+ }
+
+ // Generate the function call
+ t_struct* arg_struct = tfunction->get_arglist();
+ const std::vector<t_field*>& fields = arg_struct->get_members();
+ vector<t_field*>::const_iterator f_iter;
+
+ f_service_ <<
+ indent() << "this._handler." << tfunction->get_name() << "(";
+
+ bool first = true;
+ for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+ if (first) {
+ first = false;
+ } else {
+ f_service_ << ", ";
+ }
+ f_service_ << "args." << (*f_iter)->get_name();
+ }
+
+ // Shortcut out here for oneway functions
+ if (tfunction->is_oneway()) {
+ f_service_ << ")" << endl;
+ scope_down(f_service_);
+ f_service_ << endl;
+ return;
+ }
+
+ if (!first) {
+ f_service_ << ", ";
+ }
+ f_service_ << "function(success) {" << endl;
+ indent_up();
+
+ f_service_ <<
+ indent() << "result.success = success" << endl <<
+ indent() << "output.writeMessageBegin(\"" << tfunction->get_name() <<
+ "\", Thrift.MessageType.REPLY, seqid)" << endl <<
+ indent() << "result.write(output)" << endl <<
+ indent() << "output.writeMessageEnd()" << endl <<
+ indent() << "output.flush()" << endl;
+
+ indent_down();
+ indent(f_service_) << "})" << endl;
+
+ scope_down(f_service_);
+ f_service_ << endl;
}
/**
@@ -675,7 +850,7 @@
t_struct* ts = (*f_iter)->get_arglist();
string name = ts->get_name();
ts->set_name(service_name_ + "_" + name);
- generate_js_struct_definition(f_service_, ts, false);
+ generate_js_struct_definition(f_service_, ts, false, false);
generate_js_function_helpers(*f_iter);
ts->set_name(name);
}
@@ -700,7 +875,7 @@
result.append(*f_iter);
}
- generate_js_struct_definition(f_service_, &result, false);
+ generate_js_struct_definition(f_service_, &result, false, false);
}
/**
@@ -727,16 +902,31 @@
void t_js_generator::generate_service_client(t_service* tservice) {
string extends = "";
- f_service_ <<
- js_namespace(tservice->get_program()) << service_name_ << "Client = function(input, output) {"<<endl;
+ if (gen_node_) {
+ f_service_ <<
+ "var " << js_namespace(tservice->get_program()) << service_name_ << "Client = " <<
+ "exports.Client = function(output, pClass) {"<<endl;
+ } else {
+ f_service_ <<
+ js_namespace(tservice->get_program()) << service_name_ << "Client = function(input, output) {"<<endl;
+ }
indent_up();
- f_service_ <<
+ if (gen_node_) {
+ f_service_ <<
+ indent() << " this.output = output;" << endl <<
+ indent() << " this.pClass = pClass;" << endl <<
+ indent() << " this.seqid = 0;" << endl <<
+ indent() << " this._reqs = {}" << endl;
+ } else {
+ f_service_ <<
indent() << " this.input = input" << endl <<
indent() << " this.output = null == output ? input : output" << endl <<
indent() << " this.seqid = 0" << endl;
+ }
+
indent_down();
@@ -766,10 +956,16 @@
string funname = (*f_iter)->get_name();
// Open function
- f_service_ << js_namespace(tservice->get_program())<<service_name_<<"Client.prototype." << function_signature(*f_iter) << "{" << endl;
+ f_service_ << js_namespace(tservice->get_program())<<service_name_<<"Client.prototype." << function_signature(*f_iter, "", gen_node_) << "{" << endl;
indent_up();
+ if (gen_node_) {
+ f_service_ <<
+ indent() << "this.seqid += 1;" << endl <<
+ indent() << "this._reqs[this.seqid] = callback;" << endl;
+ }
+
indent(f_service_) << indent() <<
"this.send_" << funname << "(";
@@ -784,7 +980,7 @@
}
f_service_ << ")" << endl;
- if (!(*f_iter)->is_oneway()) {
+ if (!gen_node_ && !(*f_iter)->is_oneway()) {
f_service_ << indent();
if (!(*f_iter)->get_returntype()->is_void()) {
f_service_ << "return ";
@@ -802,11 +998,20 @@
indent_up();
+ std::string outputVar;
+ if (gen_node_) {
+ f_service_ <<
+ indent() << "var output = new this.pClass(this.output);" << endl;
+ outputVar = "output";
+ } else {
+ outputVar = "this.output";
+ }
+
std::string argsname = js_namespace(program_)+ service_name_ + "_" + (*f_iter)->get_name() + "_args";
// Serialize the request header
f_service_ <<
- indent() << "this.output.writeMessageBegin('" << (*f_iter)->get_name() << "', Thrift.MessageType.CALL, this.seqid)" << endl;
+ indent() << outputVar << ".writeMessageBegin('" << (*f_iter)->get_name() << "', Thrift.MessageType.CALL, this.seqid)" << endl;
f_service_ <<
indent() << "var args = new " << argsname << "()" << endl;
@@ -818,9 +1023,14 @@
// Write to the stream
f_service_ <<
- indent() << "args.write(this.output)" << endl <<
- indent() << "this.output.writeMessageEnd()" << endl <<
- indent() << "return this.output.getTransport().flush()" << endl;
+ indent() << "args.write(" << outputVar << ")" << endl <<
+ indent() << outputVar << ".writeMessageEnd()" << endl;
+
+ if (gen_node_) {
+ f_service_ << indent() << "return this.output.flush()" << endl;
+ } else {
+ f_service_ << indent() << "return this.output.getTransport().flush()" << endl;
+ }
indent_down();
@@ -830,66 +1040,88 @@
if (!(*f_iter)->is_oneway()) {
std::string resultname = js_namespace(tservice->get_program()) + service_name_ + "_" + (*f_iter)->get_name() + "_result";
- t_struct noargs(program_);
- t_function recv_function((*f_iter)->get_returntype(),
- string("recv_") + (*f_iter)->get_name(),
- &noargs);
- // Open function
- f_service_ <<
- endl << js_namespace(tservice->get_program())<<service_name_ <<
- "Client.prototype." << function_signature(&recv_function) << "{" << endl;
+ if (gen_node_) {
+ // Open function
+ f_service_ <<
+ endl << js_namespace(tservice->get_program())<<service_name_ <<
+ "Client.prototype.recv_" << (*f_iter)->get_name() << " = function(input,mtype,rseqid){" << endl;
+ } else {
+ t_struct noargs(program_);
+
+ t_function recv_function((*f_iter)->get_returntype(),
+ string("recv_") + (*f_iter)->get_name(),
+ &noargs);
+ // Open function
+ f_service_ <<
+ endl << js_namespace(tservice->get_program())<<service_name_ <<
+ "Client.prototype." << function_signature(&recv_function) << "{" << endl;
+ }
indent_up();
- f_service_ <<
+ std::string inputVar;
+ if (gen_node_) {
+ inputVar = "input";
+ } else {
+ inputVar = "this.input";
+ }
+
+ if (gen_node_) {
+ f_service_ <<
+ indent() << "var callback = this._reqs[rseqid] || function() {};" << endl <<
+ indent() << "delete this._reqs[rseqid];" << endl;
+ } else {
+ f_service_ <<
indent() << "var ret = this.input.readMessageBegin()" << endl <<
indent() << "var fname = ret.fname" << endl <<
indent() << "var mtype = ret.mtype" << endl <<
- indent() << "var rseqid= ret.rseqid" <<endl <<
+ indent() << "var rseqid= ret.rseqid" <<endl;
+ }
+
+ f_service_ <<
indent() << "if (mtype == Thrift.MessageType.EXCEPTION) {" << endl <<
- indent() << " var x = new Thrift.ApplicationException()" << endl <<
- indent() << " x.read(this.input)" << endl <<
- indent() << " this.input.readMessageEnd()" << endl <<
- indent() << " throw x" << endl <<
+ indent() << " var x = new Thrift.TApplicationException()" << endl <<
+ indent() << " x.read(" << inputVar << ")" << endl <<
+ indent() << " " << inputVar << ".readMessageEnd()" << endl <<
+ indent() << " " << render_recv_throw("x") << endl <<
indent() << "}" << endl;
f_service_ <<
indent() << "var result = new " << resultname << "()" << endl <<
- indent() << "result.read(this.input)" << endl;
+ indent() << "result.read(" << inputVar << ")" << endl;
f_service_ <<
- indent() << "this.input.readMessageEnd()" << endl <<
+ indent() << inputVar << ".readMessageEnd()" << endl <<
endl;
- // Careful, only return result if not a void function
- if (!(*f_iter)->get_returntype()->is_void()) {
- f_service_ <<
- indent() << "if (null != result.success ) {" << endl <<
- indent() << " return result.success" << endl <<
- indent() << "}" << endl;
- }
-
t_struct* xs = (*f_iter)->get_xceptions();
const std::vector<t_field*>& xceptions = xs->get_members();
vector<t_field*>::const_iterator x_iter;
for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
f_service_ <<
indent() << "if (null != result." << (*x_iter)->get_name() << ") {" << endl <<
- indent() << " throw result." << (*x_iter)->get_name() << endl <<
+ indent() << " " << render_recv_throw("result." + (*x_iter)->get_name()) << endl <<
indent() << "}" << endl;
}
- // Careful, only return _result if not a void function
- if ((*f_iter)->get_returntype()->is_void()) {
- indent(f_service_) <<
- "return" << endl;
- } else {
+ // Careful, only return result if not a void function
+ if (!(*f_iter)->get_returntype()->is_void()) {
f_service_ <<
- indent() << "throw \"" << (*f_iter)->get_name() << " failed: unknown result\"" << endl;
+ indent() << "if (null != result.success ) {" << endl <<
+ indent() << " " << render_recv_return("result.success") << endl <<
+ indent() << "}" << endl;
+ f_service_ <<
+ indent() << render_recv_throw("\"" + (*f_iter)->get_name() + " failed: unknown result\"") << endl;
+ } else {
+ if (gen_node_) {
+ indent(f_service_) << "callback(null)" << endl;
+ } else {
+ indent(f_service_) << "return" << endl;
+ }
}
// Close function
@@ -901,6 +1133,22 @@
}
+std::string t_js_generator::render_recv_throw(std::string var) {
+ if (gen_node_) {
+ return "return callback(" + var + ");";
+ } else {
+ return "throw " + var;
+ }
+}
+
+std::string t_js_generator::render_recv_return(std::string var) {
+ if (gen_node_) {
+ return "return callback(null, " + var + ");";
+ } else {
+ return "return " + var;
+ }
+}
+
/**
* Deserializes a field of any type.
*/
@@ -925,7 +1173,7 @@
} else if (type->is_container()) {
generate_deserialize_container(out, type, name);
} else if (type->is_base_type() || type->is_enum()) {
- indent(out) << "var rtmp = input.";
+ indent(out) << name << " = input.";
if (type->is_base_type()) {
t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
@@ -961,11 +1209,12 @@
} else if (type->is_enum()) {
out << "readI32()";
}
+
+ if (!gen_node_) {
+ out << ".value";
+ }
+
out << endl;
-
- out <<name << " = rtmp.value"<<endl;
-
-
} else {
printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
tfield->get_name().c_str(), type->get_name().c_str());
@@ -982,7 +1231,7 @@
t_struct* tstruct,
string prefix) {
out <<
- indent() << prefix << " = new " << js_namespace(tstruct->get_program())<<tstruct->get_name() << "()" << endl <<
+ indent() << prefix << " = new " << js_type_namespace(tstruct->get_program())<<tstruct->get_name() << "()" << endl <<
indent() << prefix << ".read(input)" << endl;
}
@@ -1082,9 +1331,9 @@
t_field fval(tmap->get_val_type(), val);
indent(out) <<
- declare_field(&fkey, true, false) << endl;
+ declare_field(&fkey, false, false) << endl;
indent(out) <<
- declare_field(&fval, true, false) << endl;
+ declare_field(&fval, false, false) << endl;
generate_deserialize_field(out, &fkey);
generate_deserialize_field(out, &fval);
@@ -1252,9 +1501,12 @@
string viter = tmp("viter");
indent(out) << "for(var "<<kiter<<" in "<<prefix<<")";
scope_up(out);
+ indent(out) << "if ("<<prefix<<".hasOwnProperty("<<kiter<<"))" <<endl;
+ scope_up(out);
indent(out) << "var "<<viter<<" = "<<prefix<<"["<<kiter<<"]"<<endl;
generate_serialize_map_element(out, (t_map*)ttype, kiter, viter);
scope_down(out);
+ scope_down(out);
} else if (ttype->is_set()) {
@@ -1262,9 +1514,12 @@
indent(out) <<
"for(var "<<iter<<" in " << prefix << ")" << endl;
scope_up(out);
+ indent(out) << "if ("<<prefix<<".hasOwnProperty("<<iter<<"))" <<endl;
+ scope_up(out);
indent(out) << iter << "=" << prefix << "[" << iter << "]"<< endl;
generate_serialize_set_element(out, (t_set*)ttype, iter);
scope_down(out);
+ scope_down(out);
} else if (ttype->is_list()) {
@@ -1272,9 +1527,12 @@
indent(out) <<
"for(var "<<iter<<" in "<< prefix << ")" << endl;
scope_up(out);
+ indent(out) << "if ("<<prefix<<".hasOwnProperty("<<iter<<"))" <<endl;
+ scope_up(out);
indent(out) << iter << "=" << prefix << "[" << iter << "]"<< endl;
generate_serialize_list_element(out, (t_list*)ttype, iter);
scope_down(out);
+ scope_down(out);
}
scope_down(out);
@@ -1367,11 +1625,13 @@
result += " = null";
} else if (type->is_struct() || type->is_xception()) {
if (obj) {
- result += " = new " +js_namespace(type->get_program()) + type->get_name() + "()";
+ result += " = new " +js_type_namespace(type->get_program()) + type->get_name() + "()";
} else {
result += " = null";
}
}
+ } else {
+ result += " = null";
}
return result;
}
@@ -1383,7 +1643,8 @@
* @return String of rendered function definition
*/
string t_js_generator::function_signature(t_function* tfunction,
- string prefix) {
+ string prefix,
+ bool include_callback) {
string str;
@@ -1402,6 +1663,12 @@
str += (*f_iter)->get_name();
}
+ if (include_callback) {
+ if (!fields.empty()) {
+ str += ",";
+ }
+ str += "callback";
+ }
str += ")";
return str;
@@ -1469,5 +1736,6 @@
}
-THRIFT_REGISTER_GENERATOR(js, "Javascript", "")
+THRIFT_REGISTER_GENERATOR(js, "Javascript",
+" node: Generate node.js compatible code.\n")
diff --git a/lib/nodejs/README.md b/lib/nodejs/README.md
new file mode 100644
index 0000000..2832c1b
--- /dev/null
+++ b/lib/nodejs/README.md
@@ -0,0 +1,60 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+NOTE: you must use the framed thrift transport, TFramedTransport in most
+implementations, on the server side. Using a popular example, this is enabled
+by default in Cassandra 0.7 (but configuration must be changed in Cassandra
+0.6.x and earlier).
+
+## Install
+
+ npm install thrift
+
+## Thrift Compiler
+
+You can compile nodejs sources by running the following:
+
+ thrift --gen js:node thrift_file
+
+## Cassandra Client Example:
+
+Here is a Cassandra example:
+
+ var thrift = require('thrift'),
+ Cassandra = require('./gen-nodejs/Cassandra')
+ ttypes = require('./gen-nodejs/cassandra_types');
+
+ var connection = thrift.createConnection("localhost", 9160),
+ client = thrift.createClient(Cassandra, connection);
+
+ connection.on('error', function(err) {
+ console.error(err);
+ });
+
+ client.get_slice("Keyspace", "key", new ttypes.ColumnParent({column_family: "ExampleCF"}), new ttypes.SlicePredicate({slice_range: new ttypes.SliceRange({start: '', finish: ''})}), ttypes.ConsistencyLevel.ONE, function(err, data) {
+ if (err) {
+ // handle err
+ } else {
+ // data == [ttypes.ColumnOrSuperColumn, ...]
+ }
+ connection.end();
+ });
+
+## Custom client and server example
+
+An example based on the one shown on the Thrift front page is included in the examples/ folder.
diff --git a/lib/nodejs/examples/Makefile b/lib/nodejs/examples/Makefile
new file mode 100644
index 0000000..1930279
--- /dev/null
+++ b/lib/nodejs/examples/Makefile
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+ALL:
+ ../../../compiler/cpp/thrift --gen js:node user.thrift
diff --git a/lib/nodejs/examples/README.md b/lib/nodejs/examples/README.md
new file mode 100644
index 0000000..a87581f
--- /dev/null
+++ b/lib/nodejs/examples/README.md
@@ -0,0 +1,29 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# Running the user example
+
+#Generate the bindings:
+../../../compiler/cpp/thrift --gen js:node user.thrift
+
+#To run the user example, first start up the server in one terminal:
+NODE_PATH=../lib:../lib/thrift node server.js
+
+#Now run the client:
+NODE_PATH=../lib:../lib/thrift node client.js
+
+
+
diff --git a/lib/nodejs/examples/client.js b/lib/nodejs/examples/client.js
new file mode 100644
index 0000000..c83b342
--- /dev/null
+++ b/lib/nodejs/examples/client.js
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var thrift = require('thrift');
+
+var UserStorage = require('./gen-nodejs/UserStorage.js'),
+ ttypes = require('./gen-nodejs/user_types');
+
+var connection = thrift.createConnection('localhost', 9090),
+ client = thrift.createClient(UserStorage, connection);
+
+var user = new ttypes.UserProfile({uid: 1,
+ name: "Mark Slee",
+ blurb: "I'll find something to put here."});
+
+connection.on('error', function(err) {
+ console.error(err);
+});
+
+client.store(user, function(err, response) {
+ if (err) {
+ console.error(err);
+ } else {
+ console.log("client stored:", user.uid);
+ client.retrieve(user.uid, function(err, responseUser) {
+ if (err) {
+ console.error(err);
+ } else {
+ console.log("client retrieved:", responseUser.uid);
+ connection.end();
+ }
+ });
+ }
+});
diff --git a/lib/nodejs/examples/server.js b/lib/nodejs/examples/server.js
new file mode 100644
index 0000000..3b8c046
--- /dev/null
+++ b/lib/nodejs/examples/server.js
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var thrift = require('thrift');
+
+var UserStorage = require('./gen-nodejs/UserStorage.js'),
+ ttypes = require('./gen-nodejs/user_types');
+
+var users = {};
+
+var server = thrift.createServer(UserStorage, {
+ store: function(user, success) {
+ console.log("server stored:", user.uid);
+ users[user.uid] = user;
+ success();
+ },
+
+ retrieve: function(uid, success) {
+ console.log("server retrieved:", uid);
+ success(users[uid]);
+ },
+});
+
+server.listen(9090);
diff --git a/lib/nodejs/examples/user.thrift b/lib/nodejs/examples/user.thrift
new file mode 100644
index 0000000..ee260e5
--- /dev/null
+++ b/lib/nodejs/examples/user.thrift
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+struct UserProfile {
+ 1: i32 uid,
+ 2: string name,
+ 3: string blurb
+}
+
+service UserStorage {
+ void store(1: UserProfile user),
+ UserProfile retrieve(1: i32 uid)
+}
diff --git a/lib/nodejs/lib/thrift/binary_parser.js b/lib/nodejs/lib/thrift/binary_parser.js
new file mode 100644
index 0000000..2aeda46
--- /dev/null
+++ b/lib/nodejs/lib/thrift/binary_parser.js
@@ -0,0 +1,274 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var sys = require('sys');
+var chr = String.fromCharCode;
+
+var p = exports.BinaryParser = function( bigEndian, allowExceptions ){
+ this.bigEndian = bigEndian || true;
+ this.allowExceptions = allowExceptions;
+};
+p.bigEndian = true;
+
+var IBuffer = exports.BinaryParser.IBuffer = function( bigEndian, buffer ){
+ this.bigEndian = bigEndian || 1;
+ this.buffer = [];
+ this.setBuffer( buffer );
+};
+
+IBuffer.prototype.setBuffer = function( data ){
+ if( data ){
+ for( var l, i = l = data.length, b = this.buffer = new Array( l ); i; b[l - i] = data[--i] );
+ this.bigEndian && b.reverse();
+ }
+};
+
+IBuffer.prototype.hasNeededBits = function( neededBits ){
+ return this.buffer.length >= -( -neededBits >> 3 );
+};
+
+IBuffer.prototype.checkBuffer = function( neededBits ){
+ if( !this.hasNeededBits( neededBits ) ) {
+ console.log("missing: " + neededBits + " " + this.buffer.length);
+ throw new Error( "checkBuffer::missing bytes" );
+ }
+};
+
+IBuffer.prototype.readBits = function( start, length ){
+ //shl fix: Henri Torgemane ~1996 (compressed by Jonas Raoni)
+ function shl( a, b ){
+ for( ; b--; a = ( ( a %= 0x7fffffff + 1 ) & 0x40000000 ) == 0x40000000 ? a * 2 : ( a - 0x40000000 ) * 2 + 0x7fffffff + 1 );
+ return a;
+ }
+ if( start < 0 || length <= 0 )
+ return 0;
+ this.checkBuffer( start + length );
+ for( var offsetLeft, offsetRight = start % 8, curByte = this.buffer.length - ( start >> 3 ) - 1, lastByte = this.buffer.length + ( -( start + length ) >> 3 ), diff = curByte - lastByte, sum = ( ( this.buffer[ curByte ] >> offsetRight ) & ( ( 1 << ( diff ? 8 - offsetRight : length ) ) - 1 ) ) + ( diff && ( offsetLeft = ( start + length ) % 8 ) ? ( this.buffer[ lastByte++ ] & ( ( 1 << offsetLeft ) - 1 ) ) << ( diff-- << 3 ) - offsetRight : 0 ); diff; sum += shl( this.buffer[ lastByte++ ], ( diff-- << 3 ) - offsetRight ) );
+ return sum;
+};
+
+p.warn = function( msg ){
+ if( this.allowExceptions )
+ throw new Error( msg );
+ return 1;
+};
+p.decodeFloat = function( data, precisionBits, exponentBits ){
+ var b = new this.IBuffer( this.bigEndian, data );
+ b.checkBuffer( precisionBits + exponentBits + 1 );
+ var bias = Math.pow( 2, exponentBits - 1 ) - 1, signal = b.readBits( precisionBits + exponentBits, 1 ), exponent = b.readBits( precisionBits, exponentBits ), significand = 0,
+ divisor = 2, curByte = b.buffer.length + ( -precisionBits >> 3 ) - 1;
+ do{
+ for( var byteValue = b.buffer[ ++curByte ], startBit = precisionBits % 8 || 8, mask = 1 << startBit; mask >>= 1; ( byteValue & mask ) && ( significand += 1 / divisor ), divisor *= 2 );
+ }while( precisionBits -= startBit );
+ return exponent == ( bias << 1 ) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity : ( 1 + signal * -2 ) * ( exponent || significand ? !exponent ? Math.pow( 2, -bias + 1 ) * significand : Math.pow( 2, exponent - bias ) * ( 1 + significand ) : 0 );
+};
+p.decodeInt = function( data, bits, signed, forceBigEndian ){
+ //console.log("decodeInt: ", data, bits, signed);
+ var b = new this.IBuffer( this.bigEndian||forceBigEndian, data ), x = b.readBits( 0, bits ), max = Math.pow( 2, bits );
+ return signed && x >= max / 2 ? x - max : x;
+};
+p.encodeFloat = function( data, precisionBits, exponentBits ){
+ var bias = Math.pow( 2, exponentBits - 1 ) - 1, minExp = -bias + 1, maxExp = bias, minUnnormExp = minExp - precisionBits,
+ status = isNaN( n = parseFloat( data ) ) || n == -Infinity || n == +Infinity ? n : 0,
+ exp = 0, len = 2 * bias + 1 + precisionBits + 3, bin = new Array( len ),
+ signal = ( n = status !== 0 ? 0 : n ) < 0, n = Math.abs( n ), intPart = Math.floor( n ), floatPart = n - intPart,
+ i, lastBit, rounded, j, result;
+ for( i = len; i; bin[--i] = 0 );
+ for( i = bias + 2; intPart && i; bin[--i] = intPart % 2, intPart = Math.floor( intPart / 2 ) );
+ for( i = bias + 1; floatPart > 0 && i; ( bin[++i] = ( ( floatPart *= 2 ) >= 1 ) - 0 ) && --floatPart );
+ for( i = -1; ++i < len && !bin[i]; );
+ if( bin[( lastBit = precisionBits - 1 + ( i = ( exp = bias + 1 - i ) >= minExp && exp <= maxExp ? i + 1 : bias + 1 - ( exp = minExp - 1 ) ) ) + 1] ){
+ if( !( rounded = bin[lastBit] ) ){
+ for( j = lastBit + 2; !rounded && j < len; rounded = bin[j++] );
+ }
+ for( j = lastBit + 1; rounded && --j >= 0; ( bin[j] = !bin[j] - 0 ) && ( rounded = 0 ) );
+ }
+ for( i = i - 2 < 0 ? -1 : i - 3; ++i < len && !bin[i]; );
+ if( ( exp = bias + 1 - i ) >= minExp && exp <= maxExp )
+ ++i;
+ else if( exp < minExp ){
+ exp != bias + 1 - len && exp < minUnnormExp && this.warn( "encodeFloat::float underflow" );
+ i = bias + 1 - ( exp = minExp - 1 );
+ }
+ if( intPart || status !== 0 ){
+ this.warn( intPart ? "encodeFloat::float overflow" : "encodeFloat::" + status );
+ exp = maxExp + 1;
+ i = bias + 2;
+ if( status == -Infinity )
+ signal = 1;
+ else if( isNaN( status ) )
+ bin[i] = 1;
+ }
+ for( n = Math.abs( exp + bias ), j = exponentBits + 1, result = ""; --j; result = ( n % 2 ) + result, n = n >>= 1 );
+ for( n = 0, j = 0, i = ( result = ( signal ? "1" : "0" ) + result + bin.slice( i, i + precisionBits ).join( "" ) ).length, r = []; i; j = ( j + 1 ) % 8 ){
+ n += ( 1 << j ) * result.charAt( --i );
+ if( j == 7 ){
+ r[r.length] = n;
+ n = 0;
+ }
+ }
+ if (n) {
+ r[r.length] = n;
+ }
+ return new Buffer( this.bigEndian ? r.reverse() : r );
+};
+p.encodeInt = function( data, bits, signed, forceBigEndian ){
+ var max = Math.pow( 2, bits );
+ ( data >= max || data < -( max / 2 ) ) && this.warn( "encodeInt::overflow" ) && ( data = 0 );
+ data < 0 && ( data += max );
+ for( var r = []; data; r[r.length] = data % 256, data = Math.floor( data / 256 ) );
+ for( bits = -( -bits >> 3 ) - r.length; bits--; r[r.length] = 0 );
+ return new Buffer((this.bigEndian||forceBigEndian) ? r.reverse() : r );
+};
+p.toSmall = function( data ){ return this.decodeInt( data, 8, true ); };
+p.fromSmall = function( data ){ return this.encodeInt( data, 8, true ); };
+p.toByte = function( data ){ return this.decodeInt( data, 8, false ); };
+p.fromByte = function( data ){ return this.encodeInt( data, 8, false ); };
+p.toShort = function( data ){ return this.decodeInt( data, 16, true ); };
+p.fromShort = function( data ){ return this.encodeInt( data, 16, true ); };
+p.toWord = function( data ){ return this.decodeInt( data, 16, false ); };
+p.fromWord = function( data ){ return this.encodeInt( data, 16, false ); };
+p.toInt = function( data ){ return this.decodeInt( data, 32, true ); };
+p.fromInt = function( data ){ return this.encodeInt( data, 32, true ); };
+p.toLong = function( data ){ return this.decodeInt( data, 64, true ); };
+p.fromLong = function( data ){ return this.encodeInt( data, 64, true ); };
+p.toDWord = function( data ){ return this.decodeInt( data, 32, false ); };
+p.fromDWord = function( data ){ return this.encodeInt( data, 32, false ); };
+p.toQWord = function( data ){ return this.decodeInt( data, 64, true ); };
+p.fromQWord = function( data ){ return this.encodeInt( data, 64, true ); };
+p.toFloat = function( data ){ return this.decodeFloat( data, 23, 8 ); };
+p.fromFloat = function( data ){ return this.encodeFloat( data, 23, 8 ); };
+p.toDouble = function( data ){ return this.decodeFloat( data, 52, 11 ); };
+p.fromDouble = function( data ){ return this.encodeFloat( data, 52, 11 ); };
+
+// Factor out the encode so it can be shared by add_header and push_int32
+p.encode_int32 = function(number) {
+ var a, b, c, d, unsigned;
+ unsigned = (number < 0) ? (number + 0x100000000) : number;
+ a = Math.floor(unsigned / 0xffffff);
+ unsigned &= 0xffffff;
+ b = Math.floor(unsigned / 0xffff);
+ unsigned &= 0xffff;
+ c = Math.floor(unsigned / 0xff);
+ unsigned &= 0xff;
+ d = Math.floor(unsigned);
+ return chr(a) + chr(b) + chr(c) + chr(d);
+};
+
+p.encode_int64 = function(number) {
+ var a, b, c, d, e, f, g, h, unsigned;
+ unsigned = (number < 0) ? (number + 0x10000000000000000) : number;
+ a = Math.floor(unsigned / 0xffffffffffffff);
+ unsigned &= 0xffffffffffffff;
+ b = Math.floor(unsigned / 0xffffffffffff);
+ unsigned &= 0xffffffffffff;
+ c = Math.floor(unsigned / 0xffffffffff);
+ unsigned &= 0xffffffffff;
+ d = Math.floor(unsigned / 0xffffffff);
+ unsigned &= 0xffffffff;
+ e = Math.floor(unsigned / 0xffffff);
+ unsigned &= 0xffffff;
+ f = Math.floor(unsigned / 0xffff);
+ unsigned &= 0xffff;
+ g = Math.floor(unsigned / 0xff);
+ unsigned &= 0xff;
+ h = Math.floor(unsigned);
+ return chr(a) + chr(b) + chr(c) + chr(d) + chr(e) + chr(f) + chr(g) + chr(h);
+};
+
+/**
+ UTF8 methods
+**/
+
+// Take a raw binary string and return a utf8 string
+p.decode_utf8 = function(a) {
+ var string = "";
+ var i = 0;
+ var c = c1 = c2 = 0;
+
+ while ( i < a.length ) {
+ c = a.charCodeAt(i);
+ if (c < 128) {
+ string += String.fromCharCode(c);
+ i++;
+ } else if((c > 191) && (c < 224)) {
+ c2 = a.charCodeAt(i+1);
+ string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
+ i += 2;
+ } else {
+ c2 = a.charCodeAt(i+1);
+ c3 = a.charCodeAt(i+2);
+ string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+ i += 3;
+ }
+ }
+ return string;
+};
+
+// Encode a cstring correctly
+p.encode_cstring = function(s) {
+ return unescape(encodeURIComponent(s)) + p.fromByte(0);
+};
+
+// Take a utf8 string and return a binary string
+p.encode_utf8 = function(s) {
+ var a="";
+ for (var n=0; n< s.length; n++) {
+ var c=s.charCodeAt(n);
+ if (c<128) {
+ a += String.fromCharCode(c);
+ } else if ((c>127)&&(c<2048)) {
+ a += String.fromCharCode( (c>>6) | 192) ;
+ a += String.fromCharCode( (c&63) | 128);
+ } else {
+ a += String.fromCharCode( (c>>12) | 224);
+ a += String.fromCharCode( ((c>>6) & 63) | 128);
+ a += String.fromCharCode( (c&63) | 128);
+ }
+ }
+ return a;
+};
+
+p.hprint = function(s) {
+ for (var i=0; i<s.length; i++) {
+ if (s.charCodeAt(i)<32) {
+ var number = s.charCodeAt(i) <= 15 ? "0" + s.charCodeAt(i).toString(16) : s.charCodeAt(i).toString(16);
+ sys.debug(number+' : ');}
+ else {
+ var number = s.charCodeAt(i) <= 15 ? "0" + s.charCodeAt(i).toString(16) : s.charCodeAt(i).toString(16);
+ sys.debug(number+' : '+ s.charAt(i));}
+ }
+};
+
+p.to_byte_array = function(s) {
+ var array = [];
+
+ for (var i=0; i<s.length; i++) {
+ if (s.charCodeAt(i)<32) {array.push(s.charCodeAt(i));}
+ else {array.push(s.charCodeAt(i))}
+ }
+
+ sys.puts(array);
+}
+
+p.pprint = function(s) {
+ for (var i=0; i<s.length; i++) {
+ if (s.charCodeAt(i)<32) {sys.puts(s.charCodeAt(i)+' : ');}
+ else {sys.puts(s.charCodeAt(i)+' : '+ s.charAt(i));}
+ }
+};
diff --git a/lib/nodejs/lib/thrift/connection.js b/lib/nodejs/lib/thrift/connection.js
new file mode 100644
index 0000000..e469f49
--- /dev/null
+++ b/lib/nodejs/lib/thrift/connection.js
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var sys = require('sys'),
+ EventEmitter = require("events").EventEmitter,
+ net = require('net'),
+ TMemoryBuffer = require('./transport').TMemoryBuffer,
+ TBinaryProtocol = require('./protocol').TBinaryProtocol;
+
+var BinaryParser = require('./binary_parser').BinaryParser;
+BinaryParser.bigEndian = true;
+
+var int32FramedReceiver = exports.int32FramedReceiver = function (callback) {
+ var frameLeft = 0,
+ framePos = 0,
+ frame = null;
+
+ return function(data) {
+ // var buf = new Buffer(data, 'binary');
+ // console.log(buf);
+ // framed transport
+ while (data.length) {
+ if (frameLeft === 0) {
+ // TODO assumes we have all 4 bytes
+ if (data.length < 4) {
+ throw Error("Not enough bytes");
+ }
+ frameLeft = BinaryParser.toInt(data.slice(0,4));
+ frame = new Buffer(frameLeft);
+ framePos = 0;
+ data = data.slice(4, data.length);
+ }
+
+ if (data.length >= frameLeft) {
+ data.copy(frame, framePos, 0, frameLeft);
+ data = data.slice(frameLeft, data.length);
+
+ frameLeft = 0;
+ callback(frame);
+ } else if (data.length) {
+ data.copy(frame, framePos, 0, data.length);
+ frameLeft -= data.length;
+ framePos += data.length;
+ data = data.slice(data.length, data.length);
+ }
+ }
+ };
+}
+
+var Connection = exports.Connection = function(stream, options) {
+ var self = this;
+ EventEmitter.call(this);
+
+ this.connection = stream;
+ this.offline_queue = [];
+ this.options = options || {};
+ this.connected = false;
+
+ this.connection.addListener("connect", function() {
+ self.connected = true;
+ this.setTimeout(self.options.timeout || 0);
+ this.setNoDelay();
+ this.frameLeft = 0;
+ this.framePos = 0;
+ this.frame = null;
+
+ self.offline_queue.forEach(function(data) {
+ self.connection.write(data);
+ });
+
+ self.emit("connect");
+ });
+
+ this.connection.addListener("error", function(err) {
+ self.emit("error", err);
+ });
+
+ // Add a close listener
+ this.connection.addListener("close", function() {
+ self.emit("close");
+ });
+
+ this.connection.addListener("timeout", function() {
+ self.emit("timeout");
+ });
+
+ this.connection.addListener("data", int32FramedReceiver(function(data) {
+ // console.log(typeof(data));
+ var input = new TBinaryProtocol(new TMemoryBuffer(data));
+ var r = input.readMessageBegin();
+ // console.log(r);
+ self.client['recv_' + r.fname](input, r.mtype, r.rseqid);
+ // self.emit("data", data);
+ }));
+}
+sys.inherits(Connection, EventEmitter);
+
+exports.createConnection = function(host, port, options) {
+ var stream = net.createConnection(port, host);
+ var connection = new Connection(stream, options);
+ connection.host = host;
+ connection.port = port;
+
+ return connection;
+}
+
+exports.createClient = function(cls, connection) {
+ if (cls.Client) {
+ cls = cls.Client;
+ }
+ var client = new cls(new TMemoryBuffer(undefined, function(buf) {
+ connection.write(buf);
+ }), TBinaryProtocol);
+
+ // TODO clean this up
+ connection.client = client;
+
+ return client;
+}
+
+Connection.prototype.end = function() {
+ this.connection.end();
+}
+
+Connection.prototype.write = function(buf) {
+ // TODO: optimize this better, allocate one buffer instead of both:
+ var msg = new Buffer(buf.length + 4);
+ BinaryParser.fromInt(buf.length).copy(msg, 0, 0, 4);
+ buf.copy(msg, 4, 0, buf.length);
+ if (!this.connected) {
+ this.offline_queue.push(msg);
+ } else {
+ this.connection.write(msg);
+ }
+}
diff --git a/lib/nodejs/lib/thrift/index.js b/lib/nodejs/lib/thrift/index.js
new file mode 100644
index 0000000..f4fa3cd
--- /dev/null
+++ b/lib/nodejs/lib/thrift/index.js
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+exports.Thrift = require('./thrift');
+
+var connection = require('./connection');
+exports.Connection = connection.Connection;
+exports.createClient = connection.createClient;
+exports.createConnection = connection.createConnection;
+
+exports.createServer = require('./server').createServer;
diff --git a/lib/nodejs/lib/thrift/protocol.js b/lib/nodejs/lib/thrift/protocol.js
new file mode 100644
index 0000000..f333bb9
--- /dev/null
+++ b/lib/nodejs/lib/thrift/protocol.js
@@ -0,0 +1,337 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var sys = require('sys'),
+ Thrift = require('./thrift'),
+ Type = Thrift.Type;
+
+var BinaryParser = require('./binary_parser').BinaryParser;
+BinaryParser.bigEndian = true;
+
+var UNKNOWN = 0,
+ INVALID_DATA = 1,
+ NEGATIVE_SIZE = 2,
+ SIZE_LIMIT = 3,
+ BAD_VERSION = 4;
+
+var TProtocolException = function(type, message) {
+ Error.call(this, message);
+ this.name = 'TProtocolException';
+ this.type = type;
+}
+sys.inherits(TProtocolException, Error);
+
+var TBinaryProtocol = exports.TBinaryProtocol = function(trans, strictRead, strictWrite) {
+ this.trans = trans;
+ this.strictRead = (strictRead !== undefined ? strictRead : false);
+ this.strictWrite = (strictWrite !== undefined ? strictWrite : true);
+}
+
+TBinaryProtocol.prototype.flush = function() {
+ return this.trans.flush();
+}
+
+// NastyHaxx. JavaScript forces hex constants to be
+// positive, converting this into a long. If we hardcode the int value
+// instead it'll stay in 32 bit-land.
+
+var VERSION_MASK = -65536, // 0xffff0000
+ VERSION_1 = -2147418112, // 0x80010000
+ TYPE_MASK = 0x000000ff;
+
+TBinaryProtocol.prototype.writeMessageBegin = function(name, type, seqid) {
+ if (this.strictWrite) {
+ this.writeI32(VERSION_1 | type);
+ this.writeString(name);
+ this.writeI32(seqid);
+ } else {
+ this.writeString(name);
+ this.writeByte(type);
+ this.writeI32(seqid);
+ }
+}
+
+TBinaryProtocol.prototype.writeMessageEnd = function() {
+}
+
+TBinaryProtocol.prototype.writeStructBegin = function(name) {
+}
+
+TBinaryProtocol.prototype.writeStructEnd = function() {
+}
+
+TBinaryProtocol.prototype.writeFieldBegin = function(name, type, id) {
+ this.writeByte(type);
+ this.writeI16(id);
+}
+
+TBinaryProtocol.prototype.writeFieldEnd = function() {
+}
+
+TBinaryProtocol.prototype.writeFieldStop = function() {
+ this.writeByte(Type.STOP);
+}
+
+TBinaryProtocol.prototype.writeMapBegin = function(ktype, vtype, size) {
+ this.writeByte(ktype);
+ this.writeByte(vtype);
+ this.writeI32(size);
+}
+
+TBinaryProtocol.prototype.writeMapEnd = function() {
+}
+
+TBinaryProtocol.prototype.writeListBegin = function(etype, size) {
+ this.writeByte(etype);
+ this.writeI32(size);
+}
+
+TBinaryProtocol.prototype.writeListEnd = function() {
+}
+
+TBinaryProtocol.prototype.writeSetBegin = function(etype, size) {
+ console.log('write set', etype, size);
+ this.writeByte(etype);
+ this.writeI32(size);
+}
+
+TBinaryProtocol.prototype.writeSetEnd = function() {
+}
+
+TBinaryProtocol.prototype.writeBool = function(bool) {
+ if (bool) {
+ this.writeByte(1);
+ } else {
+ this.writeByte(0);
+ }
+}
+
+TBinaryProtocol.prototype.writeByte = function(byte) {
+ this.trans.write(BinaryParser.fromByte(byte));
+}
+
+TBinaryProtocol.prototype.writeI16 = function(i16) {
+ this.trans.write(BinaryParser.fromShort(i16));
+}
+
+TBinaryProtocol.prototype.writeI32 = function(i32) {
+ this.trans.write(BinaryParser.fromInt(i32));
+}
+
+TBinaryProtocol.prototype.writeI64 = function(i64) {
+ this.trans.write(BinaryParser.fromLong(i64));
+}
+
+TBinaryProtocol.prototype.writeDouble = function(dub) {
+ this.trans.write(BinaryParser.fromDouble(dub));
+}
+
+TBinaryProtocol.prototype.writeString = function(str) {
+ this.writeI32(str.length);
+ this.trans.write(str);
+}
+
+TBinaryProtocol.prototype.readMessageBegin = function() {
+ var sz = this.readI32();
+ var type, name, seqid;
+
+ if (sz < 0) {
+ var version = sz & VERSION_MASK;
+ if (version != VERSION_1) {
+ console.log("BAD: " + version);
+ throw TProtocolException(BAD_VERSION, "Bad version in readMessageBegin: " + sz);
+ }
+ type = sz & TYPE_MASK;
+ name = this.readString();
+ seqid = this.readI32();
+ } else {
+ if (this.strictRead) {
+ throw TProtocolException(BAD_VERSION, "No protocol version header");
+ }
+ name = this.trans.read(sz);
+ type = this.readByte();
+ seqid = this.readI32();
+ }
+ return {fname: name, mtype: type, rseqid: seqid};
+}
+
+TBinaryProtocol.prototype.readMessageEnd = function() {
+}
+
+TBinaryProtocol.prototype.readStructBegin = function() {
+ return {fname: ''}
+}
+
+TBinaryProtocol.prototype.readStructEnd = function() {
+}
+
+TBinaryProtocol.prototype.readFieldBegin = function() {
+ var type = this.readByte();
+ if (type == Type.STOP) {
+ return {fname: null, ftype: type, fid: 0};
+ }
+ var id = this.readI16();
+ return {fname: null, ftype: type, fid: id};
+}
+
+TBinaryProtocol.prototype.readFieldEnd = function() {
+}
+
+TBinaryProtocol.prototype.readMapBegin = function() {
+ var ktype = this.readByte();
+ var vtype = this.readByte();
+ var size = this.readI32();
+ return {ktype: ktype, vtype: vtype, size: size};
+}
+
+TBinaryProtocol.prototype.readMapEnd = function() {
+}
+
+TBinaryProtocol.prototype.readListBegin = function() {
+ var etype = this.readByte();
+ var size = this.readI32();
+ return {etype: etype, size: size};
+}
+
+TBinaryProtocol.prototype.readListEnd = function() {
+}
+
+TBinaryProtocol.prototype.readSetBegin = function() {
+ var etype = this.readByte();
+ var size = this.readI32();
+ return {etype: etype, size: size};
+}
+
+TBinaryProtocol.prototype.readSetEnd = function() {
+}
+
+TBinaryProtocol.prototype.readBool = function() {
+ var byte = this.readByte();
+ if (byte == 0) {
+ return false;
+ }
+ return true;
+}
+
+TBinaryProtocol.prototype.readByte = function() {
+ var buff = this.trans.read(1);
+ return BinaryParser.toByte(buff);
+}
+
+TBinaryProtocol.prototype.readI16 = function() {
+ var buff = this.trans.read(2);
+ return BinaryParser.toShort(buff);
+}
+
+TBinaryProtocol.prototype.readI32 = function() {
+ var buff = this.trans.read(4);
+ // console.log("read buf: ");
+ // console.log(buff);
+ // console.log("result: " + BinaryParser.toInt(buff));
+ return BinaryParser.toInt(buff);
+}
+
+TBinaryProtocol.prototype.readI64 = function() {
+ var buff = this.trans.read(8);
+ return BinaryParser.toLong(buff);
+}
+
+TBinaryProtocol.prototype.readDouble = function() {
+ var buff = this.trans.read(8);
+ return BinaryParser.toFloat(buff);
+}
+
+TBinaryProtocol.prototype.readBinary = function() {
+ var len = this.readI32();
+ return this.trans.read(len);
+}
+
+TBinaryProtocol.prototype.readString = function() {
+ var r = this.readBinary().toString('binary');
+ // console.log("readString: " + r);
+ return r;
+}
+
+TBinaryProtocol.prototype.getTransport = function() {
+ return this.trans;
+}
+
+TBinaryProtocol.prototype.skip = function(type) {
+ // console.log("skip: " + type);
+ switch (type) {
+ case Type.STOP:
+ return;
+ case Type.BOOL:
+ this.readBool();
+ break;
+ case Type.BYTE:
+ this.readByte();
+ break;
+ case Type.I16:
+ this.readI16();
+ break;
+ case Type.I32:
+ this.readI32();
+ break;
+ case Type.I64:
+ this.readI64();
+ break;
+ case Type.DOUBLE:
+ this.readDouble();
+ break;
+ case Type.STRING:
+ this.readString();
+ break;
+ case Type.STRUCT:
+ this.readStructBegin();
+ while (true) {
+ var r = this.readFieldBegin();
+ if (r.ftype === Type.STOP) {
+ break;
+ }
+ this.skip(r.ftype);
+ this.readFieldEnd();
+ }
+ this.readStructEnd();
+ break;
+ case Type.MAP:
+ var r = this.readMapBegin();
+ for (var i = 0; i < r.size; ++i) {
+ this.skip(r.ktype);
+ this.skip(r.vtype);
+ }
+ this.readMapEnd();
+ break;
+ case Type.SET:
+ var r = this.readSetBegin();
+ for (var i = 0; i < r.size; ++i) {
+ this.skip(r.etype);
+ }
+ this.readSetEnd();
+ break;
+ case Type.LIST:
+ var r = this.readListBegin();
+ for (var i = 0; i < r.size; ++i) {
+ this.skip(r.etype);
+ }
+ this.readListEnd();
+ break;
+ default:
+ throw Error("Invalid type: " + type);
+ }
+}
diff --git a/lib/nodejs/lib/thrift/server.js b/lib/nodejs/lib/thrift/server.js
new file mode 100644
index 0000000..43a7967
--- /dev/null
+++ b/lib/nodejs/lib/thrift/server.js
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var sys = require('sys'),
+ net = require('net');
+
+var BinaryParser = require('./binary_parser').BinaryParser,
+ TMemoryBuffer = require('./transport').TMemoryBuffer,
+ TBinaryProtocol = require('./protocol').TBinaryProtocol,
+ int32FramedReceiver = require('./connection').int32FramedReceiver;
+
+exports.createServer = function(cls, handler) {
+ if (cls.Processor) {
+ cls = cls.Processor;
+ }
+ var processor = new cls(handler);
+
+ return net.createServer(function(stream) {
+ stream.on('data', int32FramedReceiver(function(data) {
+ var input = new TBinaryProtocol(new TMemoryBuffer(data));
+ var output = new TBinaryProtocol(new TMemoryBuffer(undefined, function(buf) {
+ // TODO: optimize this better, allocate one buffer instead of both:
+ var msg = new Buffer(buf.length + 4);
+ BinaryParser.fromInt(buf.length).copy(msg, 0, 0, 4);
+ buf.copy(msg, 4, 0, buf.length);
+ stream.write(msg);
+ }));
+
+ processor.process(input, output);
+ }));
+
+ stream.on('end', function() {
+ stream.end();
+ });
+ });
+}
diff --git a/lib/nodejs/lib/thrift/thrift.js b/lib/nodejs/lib/thrift/thrift.js
new file mode 100644
index 0000000..73f772b
--- /dev/null
+++ b/lib/nodejs/lib/thrift/thrift.js
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var sys = require('sys');
+
+var Type = exports.Type = {
+ STOP: 0,
+ VOID: 1,
+ BOOL: 2,
+ BYTE: 3,
+ I08: 3,
+ DOUBLE: 4,
+ I16: 6,
+ I32: 8,
+ I64: 10,
+ STRING: 11,
+ UTF7: 11,
+ STRUCT: 12,
+ MAP: 13,
+ SET: 14,
+ LIST: 15,
+ UTF8: 16,
+ UTF16: 17,
+}
+
+exports.MessageType = {
+ CALL: 1,
+ REPLY: 2,
+ EXCEPTION: 3,
+ ONEWAY: 4,
+}
+
+var TException = exports.TException = function(message) {
+ Error.call(this, message);
+ this.name = 'TException';
+}
+sys.inherits(TException, Error);
+
+var TApplicationExceptionType = exports.TApplicationExceptionType = {
+ UNKNOWN: 0,
+ UNKNOWN_METHOD: 1,
+ INVALID_MESSAGE_TYPE: 2,
+ WRONG_METHOD_NAME: 3,
+ BAD_SEQUENCE_ID: 4,
+ MISSING_RESULT: 5
+}
+
+var TApplicationException = exports.TApplicationException = function(type, message) {
+ TException.call(this, message);
+ this.type = type || TApplicationExceptionType.UNKNOWN;
+ this.name = 'TApplicationException';
+}
+sys.inherits(TApplicationException, TException);
+
+TApplicationException.prototype.read = function(input) {
+ var ftype
+ var fid
+ var ret = input.readStructBegin('TApplicationException')
+
+ while(1){
+
+ ret = input.readFieldBegin()
+
+ if(ret.ftype == Type.STOP)
+ break
+
+ var fid = ret.fid
+
+ switch(fid){
+ case 1:
+ if( ret.ftype == Type.STRING ){
+ ret = input.readString()
+ this.message = ret.value
+ } else {
+ ret = input.skip(ret.ftype)
+ }
+
+ break
+ case 2:
+ if( ret.ftype == Type.I32 ){
+ ret = input.readI32()
+ this.type = ret.value
+ } else {
+ ret = input.skip(ret.ftype)
+ }
+ break
+
+ default:
+ ret = input.skip(ret.ftype)
+ break
+ }
+ input.readFieldEnd()
+ }
+
+ input.readStructEnd()
+}
+
+TApplicationException.prototype.write = function(output){
+ output.writeStructBegin('TApplicationException');
+
+ if (this.message) {
+ output.writeFieldBegin('message', Type.STRING, 1)
+ output.writeString(this.message)
+ output.writeFieldEnd()
+ }
+
+ if (this.code) {
+ output.writeFieldBegin('type', Type.I32, 2)
+ output.writeI32(this.code)
+ output.writeFieldEnd()
+ }
+
+ output.writeFieldStop()
+ output.writeStructEnd()
+}
diff --git a/lib/nodejs/lib/thrift/transport.js b/lib/nodejs/lib/thrift/transport.js
new file mode 100644
index 0000000..0c10ef0
--- /dev/null
+++ b/lib/nodejs/lib/thrift/transport.js
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+var TMemoryBuffer = exports.TMemoryBuffer = function(buffer, flushCallback) {
+ if (buffer !== undefined) {
+ this.recv_buf = buffer;
+ } else {
+ this.recv_buf = new Buffer(0);
+ }
+ this.recv_buf_sz = this.recv_buf.length;
+ this.send_buf = [];
+ this.rpos = 0;
+ this.flushCallback = flushCallback;
+}
+
+TMemoryBuffer.prototype.isOpen = function() {
+ // TODO
+ return true;
+}
+
+TMemoryBuffer.prototype.open = function() {
+}
+
+TMemoryBuffer.prototype.close = function() {
+}
+
+TMemoryBuffer.prototype.read = function(len) {
+ var avail = this.recv_buf_sz - this.rpos;
+ // console.log("avail: " + avail);
+
+ if(avail == 0)
+ return new Buffer(0);
+
+ var give = len
+
+ if(avail < len) {
+ console.log("asked for: " + len);
+ throw new Error("asked for too much");
+ give = avail
+ }
+
+ // console.log(this.rpos + "," + give);
+ var ret = this.recv_buf.slice(this.rpos,this.rpos + give)
+ this.rpos += give
+ // console.log(ret);
+
+ //clear buf when complete?
+ return ret
+
+}
+
+TMemoryBuffer.prototype.readAll = function() {
+ return this.recv_buf;
+}
+
+TMemoryBuffer.prototype.write = function(buf) {
+ // TODO
+ if (typeof(buf) === "string") {
+ for (var i = 0; i < buf.length; ++i) {
+ this.send_buf.push(buf.charCodeAt(i));
+ }
+ } else {
+ for (var i = 0; i < buf.length; ++i) {
+ this.send_buf.push(buf[i]);
+ }
+ }
+}
+
+TMemoryBuffer.prototype.flush = function() {
+ this.flushCallback(new Buffer(this.send_buf));
+ this.send_buf = [];
+}
diff --git a/lib/nodejs/package.json b/lib/nodejs/package.json
new file mode 100644
index 0000000..e6ebca9
--- /dev/null
+++ b/lib/nodejs/package.json
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+{
+ "name": "thrift",
+ "description": "node-thrift",
+ "version": "0.6.0",
+ "author": "Apache Thrift",
+ "directories" : { "lib" : "./lib/thrift" },
+ "main": "./lib/thrift",
+ "engines": { "node": ">= 0.2.4" },
+}